Skip to content

Commit

Permalink
Merge pull request #593 from aydun/replaceurl
Browse files Browse the repository at this point in the history
Enhance api call `replaceurls`
  • Loading branch information
mattwire authored May 23, 2023
2 parents d1edc7c + 69bdba1 commit c0079b7
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 13 deletions.
62 changes: 54 additions & 8 deletions CRM/Mosaico/BAO/MosaicoTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,70 @@ public static function create($params) {
* Helps updating the URLs in templates so they can be reused
* after restoring a dump database in a new server.
*
* URL's can be include either scheme/host or path or both
*
* @param string $fromUrl URL of the server where the
* templates were created
* @param string $toUrl URL of the current server
*/
public static function replaceUrls($fromUrl, $toUrl) {
$replaceQuery = "UPDATE civicrm_mosaico_template
SET metadata = json_replace(metadata, '$.template',
replace(
json_unquote(
json_extract(metadata, '$.template')
),
$from = parse_url($fromUrl);
$to = parse_url($toUrl);

// Update 'template': only uses path without leading slash
if ($from['path']) {
$replaceQuery = "UPDATE civicrm_mosaico_template
SET metadata = JSON_REPLACE(metadata, '$.template',
REPLACE(
JSON_UNQUOTE(
JSON_EXTRACT(metadata, '$.template')
),
%1, %2)
);";
);";

CRM_Core_DAO::executeQuery($replaceQuery, [
1 => [ltrim($from['path'], '/'), 'String'],
2 => [ltrim($to['path'] ?? '', '/'), 'String'],
]);
}

// Update 'html' and 'content'
$replaceQuery = "UPDATE civicrm_mosaico_template
SET html = REPLACE(html, %1, %2),
content = REPLACE(content, %1, %2)
;";

// ... with unencoded strings
CRM_Core_DAO::executeQuery($replaceQuery, [
1 => [$fromUrl, 'String'],
2 => [$toUrl, 'String'],
]);

// ... with encoded strings
CRM_Core_DAO::executeQuery($replaceQuery, [
1 => [urlencode($fromUrl), 'String'],
2 => [urlencode($toUrl), 'String'],
]);

// Images load from https://example.com/civicrm/mosaico/img, so update the host part as well
$hostFrom = str_replace($from['path'], '', $fromUrl);
$hostTo = str_replace($to['path'], '', $toUrl);
if ($hostFrom && $hostTo) {
CRM_Core_DAO::executeQuery($replaceQuery, [
1 => [$hostFrom . "/civicrm/mosaico", 'String'],
2 => [$hostTo . "/civicrm/mosaico", 'String'],
]);
}

// However, 'content' is a json string, and the encoded representation depends on the json encoding options
// The above works where the JSON_UNESCAPED_SLASHES flag has been used so that the encoded representation is like '/my/path'
// But without that option, the encoded representation is '\/my\/path'
// Comparing the decoded values would be better, but this should work for our purposes...
// executeQuery doesn't like backslahes in 'String', so do this directly.

$slashedFrom = str_replace('/', '\\\/', $fromUrl);
$slashedTo = str_replace('/', '\\\/', $toUrl);
CRM_Core_DAO::executeQuery("UPDATE civicrm_mosaico_template SET content = REPLACE(content, '$slashedFrom', '$slashedTo');");
}

/**
Expand All @@ -65,7 +111,7 @@ public static function findBaseTemplates($ignoreCache = FALSE, $dispatchHooks =
$templatesLocation[] = ['dir' => $templatesDir, 'url' => $templatesUrl];

$customTemplatesDir = \Civi::paths()->getPath(\Civi::settings()->get('mosaico_custom_templates_dir'));
$customTemplatesUrl = \Civi::paths()->getUrl(\Civi::settings()->get('mosaico_custom_templates_url'),'absolute');
$customTemplatesUrl = \Civi::paths()->getUrl(\Civi::settings()->get('mosaico_custom_templates_url'), 'absolute');
if (!is_null($customTemplatesDir) && !is_null($customTemplatesUrl)) {
if (is_dir($customTemplatesDir)) {
$templatesLocation[] = ['dir' => $customTemplatesDir, 'url' => $customTemplatesUrl];
Expand Down
14 changes: 9 additions & 5 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ This extension defines a few new APIs:
old v1.x templates.
* `MosaicoTemplate.*`: This API provides access to the user-configurable templates. It supports all standard CRUD
actions (`get`, `create`, `delete`etc). Its data-structure closely adheres to Mosaico's canonical storage format.
* `MosaicoTemplate.replaceurls`: When a database is restored in a server with a different URL, templates will need to be updated. The `replaceurls` method facilitates that migration task:
* `MosaicoTemplate.replaceurls`: When a database is restored in a server with a different URL or if paths change then templates will need to be updated. The `replaceurls` method facilitates that migration task:

```
cv api MosaicoTemplate.replaceurls from_url="http://old.server.org" to_url="https://new.server.org"
```
Migrating from Drupal 7 to WordPress:
```
cv api MosaicoTemplate.replaceurls from_url="/sites/default/files/civicrm/" to_url="/wp-content/uploads/civicrm/"
```

* `MosaicoBaseTemplate.get`: This API provides access to the *base templates*. A base template (such as `versafix-1`)
Expand Down Expand Up @@ -116,18 +120,18 @@ Example usage:
function example_civicrm_mosaicoPlugin(&$plugins) {
$plugins[] = _example_alert_plugin();
}
/**
* Example plugin to display alert on init and dispose.
*/
*/
function _example_alert_plugin(){
$plugin = <<< JS
function(vm) {
alert("test-plugin");
return {
init: function(vm){alert("init");},
init: function(vm){alert("init");},
dispose: function(vm){alert("dispose");}
}
}
}
JS;
Expand Down
88 changes: 88 additions & 0 deletions tests/phpunit/CRM/Mosaico/MosaicoTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,92 @@ public function testClone(): void {
$this->assertEquals(json_encode(['abc' => 'def']), $clone['content']);
}

/**
* Test replaceUrls with full URL
*/
public function testReplaceUrlsFull(): void {
$createResult = $this->callAPISuccess('MosaicoTemplate', 'create', [
'title' => 'MosaicoTemplateTest baz',
'base' => 'versafix-1',
// As extracted from a real template
'html' => '<img src=\"https://old-site.org/civicrm/mosaico/img?src=https%3A%2F%2Fold-site.org%2Fsites%2Fdefault%2Ffiles%2Fcivicrm%2Fpersist%2Fcontribute%2Fimages%2Fuploads%2Flogo_bced05c6746c32f9adb3fea8a7060dac.png&amp;method=resize&amp;params=258%2Cnull\">',
'metadata' => json_encode(['template' => '/sites/default/files/civicrm/mosaico_tpl/CiviVersafix/template-CiviVersafix.html', 'templateversion' => '1.0.5', 'editorversion' => '0.15.0'], JSON_UNESCAPED_SLASHES),
'content' => json_encode(['src' => 'https://old-site.org/sites/default/files/civicrm/persist/contribute/images/uploads/logo_bced05c6746c32f9adb3fea8a7060dac.png']),
]);

$this->callAPISuccess('MosaicoTemplate', 'replaceurls', [
'from_url' => 'https://old-site.org/sites/default/files/civicrm/',
'to_url' => 'https://new-site.org/wp-content/uploads/civicrm/',
]);

$getResult = $this->callAPISuccess('MosaicoTemplate', 'getsingle', ['id' => $createResult['id']]);
foreach (['html', 'content', 'metadata'] as $element) {
$this->assertStringNotContainsString('old-site.org', $getResult[$element]);
$this->assertStringNotContainsString('sites', $getResult[$element]);
$this->assertStringContainsString('wp-content', $getResult[$element]);
if ($element != 'metadata') {
$this->assertStringContainsString('new-site.org', $getResult[$element]);
$this->assertStringContainsString('logo_bced', $getResult[$element]);
}
}
}

/**
* Test replaceUrls with host only
*/
public function testReplaceUrlsHost(): void {
$createResult = $this->callAPISuccess('MosaicoTemplate', 'create', [
'title' => 'MosaicoTemplateTest baz',
'base' => 'versafix-1',
// As extracted from a real template
'html' => '<img src=\"https://old-site.org/civicrm/mosaico/img?src=https%3A%2F%2Fold-site.org%2Fsites%2Fdefault%2Ffiles%2Fcivicrm%2Fpersist%2Fcontribute%2Fimages%2Fuploads%2Flogo_bced05c6746c32f9adb3fea8a7060dac.png&amp;method=resize&amp;params=258%2Cnull\">',
'metadata' => json_encode(['template' => '/sites/default/files/civicrm/mosaico_tpl/CiviVersafix/template-CiviVersafix.html', 'templateversion' => '1.0.5', 'editorversion' => '0.15.0'], JSON_UNESCAPED_SLASHES),
'content' => json_encode(['src' => 'https://old-site.org/sites/default/files/civicrm/persist/contribute/images/uploads/logo_bced05c6746c32f9adb3fea8a7060dac.png']),
]);

$this->callAPISuccess('MosaicoTemplate', 'replaceurls', [
'from_url' => 'https://old-site.org/',
'to_url' => 'https://new-site.org/',
]);

$getResult = $this->callAPISuccess('MosaicoTemplate', 'getsingle', ['id' => $createResult['id']]);
foreach (['html', 'content', 'metadata'] as $element) {
$this->assertStringNotContainsString('old-site.org', $getResult[$element]);
$this->assertStringContainsString('sites', $getResult[$element]);
if ($element != 'metadata') {
$this->assertStringContainsString('new-site.org', $getResult[$element]);
$this->assertStringContainsString('logo_bced', $getResult[$element]);
}
}
}

/**
* Test replaceUrls with path only
*/
public function testReplaceUrlsPath(): void {
$createResult = $this->callAPISuccess('MosaicoTemplate', 'create', [
'title' => 'MosaicoTemplateTest baz',
'base' => 'versafix-1',
// As extracted from a real template
'html' => '<img src=\"https://old-site.org/civicrm/mosaico/img?src=https%3A%2F%2Fold-site.org%2Fsites%2Fdefault%2Ffiles%2Fcivicrm%2Fpersist%2Fcontribute%2Fimages%2Fuploads%2Flogo_bced05c6746c32f9adb3fea8a7060dac.png&amp;method=resize&amp;params=258%2Cnull\">',
'metadata' => json_encode(['template' => '/sites/default/files/civicrm/mosaico_tpl/CiviVersafix/template-CiviVersafix.html', 'templateversion' => '1.0.5', 'editorversion' => '0.15.0'], JSON_UNESCAPED_SLASHES),
'content' => json_encode(['src' => 'https://old-site.org/sites/default/files/civicrm/persist/contribute/images/uploads/logo_bced05c6746c32f9adb3fea8a7060dac.png']),
]);

$this->callAPISuccess('MosaicoTemplate', 'replaceurls', [
'from_url' => '/sites/default/files/civicrm/',
'to_url' => '/wp-content/uploads/civicrm/',
]);

$getResult = $this->callAPISuccess('MosaicoTemplate', 'getsingle', ['id' => $createResult['id']]);
foreach (['html', 'content', 'metadata'] as $element) {
$this->assertStringNotContainsString('sites', $getResult[$element]);
$this->assertStringContainsString('wp-content', $getResult[$element]);
if ($element != 'metadata') {
$this->assertStringContainsString('old-site.org', $getResult[$element]);
$this->assertStringContainsString('logo_bced', $getResult[$element]);
}
}
}

}

0 comments on commit c0079b7

Please sign in to comment.