| 1 |
<?php |
| 2 |
/** |
| 3 |
* Cookie Policy API class. |
| 4 |
* |
| 5 |
* Handles REST API endpoint for auto-generating cookie policy pages. |
| 6 |
* |
| 7 |
* @package SureCookie\Inc\Modules\CookiePolicy |
| 8 |
* @since 0.0.0-alpha.1 |
| 9 |
*/ |
| 10 |
|
| 11 |
namespace SureCookie\Inc\Modules\CookiePolicy; |
| 12 |
|
| 13 |
use SureCookie\Inc\API\Base; |
| 14 |
use SureCookie\Inc\Functions\SendJson; |
| 15 |
use SureCookie\Inc\Functions\Settings; |
| 16 |
use SureCookie\Inc\Modules\PrivacyPolicy\Validator; |
| 17 |
use SureCookie\Inc\Traits\GetInstance; |
| 18 |
use WP_REST_Request; |
| 19 |
use WP_REST_Server; |
| 20 |
|
| 21 |
if ( ! defined( 'ABSPATH' ) ) { |
| 22 |
exit; // Exit if accessed directly. |
| 23 |
} |
| 24 |
|
| 25 |
/** |
| 26 |
* Class Api |
| 27 |
* |
| 28 |
* @package SureCookie\Inc\Modules\CookiePolicy |
| 29 |
* @since 0.0.0-alpha.1 |
| 30 |
*/ |
| 31 |
class Api extends Base { |
| 32 |
use GetInstance; |
| 33 |
|
| 34 |
/** |
| 35 |
* Route for generating a cookie policy page. |
| 36 |
*/ |
| 37 |
protected const GENERATE_PAGE = '/cookie-policy/generate-page'; |
| 38 |
|
| 39 |
/** |
| 40 |
* Register API routes. |
| 41 |
* |
| 42 |
* @since 0.0.0-alpha.1 |
| 43 |
* @return void |
| 44 |
*/ |
| 45 |
public function register_routes(): void { |
| 46 |
register_rest_route( |
| 47 |
$this->get_api_namespace(), |
| 48 |
self::GENERATE_PAGE, |
| 49 |
[ |
| 50 |
'methods' => WP_REST_Server::CREATABLE, |
| 51 |
'callback' => [ $this, 'generate_page' ], |
| 52 |
'permission_callback' => [ $this, 'validate_permission' ], |
| 53 |
] |
| 54 |
); |
| 55 |
} |
| 56 |
|
| 57 |
/** |
| 58 |
* Generate a cookie policy page. |
| 59 |
* |
| 60 |
* Creates a published WordPress page containing the cookie policy shortcode. |
| 61 |
* If a valid published page already exists, returns its details instead. |
| 62 |
* |
| 63 |
* @param WP_REST_Request $request REST request object. |
| 64 |
* @return void |
| 65 |
* @since 0.0.0-alpha.1 |
| 66 |
*/ |
| 67 |
public function generate_page( WP_REST_Request $request ): void { |
| 68 |
// Check if a valid cookie policy page already exists. |
| 69 |
$existing_id = absint( Settings::get( 'cookie_policy_page_id' ) ); |
| 70 |
|
| 71 |
if ( $existing_id > 0 ) { |
| 72 |
$page = get_post( $existing_id ); |
| 73 |
|
| 74 |
if ( $page instanceof \WP_Post && $page->post_status === 'publish' ) { |
| 75 |
SendJson::success( |
| 76 |
[ |
| 77 |
'message' => __( 'Cookie Policy page already exists.', 'surecookie' ), |
| 78 |
'page_id' => $page->ID, |
| 79 |
'edit_url' => get_edit_post_link( $page->ID, 'raw' ), |
| 80 |
'view_url' => get_permalink( $page->ID ), |
| 81 |
'already_exists' => true, |
| 82 |
] |
| 83 |
); |
| 84 |
return; // wp_send_json exits, but return for clarity. |
| 85 |
} |
| 86 |
} |
| 87 |
|
| 88 |
// Create a new cookie policy page. |
| 89 |
$page_id = wp_insert_post( |
| 90 |
[ |
| 91 |
'post_title' => __( 'Cookie Policy', 'surecookie' ), |
| 92 |
'post_content' => self::get_default_page_content(), |
| 93 |
'post_status' => 'publish', |
| 94 |
'post_type' => 'page', |
| 95 |
], |
| 96 |
true |
| 97 |
); |
| 98 |
|
| 99 |
if ( is_wp_error( $page_id ) ) { |
| 100 |
SendJson::error( |
| 101 |
[ |
| 102 |
'message' => $page_id->get_error_message(), |
| 103 |
] |
| 104 |
); |
| 105 |
return; |
| 106 |
} |
| 107 |
|
| 108 |
// Save the new page ID to plugin settings. |
| 109 |
Settings::update( 'cookie_policy_page_id', $page_id ); |
| 110 |
|
| 111 |
SendJson::success( |
| 112 |
[ |
| 113 |
'message' => __( 'Cookie Policy page created successfully.', 'surecookie' ), |
| 114 |
'page_id' => $page_id, |
| 115 |
'edit_url' => get_edit_post_link( $page_id, 'raw' ), |
| 116 |
'view_url' => get_permalink( $page_id ), |
| 117 |
'already_exists' => false, |
| 118 |
] |
| 119 |
); |
| 120 |
} |
| 121 |
|
| 122 |
/** |
| 123 |
* Build the default page content with comprehensive cookie policy sections. |
| 124 |
* |
| 125 |
* The content is inserted as native WordPress blocks so that end-users |
| 126 |
* can freely edit every section in the block editor. The shortcode block |
| 127 |
* renders the dynamic cookie tables, table of contents, and last-updated |
| 128 |
* date. |
| 129 |
* |
| 130 |
* @since 0.0.0-alpha.1 |
| 131 |
* @return string Block-editor-ready post content. |
| 132 |
*/ |
| 133 |
private static function get_default_page_content(): string { |
| 134 |
$site_name = get_bloginfo( 'name' ); |
| 135 |
$sections = self::get_page_sections( $site_name ); |
| 136 |
$content = ''; |
| 137 |
|
| 138 |
foreach ( $sections as $section ) { |
| 139 |
if ( $section['type'] === 'shortcode' ) { |
| 140 |
$content .= '<!-- wp:shortcode -->' . "\n"; |
| 141 |
$content .= '[surecookie_cookie_policy_content]' . "\n"; |
| 142 |
$content .= '<!-- /wp:shortcode -->' . "\n\n"; |
| 143 |
continue; |
| 144 |
} |
| 145 |
|
| 146 |
if ( $section['type'] === 'heading' ) { |
| 147 |
$level = $section['level'] ?? 2; |
| 148 |
$tag = 'h' . $level; |
| 149 |
$content .= '<!-- wp:heading {"level":' . $level . '} -->' . "\n"; |
| 150 |
$content .= '<' . $tag . '>' . esc_html( $section['text'] ?? '' ) . '</' . $tag . '>' . "\n"; |
| 151 |
$content .= '<!-- /wp:heading -->' . "\n\n"; |
| 152 |
continue; |
| 153 |
} |
| 154 |
|
| 155 |
if ( $section['type'] === 'paragraph' ) { |
| 156 |
$content .= '<!-- wp:paragraph -->' . "\n"; |
| 157 |
$content .= '<p>' . esc_html( $section['text'] ?? '' ) . '</p>' . "\n"; |
| 158 |
$content .= '<!-- /wp:paragraph -->' . "\n\n"; |
| 159 |
} |
| 160 |
} |
| 161 |
|
| 162 |
return $content; |
| 163 |
} |
| 164 |
|
| 165 |
/** |
| 166 |
* Get the structured sections for the cookie policy page. |
| 167 |
* |
| 168 |
* Each section is either a heading, paragraph, or shortcode placeholder. |
| 169 |
* All user-facing strings are translatable. |
| 170 |
* |
| 171 |
* @param string $site_name The site name for dynamic text. |
| 172 |
* @since 0.0.0-alpha.1 |
| 173 |
* @return array<int, array{type: string, text?: string, level?: int}> Ordered sections. |
| 174 |
*/ |
| 175 |
private static function get_page_sections( string $site_name ): array { |
| 176 |
$sections = []; |
| 177 |
|
| 178 |
// --- Introduction --- |
| 179 |
$sections[] = [ |
| 180 |
'type' => 'paragraph', |
| 181 |
'text' => sprintf( |
| 182 |
/* translators: %s: site name. */ |
| 183 |
__( 'This Cookie Policy explains how %s uses cookies and similar tracking technologies when you visit our website. It describes what these technologies are, why we use them, and your rights to control our use of them.', 'surecookie' ), |
| 184 |
$site_name |
| 185 |
), |
| 186 |
]; |
| 187 |
|
| 188 |
// --- What Are Cookies --- |
| 189 |
$sections[] = [ |
| 190 |
'type' => 'heading', |
| 191 |
'level' => 2, |
| 192 |
'text' => __( 'What Are Cookies', 'surecookie' ), |
| 193 |
]; |
| 194 |
$sections[] = [ |
| 195 |
'type' => 'paragraph', |
| 196 |
'text' => __( 'Cookies are small text files that are stored on your computer or mobile device when you visit a website. They are widely used to make websites work more efficiently, provide a better browsing experience, and supply information to the owners of the site. Cookies can be "persistent" or "session" cookies. Persistent cookies remain on your device for a set period or until you delete them, while session cookies are deleted as soon as you close your web browser.', 'surecookie' ), |
| 197 |
]; |
| 198 |
$sections[] = [ |
| 199 |
'type' => 'paragraph', |
| 200 |
'text' => __( 'In addition to cookies, we may also use similar technologies such as pixel tags, web beacons, and local storage to collect and store information. These technologies work in a similar way to cookies and allow us to monitor and improve our website and your experience of it.', 'surecookie' ), |
| 201 |
]; |
| 202 |
|
| 203 |
// --- Types of Cookies We Use --- |
| 204 |
$sections[] = [ |
| 205 |
'type' => 'heading', |
| 206 |
'level' => 2, |
| 207 |
'text' => __( 'Types of Cookies We Use', 'surecookie' ), |
| 208 |
]; |
| 209 |
$sections[] = [ |
| 210 |
'type' => 'paragraph', |
| 211 |
'text' => __( 'We use different types of cookies for various reasons. The table below provides detailed information about the cookies we use, organized by category, including the name of each cookie, its purpose, duration, and the domain it belongs to. Some cookies are essential for the website to function properly, while others help us understand how visitors interact with our site, remember your preferences, or deliver relevant advertising.', 'surecookie' ), |
| 212 |
]; |
| 213 |
|
| 214 |
// --- Dynamic shortcode --- |
| 215 |
$sections[] = [ 'type' => 'shortcode' ]; |
| 216 |
|
| 217 |
// --- How to Manage Preferences --- |
| 218 |
$sections[] = [ |
| 219 |
'type' => 'heading', |
| 220 |
'level' => 2, |
| 221 |
'text' => __( 'How to Manage Your Cookie Preferences', 'surecookie' ), |
| 222 |
]; |
| 223 |
$sections[] = [ |
| 224 |
'type' => 'paragraph', |
| 225 |
'text' => __( 'When you first visit our website, you will be shown a cookie consent banner that allows you to accept or decline non-essential cookies. You can change your preferences at any time by clicking the cookie preferences option available on our website. Essential cookies cannot be disabled as they are necessary for the basic functioning of the website.', 'surecookie' ), |
| 226 |
]; |
| 227 |
$sections[] = [ |
| 228 |
'type' => 'paragraph', |
| 229 |
'text' => __( 'Please note that restricting cookies may impact your experience of the website, as some features and services may not function properly without certain cookies enabled.', 'surecookie' ), |
| 230 |
]; |
| 231 |
|
| 232 |
// --- Browser Control --- |
| 233 |
$sections[] = [ |
| 234 |
'type' => 'heading', |
| 235 |
'level' => 2, |
| 236 |
'text' => __( 'How to Control Cookies in Your Browser', 'surecookie' ), |
| 237 |
]; |
| 238 |
$sections[] = [ |
| 239 |
'type' => 'paragraph', |
| 240 |
'text' => __( 'Most web browsers allow you to manage your cookie preferences through their settings. You can set your browser to refuse cookies, delete existing cookies, or alert you when a cookie is being set. The steps to manage cookies vary by browser. Below are general instructions for the most common browsers.', 'surecookie' ), |
| 241 |
]; |
| 242 |
$sections[] = [ |
| 243 |
'type' => 'paragraph', |
| 244 |
'text' => __( 'Google Chrome: Open Settings, go to Privacy and Security, then click on Cookies and other site data. From there, you can block third-party cookies, clear cookies when you close the browser, or block all cookies.', 'surecookie' ), |
| 245 |
]; |
| 246 |
$sections[] = [ |
| 247 |
'type' => 'paragraph', |
| 248 |
'text' => __( 'Mozilla Firefox: Open Settings, go to Privacy and Security, and under Cookies and Site Data you can manage how Firefox handles cookies including clearing data and managing exceptions.', 'surecookie' ), |
| 249 |
]; |
| 250 |
$sections[] = [ |
| 251 |
'type' => 'paragraph', |
| 252 |
'text' => __( 'Safari: Open Preferences, go to the Privacy tab, and configure your cookie blocking preferences. Safari also offers an option to prevent cross-site tracking.', 'surecookie' ), |
| 253 |
]; |
| 254 |
$sections[] = [ |
| 255 |
'type' => 'paragraph', |
| 256 |
'text' => __( 'Microsoft Edge: Open Settings, go to Privacy, Search, and Services. Under the Cookies section, you can choose to block third-party cookies or all cookies and manage cookie exceptions.', 'surecookie' ), |
| 257 |
]; |
| 258 |
|
| 259 |
// --- Consequences of Disabling Cookies --- |
| 260 |
$sections[] = [ |
| 261 |
'type' => 'heading', |
| 262 |
'level' => 2, |
| 263 |
'text' => __( 'Consequences of Disabling Cookies', 'surecookie' ), |
| 264 |
]; |
| 265 |
$sections[] = [ |
| 266 |
'type' => 'paragraph', |
| 267 |
'text' => __( 'If you choose to disable or decline cookies, some parts of this website may not function as intended. For example, you may not be able to log in, your preferences may not be saved between visits, and certain interactive features may be limited. Disabling essential cookies may make it impossible to use some or all of the services provided through this website.', 'surecookie' ), |
| 268 |
]; |
| 269 |
$sections[] = [ |
| 270 |
'type' => 'paragraph', |
| 271 |
'text' => __( 'Disabling analytics or marketing cookies will not affect the core functionality of the website, but it may limit our ability to improve the website based on user behavior or to provide you with personalized content and advertisements.', 'surecookie' ), |
| 272 |
]; |
| 273 |
|
| 274 |
// --- Changes to This Cookie Policy --- |
| 275 |
$sections[] = [ |
| 276 |
'type' => 'heading', |
| 277 |
'level' => 2, |
| 278 |
'text' => __( 'Changes to This Cookie Policy', 'surecookie' ), |
| 279 |
]; |
| 280 |
$sections[] = [ |
| 281 |
'type' => 'paragraph', |
| 282 |
'text' => __( 'We may update this Cookie Policy from time to time to reflect changes in technology, legislation, our business operations, or any other reason we determine is necessary or appropriate. Any changes will be posted on this page with an updated revision date. We encourage you to review this Cookie Policy periodically to stay informed about how we are using cookies.', 'surecookie' ), |
| 283 |
]; |
| 284 |
|
| 285 |
// --- Contact Us --- |
| 286 |
// Emitted only when there is something real to print. Generation runs |
| 287 |
// once and never rewrites the page, so a contact block built from empty |
| 288 |
// Business Details would be a permanently blank section on a live page. |
| 289 |
if ( self::has_contact_details() ) { |
| 290 |
$sections[] = [ |
| 291 |
'type' => 'heading', |
| 292 |
'level' => 2, |
| 293 |
'text' => __( 'Contact Us', 'surecookie' ), |
| 294 |
]; |
| 295 |
$sections[] = [ |
| 296 |
'type' => 'paragraph', |
| 297 |
'text' => __( 'If you have any questions or concerns about this Cookie Policy or our use of cookies, please contact us at:', 'surecookie' ), |
| 298 |
]; |
| 299 |
// Shortcodes rather than literal placeholders, so the page follows |
| 300 |
// Business Details instead of freezing whatever was true on the day |
| 301 |
// it was generated. |
| 302 |
$sections[] = [ |
| 303 |
'type' => 'paragraph', |
| 304 |
'text' => '[surecookie_company_name], [surecookie_address], [surecookie_contact_email]', |
| 305 |
]; |
| 306 |
} |
| 307 |
|
| 308 |
return $sections; |
| 309 |
} |
| 310 |
|
| 311 |
/** |
| 312 |
* Whether Business Details carry enough to print a contact block. |
| 313 |
* |
| 314 |
* @since 1.5.0 |
| 315 |
* @return bool |
| 316 |
*/ |
| 317 |
private static function has_contact_details(): bool { |
| 318 |
$details = Validator::details(); |
| 319 |
|
| 320 |
return trim( (string) ( $details['legal_entity_name'] ?? '' ) ) !== '' |
| 321 |
&& is_email( trim( (string) ( $details['privacy_contact_email'] ?? '' ) ) ); |
| 322 |
} |
| 323 |
} |
| 324 |
|