Files
2026-04-28 15:13:50 +02:00

365 lines
13 KiB
PHP

<?php // phpcs:disable WordPress.Files.FileName.InvalidClassFileName -- Main plugin file; name dictated by WordPress plugin conventions.
/**
* Plugin Name: Complianz - Terms and Conditions
* Plugin URI: https://wordpress.org/plugins/complianz-terms-conditions
* Description: Plugin from Complianz to generate Terms & Conditions for your website.
* Version: 1.3.0
* Requires at least: 5.7
* Requires PHP: 7.4
* Text Domain: complianz-terms-conditions
* Domain Path: /languages
* Author: Complianz
* Author URI: https://complianz.io
*
* @package Complianz_Terms_Conditions
*/
/**
* Main plugin bootstrap file for Complianz Terms & Conditions.
*
* Declares the plugin header, defines the core COMPLIANZ_TC singleton class,
* registers activation hooks, and requires the global functions file. All
* plugin components (admin, wizard, document, REST API, Gutenberg block) are
* loaded and instantiated from here in response to the `plugins_loaded` hook.
*
* @package Complianz_Terms_Conditions
* @author Complianz
* @copyright 2023 Complianz.io
* @license GPL-2.0-or-later
* @link https://complianz.io
*
* @since 1.0.0
*/
/*
Copyright 2023 Complianz.io (email : support@complianz.io)
*/
// Prevent direct file access outside of the WordPress bootstrap.
defined( 'ABSPATH' ) || die( 'you do not have access to this page!' );
// Flag this as the free edition of the plugin (used by premium add-ons to detect edition).
define( 'cmplz_tc_free', true ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase -- Established constant name used across the plugin ecosystem; renaming would break add-ons.
if ( ! function_exists( 'cmplz_tc_activation_check' ) ) {
/**
* Validates minimum environment requirements before the plugin is activated.
*
* Deactivates the plugin and shows a wp_die() notice when the server does not
* meet the minimum PHP or WordPress version requirements. This prevents fatal
* errors from being thrown on sites running older software stacks.
*
* @since 2.1.5
*
* @see deactivate_plugins()
* @see register_activation_hook()
*
* @return void
*/
function cmplz_tc_activation_check() {
if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
deactivate_plugins( plugin_basename( __FILE__ ) );
wp_die( esc_html__( 'Complianz - Terms & Conditions cannot be activated. The plugin requires PHP 7.4 or higher', 'complianz-terms-conditions' ) );
}
global $wp_version;
if ( version_compare( $wp_version, '4.9', '<' ) ) {
deactivate_plugins( plugin_basename( __FILE__ ) );
wp_die( esc_html__( 'Complianz - Terms & Conditions cannot be activated. The plugin requires WordPress 4.9 or higher', 'complianz-terms-conditions' ) );
}
}
// Run the environment check immediately on plugin activation.
register_activation_hook( __FILE__, 'cmplz_tc_activation_check' );
}
// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- Main plugin bootstrap file intentionally combines the plugin class with activation functions.
if ( ! class_exists( 'COMPLIANZ_TC' ) ) {
/**
* Core plugin class — singleton container for all Complianz T&C components.
*
* Manages plugin initialisation by setting up constants, requiring component
* files, registering WordPress hooks, and instantiating the individual
* sub-system objects (config, admin, wizard, document, etc.). Only one
* instance is ever created; use COMPLIANZ_TC::get_instance() to access it.
*
* Admin-only components (review prompt, admin UI, field handling, wizard,
* callback notices) are loaded and instantiated exclusively inside the
* is_admin() branch to avoid unnecessary overhead on the frontend.
*
* @package Complianz_Terms_Conditions
*
* @since 1.0.0
*/
class COMPLIANZ_TC {
/**
* Holds the single instance of this class.
*
* @since 1.0.0
* @access public
* @var COMPLIANZ_TC|null
*/
public static $instance;
/**
* Plugin configuration object (document types, field definitions, regions).
*
* @since 1.0.0
* @access public
* @var cmplz_tc_config
*/
public static $config;
/**
* Review prompt handler (shows the admin "please review us" notice).
*
* @since 1.0.0
* @access public
* @var cmplz_tc_review
*/
public static $review;
/**
* Admin UI controller (settings pages, menus, notices).
*
* @since 1.0.0
* @access public
* @var cmplz_tc_admin
*/
public static $admin;
/**
* Field renderer and option persistence handler.
*
* @since 1.0.0
* @access public
* @var cmplz_tc_field
*/
public static $field;
/**
* Step-by-step setup wizard controller.
*
* @since 1.0.0
* @access public
* @var cmplz_tc_wizard
*/
public static $wizard;
/**
* Placeholder for a future guided tour component (currently unused).
*
* @since 1.0.0
* @access public
* @var mixed
*/
public static $tour;
/**
* Document generator: builds, stores, and exports T&C documents.
*
* @since 1.0.0
* @access public
* @var cmplz_tc_document
*/
public static $document;
/**
* Initialises the plugin by setting up constants, loading files, and instantiating components.
*
* Called once via get_instance(). Admin-only components are conditionally
* loaded to avoid overhead on frontend requests.
*
* @since 1.0.0
* @access private
*/
private function __construct() {
self::setup_constants();
self::includes();
self::hooks();
self::$config = new cmplz_tc_config();
// Only instantiate heavy admin components when in the WordPress admin area.
if ( is_admin() ) {
self::$review = new cmplz_tc_review();
self::$admin = new cmplz_tc_admin();
self::$field = new cmplz_tc_field();
self::$wizard = new cmplz_tc_wizard();
}
// The document object is needed both on the frontend (shortcode) and in admin.
self::$document = new cmplz_tc_document();
}
/**
* Returns the single instance of COMPLIANZ_TC, creating it on first call.
*
* Implements the singleton pattern so that only one instance of the plugin
* class is ever active per request. All callers — including REST-API routes,
* shortcodes, and the PDF download endpoint — access plugin components
* through this method (e.g. COMPLIANZ_TC::$document).
*
* @since 1.0.0
* @access public
*
* @return COMPLIANZ_TC The single plugin instance.
*/
public static function get_instance() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof COMPLIANZ ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Defines all plugin-wide constants used throughout the codebase.
*
* Centralises path, URL, and tuning constants so they are set once and
* available globally. The version constant appends a cache-busting timestamp
* when SCRIPT_DEBUG is enabled, forcing browsers to reload assets during
* active development.
*
* @since 1.0.0
* @access private
*
* @return void
*/
private function setup_constants() {
// Average minutes required to complete a single wizard question (used for time-estimate display).
define( 'CMPLZ_TC_MINUTES_PER_QUESTION', 0.18 );
// Reduced estimate used when the user opts for the quick-setup flow.
define( 'CMPLZ_TC_MINUTES_PER_QUESTION_QUICK', 0.1 );
// Admin menu position for the plugin's top-level menu item.
define( 'CMPLZ_TC_MAIN_MENU_POSITION', 40 );
// Trailing-slash URL to the plugin root directory.
define( 'cmplz_tc_url', plugin_dir_url( __FILE__ ) ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase -- Lowercase constant name; established across codebase and add-ons.
// Trailing-slash filesystem path to the plugin root directory.
define( 'cmplz_tc_path', plugin_dir_path( __FILE__ ) ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase -- Lowercase constant name; established across codebase and add-ons.
// Plugin basename (e.g. complianz-terms-conditions/complianz-terms-conditions.php).
define( 'cmplz_tc_plugin', plugin_basename( __FILE__ ) ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase -- Lowercase constant name; established across codebase and add-ons.
// Absolute filesystem path to this main plugin file.
define( 'cmplz_tc_plugin_file', __FILE__ ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase -- Lowercase constant name; established across codebase and add-ons.
// Append a timestamp in SCRIPT_DEBUG mode to bust browser/CDN asset caches.
$debug = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? time() : '';
define( 'cmplz_tc_version', '1.3.0' . $debug ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase -- Lowercase constant name; established across codebase and add-ons.
}
/**
* Requires all plugin component files in dependency order.
*
* Files are loaded conditionally where appropriate: the Gutenberg block is
* only included when the block editor is active; admin-only files are
* deferred to the is_admin() branch to avoid loading unnecessary code on
* public-facing requests.
*
* @since 1.0.0
* @access private
*
* @return void
*/
private function includes() {
// Document class is required on all requests (shortcode + PDF endpoint).
require_once cmplz_tc_path . 'class-document.php';
// Only load the Gutenberg block integration when the block editor is in use.
if ( cmplz_tc_uses_gutenberg() ) {
require_once plugin_dir_path( __FILE__ ) . 'gutenberg/block.php';
}
// REST API routes are registered on every request so they are always reachable.
require_once plugin_dir_path( __FILE__ ) . 'rest-api/rest-api.php';
// Admin-only files: defer loading to reduce frontend memory footprint.
if ( is_admin() ) {
require_once cmplz_tc_path . '/assets/icons.php';
require_once cmplz_tc_path . 'class-admin.php';
require_once cmplz_tc_path . 'class-review.php';
require_once cmplz_tc_path . 'class-field.php';
require_once cmplz_tc_path . 'class-wizard.php';
require_once cmplz_tc_path . 'callback-notices.php';
}
// Config is required on all requests: it holds document definitions and field maps.
require_once cmplz_tc_path . 'config/class-config.php';
}
/**
* Registers WordPress action and filter hooks for the plugin.
*
* Defers translation loading to the `init` hook to comply with WordPress 6.7+
* requirements (loading translations earlier triggers a _doing_it_wrong() notice
* in WP 6.7 and above).
*
* @since 1.0.0
* @access private
*
* @return void
*/
private function hooks() {
add_action(
'init',
function () {
// Load the plugin's text domain for i18n; deferred to init for WP 6.7 compatibility.
load_plugin_textdomain( 'complianz-terms-conditions' );
}
);
}
}
/**
* Boots the plugin on `plugins_loaded` at priority 9.
*
* Priority 9 (rather than the default 10) ensures the plugin instance is
* available before other plugins that hook at the default priority and may
* depend on COMPLIANZ_TC::$document or other static properties.
*/
add_action(
'plugins_loaded',
function () {
COMPLIANZ_TC::get_instance();
},
9
);
}
/**
* Runs first-time setup tasks when the plugin is activated.
*
* Seeds the `cmplz_generate_pdf_languages` option with the current WordPress
* site locale so a PDF is generated for the active language on first use.
* Also sets a short-lived transient that triggers a redirect to the plugin
* settings page immediately after activation, giving users a smooth onboarding
* experience.
*
* The language seeding is guarded with a get_option() check so it only runs
* once; subsequent activations (e.g. after deactivate/reactivate) do not
* overwrite any languages the user may have added.
*
* @since 1.0.0
*
* @see cmplz_tc_sanitize_language()
* @see register_activation_hook()
*
* @return void
*/
function cmplz_tc_activation() {
// Seed the PDF languages list only on the very first activation.
if ( ! get_option( 'cmplz_generate_pdf_languages' ) ) {
// Build a map of sanitised locale → 1 (enabled) for the site's current locale.
$languages = array( cmplz_tc_sanitize_language( get_locale() ) => 1 );
// Remove any empty keys that result from an unrecognised locale format.
// @phpstan-ignore-next-line -- The value may be an empty string if the locale format is unrecognised.
$languages = array_filter( $languages );
update_option( 'cmplz_generate_pdf_languages', $languages );
}
// Set a transient consumed by the admin redirect handler to forward the user to settings.
set_transient( 'cmplz_tc_redirect_to_settings', true, DAY_IN_SECONDS );
}
register_activation_hook( __FILE__, 'cmplz_tc_activation' );
// Load global helper functions used throughout the plugin.
require_once plugin_dir_path( __FILE__ ) . 'functions.php';