Skip to content

Commit 24de6cb

Browse files
committed
Merge branch 'civix-upgrade'
[#55] Upgrade civix-generated code to civix format version 25.01.1
2 parents 8de36cb + de35952 commit 24de6cb

13 files changed

+996
-233
lines changed

info.xml

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@
1414
<url desc="Support">https://github.com/systopia/de.systopia.moregreetings/issues</url>
1515
<url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url>
1616
</urls>
17-
<releaseDate></releaseDate>
17+
<releaseDate/>
1818
<version>1.3-dev</version>
1919
<develStage>dev</develStage>
2020
<compatibility>
21-
<ver>5.38</ver>
21+
<ver>5.75</ver>
2222
</compatibility>
2323
<comments/>
2424
<civix>
2525
<namespace>CRM/Moregreetings</namespace>
26-
<format>23.02.1</format>
26+
<format>25.01.1</format>
2727
</civix>
2828
<mixins>
2929
<mixin>[email protected]</mixin>
3030
<mixin>[email protected]</mixin>
31-
<mixin>smarty-v2@1.0.0</mixin>
31+
<mixin>[email protected].3</mixin>
3232
</mixins>
3333
<classloader>
3434
<psr0 prefix="CRM_" path="."/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
namespace CiviMix\Schema;
3+
4+
\pathload()->activatePackage('civimix-schema@5', __DIR__, [
5+
'reloadable' => TRUE,
6+
// The civimix-schema library specifically supports installation processes. From a
7+
// bootstrap/service-availability POV, this is a rough environment which leads to
8+
// the "Multi-Activation Issue" and "Multi-Download Issue". To adapt to them,
9+
// civimix-schema follows "Reloadable Library" patterns.
10+
// More information: https://github.com/totten/pathload-poc/blob/master/doc/issues.md
11+
]);
12+
13+
// When reloading, we make newer instance of the Facade object.
14+
$GLOBALS['CiviMixSchema'] = require __DIR__ . '/src/CiviMixSchema.php';
15+
16+
if (!interface_exists(__NAMESPACE__ . '\SchemaHelperInterface')) {
17+
require __DIR__ . '/src/SchemaHelperInterface.php';
18+
}
19+
20+
// \CiviMix\Schema\loadClass() is a facade. The facade should remain identical across versions.
21+
if (!function_exists(__NAMESPACE__ . '\loadClass')) {
22+
23+
function loadClass(string $class) {
24+
return $GLOBALS['CiviMixSchema']->loadClass($class);
25+
}
26+
27+
spl_autoload_register(__NAMESPACE__ . '\loadClass');
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
namespace CiviMix\Schema;
4+
5+
use Civi\Test\Invasive;
6+
7+
/**
8+
* The "AutomaticUpgrader" will create and destroy the SQL tables
9+
* using schema files (`SchemaHelper`). It also calls-out to any custom
10+
* upgrade code (eg `CRM_Myext_Upgrader`).
11+
*
12+
* To simplify backport considerations, `AutomaticUpgrader` does not have formal name.
13+
* It is accessed via aliases like "CiviMix\Schema\*\AutomaticUpgrader".
14+
*
15+
* Target: CiviCRM v5.38+
16+
*/
17+
return new class() implements \CRM_Extension_Upgrader_Interface {
18+
19+
use \CRM_Extension_Upgrader_IdentityTrait {
20+
21+
init as initIdentity;
22+
23+
}
24+
25+
/**
26+
* Optionally delegate to "CRM_Myext_Upgrader" or "Civi\Myext\Upgrader".
27+
*
28+
* @var \CRM_Extension_Upgrader_Interface|null
29+
*/
30+
private $customUpgrader;
31+
32+
public function init(array $params) {
33+
$this->initIdentity($params);
34+
if ($info = $this->getInfo()) {
35+
if ($class = $this->getDelegateUpgraderClass($info)) {
36+
$this->customUpgrader = new $class();
37+
$this->customUpgrader->init($params);
38+
if ($errors = $this->checkDelegateCompatibility($this->customUpgrader)) {
39+
throw new \CRM_Core_Exception("AutomaticUpgrader is not compatible with $class:\n" . implode("\n", $errors));
40+
}
41+
}
42+
}
43+
}
44+
45+
public function notify(string $event, array $params = []) {
46+
$info = $this->getInfo();
47+
if (!$info) {
48+
return;
49+
}
50+
51+
if ($event === 'install') {
52+
$GLOBALS['CiviMixSchema']->getHelper($this->getExtensionKey())->install();
53+
}
54+
55+
if ($this->customUpgrader) {
56+
$result = $this->customUpgrader->notify($event, $params);
57+
// for upgrade checks, we need to pass check results up to the caller
58+
// (for now - could definitely be more elegant!)
59+
if ($event === 'upgrade') {
60+
return $result;
61+
}
62+
}
63+
64+
if ($event === 'uninstall') {
65+
$GLOBALS['CiviMixSchema']->getHelper($this->getExtensionKey())->uninstall();
66+
}
67+
}
68+
69+
/**
70+
* Civix-based extensions have a conventional name for their upgrader class ("CRM_Myext_Upgrader"
71+
* or "Civi\Myext\Upgrader"). Figure out if this class exists.
72+
*
73+
* @param \CRM_Extension_Info $info
74+
* @return string|null
75+
* Ex: 'CRM_Myext_Upgrader' or 'Civi\Myext\Upgrader'
76+
*/
77+
public function getDelegateUpgraderClass(\CRM_Extension_Info $info): ?string {
78+
$candidates = [];
79+
80+
if (!empty($info->civix['namespace'])) {
81+
$namespace = $info->civix['namespace'];
82+
$candidates[] = sprintf('%s_Upgrader', str_replace('/', '_', $namespace));
83+
$candidates[] = sprintf('%s\\Upgrader', str_replace('/', '\\', $namespace));
84+
}
85+
86+
foreach ($candidates as $candidate) {
87+
if (class_exists($candidate)) {
88+
return $candidate;
89+
}
90+
}
91+
92+
return NULL;
93+
}
94+
95+
public function getInfo(): ?\CRM_Extension_Info {
96+
try {
97+
return \CRM_Extension_System::singleton()->getMapper()->keyToInfo($this->extensionName);
98+
}
99+
catch (\CRM_Extension_Exception_ParseException $e) {
100+
\Civi::log()->error("Parse error in extension " . $this->extensionName . ": " . $e->getMessage());
101+
return NULL;
102+
}
103+
}
104+
105+
/**
106+
* @param \CRM_Extension_Upgrader_Interface $upgrader
107+
* @return array
108+
* List of error messages.
109+
*/
110+
public function checkDelegateCompatibility($upgrader): array {
111+
$class = get_class($upgrader);
112+
113+
$errors = [];
114+
115+
if (!($upgrader instanceof \CRM_Extension_Upgrader_Base)) {
116+
$errors[] = "$class is not based on CRM_Extension_Upgrader_Base.";
117+
return $errors;
118+
}
119+
120+
// In the future, we will probably modify AutomaticUpgrader to build its own
121+
// sequence of revisions (based on other sources of data). AutomaticUpgrader
122+
// is only regarded as compatible with classes that strictly follow the standard revision-model.
123+
$methodNames = [
124+
'appendTask',
125+
'onUpgrade',
126+
'getRevisions',
127+
'getCurrentRevision',
128+
'setCurrentRevision',
129+
'enqueuePendingRevisions',
130+
'hasPendingRevisions',
131+
];
132+
foreach ($methodNames as $methodName) {
133+
$method = new \ReflectionMethod($upgrader, $methodName);
134+
if ($method->getDeclaringClass()->getName() !== 'CRM_Extension_Upgrader_Base') {
135+
$errors[] = "To ensure future interoperability, AutomaticUpgrader only supports {$class}::{$methodName}() if it's inherited from CRM_Extension_Upgrader_Base";
136+
}
137+
}
138+
139+
return $errors;
140+
}
141+
142+
public function __set($property, $value) {
143+
switch ($property) {
144+
// _queueAdapter() needs these properties.
145+
case 'ctx':
146+
case 'queue':
147+
if (!$this->customUpgrader) {
148+
throw new \RuntimeException("AutomaticUpgrader($this->extensionName): Cannot assign delegated property: $property (No custom-upgrader found)");
149+
}
150+
// "Invasive": unlike QueueTrait, we are not in the same class as the recipient. And we can't replace previously-published QueueTraits.
151+
Invasive::set([$this->customUpgrader, $property], $value);
152+
return;
153+
}
154+
155+
throw new \RuntimeException("AutomaticUpgrader($this->extensionName): Cannot assign unknown property: $property");
156+
}
157+
158+
public function __get($property) {
159+
switch ($property) {
160+
// _queueAdapter() needs these properties.
161+
case 'ctx':
162+
case 'queue':
163+
if (!$this->customUpgrader) {
164+
throw new \RuntimeException("AutomaticUpgrader($this->extensionName): Cannot read delegated property: $property (No custom-upgrader found)");
165+
}
166+
// "Invasive": Unlike QueueTrait, we are not in the same class as the recipient. And we can't replace previously-published QueueTraits.
167+
return Invasive::get([$this->customUpgrader, $property]);
168+
}
169+
throw new \RuntimeException("AutomaticUpgrader($this->extensionName): Cannot read unknown property: $property");
170+
}
171+
172+
public function __call($name, $arguments) {
173+
if ($this->customUpgrader) {
174+
return call_user_func_array([$this->customUpgrader, $name], $arguments);
175+
}
176+
else {
177+
throw new \RuntimeException("AutomaticUpgrader($this->extensionName): Cannot delegate method $name (No custom-upgrader found)");
178+
}
179+
}
180+
181+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
namespace CiviMix\Schema;
3+
4+
/**
5+
* This object is known as $GLOBALS['CiviMixSchema']. It is a reloadable service-object.
6+
* (It may be reloaded if you enable a new extension that includes an upgraded copy.)
7+
*/
8+
return new class() {
9+
10+
/**
11+
* @var string
12+
* Regular expression. Note the 2 groupings. $m[1] identifies a per-extension namespace. $m[2] identifies the actual class.
13+
*/
14+
private $regex = ';^CiviMix\\\Schema\\\(\w+)\\\(AutomaticUpgrader|DAO)$;';
15+
16+
/**
17+
* If someone requests a class like:
18+
*
19+
* CiviMix\Schema\MyExt\AutomaticUpgrader
20+
*
21+
* then load the latest version of:
22+
*
23+
* civimix-schema/src/Helper.php
24+
*/
25+
public function loadClass(string $class) {
26+
if (preg_match($this->regex, $class, $m)) {
27+
$absPath = __DIR__ . DIRECTORY_SEPARATOR . $m[2] . '.php';
28+
class_alias(get_class(require $absPath), $class);
29+
}
30+
}
31+
32+
/**
33+
* @param string $extensionKey
34+
* Ex: 'org.civicrm.flexmailer'
35+
* @return \CiviMix\Schema\SchemaHelperInterface
36+
*/
37+
public function getHelper(string $extensionKey) {
38+
$store = &\Civi::$statics['CiviMixSchema-helpers'];
39+
if (!isset($store[$extensionKey])) {
40+
$class = get_class(require __DIR__ . '/SchemaHelper.php');
41+
$store[$extensionKey] = new $class($extensionKey);
42+
}
43+
return $store[$extensionKey];
44+
}
45+
46+
};

0 commit comments

Comments
 (0)