Skip to content

Commit 4466d1e

Browse files
authored
Merge pull request #122 from Kit/add-get-body-html
Move WordPress Libraries Methods to Trait
2 parents 3633f84 + 5ef3974 commit 4466d1e

File tree

3 files changed

+181
-28
lines changed

3 files changed

+181
-28
lines changed

src/ConvertKit_API.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,6 @@ public function refresh_token(string $refreshToken, string $redirectURI)
274274
* @param string $url URL of HTML page.
275275
*
276276
* @throws \InvalidArgumentException If the URL is not a valid URL format.
277-
* @throws \Exception If parsing the legacy form or landing page failed.
278277
*
279278
* @return false|string
280279
*/
@@ -328,21 +327,11 @@ public function get_resource(string $url)
328327
$this->convert_relative_to_absolute_urls($html->getElementsByTagName('script'), 'src', $url_scheme_host_only);
329328
$this->convert_relative_to_absolute_urls($html->getElementsByTagName('form'), 'action', $url_scheme_host_only);
330329

331-
// Save HTML.
332-
$resource = $html->saveHTML();
333-
334-
// If the result is false, return a blank string.
335-
if (!$resource) {
336-
throw new \Exception(sprintf('Could not parse %s', $url));
337-
}
338-
339330
// Remove some HTML tags that DOMDocument adds, returning the output.
340331
// We do this instead of using LIBXML_HTML_NOIMPLIED in loadHTML(), because Legacy Forms
341332
// are not always contained in a single root / outer element, which is required for
342333
// LIBXML_HTML_NOIMPLIED to correctly work.
343-
$resource = $this->strip_html_head_body_tags($resource);
344-
345-
return $resource;
334+
return $this->get_body_html($html);
346335
}
347336

348337
/**

src/ConvertKit_API_Traits.php

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,21 @@ public function add_subscriber_to_form(int $form_id, int $subscriber_id, string
308308
);
309309
}
310310

311+
/**
312+
* Adds a subscriber to a legacy form by subscriber ID
313+
*
314+
* @param integer $form_id Legacy Form ID.
315+
* @param integer $subscriber_id Subscriber ID.
316+
*
317+
* @since 2.0.0
318+
*
319+
* @return false|mixed
320+
*/
321+
public function add_subscriber_to_legacy_form(int $form_id, int $subscriber_id)
322+
{
323+
return $this->post(sprintf('landing_pages/%s/subscribers/%s', $form_id, $subscriber_id));
324+
}
325+
311326
/**
312327
* List subscribers for a form
313328
*
@@ -1914,8 +1929,13 @@ public function get_segments(
19141929
*/
19151930
public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string $attribute, string $url) // phpcs:ignore Squiz.Commenting.FunctionComment.IncorrectTypeHint, Generic.Files.LineLength.TooLong
19161931
{
1917-
// Anchor hrefs.
1932+
// Store DOMNodeList in array, as iteration stops if a node is modified.
1933+
$nodes = [];
19181934
foreach ($elements as $element) {
1935+
$nodes[] = $element;
1936+
}
1937+
1938+
foreach ($nodes as $element) {
19191939
// Skip if the attribute's value is empty.
19201940
if (empty($element->getAttribute($attribute))) {
19211941
continue;
@@ -1931,31 +1951,43 @@ public function convert_relative_to_absolute_urls(\DOMNodeList $elements, string
19311951
continue;
19321952
}
19331953

1954+
// Remove element if it's rocket-loader.min.js. Including it prevents landing page redirects from working.
1955+
if (strpos($element->getAttribute($attribute), 'rocket-loader.min.js') !== false) {
1956+
if ($element->parentNode instanceof \DOMNode) {
1957+
$element->parentNode->removeChild($element);
1958+
}
1959+
continue;
1960+
}
1961+
19341962
// If here, the attribute's value is a relative URL, missing the http(s) and domain.
19351963
// Prepend the URL to the attribute's value.
19361964
$element->setAttribute($attribute, $url . $element->getAttribute($attribute));
1937-
}
1965+
}//end foreach
19381966
}
19391967

19401968
/**
1941-
* Strips <html>, <head> and <body> opening and closing tags from the given markup,
1942-
* as well as the Content-Type meta tag we might have added in get_html().
1969+
* Returns the HTML within the DOMDocument's <body> tag as a string.
1970+
*
1971+
* @param \DOMDocument $dom DOM Document.
19431972
*
1944-
* @param string $markup HTML Markup.
1973+
* @since 2.1.0
19451974
*
1946-
* @return string HTML Markup
1975+
* @return string
19471976
*/
1948-
public function strip_html_head_body_tags(string $markup)
1977+
public function get_body_html(\DOMDocument $dom)
19491978
{
1950-
$markup = str_replace('<html>', '', $markup);
1951-
$markup = str_replace('</html>', '', $markup);
1952-
$markup = str_replace('<head>', '', $markup);
1953-
$markup = str_replace('</head>', '', $markup);
1954-
$markup = str_replace('<body>', '', $markup);
1955-
$markup = str_replace('</body>', '', $markup);
1956-
$markup = str_replace('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">', '', $markup);
1957-
1958-
return $markup;
1979+
$body = $dom->getElementsByTagName('body')->item(0);
1980+
1981+
if (! $body instanceof \DOMElement) {
1982+
return '';
1983+
}
1984+
1985+
$html = '';
1986+
foreach ($body->childNodes as $child) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1987+
$html .= $dom->saveHTML($child);
1988+
}
1989+
1990+
return $html;
19591991
}
19601992

19611993
/**

tests/ConvertKitMethodsTest.php

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
use Dotenv\Dotenv;
5+
use ConvertKit_API\ConvertKit_API;
6+
7+
/**
8+
* Test methods in ConvertKit_API_Traits that don't interact with the API,
9+
* such as convert_relative_to_absolute_urls().
10+
*/
11+
class ConvertKitMethodsTest extends TestCase
12+
{
13+
/**
14+
* Kit API Class
15+
*
16+
* @var object
17+
*/
18+
protected $api;
19+
20+
/**
21+
* Initialize the API class before each test.
22+
*
23+
* @since 2.4.1
24+
*
25+
* @return void
26+
*/
27+
protected function setUp(): void
28+
{
29+
$this->api = new ConvertKit_API();
30+
}
31+
32+
/**
33+
* Test the convert_relative_to_absolute_urls() method.
34+
*
35+
* @since 2.4.1
36+
*
37+
* @return void
38+
*/
39+
public function testConvertRelativeToAbsoluteUrls()
40+
{
41+
// Setup HTML in DOMDocument.
42+
$html = new \DOMDocument();
43+
$html->loadHTML('<html>
44+
<head>
45+
<script type="text/javascript" src="rocket-loader.min.js"></script>
46+
<link rel="stylesheet" href="//fonts.googleapis.com">
47+
</head>
48+
<body>
49+
<a href="/test">Test</a>
50+
<img src="/test.jpg" />
51+
<script type="text/javascript" src="/test.js"></script>
52+
<form action="/test">Test</form>
53+
</body>
54+
</html>');
55+
56+
// Define URL to prepend to relative URLs.
57+
$url_scheme_host_only = 'https://example.com';
58+
59+
// Convert relative URLs to absolute URLs for elements we want to test.
60+
$this->api->convert_relative_to_absolute_urls(
61+
$html->getElementsByTagName('a'),
62+
'href',
63+
$url_scheme_host_only
64+
);
65+
$this->api->convert_relative_to_absolute_urls(
66+
$html->getElementsByTagName('link'),
67+
'href',
68+
$url_scheme_host_only
69+
);
70+
$this->api->convert_relative_to_absolute_urls(
71+
$html->getElementsByTagName('img'),
72+
'src',
73+
$url_scheme_host_only
74+
);
75+
$this->api->convert_relative_to_absolute_urls(
76+
$html->getElementsByTagName('script'),
77+
'src',
78+
$url_scheme_host_only
79+
);
80+
$this->api->convert_relative_to_absolute_urls(
81+
$html->getElementsByTagName('form'),
82+
'action',
83+
$url_scheme_host_only
84+
);
85+
86+
// Fetch HTML string.
87+
$output = $html->saveHTML();
88+
89+
// Assert string contains expected HTML elements that should not be modified.
90+
$this->assertStringContainsString('<link rel="stylesheet" href="//fonts.googleapis.com">', $output);
91+
92+
// Assert string does not contain HTML elements that should be removed.
93+
$this->assertStringNotContainsString(
94+
'<script type="text/javascript" src="rocket-loader.min.js"></script>',
95+
$output
96+
);
97+
98+
// Assert string contains expected HTML elements that should be modified.
99+
$this->assertStringContainsString(
100+
'<a href="' . $url_scheme_host_only . '/test">Test</a>',
101+
$output
102+
);
103+
$this->assertStringContainsString(
104+
'<img src="' . $url_scheme_host_only . '/test.jpg">',
105+
$output
106+
);
107+
$this->assertStringContainsString(
108+
'<script type="text/javascript" src="' . $url_scheme_host_only . '/test.js"></script>',
109+
$output
110+
);
111+
$this->assertStringContainsString(
112+
'<form action="' . $url_scheme_host_only . '/test">Test</form>',
113+
$output
114+
);
115+
}
116+
117+
/**
118+
* Test that the get_body_html() method returns the expected HTML.
119+
*
120+
* @since 2.4.1
121+
*/
122+
public function testGetBodyHtml()
123+
{
124+
$content = '<h1>Vantar þinn ungling sjálfstraust í stærðfræði?</h1><p>This is a test</p>';
125+
$html = new \DOMDocument();
126+
$html->loadHTML('
127+
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>
128+
<body>' . $content . '</body>
129+
</html>');
130+
$this->assertEquals($content, $this->api->get_body_html($html));
131+
}
132+
}

0 commit comments

Comments
 (0)