Skip to content

Commit b3939c3

Browse files
nathangavinmichalkleinercaddoomneudertinnocraft-automation
authored
Update CNIL demo to use a new settings architecture (#23526)
* WIP PoC * Turn into a plugin that can be activated in plugin administration * Move getters to a separate folder * Limit plugin.setting explode to 2 items to keep possible dots in setting name intact * Add example controller and README * Add hasValue methods to Settings plugin * Add and use `hasSetting` method * Use default hierarchy * poc * saving progress * update * finish PoC with working example * v2 with working example * remove unnecessary additions * remove commented code * ws / cs / cleanup * Reuse existing "getPluginName" lookup * Require interfaces with trait usage * Connect new settings with old settings * Connect compliance settings and dashboard * Add optional template types to settings * rearrange code into different core folders * rename policies folder to policy to match core * hooked new settings up to dashboard * add required imports for phpstan * ws / cs * Remove return type override from API * Connect title/inlinehelp to settings pages * Stub actual feature usage of new "disable visitor log" setting * implement dashboard as full mvp (#23593) * reuse system and measurable traits to save policy status * complete implementing mvp dashboard * update existing tests and add new unit tests (#23594) * fix vue issue * update existing system tests * fix vue * fix missing test files * Build vue files * update broken UI tests * update policymanager to handle more generic functions * begin adding new unit tests * update unit tests to work correctly * fix return type issue and also fix some mocking * PHPCS * more PHPCS * simplify some code to make the tests run better * phpcs * add unit tests for some new setting code * cover extra function in policyComparisonTrait --------- Co-authored-by: innocraft-automation <innocraft-automation@users.noreply.github.com> * undo some example code * more undoing * add translations, remove example code * fix visitorlog isCompliant condition * fix merging issue * phpcs * Build vue files * update broken test * update broken tests * update broken tests * fix broken UI tests * add extra translations * abstract null checks in compareStrictness function * PHPCS * new PHPCS comma changes * update UI test images * Fix UI test selector * Update UI test screenshot --------- Co-authored-by: Michal Kleiner <michal@innocraft.com> Co-authored-by: Matt <1169490+caddoo@users.noreply.github.com> Co-authored-by: Marc Neudert <marc@innocraft.com> Co-authored-by: innocraft-automation <innocraft-automation@users.noreply.github.com>
1 parent be81d28 commit b3939c3

53 files changed

Lines changed: 1840 additions & 148 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.

core/Plugin/Manager.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,12 @@ public function findComponents($componentName, $expectedSubclass)
614614
return $components;
615615
}
616616

617+
/**
618+
* @template T of object
619+
* @param string $directoryWithinPlugin
620+
* @param class-string<T> $expectedSubclass
621+
* @return array<class-string<T>>
622+
*/
617623
public function findMultipleComponents($directoryWithinPlugin, $expectedSubclass)
618624
{
619625
$plugins = $this->getPluginsLoadedAndActivated();

core/Policy/CnilPolicy.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Piwik\Policy;
4+
5+
use Piwik\Piwik;
6+
7+
class CnilPolicy extends CompliancePolicy
8+
{
9+
public static function getName(): string
10+
{
11+
return 'cnil_v1';
12+
}
13+
14+
public static function getDescription(): string
15+
{
16+
return Piwik::translate('General_ComplianceCNILDescription');
17+
}
18+
19+
public static function getTitle(): string
20+
{
21+
return Piwik::translate('General_ComplianceCNILTitle');
22+
}
23+
24+
protected static function getMinimumRequiredPlugins(): array
25+
{
26+
return [
27+
'PrivacyManager',
28+
'Live',
29+
'WebsiteMeasurable',
30+
];
31+
}
32+
}

core/Policy/CompliancePolicy.php

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
namespace Piwik\Policy;
4+
5+
use Exception;
6+
use Piwik\Plugin\Manager;
7+
use Piwik\Settings\FieldConfig;
8+
use Piwik\Settings\Interfaces\MeasurableSettingInterface;
9+
use Piwik\Settings\Interfaces\SystemSettingInterface;
10+
use Piwik\Settings\Interfaces\Traits\Setters\MeasurableSetterTrait;
11+
use Piwik\Settings\Interfaces\Traits\Setters\SystemSetterTrait;
12+
13+
/**
14+
* @implements SystemSettingInterface<bool>
15+
* @implements MeasurableSettingInterface<bool>
16+
*/
17+
abstract class CompliancePolicy implements SystemSettingInterface, MeasurableSettingInterface
18+
{
19+
/**
20+
* @use SystemSetterTrait<bool>
21+
*/
22+
use SystemSetterTrait;
23+
24+
/**
25+
* @use MeasurableSetterTrait<bool>
26+
*/
27+
use MeasurableSetterTrait;
28+
29+
abstract public static function getName(): string;
30+
abstract public static function getDescription(): string;
31+
abstract public static function getTitle(): string;
32+
33+
/**
34+
* @return array<string> of plugin names that are required for this policy to function
35+
*/
36+
abstract protected static function getMinimumRequiredPlugins(): array;
37+
38+
/**
39+
* @return array<string, string>
40+
*/
41+
public static function getDetails(): array
42+
{
43+
return [
44+
'id' => static::getName(),
45+
'title' => static::getTitle(),
46+
'description' => static::getDescription(),
47+
];
48+
}
49+
50+
/**
51+
* @throws \Exception when required plugins are not active
52+
*/
53+
protected static function checkRequiredPluginsActive(): void
54+
{
55+
$plugins = static::getMinimumRequiredPlugins();
56+
$pluginManager = static::getPluginManagerInstance();
57+
58+
foreach ($plugins as $plugin) {
59+
if (!$pluginManager->isPluginActivated($plugin)) {
60+
throw new Exception("Plugin $plugin is not activated");
61+
}
62+
}
63+
}
64+
65+
protected static function getPluginManagerInstance(): Manager
66+
{
67+
return Manager::getInstance();
68+
}
69+
70+
protected static function getSystemDefaultValue()
71+
{
72+
return false;
73+
}
74+
75+
protected static function getSystemName(): string
76+
{
77+
return preg_replace('/\s+/', '', static::getName()) . '_policy_enabled';
78+
}
79+
80+
protected static function getSystemType(): string
81+
{
82+
return FieldConfig::TYPE_BOOL;
83+
}
84+
85+
protected static function getMeasurableDefaultValue()
86+
{
87+
return false;
88+
}
89+
90+
protected static function getMeasurableName(): string
91+
{
92+
return preg_replace('/\s+/', '', static::getName()) . '_policy_enabled';
93+
}
94+
95+
protected static function getMeasurableType(): string
96+
{
97+
return FieldConfig::TYPE_BOOL;
98+
}
99+
100+
/**
101+
* If the policy is active at the instance level,
102+
* disabling the policy for a site will also disable it
103+
* for the instance.
104+
*/
105+
public static function setActiveStatus(?int $idSite, bool $isActive): void
106+
{
107+
if (isset($idSite)) {
108+
static::setMeasurableValue($idSite, $isActive);
109+
if (static::getSystemValue() && !$isActive) {
110+
static::setSystemValue($isActive);
111+
}
112+
return;
113+
}
114+
static::setSystemValue($isActive);
115+
}
116+
117+
/**
118+
* If the policy is active at the instance level, then
119+
* this function will return true for all sites.
120+
*/
121+
public static function isActive(?int $idSite): bool
122+
{
123+
try {
124+
self::checkRequiredPluginsActive();
125+
} catch (Exception $e) {
126+
return false;
127+
}
128+
129+
$instanceLevel = static::getSystemValue();
130+
if (!$instanceLevel && isset($idSite)) {
131+
return static::getMeasurableValue($idSite);
132+
}
133+
return $instanceLevel;
134+
}
135+
}

core/Policy/PolicyManager.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
namespace Piwik\Policy;
4+
5+
use Exception;
6+
use Piwik\Plugin\Manager;
7+
use Piwik\Settings\Interfaces\PolicyComparisonInterface;
8+
use Piwik\Settings\Interfaces\SettingValueInterface;
9+
10+
class PolicyManager
11+
{
12+
/**
13+
* @return array<class-string<CompliancePolicy>>
14+
*/
15+
public static function getAllPolicies(): array
16+
{
17+
return [
18+
CnilPolicy::class,
19+
];
20+
}
21+
22+
/**
23+
* @return array<array<string, string>>
24+
*/
25+
public static function getAllPoliciesDetails(): array
26+
{
27+
$policies = static::getAllPolicies();
28+
return array_map(function ($policyClass) {
29+
return $policyClass::getDetails();
30+
}, $policies);
31+
}
32+
33+
/**
34+
* @return class-string<CompliancePolicy>|null
35+
*/
36+
public static function getPolicyByName(string $policyName): ?string
37+
{
38+
$policies = static::getAllPolicies();
39+
foreach ($policies as $policyClass) {
40+
if ($policyName === $policyClass::getName()) {
41+
return $policyClass;
42+
}
43+
}
44+
45+
return null;
46+
}
47+
48+
/**
49+
* @return array<class-string<PolicyComparisonInterface<mixed>&SettingValueInterface<mixed>>>
50+
*/
51+
public static function getAllSettings(?int $idSite = null): array
52+
{
53+
$settings = Manager::getInstance()->findMultipleComponents('Settings', SettingValueInterface::class);
54+
$underPolicy = [];
55+
56+
foreach ($settings as $setting) {
57+
if (!is_a($setting, PolicyComparisonInterface::class, true)) {
58+
continue;
59+
}
60+
61+
$underPolicy[] = $setting;
62+
}
63+
64+
return $underPolicy;
65+
}
66+
67+
/**
68+
* @param class-string<CompliancePolicy> $policyClass
69+
* @return array<class-string<PolicyComparisonInterface<mixed>&SettingValueInterface<mixed>>>
70+
*/
71+
public static function getAllControlledSettings(string $policyClass, ?int $idSite = null): array
72+
{
73+
$settings = static::getAllSettings($idSite);
74+
$underPolicy = [];
75+
76+
foreach ($settings as $setting) {
77+
if (!$setting::isControlledBySpecificPolicy($policyClass, $idSite)) {
78+
continue;
79+
}
80+
81+
$underPolicy[] = $setting;
82+
}
83+
84+
return $underPolicy;
85+
}
86+
87+
/**
88+
* @param class-string<CompliancePolicy> $policyClass
89+
* @throws \Exception when $policyClass is not a valid policy
90+
*/
91+
public static function isPolicyActive(string $policyClass, ?int $idSite = null): bool
92+
{
93+
if (!is_a($policyClass, CompliancePolicy::class, true)) {
94+
throw new Exception('Invalid compliance policy.');
95+
}
96+
return $policyClass::isActive($idSite);
97+
}
98+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Piwik\Settings\Interfaces;
4+
5+
/**
6+
* @template T of mixed
7+
*/
8+
interface ConfigSettingInterface
9+
{
10+
/**
11+
* @return T
12+
*/
13+
public static function getConfigValue();
14+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Piwik\Settings\Interfaces;
4+
5+
use Piwik\Settings\Measurable\MeasurableProperty;
6+
use Piwik\Settings\Measurable\MeasurableSetting;
7+
8+
/**
9+
* @template T of mixed
10+
*/
11+
interface MeasurableSettingInterface
12+
{
13+
/**
14+
* @return MeasurableSetting|MeasurableProperty
15+
*/
16+
public static function getMeasurableSetting(int $idSite);
17+
18+
/**
19+
* @return T
20+
*/
21+
public static function getMeasurableValue(int $idSite);
22+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Piwik\Settings\Interfaces;
4+
5+
interface OptionSettingInterface
6+
{
7+
/**
8+
* @return string|false
9+
*/
10+
public static function getOptionValue();
11+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Piwik\Settings\Interfaces;
4+
5+
use Piwik\Policy\CompliancePolicy;
6+
7+
/**
8+
* @template T of mixed
9+
*/
10+
interface PolicyComparisonInterface
11+
{
12+
/**
13+
* @return array<class-string<CompliancePolicy>, T>
14+
*/
15+
public static function getPolicyRequirements(): array;
16+
17+
/**
18+
* @return array<class-string<CompliancePolicy>, T|null>
19+
*/
20+
public static function getPolicyRequiredValues(?int $idSite = null): array;
21+
22+
public static function isCompliant(string $policy, ?int $idSite = null): bool;
23+
24+
public static function isControlledBySpecificPolicy(string $policy, ?int $idSite = null): bool;
25+
26+
public static function getComplianceRequirementNote(?int $idSite = null): string;
27+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Piwik\Settings\Interfaces;
4+
5+
/**
6+
* @template T of mixed
7+
*/
8+
interface SettingValueInterface
9+
{
10+
/**
11+
* @return self<T>
12+
*/
13+
public static function getInstance(?int $idSite = null);
14+
15+
/**
16+
* @return T
17+
*/
18+
public function getValue();
19+
20+
public static function getTitle(): string;
21+
22+
public static function getInlineHelp(): string;
23+
}

0 commit comments

Comments
 (0)