PluginProbe
Instapage Plugin / trunk
Instapage Plugin vtrunk
trunk 3.2.11 3.2.12 3.2.13 3.2.14 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.10 3.5.11 3.5.12 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 All 28 releases
instapage / models / InstapageCmsPluginPageModel.php

InstapageCmsPluginPageModel.php in Instapage Plugin trunk, at models/InstapageCmsPluginPageModel.php

649 lines 20.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class responsible for managing the landing pages.
5 */
6 class InstapageCmsPluginPageModel {
7
8 /**
9 * @var object Class instance.
10 */
11 private static $pageModel = null;
12
13 /**
14 * @var int Page statistics cache duration in minutes.
15 */
16 private static $statCacheDuration = 15;
17
18 /**
19 * Gets the class instance.
20 *
21 * @return object Class instance.
22 */
23 public static function getInstance() {
24 if (self::$pageModel === null) {
25 self::$pageModel = new InstapageCmsPluginPageModel();
26 }
27
28 return self::$pageModel;
29 }
30
31 /**
32 * Updates the page baset on passed $data object.
33 *
34 * @param object $data Data object.
35 *
36 * @return integer|boolean Insert ID of false on error.
37 */
38 public function update($data) {
39 $id = isset($data->id) ? $data->id : 0;
40 $instapageId = isset($data->landingPageId) ? $data->landingPageId : null;
41 $type = isset($data->type) ? $data->type : false;
42 $slug = isset($data->slug) ? $data->slug : false;
43 $enterpriseUrl = InstapageCmsPluginConnector::getHomeURL();
44
45 if ($slug) {
46 $enterpriseUrl .= '/' . $slug;
47 }
48
49 $db = InstapageCmsPluginDBModel::getInstance();
50 $sql = 'INSERT INTO ' . $db->pagesTable . ' VALUES(%s, %s, %s, %s, NOW(), \'\', NULL, %s) ON DUPLICATE KEY UPDATE instapage_id = %s, slug = %s, type = %s, time = NOW(), stats_cache = \'\', stats_cache_expires = NULL, enterprise_url = %s';
51
52 if ($db->query($sql, $id, $instapageId, $slug, $type, $enterpriseUrl, $instapageId, $slug, $type, $enterpriseUrl)) {
53 return ($id == 0) ? $db->lastInsertId() : $id;
54 } else {
55 return false;
56 }
57 }
58
59 /**
60 * Gets all the stored pages.
61 *
62 * @param array $fields Fields to retrieve. Default: array('*').
63 * @param array $conditions. List of conditions. Logical operator between conditions: AND.
64 *
65 * @return array Lst of results.
66 */
67 public function getAll($fields = array('*'), $conditions = array()) {
68 $db = InstapageCmsPluginDBModel::getInstance();
69 $sql = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $db->pagesTable;
70
71 if (count($conditions)) {
72 $sql .= ' WHERE ' . implode(' AND ', $conditions);
73 }
74
75 return $db->getResults($sql);
76 }
77
78 /**
79 * Gest the single page based on ID.
80 *
81 * @param int $id ID of the page.
82 * @param array $fields List of fields to retrieve. Default: array('*').
83 *
84 * @return object Page object.
85 */
86 public function get($id, $fields = array('*')) {
87 $db = InstapageCmsPluginDBModel::getInstance();
88 $sql = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $db->pagesTable . ' WHERE id = \'' . $id . '\'';
89
90 return $db->getRow($sql);
91 }
92
93 /**
94 * Gest the single page based on slug.
95 *
96 * @param string $slug Slug of the page.
97 * @param array $fields List of fields to retrieve. Default: array('*').
98 *
99 * @return object Page object.
100 */
101 public function getBySlug($slug, $fields = array('*')) {
102 $db = InstapageCmsPluginDBModel::getInstance();
103 $sql = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $db->pagesTable . ' WHERE slug = %s AND type=\'page\'';
104
105 return $db->getRow($sql, $slug);
106 }
107
108 /**
109 * Gest the single page based on type and slug.
110 *
111 * @param string $type Type of the page. ('page'|'home'|'404').
112 * @param string $slug Slug of the page.
113 * @param array $fields List of fields to retrieve. Default: array('*').
114 *
115 * @return object Page object.
116 */
117 public function getByType($type, $slug = '', $fields = array('*')) {
118 $db = InstapageCmsPluginDBModel::getInstance();
119 $sql = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $db->pagesTable . ' WHERE type = %s';
120
121 if ($slug) {
122 $sql = $sql . ' AND slug = %s';
123
124 return $db->getRow($sql, $type, $slug);
125 }
126 else {
127 return $db->getRow($sql, $type);
128 }
129 }
130
131 /**
132 * Gest the single page based on ID in Instapage app.
133 *
134 * @param int $instapageId ID in Instapage app.
135 * @param array $fields List of fields to retrieve. Default: array('*').
136 *
137 * @return array List of page objects.
138 */
139 public function getByInstapageId($instapageId, $fields = array('*')) {
140 $db = InstapageCmsPluginDBModel::getInstance();
141 $sql = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $db->pagesTable . ' WHERE instapage_id = ' . $instapageId;
142
143 return $db->getResults($sql);
144 }
145
146 /**
147 * Gets the cached statistics for pages.
148 *
149 * @param array $ids List of page IDs.
150 *
151 * @return array List of objects with stats cache informations.
152 */
153 public function getPageStatsCache($ids) {
154 if (!is_array($ids) || !count($ids)) {
155 return null;
156 }
157
158 $db = InstapageCmsPluginDBModel::getInstance();
159
160 foreach ($ids as &$item) {
161 $item = intval($item);
162 }
163
164 $idsSet = implode(', ', $ids);
165 $expireInSeconds = self::$statCacheDuration * 60;
166 $sql = 'SELECT instapage_id, stats_cache FROM ' . $db->pagesTable . ' WHERE instapage_id IN(' . $idsSet . ') AND stats_cache_expires + ' . $expireInSeconds . ' > ' . time();
167 $results = $db->getResults($sql);
168 $stats = array();
169
170 if ($results) {
171 foreach ($results as &$item) {
172 $stats[$item->instapage_id] = json_decode($item->stats_cache);
173 }
174
175 return $stats;
176 }
177
178 return array();
179 }
180
181 /**
182 * Sends a request to publish a page in Instapage app.
183 *
184 * @param object $data Page object.
185 *
186 * @return string JSON object with API response.
187 */
188 public function publishPage($data) {
189 $api = InstapageCmsPluginAPIModel::getInstance();
190 $subaccount = InstapageCmsPluginSubaccountModel::getInstance();
191 $url = $data->slug ? InstapageCmsPluginConnector::getHomeURL() . '/' . $data->slug : InstapageCmsPluginConnector::getHomeURL();
192 $url = InstapageCmsPluginHelper::prepareUrlForUpdate($url);
193 $tokens = isset($data->apiTokens) ? $data->apiTokens : false;
194 $success = true;
195
196 if (!$tokens) {
197 $tokens = $subaccount->getAllTokens();
198 }
199
200 $oldPageId = isset($data->id) ? $data->id : null;
201 $newInstapageId = isset($data->landingPageId) ? $data->landingPageId : null;
202
203 if ($oldPageId) {
204 $oldPage = $this->get($oldPageId, array('instapage_id'));
205
206 if ($oldPage->instapage_id != $newInstapageId) {
207 $apiData = array(
208 'page' => $oldPage->instapage_id,
209 'url' => '',
210 'publish' => 0
211 );
212 $headers = array('accountkeys' => InstapageCmsPluginHelper::getAuthHeader($tokens));
213 $responseJson = $api->apiCall('page/edit', $apiData, $headers);
214 $response = json_decode($responseJson);
215
216 if (!InstapageCmsPluginHelper::checkResponse($response, null, false) || !$response->success) {
217 $success = false;
218 }
219 }
220 }
221
222 if ($success) {
223 $apiData = array(
224 'page' => $data->landingPageId,
225 'url' => $url,
226 'publish' => 1
227 );
228 $headers = array('accountkeys' => InstapageCmsPluginHelper::getAuthHeader($tokens));
229 $responseJson = $api->apiCall('page/edit', $apiData, $headers);
230 $response = json_decode($responseJson);
231 }
232
233 if (!$success || !InstapageCmsPluginHelper::checkResponse($response, null, false) || !$response->success) {
234 if (isset($response->message) && $response->message !== '') {
235 return InstapageCmsPluginHelper::formatJsonMessage(InstapageCmsPluginConnector::lang($response->message), 'ERROR');
236 }
237 else {
238 return InstapageCmsPluginHelper::formatJsonMessage(InstapageCmsPluginConnector::lang('There was an error during page update process.'), 'ERROR');
239 }
240
241 return false;
242 }
243
244 $updatedId = $this->update($data);
245
246 if ($updatedId) {
247 return json_encode((object) array(
248 'status' => 'OK',
249 'message' => InstapageCmsPluginConnector::lang('Page updated successfully.'),
250 'updatedId' => $updatedId
251 ));
252 }
253 else {
254 return InstapageCmsPluginHelper::formatJsonMessage(InstapageCmsPluginConnector::lang('There was a database error during page update process.'), 'ERROR');
255 }
256 }
257
258 /**
259 * Migrates the depracated pages to current DB structure.
260 *
261 * @param array $data List of pages to migrate.
262 *
263 * @return array List of messages to display as a migration raport.
264 */
265 public function migrateDeprecatedData($data) {
266 InstapageCmsPluginHelper::writeDiagnostics($data, 'Migration data');
267 $raport = array();
268
269 if (!is_array($data) || !count($data)) {
270 return $raport;
271 }
272
273 foreach ($data as $deprecatedPage) {
274 if ($deprecatedPage->type == 'home') {
275 $deprecatedPage->slug = '';
276 }
277
278 $landingPagesById = $this->getByInstapageId($deprecatedPage->landingPageId);
279 $landingPagesBySlug = null;
280 $landingPagesByType = null;
281
282 if (count($landingPagesById)) {
283 $newLandingPage = array_pop($landingPagesById);
284 $raport[] = InstapageCmsPluginConnector::lang('Old version of page (slug: %s, Instapage ID: %s) is present in new database (slug: %s) and won\'t be migrated.', $deprecatedPage->slug, $deprecatedPage->landingPageId, $newLandingPage->slug);
285
286 continue;
287 }
288
289 if ($deprecatedPage->slug && $deprecatedPage->type == 'page') {
290 $landingPagesBySlug = $this->getBySlug($deprecatedPage->slug);
291 }
292
293 if ($landingPagesBySlug) {
294 $newLandingPage = $landingPagesBySlug;
295 $raport[] = InstapageCmsPluginConnector::lang('Slug: %s is already taken in new database. Old page (slug: %s, Instapage ID: %s) won\'t be migrated.', $deprecatedPage->slug, $deprecatedPage->slug, $deprecatedPage->landingPageId);
296
297 continue;
298 }
299
300 if ($deprecatedPage->type !== 'page') {
301 $landingPagesByType = $this->getByType($deprecatedPage->type);
302 }
303
304 if ($landingPagesByType) {
305 $newLandingPage = $landingPagesByType;
306 $raport[] = InstapageCmsPluginConnector::lang('One %s page already exists in new database. Old page (slug: %s, Instapage ID: %s) won\'t be migrated.', $deprecatedPage->type, $deprecatedPage->slug, $deprecatedPage->landingPageId);
307
308 continue;
309 }
310
311 if ($this->update($deprecatedPage)) {
312 $raport[] = InstapageCmsPluginConnector::lang('Old version of page (slug: %s, Instapage ID: %s) successfully migrated.', $deprecatedPage->slug, $deprecatedPage->landingPageId);
313 }
314 else {
315 $raport[] = InstapageCmsPluginConnector::lang('Old version of page (slug: %s, Instapage ID: %s) cannot be migrated due to database error.', $deprecatedPage->slug, $deprecatedPage->landingPageId);
316 }
317 }
318
319 InstapageCmsPluginHelper::writeDiagnostics($raport, 'Migration raport');
320
321 return $raport;
322 }
323
324 /**
325 * Saves the statistics of the page as a cache.
326 *
327 * @param array $data Lst of elements to save.
328 */
329 public function savePageStatsCache($data) {
330 $db = InstapageCmsPluginDBModel::getInstance();
331
332 foreach ($data as $key => $item) {
333 $sql = 'UPDATE ' . $db->pagesTable . ' SET stats_cache = %s, stats_cache_expires = ' . time() . ' WHERE instapage_id = %s';
334 $db->query($sql, json_encode($item), $key );
335 }
336 }
337
338 /**
339 * Gets a landing page saved as a homepage.
340 *
341 * @param array $fields List of fields to retrieve. Default: array('*').
342 *
343 * @return object Page object.
344 */
345 public function getHomepage($fields = array('*')) {
346 $db = InstapageCmsPluginDBModel::getInstance();
347 $sql = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $db->pagesTable . ' WHERE type=\'home\'';
348
349 return $db->getRow($sql);
350 }
351
352 /**
353 * Gets a landing page saved as a 404 page.
354 *
355 * @param array $fields List of fields to retrieve. Default: array('*').
356 *
357 * @return object Page object.
358 */
359 public function get404($fields = array('*')) {
360 $db = InstapageCmsPluginDBModel::getInstance();
361 $sql = 'SELECT ' . implode(', ', $fields) . ' FROM ' . $db->pagesTable . ' WHERE type=\'404\'';
362
363 return $db->getRow($sql);
364 }
365
366 /**
367 * Gets the data about landing pages stores in local database and completes them with data from Instapage app.
368 *
369 * @param array localData List of pages stored locally. Data will be changed during the process.
370 *
371 * @param array $appData List of information from the Instapage app.
372 */
373 public function mergeListPagesResults(&$localData, $appData) {
374 foreach ($localData as &$localItem) {
375 $instapageId = $localItem->instapage_id;
376 $appItem = $this->getPageFromArray($instapageId, $appData);
377
378 if (!is_null($appItem)) {
379 $localItem->screenshot = $appItem->screenshot;
380 $localItem->title = $appItem->title;
381 $localItem->subaccount = $appItem->subaccount;
382 }
383 }
384 }
385
386 /**
387 * Checks (and returns) if a landing page should be displayed instead of normal content served by CMS.
388 *
389 * @param string $type Type of page to check ('page', 'home' or '404').
390 * @param string $slug Slug to check. Default: ''.
391 *
392 * @return string HTML to display.
393 */
394 public function check($type, $slug = '') {
395 if (!InstapageCmsPluginConnector::isHtmlReplaceNecessary()) {
396 return;
397 }
398
399 $result = $this->getByType($type, $slug, array('instapage_id', 'slug', 'enterprise_url'));
400
401 if (!$result) {
402 return;
403 }
404
405 $result->slug = $result->slug ? $result->slug : $slug;
406 $result->enterprise_url = $result->enterprise_url ? $result->enterprise_url : InstapageCmsPluginConnector::getHomeURL() . '/' . $result->slug;
407 $result->enterprise_url = rtrim($result->enterprise_url, '/');
408
409 return $result;
410 }
411
412 /**
413 * Displays the landing page.
414 *
415 * @param object $page Landing page to display.
416 * @param ?int $forcedStatus Status to be set as a header. Default: null.
417 */
418 public function display($page, $forcedStatus = null)
419 {
420 require_once(__DIR__ . '/../modules/lpAjaxLoader/InstapageCmsPluginLPAjaxLoaderController.php');
421 $lpAjaxLoaderController = new InstapageCmsPluginLPAjaxLoaderController();
422 $instapageId = $page->instapage_id;
423 $slug = $page->slug;
424 $host = parse_url($page->enterprise_url, PHP_URL_HOST);
425 InstapageCmsPluginHelper::writeDiagnostics($slug . ' : ' . $instapageId, 'slug : instapage_id');
426
427 $api = InstapageCmsPluginAPIModel::getInstance();
428 $querySufix = '';
429 $cookies = filter_input_array(INPUT_COOKIE) ?: [];
430
431 if (!empty($_GET)) {
432 if ($lpAjaxLoaderController->shouldDecodeQuery()) {
433 $querySufix = '?' . base64_decode(InstapageCmsPluginHelper::filterInput(INPUT_GET, 'b64'));
434 } else {
435 $querySufix = '?' . http_build_query(filter_input_array(INPUT_GET) ?: []);
436 }
437 } elseif (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
438 $querySufix = '?' . InstapageCmsPluginHelper::filterInput(INPUT_SERVER, 'QUERY_STRING');
439 }
440
441 if (is_array($cookies) && count($cookies)) {
442 $cookiesWeNeed = array("instapage-variant-{$instapageId}");
443
444 foreach ($cookies as $key => $value) {
445 if (!in_array($key, $cookiesWeNeed)) {
446 unset($cookies[$key]);
447 }
448 }
449 }
450
451 $url = preg_replace('/https?:\/\/' . $host . '/', INSTAPAGE_ENTERPRISE_ENDPOINT, $page->enterprise_url);
452 $url .= $querySufix;
453 $url = InstapageCmsPluginConnector::getURLWithSelectedProtocol($url);
454
455 $enterpriseCallResult = $api->enterpriseCall($url, $host, $cookies);
456 $html = $this->getLandingPageHTMLFromTheApp($enterpriseCallResult);
457 $this->setVariantCookie($enterpriseCallResult, $instapageId);
458
459 if ($lpAjaxLoaderController->shouldBeUsed($url)) {
460 $html = $lpAjaxLoaderController->injectScript($html);
461 $html = $lpAjaxLoaderController->addDisplayNoneOnBody($html);
462 }
463
464 if ($forcedStatus) {
465 $status = $forcedStatus;
466 } else {
467 $status = isset($enterpriseCallResult['code']) ? (int) $enterpriseCallResult['code'] : 200;
468 }
469
470 if ($html) {
471 ob_start();
472 InstapageCmsPluginHelper::disableCaching();
473 InstapageCmsPluginHelper::httpResponseCode($status);
474 print $html;
475 ob_end_flush();
476 die();
477 } else {
478 return false;
479 }
480 }
481
482 /**
483 * Gets and prepares Landing Page HTML from app (from request to so called enterprise)
484 *
485 * @param array $enterpriseCallResult Result from enterpriseCall() method
486 * @return string
487 */
488 public function getLandingPageHTMLFromTheApp($enterpriseCallResult)
489 {
490 $html = isset($enterpriseCallResult['body']) ? $enterpriseCallResult['body'] : false;
491 $html = $this->disableCloudFlareScriptReplace($html);
492 $html = $this->fixHtmlHead($html);
493
494 return $html;
495 }
496
497 /**
498 * Set cookie with variant value
499 *
500 * @param array $enterpriseCallResult Result from enterpriseCall() method
501 * @param int $instapageId
502 */
503 public function setVariantCookie($enterpriseCallResult, $instapageId)
504 {
505 $pageserverCookie = isset($enterpriseCallResult['headers']['set-cookie']) ? $enterpriseCallResult['headers']['set-cookie'] : '';
506
507 if (is_array($pageserverCookie)) {
508 $pageserverCookie = array_pop($pageserverCookie);
509 }
510
511 $instapageVariant = InstapageCmsPluginHelper::getVariant((string) $pageserverCookie);
512
513 if (!empty($instapageVariant)) {
514 $variantCookieName = "instapage-variant-{$instapageId}";
515 $variantCookieOptions = [
516 'expires' => strtotime('+12 month'),
517 'path' => '/',
518 'domain' => '',
519 ];
520
521 if (InstapageCmsPluginConnector::isSSL()) {
522 $variantCookieOptions['samesite'] = 'None';
523 $variantCookieOptions['secure'] = InstapageCmsPluginConnector::isSSL();
524 }
525
526
527 if (version_compare(phpversion(), '7.3', '<')) {
528 if (InstapageCmsPluginConnector::isSSL()) {
529 setcookie(
530 $variantCookieName,
531 $instapageVariant,
532 $variantCookieOptions['expires'],
533 $variantCookieOptions['path'] . '; samesite=' . $variantCookieOptions['samesite'],
534 $variantCookieOptions['domain'],
535 $variantCookieOptions['secure']
536 );
537 } else {
538 setcookie(
539 $variantCookieName,
540 $instapageVariant,
541 $variantCookieOptions['expires'],
542 $variantCookieOptions['path'],
543 $variantCookieOptions['domain']
544 );
545 }
546
547 } else {
548 setcookie(
549 $variantCookieName,
550 $instapageVariant,
551 $variantCookieOptions
552 );
553 }
554 }
555 }
556
557 /**
558 * Deletes a page from local DB.
559 *
560 * @param int $id ID of a page to be deleted.
561 */
562 public function delete($id) {
563
564 $db = InstapageCmsPluginDBModel::getInstance();
565 $sql = 'DELETE FROM ' . $db->pagesTable . ' WHERE id = %s';
566
567 return $db->query($sql, $id);
568 }
569
570 /**
571 * Gets the page object from an array of page objects.
572 *
573 * @param int $id ID of a page.
574 * @param array $array List of page objects.
575 *
576 * @return object|null Page object or null if no pages found.
577 */
578 private function getPageFromArray($id, $array) {
579 if (is_array($array)) {
580 foreach ($array as $item) {
581 if ($item->id == $id) {
582 return $item;
583 }
584 }
585 }
586
587 return null;
588 }
589
590 /**
591 * Composes a random slug.
592 *
593 * @param bool $prefix Do you want to add a prefix?
594 *
595 * @return string Random slug.
596 */
597 public function getRandomSlug($prefix = true) {
598 $randomPrefix = 'random-url-';
599 $randomSufixSet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
600 $randomSufixLength = 10;
601 $randomString = '';
602
603 for ($i = 0; $i < $randomSufixLength; $i++) {
604 $randomString .= $randomSufixSet[rand(0, strlen($randomSufixSet) - 1)];
605 }
606
607 return $prefix ? $randomPrefix . $randomString : $randomString;
608 }
609
610 /**
611 * Removes the CloudFlare JS from a landing page content.
612 *
613 * @param string $html HTML of a landing page.
614 *
615 * @return string HTML without CloudFlare script.
616 */
617 private function disableCloudFlareScriptReplace($html) {
618 $pattern = '/(<script)(type="text\/javascript")?(.*?)>/';
619
620 return preg_replace($pattern, "$1$2 data-cfasync=\"false\" $3>", $html);
621 }
622
623 /**
624 * Sets up the proper URL for Instapage proxy, if it is enabled.
625 *
626 * @param string $html HTML to be fixed.
627 *
628 * @return string HTML with propely set proxy URLs.
629 */
630 public function fixHtmlHead($html) {
631 $useProxy = InstapageCmsPluginHelper::getOption('crossOrigin', false);
632
633 if ($useProxy) {
634 $html = str_replace('PROXY_SERVICES', str_replace(array('http://', 'https://'), array('//', '//'), InstapageCmsPluginConnector::getHomeURL()) ."/instapage-proxy-services?url=", $html);
635 }
636
637 $searchArray = array(
638 '<meta name="iy453p9485yheisruhs5" content="" />',
639 '<meta name="robots" content="noindex, nofollow" />'
640 );
641
642 if (strpos($html, $searchArray[0]) !== false) {
643 $html = str_replace($searchArray, '', $html);
644 }
645
646 return $html;
647 }
648 }
649