Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ and all plausible values, so wherever a string *is* accepted it is the value its
'conflict_policy' => static fn( Sub_Plugin $sub_plugin ) => give_conflict_policy_for( $sub_plugin ),
```

`Conflict_Policy::all()` is every policy string, in a stable order, for a settings control that has
to offer them — see
[ship the absorption over several releases](recipes.md#ship-the-absorption-over-several-releases).
It carries no labels: how each policy is described to a site owner is yours to word.

`standalone_plugin_basename` takes a string only: it names a file already on disk.
`dependency_check` and `activation_callback` have nothing a string could collide with, so they
accept every callable form, a plain function name included.
Expand Down
25 changes: 25 additions & 0 deletions docs/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,31 @@ if the policy comes from a callable reading a value you can move without shippin
An unrecognised value is treated as `NOTICE_ONLY`, never as consent to deactivate, so a stale or
misspelt option cannot turn a plugin off.

The control that writes that option asks `Conflict_Policy::all()` for its choices rather than
listing the constants by hand, so a policy added in a later release reaches your screen without you
editing it. It returns the policy strings and nothing else: what a site owner reads is your wording,
in your textdomain, and a label shipped from here would be neither.

```php
$labels = [
Conflict_Policy::DEFER => __( 'Bundled, dormant on sites running the add-on', 'give' ),
Conflict_Policy::NOTICE_ONLY => __( 'Ask those sites to deactivate it', 'give' ),
Conflict_Policy::DEACTIVATE => __( 'Merge them in', 'give' ),
];

$stage = get_option( 'give_absorption_stage', Conflict_Policy::DEFER );

foreach ( Conflict_Policy::all() as $policy ) {
printf(
'<option value="%s"%s>%s</option>',
esc_attr( $policy ),
selected( $policy, $stage, false ),
// A policy you have not written a label for yet still appears, under its own name.
esc_html( $labels[ $policy ] ?? $policy )
);
}
```

## Defer to a standalone that is a new codebase

This is an edge case, but one that has occurred before: a later version of a standalone that shares
Expand Down
38 changes: 23 additions & 15 deletions src/Conflict_Policy.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@ final class Conflict_Policy {
*/
public const NOTICE_ONLY = 'notice_only';

/**
* Every policy this library understands, in declaration order.
*
* A host staging an absorption puts the policy behind a setting, and the control that writes
* it has to list the policies from somewhere: this, rather than three constants written out
* by hand, so a policy added later reaches that control without the host editing it.
*
* Bare strings, and no labels beside them. What a site owner should be told a policy does is
* the host's wording in the host's textdomain, and a label produced here would be neither —
* which is also why the order is only stable, not meaningful: a screen orders its own options.
*
* @since 1.0.0
*
* @return string[]
*/
public static function all(): array {
return [
self::DEACTIVATE,
self::DEFER,
self::NOTICE_ONLY,
];
}

/**
* The policy that applies when a sub-plugin configures none.
*
Expand Down Expand Up @@ -77,19 +100,4 @@ public static function default(): string {
public static function is_valid( string $policy ): bool {
return in_array( $policy, self::all(), true );
}

/**
* Every policy this library understands.
*
* @since 1.0.0
*
* @return string[]
*/
private static function all(): array {
return [
self::DEACTIVATE,
self::DEFER,
self::NOTICE_ONLY,
];
}
}
25 changes: 25 additions & 0 deletions tests/unit/ConflictPolicyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,31 @@ public function test_no_policy_is_added_or_removed_unnoticed(): void {
);
}

/**
* The list a host builds its own settings control from. Order is pinned because a control
* rendering the list as it is handed would otherwise shuffle its options between releases.
*/
public function test_it_lists_every_policy(): void {
$this->assertSame(
[
Conflict_Policy::DEACTIVATE,
Conflict_Policy::DEFER,
Conflict_Policy::NOTICE_ONLY,
],
Conflict_Policy::all()
);
}

/**
* Cross-checked against the constants, so a fourth policy declared without being added to
* the list fails here rather than going silently missing from every control built on it.
*/
public function test_the_list_holds_every_policy_constant(): void {
$constants = ( new ReflectionClass( Conflict_Policy::class ) )->getConstants();

$this->assertSame( array_values( $constants ), Conflict_Policy::all() );
}

public function test_the_default_is_to_deactivate(): void {
$this->assertSame( Conflict_Policy::DEACTIVATE, Conflict_Policy::default() );
}
Expand Down
Loading