| @@ -5,9 +5,8 @@ | ||
| 5 | 5 | use Better_Payment\Lite\Admin\DB; |
| 6 | 6 | use Better_Payment\Lite\Campaign\CampaignStats; |
| 7 | 7 | use Better_Payment\Lite\Campaign\MetaBox; |
| 8 | 8 | use Better_Payment\Lite\Campaign\Templates\TemplateManager; |
| 9 | -use Better_Payment\Lite\Campaign\Templates\ProTemplatePreviews; | |
| 10 | 9 | use Better_Payment\Lite\Campaign\Support\Money; |
| 11 | 10 | use Better_Payment\Lite\Campaign\Elements\ElementRegistry; |
| 12 | 11 | use Better_Payment\Lite\Campaign\Elements\ProElementPreview; |
| 13 | 12 | |
| @@ -1229,45 +1228,31 @@ | ||
| 1229 | 1228 | * frontend uses; the lightbox scrolls it. A static screenshot cannot do that |
| 1230 | 1229 | * (it is a fixed crop of the top of the page) and drifts from the layout the |
| 1231 | 1230 | * moment a template is redesigned. |
| 1232 | 1231 | * |
| 1233 | - * A locked Pro card (ProTemplateCatalog) carries `columns => []` on purpose — | |
| 1234 | - * the registry never hands a Pro layout to the client, and that is the gate. | |
| 1235 | - * Its preview comes from `ProTemplatePreviews`, a generated mirror read ONLY | |
| 1236 | - * here: the document that goes back is rendered HTML, which cannot be applied. | |
| 1232 | + * Only a template with a layout of its own previews live. A locked Pro card | |
| 1233 | + * (ProTemplateCatalog, free install only) carries `columns => []`, and Lite | |
| 1234 | + * ships no copy of Pro's layouts, so it returns '' and the lightbox shows the | |
| 1235 | + * card's screenshot — the builder does not even request it. With Pro active | |
| 1236 | + * the same key resolves to Pro's real template and renders like any other. | |
| 1237 | 1237 | * Falling through to `default_layout()` instead would advertise Pro's design |
| 1238 | - * as a generic one-column campaign. | |
| 1238 | + * as a generic campaign. | |
| 1239 | 1239 | * |
| 1240 | - * Returns '' — and the client falls back to the screenshot — only when there | |
| 1241 | - * is no layout from either source. | |
| 1242 | - * | |
| 1243 | 1240 | * @param string $key Template key from TemplateManager. |
| 1244 | 1241 | * @return string Full HTML document, or '' when the key is unknown or has no layout. |
| 1245 | 1242 | */ |
| 1246 | 1243 | public static function build_template_preview_document( string $key ): string { |
| 1247 | 1244 | $templates = TemplateManager::get_all(); |
| 1248 | - if ( ! isset( $templates[ $key ] ) ) { | |
| 1245 | + if ( empty( $templates[ $key ]['columns'] ) ) { | |
| 1249 | 1246 | return ''; |
| 1250 | 1247 | } |
| 1251 | 1248 | |
| 1252 | 1249 | $template = $templates[ $key ]; |
| 1253 | - $columns = $template['columns'] ?? []; | |
| 1254 | - $layout = $template['layout'] ?? '1-column'; | |
| 1255 | 1250 | |
| 1256 | - if ( empty( $columns ) ) { | |
| 1257 | - $mirror = ProTemplatePreviews::layout( $key ); | |
| 1258 | - if ( empty( $mirror['columns'] ) ) { | |
| 1259 | - return ''; | |
| 1260 | - } | |
| 1261 | - | |
| 1262 | - $columns = $mirror['columns']; | |
| 1263 | - $layout = $mirror['layout'] ?? $layout; | |
| 1264 | - } | |
| 1265 | - | |
| 1266 | 1251 | return self::build_preview_document( |
| 1267 | 1252 | [ |
| 1268 | - 'layout' => $layout, | |
| 1269 | - 'columns' => $columns, | |
| 1253 | + 'layout' => $template['layout'] ?? '1-column', | |
| 1254 | + 'columns' => $template['columns'], | |
| 1270 | 1255 | ], |
| 1271 | 1256 | self::template_preview_meta( $key, $template ), |
| 1272 | 1257 | 0, |
| 1273 | 1258 | self::template_preview_stats() |
| @@ -1423,9 +1408,9 @@ | ||
| 1423 | 1408 | // hotspot, an element that cannot be selected or removed. |
| 1424 | 1409 | // |
| 1425 | 1410 | // Show a mock preview instead so the element is visible, |
| 1426 | 1411 | // selectable, and demonstrates what Pro would do with the |
| 1427 | - // settings shown (disabled) in the sidebar. | |
| 1412 | + // upgrade banner shown in the sidebar. | |
| 1428 | 1413 | // |
| 1429 | 1414 | // This is the ONLY call site — the public render path has no |
| 1430 | 1415 | // reference to ProElementPreview, and ProElementPreview::render() |
| 1431 | 1416 | // re-checks the preview flag itself. Its data is fabricated; |
| @@ -1470,8 +1455,16 @@ | ||
| 1470 | 1455 | $extra_link = file_exists( BETTER_PAYMENT_ASSETS_PATH . '/css/fundraising-campaign.min.css' ) |
| 1471 | 1456 | ? '<link rel="stylesheet" href="' . esc_url( $fundraising_css ) . '?v=' . $v . '">' . "\n" |
| 1472 | 1457 | : ''; |
| 1473 | 1458 | |
| 1459 | + // Template theme CSS from an add-on — Pro links its themes through | |
| 1460 | + // `better_payment/campaign/preview_stylesheets`. A free install links no | |
| 1461 | + // Pro design. See Campaign\CampaignStyles. | |
| 1462 | + $theme_links = ''; | |
| 1463 | + foreach ( \Better_Payment\Lite\Campaign\CampaignStyles::preview_stylesheets() as $theme_css ) { | |
| 1464 | + $theme_links .= '<link rel="stylesheet" href="' . esc_url( $theme_css ) . '">' . "\n"; | |
| 1465 | + } | |
| 1466 | + | |
| 1474 | 1467 | return '<!DOCTYPE html>' . "\n" |
| 1475 | 1468 | . '<html>' . "\n" |
| 1476 | 1469 | . '<head>' . "\n" |
| 1477 | 1470 | . '<meta charset="utf-8">' . "\n" |
| @@ -1477,8 +1470,9 @@ | ||
| 1477 | 1470 | . '<meta charset="utf-8">' . "\n" |
| 1478 | 1471 | . '<meta name="viewport" content="width=device-width, initial-scale=1">' . "\n" |
| 1479 | 1472 | . '<base href="' . esc_url( home_url( '/' ) ) . '">' . "\n" |
| 1480 | 1473 | . '<link rel="stylesheet" href="' . esc_url( $display_css ) . '?v=' . $v . '">' . "\n" |
| 1474 | + . $theme_links | |
| 1481 | 1475 | . $extra_link |
| 1482 | 1476 | . '<style>' . "\n" |
| 1483 | 1477 | . '*, *::before, *::after { box-sizing: border-box; }' . "\n" |
| 1484 | 1478 | . 'html, body { margin: 0; padding: 0; background: #fff; }' . "\n" |
| @@ -1516,44 +1510,57 @@ | ||
| 1516 | 1510 | . '</html>'; |
| 1517 | 1511 | } |
| 1518 | 1512 | |
| 1519 | 1513 | /** |
| 1514 | + * Per-request memo for pro_locked_types(). A property rather than a | |
| 1515 | + * function-local static so tests can reset it. | |
| 1516 | + * | |
| 1517 | + * @var array<string, array>|null | |
| 1518 | + */ | |
| 1519 | + private static $pro_locked_types_cache = null; | |
| 1520 | + | |
| 1521 | + /** | |
| 1520 | 1522 | * Element types that are Pro-only AND currently locked (Pro inactive). |
| 1521 | 1523 | * |
| 1522 | 1524 | * Returns a map of type => schema (label/icon) for elements that carry the |
| 1523 | 1525 | * `pro` flag in the filtered registry while `better_payment/pro_enabled` is |
| 1524 | - * false. When Pro is active these are overridden by the full schema (no `pro` | |
| 1525 | - * flag) so the map is empty and nothing is locked. Result is cached per | |
| 1526 | - * request. | |
| 1526 | + * false. With Pro active the map is empty and nothing is locked. Result is | |
| 1527 | + * cached per request. | |
| 1527 | 1528 | * |
| 1528 | 1529 | * @return array<string, array> |
| 1529 | 1530 | */ |
| 1530 | 1531 | private static function pro_locked_types(): array { |
| 1531 | - static $cache = null; | |
| 1532 | - | |
| 1533 | - if ( null !== $cache ) { | |
| 1534 | - return $cache; | |
| 1532 | + if ( null !== self::$pro_locked_types_cache ) { | |
| 1533 | + return self::$pro_locked_types_cache; | |
| 1535 | 1534 | } |
| 1536 | 1535 | |
| 1537 | - $cache = []; | |
| 1536 | + self::$pro_locked_types_cache = []; | |
| 1538 | 1537 | |
| 1539 | 1538 | if ( apply_filters( 'better_payment/pro_enabled', false ) ) { |
| 1540 | - return $cache; | |
| 1539 | + return self::$pro_locked_types_cache; | |
| 1541 | 1540 | } |
| 1542 | 1541 | |
| 1543 | 1542 | foreach ( ElementRegistry::get_all() as $type => $schema ) { |
| 1544 | 1543 | if ( ! empty( $schema['pro'] ) ) { |
| 1545 | - $cache[ $type ] = $schema; | |
| 1544 | + self::$pro_locked_types_cache[ $type ] = $schema; | |
| 1546 | 1545 | } |
| 1547 | 1546 | } |
| 1548 | 1547 | |
| 1549 | - return $cache; | |
| 1548 | + return self::$pro_locked_types_cache; | |
| 1550 | 1549 | } |
| 1551 | 1550 | |
| 1552 | 1551 | /** |
| 1553 | - * Editor-only placeholder banner shown in the builder preview for a | |
| 1554 | - * Pro-locked element (a Pro element left over from when Pro was active). | |
| 1555 | - * Never emitted on the public frontend. | |
| 1552 | + * Generic builder-only "PRO" banner for a locked element that has no mock. | |
| 1553 | + * | |
| 1554 | + * Lite's own three Pro elements always get a ProElementPreview mock, so this | |
| 1555 | + * is reached only by some other `pro`-flagged type — a third party's, added | |
| 1556 | + * through `better_payment/campaign_elements`, or a future Pro element whose | |
| 1557 | + * mock has not been written. It is lock chrome, not Pro functionality: it | |
| 1558 | + * renders a label and an upgrade line, nothing of the element itself. Without | |
| 1559 | + * it the element's canvas wrapper would be empty and zero-height, so the canvas | |
| 1560 | + * could place no hotspot and the element could not be selected or deleted. | |
| 1561 | + * Only build_preview_document() calls it, only while Pro is inactive | |
| 1562 | + * (pro_locked_types() is empty otherwise). Never emitted on the public frontend. | |
| 1556 | 1563 | * |
| 1557 | 1564 | * @param array $schema Element schema (label, icon). |
| 1558 | 1565 | * @return string |
| 1559 | 1566 | */ |