Skip to content

Commit 620ddc5

Browse files
committed
v7.5.1.1
1 parent 1cb5c48 commit 620ddc5

116 files changed

Lines changed: 5932 additions & 820 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

languages/uncanny-automator.pot

Lines changed: 99 additions & 89 deletions
Large diffs are not rendered by default.

readme.txt

Lines changed: 93 additions & 92 deletions
Large diffs are not rendered by default.

src/app/Application/Mcp/Mcp_Client.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,10 @@ protected function create_url_agent_context( string $page_url ): Agent_Context {
13681368
public function get_launcher_html( WP_REST_Request $request ) {
13691369
unset( $request );
13701370

1371-
if ( ! $this->get_uncanny_agent_setting( Admin_Settings_Uncanny_Agent_General::ENABLED_KEY ) ) {
1371+
if (
1372+
! $this->get_uncanny_agent_setting( Admin_Settings_Uncanny_Agent_General::ENABLED_KEY )
1373+
|| ! $this->should_render_surface( 'admin_launcher' )
1374+
) {
13721375
return rest_ensure_response(
13731376
array(
13741377
'html' => '',
Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Page Builder host availability use case.
3+
* Page Builder new-page policy use case.
44
*
55
* @package Uncanny_Automator
66
*/
@@ -9,35 +9,54 @@
99

1010
namespace Uncanny_Automator\App\Application\Page_Builder;
1111

12+
use Uncanny_Automator\App\Feature_State\Application\Get_Feature_State;
13+
use Uncanny_Automator\App\Feature_State\Domain\Feature_State;
14+
1215
/**
13-
* Decides if the host can load Page Builder.
16+
* Decides if users can start new Page Builder pages.
1417
*
1518
* @since 7.5
1619
*/
1720
final class Can_Load_Page_Builder {
1821

1922
/**
20-
* Availability port.
23+
* Feature-state query.
2124
*
22-
* @var Page_Builder_Availability_Port
25+
* @var Get_Feature_State|null
2326
*/
24-
private $availability;
27+
private $feature_state;
2528

2629
/**
2730
* Create the use case.
2831
*
29-
* @param Page_Builder_Availability_Port $availability Availability port.
32+
* The legacy availability port is still accepted so existing integrations do
33+
* not fatal, but it is no longer a policy source.
34+
*
35+
* @param Get_Feature_State|Page_Builder_Availability_Port $feature_state Feature-state query or deprecated availability port.
3036
*/
31-
public function __construct( Page_Builder_Availability_Port $availability ) {
32-
$this->availability = $availability;
37+
public function __construct( $feature_state ) {
38+
if ( $feature_state instanceof Get_Feature_State ) {
39+
$this->feature_state = $feature_state;
40+
return;
41+
}
42+
43+
if ( $feature_state instanceof Page_Builder_Availability_Port ) {
44+
// Retain construction compatibility without treating the obsolete port
45+
// as policy evidence. Composition belongs at the Infrastructure edge.
46+
$this->feature_state = null;
47+
return;
48+
}
49+
50+
throw new \InvalidArgumentException( 'Page Builder requires the Automator feature-state query.' );
3351
}
3452

3553
/**
36-
* Check if Page Builder can load.
54+
* Check if users can start new Page Builder pages.
3755
*
3856
* @return bool
3957
*/
4058
public function execute(): bool {
41-
return $this->availability->is_available();
59+
return $this->feature_state instanceof Get_Feature_State
60+
&& $this->feature_state->execute()->is_visible( Feature_State::PAGE_BUILDER_MENU );
4261
}
4362
}

src/app/Application/Page_Builder/Page_Builder_Availability_Port.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
namespace Uncanny_Automator\App\Application\Page_Builder;
1111

1212
/**
13-
* Supplies the host decision that permits Page Builder.
13+
* Supplies the legacy host decision that permitted Page Builder.
1414
*
1515
* @since 7.5
16+
* @deprecated 7.5.1.1 Use Get_Feature_State with PAGE_BUILDER_MENU.
1617
*/
1718
interface Page_Builder_Availability_Port {
1819

src/app/Application/Services_Bootstrap.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212

1313
namespace Uncanny_Automator\App\Application;
1414

15-
use Uncanny_Automator\App\Application\Page_Builder\Can_Load_Page_Builder;
1615
use Uncanny_Automator\App\Components\Page_Builder\Module;
17-
use Uncanny_Automator\App\Infrastructure\Page_Builder\Page_Builder_Availability;
1816
use Uncanny_Automator\App\Recipe_Builder\Field\Services\Field_Sanitizer_Legacy_Hooks;
1917

2018
/**
@@ -38,7 +36,7 @@ class Services_Bootstrap {
3836
*/
3937
public function init(): void {
4038
$this->register_field_hooks();
41-
( new Module( new Can_Load_Page_Builder( new Page_Builder_Availability() ) ) )->register();
39+
( new Module() )->register();
4240
}
4341

4442
/**

src/app/Components/Page_Builder/Module.php

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,26 @@ class Module {
6363
*/
6464
private $compatibility;
6565

66-
/**
67-
* Page Builder availability use case.
68-
*
69-
* @var Can_Load_Page_Builder
70-
*/
71-
private $can_load_page_builder;
72-
7366
/**
7467
* Constructor.
7568
*
76-
* @param Can_Load_Page_Builder $can_load_page_builder Availability use case.
77-
* @param Compatibility|null $compatibility Compatibility policy.
69+
* The former Can_Load_Page_Builder argument remains accepted for binary
70+
* compatibility, but presentation policy no longer controls runtime boot.
71+
*
72+
* @param Can_Load_Page_Builder|Compatibility|null $legacy_gate_or_compatibility Deprecated gate or compatibility policy.
73+
* @param Compatibility|null $compatibility Compatibility policy.
7874
*/
79-
public function __construct( Can_Load_Page_Builder $can_load_page_builder, ?Compatibility $compatibility = null ) {
80-
$this->can_load_page_builder = $can_load_page_builder;
81-
$this->compatibility = $compatibility ?? new Compatibility();
75+
public function __construct( $legacy_gate_or_compatibility = null, ?Compatibility $compatibility = null ) {
76+
if ( $legacy_gate_or_compatibility instanceof Compatibility ) {
77+
$this->compatibility = $legacy_gate_or_compatibility;
78+
return;
79+
}
80+
81+
if ( null !== $legacy_gate_or_compatibility && ! $legacy_gate_or_compatibility instanceof Can_Load_Page_Builder ) {
82+
throw new \InvalidArgumentException( 'The Page Builder host received an unsupported dependency.' );
83+
}
84+
85+
$this->compatibility = $compatibility ?? new Compatibility();
8286
}
8387

8488
/**
@@ -100,17 +104,17 @@ public function register(): void {
100104
*/
101105
public function boot(): void {
102106
try {
103-
if ( ! $this->can_load_page_builder->execute() ) {
104-
return;
105-
}
106-
107+
// Feature policy controls new-page affordances, never runtime ownership.
108+
// Existing editors and published pages must survive a policy transition.
107109
$compatibility = $this->compatibility->check( $this->environment() );
108110
if ( 'ready' !== $compatibility['status'] ) {
109111
$this->set_status( $compatibility['status'], $compatibility['detail'] );
110112
return;
111113
}
112114

113-
define( 'AUTOMATOR_PAGE_BUILDER_OWNS_RUNTIME', true );
115+
if ( ! defined( 'AUTOMATOR_PAGE_BUILDER_OWNS_RUNTIME' ) ) {
116+
define( 'AUTOMATOR_PAGE_BUILDER_OWNS_RUNTIME', true );
117+
}
114118

115119
if ( ! defined( 'AUTOMATOR_PAGE_BUILDER_MODULE_VERSION' ) ) {
116120
define( 'AUTOMATOR_PAGE_BUILDER_MODULE_VERSION', self::MODULE_VERSION );
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/**
3+
* Get the current Automator feature state.
4+
*
5+
* @package Uncanny_Automator
6+
* @since 7.5.1.1
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace Uncanny_Automator\App\Feature_State\Application;
12+
13+
use Uncanny_Automator\App\Feature_State\Domain\Feature_State;
14+
use Uncanny_Automator\App\Feature_State\Domain\Feature_State_Policy;
15+
use Uncanny_Automator\App\Feature_State\Ports\Policy_State_Port;
16+
17+
/**
18+
* Application query for the request-wide feature visibility decision.
19+
*
20+
* Consumers ask this use case instead of reading licenses themselves. One
21+
* request therefore gets one coherent snapshot across settings, menus, Agent
22+
* presentation, and Setup Wizard, regardless of which consumer asks first.
23+
*/
24+
final class Get_Feature_State {
25+
26+
private Policy_State_Port $policy_states;
27+
private ?Feature_State $memoized_state = null;
28+
29+
/**
30+
* @param Policy_State_Port $policy_states Current policy-state provider.
31+
*/
32+
public function __construct( Policy_State_Port $policy_states ) {
33+
$this->policy_states = $policy_states;
34+
}
35+
36+
/**
37+
* Get a memoized feature state, hiding every feature when resolution fails.
38+
*
39+
* @return Feature_State
40+
*/
41+
public function execute(): Feature_State {
42+
if ( null !== $this->memoized_state ) {
43+
return $this->memoized_state;
44+
}
45+
46+
try {
47+
$this->memoized_state = Feature_State_Policy::evaluate( $this->policy_states->get_state() );
48+
} catch ( \Throwable $error ) {
49+
// Technical unavailability is separate from the 15 business rows. We keep
50+
// it out of Policy_State and give every consumer the same memoized
51+
// all-hidden snapshot, avoiding retries and mixed UI within one request.
52+
unset( $error );
53+
$this->memoized_state = Feature_State::all_hidden();
54+
}
55+
56+
return $this->memoized_state;
57+
}
58+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
/**
3+
* Automator allocation facts used by the feature policy.
4+
*
5+
* @package Uncanny_Automator
6+
* @since 7.5.1.1
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace Uncanny_Automator\App\Feature_State\Domain;
12+
13+
/**
14+
* Reduces a validated allocation ledger to policy-relevant observations.
15+
*/
16+
final class Allocation_Facts {
17+
18+
private int $active_allocations;
19+
private int $used_allocations;
20+
private int $expired_allocations;
21+
private Pending_First_Use_Allocation $pending_first_use_allocation;
22+
23+
/**
24+
* @param int $active_allocations Active allocation rows.
25+
* @param int $used_allocations Fully used allocation rows.
26+
* @param int $expired_allocations Expired allocation rows.
27+
* @param Pending_First_Use_Allocation|null $pending_first_use_allocation Pending grant eligibility.
28+
*/
29+
private function __construct(
30+
int $active_allocations,
31+
int $used_allocations,
32+
int $expired_allocations,
33+
?Pending_First_Use_Allocation $pending_first_use_allocation
34+
) {
35+
if (
36+
$active_allocations < 0
37+
|| $used_allocations < 0
38+
|| $expired_allocations < 0
39+
) {
40+
throw new \InvalidArgumentException( 'The allocation facts contain impossible row counts.' );
41+
}
42+
43+
$this->active_allocations = $active_allocations;
44+
$this->used_allocations = $used_allocations;
45+
$this->expired_allocations = $expired_allocations;
46+
$this->pending_first_use_allocation = $pending_first_use_allocation ?? Pending_First_Use_Allocation::none();
47+
48+
if (
49+
! $this->pending_first_use_allocation->is_none()
50+
&& ( $active_allocations > 0 || $used_allocations > 0 || $expired_allocations > 0 )
51+
) {
52+
throw new \InvalidArgumentException( 'A pending first-use allocation cannot accompany observed allocation rows.' );
53+
}
54+
}
55+
56+
/**
57+
* Create facts observed from one successful allocation response.
58+
*
59+
* @param int $active_allocations Active allocation rows.
60+
* @param int $used_allocations Fully used allocation rows.
61+
* @param int $expired_allocations Expired allocation rows.
62+
* @param Pending_First_Use_Allocation|null $pending_first_use_allocation Pending grant eligibility.
63+
*
64+
* @return self
65+
*/
66+
public static function observed(
67+
int $active_allocations,
68+
int $used_allocations,
69+
int $expired_allocations,
70+
?Pending_First_Use_Allocation $pending_first_use_allocation = null
71+
): self {
72+
return new self(
73+
$active_allocations,
74+
$used_allocations,
75+
$expired_allocations,
76+
$pending_first_use_allocation
77+
);
78+
}
79+
80+
/**
81+
* Get the independently observed pending-grant fact.
82+
*
83+
* @return Pending_First_Use_Allocation
84+
*/
85+
public function pending_first_use_allocation(): Pending_First_Use_Allocation {
86+
return $this->pending_first_use_allocation;
87+
}
88+
89+
/**
90+
* Resolve the allocation vocabulary represented by the observations.
91+
*
92+
* @return Allocation_State
93+
*/
94+
public function state(): Allocation_State {
95+
// MCP accounting classifies every allocation at one observation time.
96+
// Active takes precedence because the account can also contain old history.
97+
if ( $this->active_allocations > 0 ) {
98+
return Allocation_State::valid_allocation();
99+
}
100+
101+
if ( 0 === $this->used_allocations && 0 === $this->expired_allocations ) {
102+
return Allocation_State::never_had_allocation();
103+
}
104+
105+
// Expiry has precedence over full use when the remaining collection has
106+
// both states. Policy_State decides which license types support that state.
107+
if ( $this->expired_allocations > 0 ) {
108+
return Allocation_State::expired_allocation();
109+
}
110+
111+
return Allocation_State::used_100_percent_allocation();
112+
}
113+
}

0 commit comments

Comments
 (0)