Нет описания

Migrator.php 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. <?php
  2. namespace MailPoet\Config;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\Entities\FormEntity;
  5. use MailPoet\Models\Newsletter;
  6. use MailPoet\Models\Subscriber;
  7. use MailPoet\Settings\SettingsController;
  8. use MailPoet\Util\Helpers;
  9. // The "created_at" column must be NULL in some tables to avoid "there can be only one
  10. // TIMESTAMP column with CURRENT_TIMESTAMP" error on MySQL version < 5.6.5 that occurs
  11. // even when other timestamp is simply "NOT NULL".
  12. class Migrator {
  13. public $prefix;
  14. private $charsetCollate;
  15. private $models;
  16. private $settings;
  17. public function __construct() {
  18. $this->settings = SettingsController::getInstance();
  19. $this->prefix = Env::$dbPrefix;
  20. $this->charsetCollate = Env::$dbCharsetCollate;
  21. $this->models = [
  22. 'segments',
  23. 'settings',
  24. 'custom_fields',
  25. 'scheduled_tasks',
  26. 'stats_notifications',
  27. 'scheduled_task_subscribers',
  28. 'sending_queues',
  29. 'subscribers',
  30. 'subscriber_segment',
  31. 'subscriber_custom_field',
  32. 'subscriber_ips',
  33. 'newsletters',
  34. 'newsletter_templates',
  35. 'newsletter_option_fields',
  36. 'newsletter_option',
  37. 'newsletter_segment',
  38. 'newsletter_links',
  39. 'newsletter_posts',
  40. 'forms',
  41. 'statistics_newsletters',
  42. 'statistics_clicks',
  43. 'statistics_bounces',
  44. 'statistics_opens',
  45. 'statistics_unsubscribes',
  46. 'statistics_forms',
  47. 'statistics_woocommerce_purchases',
  48. 'mapping_to_external_entities',
  49. 'log',
  50. 'user_flags',
  51. 'feature_flags',
  52. 'dynamic_segment_filters',
  53. 'user_agents',
  54. ];
  55. }
  56. public function up() {
  57. global $wpdb;
  58. // Ensure dbDelta function
  59. require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
  60. $output = [];
  61. foreach ($this->models as $model) {
  62. $modelMethod = Helpers::underscoreToCamelCase($model);
  63. $output = array_merge(dbDelta($this->$modelMethod()), $output);
  64. }
  65. $this->updateNullInUnsubscribeStats();
  66. $this->fixScheduledTasksSubscribersTimestampColumns();
  67. $this->removeDeprecatedStatisticsIndexes();
  68. return $output;
  69. }
  70. public function down() {
  71. global $wpdb;
  72. $_this = $this;
  73. $dropTable = function($model) use($wpdb, $_this) {
  74. $table = $_this->prefix . $model;
  75. $wpdb->query("DROP TABLE {$table}");
  76. };
  77. array_map($dropTable, $this->models);
  78. }
  79. public function segments() {
  80. $attributes = [
  81. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  82. 'name varchar(90) NOT NULL,',
  83. 'type varchar(90) NOT NULL DEFAULT "default",',
  84. 'description varchar(250) NOT NULL DEFAULT "",',
  85. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  86. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  87. 'deleted_at timestamp NULL,',
  88. 'average_engagement_score FLOAT unsigned NULL,',
  89. 'average_engagement_score_updated_at timestamp NULL,',
  90. 'PRIMARY KEY (id),',
  91. 'UNIQUE KEY name (name),',
  92. 'KEY average_engagement_score_updated_at (average_engagement_score_updated_at)',
  93. ];
  94. return $this->sqlify(__FUNCTION__, $attributes);
  95. }
  96. public function settings() {
  97. $attributes = [
  98. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  99. 'name varchar(50) NOT NULL,',
  100. 'value longtext,',
  101. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  102. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  103. 'PRIMARY KEY (id),',
  104. 'UNIQUE KEY name (name)',
  105. ];
  106. return $this->sqlify(__FUNCTION__, $attributes);
  107. }
  108. public function customFields() {
  109. $attributes = [
  110. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  111. 'name varchar(90) NOT NULL,',
  112. 'type varchar(90) NOT NULL,',
  113. 'params longtext NOT NULL,',
  114. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  115. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  116. 'PRIMARY KEY (id),',
  117. 'UNIQUE KEY name (name)',
  118. ];
  119. return $this->sqlify(__FUNCTION__, $attributes);
  120. }
  121. public function scheduledTasks() {
  122. $attributes = [
  123. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  124. 'type varchar(90) NULL DEFAULT NULL,',
  125. 'status varchar(12) NULL DEFAULT NULL,',
  126. 'priority mediumint(9) NOT NULL DEFAULT 0,',
  127. 'scheduled_at timestamp NULL,',
  128. 'processed_at timestamp NULL,',
  129. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  130. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  131. 'deleted_at timestamp NULL,',
  132. 'in_progress int(1),',
  133. 'reschedule_count int(11) NOT NULL DEFAULT 0,',
  134. 'meta longtext,',
  135. 'PRIMARY KEY (id),',
  136. 'KEY type (type),',
  137. 'KEY status (status)',
  138. ];
  139. return $this->sqlify(__FUNCTION__, $attributes);
  140. }
  141. public function statsNotifications() {
  142. $attributes = [
  143. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  144. 'newsletter_id int(11) unsigned NOT NULL,',
  145. 'task_id int(11) unsigned NOT NULL,',
  146. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  147. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  148. 'PRIMARY KEY (id),',
  149. 'UNIQUE KEY newsletter_id_task_id (newsletter_id, task_id),',
  150. 'KEY task_id (task_id)',
  151. ];
  152. return $this->sqlify(__FUNCTION__, $attributes);
  153. }
  154. public function scheduledTaskSubscribers() {
  155. $attributes = [
  156. 'task_id int(11) unsigned NOT NULL,',
  157. 'subscriber_id int(11) unsigned NOT NULL,',
  158. 'processed int(1) NOT NULL,',
  159. 'failed smallint(1) NOT NULL DEFAULT 0,',
  160. 'error text NULL,',
  161. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  162. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  163. 'PRIMARY KEY (task_id, subscriber_id),',
  164. 'KEY subscriber_id (subscriber_id)',
  165. ];
  166. return $this->sqlify(__FUNCTION__, $attributes);
  167. }
  168. public function sendingQueues() {
  169. $attributes = [
  170. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  171. 'task_id int(11) unsigned NOT NULL,',
  172. 'newsletter_id int(11) unsigned NOT NULL,',
  173. 'newsletter_rendered_body longtext,',
  174. 'newsletter_rendered_subject varchar(250) NULL DEFAULT NULL,',
  175. 'subscribers longtext,',
  176. 'count_total int(11) unsigned NOT NULL DEFAULT 0,',
  177. 'count_processed int(11) unsigned NOT NULL DEFAULT 0,',
  178. 'count_to_process int(11) unsigned NOT NULL DEFAULT 0,',
  179. 'meta longtext,',
  180. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  181. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  182. 'deleted_at timestamp NULL,',
  183. 'PRIMARY KEY (id),',
  184. 'KEY task_id (task_id),',
  185. 'KEY newsletter_id (newsletter_id)',
  186. ];
  187. return $this->sqlify(__FUNCTION__, $attributes);
  188. }
  189. public function subscribers() {
  190. $attributes = [
  191. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  192. 'wp_user_id bigint(20) NULL,',
  193. 'is_woocommerce_user int(1) NOT NULL DEFAULT 0,',
  194. 'first_name varchar(255) NOT NULL DEFAULT "",',
  195. 'last_name varchar(255) NOT NULL DEFAULT "",',
  196. 'email varchar(150) NOT NULL,',
  197. 'status varchar(12) NOT NULL DEFAULT "' . Subscriber::STATUS_UNCONFIRMED . '",',
  198. 'subscribed_ip varchar(45) NULL,',
  199. 'confirmed_ip varchar(45) NULL,',
  200. 'confirmed_at timestamp NULL,',
  201. 'last_subscribed_at timestamp NULL,',
  202. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  203. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  204. 'deleted_at timestamp NULL,',
  205. 'unconfirmed_data longtext,',
  206. "source enum('form','imported','administrator','api','wordpress_user','woocommerce_user','woocommerce_checkout','unknown') DEFAULT 'unknown',",
  207. 'count_confirmations int(11) unsigned NOT NULL DEFAULT 0,',
  208. 'unsubscribe_token char(15) NULL,',
  209. 'link_token char(32) NULL,',
  210. 'engagement_score FLOAT unsigned NULL,',
  211. 'engagement_score_updated_at timestamp NULL,',
  212. 'last_engagement_at timestamp NULL,',
  213. 'PRIMARY KEY (id),',
  214. 'UNIQUE KEY email (email),',
  215. 'UNIQUE KEY unsubscribe_token (unsubscribe_token),',
  216. 'KEY wp_user_id (wp_user_id),',
  217. 'KEY updated_at (updated_at),',
  218. 'KEY status_deleted_at (status,deleted_at),',
  219. 'KEY last_subscribed_at (last_subscribed_at),',
  220. 'KEY engagement_score_updated_at (engagement_score_updated_at),',
  221. 'KEY link_token (link_token)',
  222. ];
  223. return $this->sqlify(__FUNCTION__, $attributes);
  224. }
  225. public function subscriberSegment() {
  226. $attributes = [
  227. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  228. 'subscriber_id int(11) unsigned NOT NULL,',
  229. 'segment_id int(11) unsigned NOT NULL,',
  230. 'status varchar(12) NOT NULL DEFAULT "' . Subscriber::STATUS_SUBSCRIBED . '",',
  231. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  232. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  233. 'PRIMARY KEY (id),',
  234. 'UNIQUE KEY subscriber_segment (subscriber_id,segment_id),',
  235. 'KEY segment_id (segment_id)',
  236. ];
  237. return $this->sqlify(__FUNCTION__, $attributes);
  238. }
  239. public function subscriberCustomField() {
  240. $attributes = [
  241. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  242. 'subscriber_id int(11) unsigned NOT NULL,',
  243. 'custom_field_id int(11) unsigned NOT NULL,',
  244. 'value text NOT NULL,',
  245. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  246. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  247. 'PRIMARY KEY (id),',
  248. 'UNIQUE KEY subscriber_id_custom_field_id (subscriber_id,custom_field_id)',
  249. ];
  250. return $this->sqlify(__FUNCTION__, $attributes);
  251. }
  252. public function subscriberIps() {
  253. $attributes = [
  254. 'ip varchar(45) NOT NULL,',
  255. 'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
  256. 'PRIMARY KEY (created_at, ip),',
  257. 'KEY ip (ip)',
  258. ];
  259. return $this->sqlify(__FUNCTION__, $attributes);
  260. }
  261. public function newsletters() {
  262. $attributes = [
  263. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  264. 'hash varchar(150) NULL DEFAULT NULL,',
  265. 'parent_id int(11) unsigned NULL,',
  266. 'subject varchar(250) NOT NULL DEFAULT "",',
  267. 'type varchar(20) NOT NULL DEFAULT "standard",',
  268. 'sender_address varchar(150) NOT NULL DEFAULT "",',
  269. 'sender_name varchar(150) NOT NULL DEFAULT "",',
  270. 'status varchar(20) NOT NULL DEFAULT "' . Newsletter::STATUS_DRAFT . '",',
  271. 'reply_to_address varchar(150) NOT NULL DEFAULT "",',
  272. 'reply_to_name varchar(150) NOT NULL DEFAULT "",',
  273. 'preheader varchar(250) NOT NULL DEFAULT "",',
  274. 'body longtext,',
  275. 'sent_at timestamp NULL,',
  276. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  277. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  278. 'deleted_at timestamp NULL,',
  279. 'unsubscribe_token char(15) NULL,',
  280. 'ga_campaign varchar(250) NOT NULL DEFAULT "",',
  281. 'PRIMARY KEY (id),',
  282. 'UNIQUE KEY unsubscribe_token (unsubscribe_token),',
  283. 'KEY type_status (type,status)',
  284. ];
  285. return $this->sqlify(__FUNCTION__, $attributes);
  286. }
  287. public function newsletterTemplates() {
  288. $attributes = [
  289. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  290. 'newsletter_id int(11) NULL DEFAULT 0,',
  291. 'name varchar(250) NOT NULL,',
  292. 'categories varchar(250) NOT NULL DEFAULT "[]",',
  293. 'description varchar(255) NOT NULL DEFAULT "",',
  294. 'body longtext,',
  295. 'thumbnail longtext,',
  296. 'readonly tinyint(1) DEFAULT 0,',
  297. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  298. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  299. 'PRIMARY KEY (id)',
  300. ];
  301. return $this->sqlify(__FUNCTION__, $attributes);
  302. }
  303. public function newsletterOptionFields() {
  304. $attributes = [
  305. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  306. 'name varchar(90) NOT NULL,',
  307. 'newsletter_type varchar(90) NOT NULL,',
  308. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  309. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  310. 'PRIMARY KEY (id),',
  311. 'UNIQUE KEY name_newsletter_type (newsletter_type,name)',
  312. ];
  313. return $this->sqlify(__FUNCTION__, $attributes);
  314. }
  315. public function newsletterOption() {
  316. $attributes = [
  317. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  318. 'newsletter_id int(11) unsigned NOT NULL,',
  319. 'option_field_id int(11) unsigned NOT NULL,',
  320. 'value longtext,',
  321. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  322. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  323. 'PRIMARY KEY (id),',
  324. 'UNIQUE KEY newsletter_id_option_field_id (newsletter_id,option_field_id)',
  325. ];
  326. return $this->sqlify(__FUNCTION__, $attributes);
  327. }
  328. public function newsletterSegment() {
  329. $attributes = [
  330. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  331. 'newsletter_id int(11) unsigned NOT NULL,',
  332. 'segment_id int(11) unsigned NOT NULL,',
  333. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  334. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  335. 'PRIMARY KEY (id),',
  336. 'UNIQUE KEY newsletter_segment (newsletter_id,segment_id)',
  337. ];
  338. return $this->sqlify(__FUNCTION__, $attributes);
  339. }
  340. public function newsletterLinks() {
  341. $attributes = [
  342. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  343. 'newsletter_id int(11) unsigned NOT NULL,',
  344. 'queue_id int(11) unsigned NOT NULL,',
  345. 'url varchar(2083) NOT NULL,',
  346. 'hash varchar(20) NOT NULL,',
  347. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  348. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  349. 'PRIMARY KEY (id),',
  350. 'KEY newsletter_id (newsletter_id),',
  351. 'KEY queue_id (queue_id),',
  352. 'KEY url (url(100))',
  353. ];
  354. return $this->sqlify(__FUNCTION__, $attributes);
  355. }
  356. public function newsletterPosts() {
  357. $attributes = [
  358. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  359. 'newsletter_id int(11) unsigned NOT NULL,',
  360. 'post_id int(11) unsigned NOT NULL,',
  361. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  362. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  363. 'PRIMARY KEY (id),',
  364. 'KEY newsletter_id (newsletter_id)',
  365. ];
  366. return $this->sqlify(__FUNCTION__, $attributes);
  367. }
  368. public function forms() {
  369. $attributes = [
  370. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  371. 'name varchar(90) NOT NULL,', // should be null but db_delta can't handle this change
  372. 'status varchar(20) NOT NULL DEFAULT "' . FormEntity::STATUS_ENABLED . '",',
  373. 'body longtext,',
  374. 'settings longtext,',
  375. 'styles longtext,',
  376. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  377. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  378. 'deleted_at timestamp NULL,',
  379. 'PRIMARY KEY (id)',
  380. ];
  381. return $this->sqlify(__FUNCTION__, $attributes);
  382. }
  383. public function statisticsNewsletters() {
  384. $attributes = [
  385. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  386. 'newsletter_id int(11) unsigned NOT NULL,',
  387. 'subscriber_id int(11) unsigned NOT NULL,',
  388. 'queue_id int(11) unsigned NOT NULL,',
  389. 'sent_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  390. 'PRIMARY KEY (id),',
  391. 'KEY newsletter_id (newsletter_id),',
  392. 'KEY subscriber_id (subscriber_id)',
  393. ];
  394. return $this->sqlify(__FUNCTION__, $attributes);
  395. }
  396. public function statisticsBounces() {
  397. $attributes = [
  398. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  399. 'newsletter_id int(11) unsigned NOT NULL,',
  400. 'subscriber_id int(11) unsigned NOT NULL,',
  401. 'queue_id int(11) unsigned NOT NULL,',
  402. 'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
  403. 'PRIMARY KEY (id)',
  404. ];
  405. return $this->sqlify(__FUNCTION__, $attributes);
  406. }
  407. public function statisticsClicks() {
  408. $attributes = [
  409. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  410. 'newsletter_id int(11) unsigned NOT NULL,',
  411. 'subscriber_id int(11) unsigned NOT NULL,',
  412. 'queue_id int(11) unsigned NOT NULL,',
  413. 'link_id int(11) unsigned NOT NULL,',
  414. 'user_agent_id int(11) unsigned NULL,',
  415. 'user_agent_type tinyint(1) NOT NULL DEFAULT 0,',
  416. 'count int(11) unsigned NOT NULL,',
  417. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  418. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  419. 'PRIMARY KEY (id),',
  420. 'KEY newsletter_id_subscriber_id_user_agent_type (newsletter_id, subscriber_id, user_agent_type),',
  421. 'KEY queue_id (queue_id),',
  422. 'KEY subscriber_id (subscriber_id)',
  423. ];
  424. return $this->sqlify(__FUNCTION__, $attributes);
  425. }
  426. public function statisticsOpens() {
  427. $attributes = [
  428. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  429. 'newsletter_id int(11) unsigned NOT NULL,',
  430. 'subscriber_id int(11) unsigned NOT NULL,',
  431. 'queue_id int(11) unsigned NOT NULL,',
  432. 'user_agent_id int(11) unsigned NULL,',
  433. 'user_agent_type tinyint(1) NOT NULL DEFAULT 0,',
  434. 'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
  435. 'PRIMARY KEY (id),',
  436. 'KEY newsletter_id_subscriber_id_user_agent_type (newsletter_id, subscriber_id, user_agent_type),',
  437. 'KEY queue_id (queue_id),',
  438. 'KEY subscriber_id (subscriber_id),',
  439. 'KEY created_at (created_at),',
  440. 'KEY subscriber_id_created_at (subscriber_id, created_at)',
  441. ];
  442. return $this->sqlify(__FUNCTION__, $attributes);
  443. }
  444. public function statisticsUnsubscribes() {
  445. $attributes = [
  446. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  447. 'newsletter_id int(11) unsigned NULL,',
  448. 'subscriber_id int(11) unsigned NOT NULL,',
  449. 'queue_id int(11) unsigned NULL,',
  450. 'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
  451. "source varchar(255) DEFAULT 'unknown',",
  452. 'meta varchar(255) NULL,',
  453. 'PRIMARY KEY (id),',
  454. 'KEY newsletter_id_subscriber_id (newsletter_id, subscriber_id),',
  455. 'KEY queue_id (queue_id),',
  456. 'KEY subscriber_id (subscriber_id)',
  457. ];
  458. return $this->sqlify(__FUNCTION__, $attributes);
  459. }
  460. public function statisticsForms() {
  461. $attributes = [
  462. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  463. 'form_id int(11) unsigned NOT NULL,',
  464. 'subscriber_id int(11) unsigned NOT NULL,',
  465. 'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
  466. 'PRIMARY KEY (id),',
  467. 'UNIQUE KEY form_subscriber (form_id,subscriber_id)',
  468. ];
  469. return $this->sqlify(__FUNCTION__, $attributes);
  470. }
  471. public function statisticsWoocommercePurchases() {
  472. $attributes = [
  473. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  474. 'newsletter_id int(11) unsigned NOT NULL,',
  475. 'subscriber_id int(11) unsigned NOT NULL,',
  476. 'queue_id int(11) unsigned NOT NULL,',
  477. 'click_id int(11) unsigned NOT NULL,',
  478. 'order_id bigint(20) unsigned NOT NULL,',
  479. 'order_currency char(3) NOT NULL,',
  480. 'order_price_total float NOT NULL COMMENT "With shipping and taxes in order_currency",',
  481. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  482. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  483. 'PRIMARY KEY (id),',
  484. 'KEY newsletter_id (newsletter_id),',
  485. 'KEY queue_id (queue_id),',
  486. 'KEY subscriber_id (subscriber_id),',
  487. 'UNIQUE KEY click_id_order_id (click_id, order_id)',
  488. ];
  489. return $this->sqlify(__FUNCTION__, $attributes);
  490. }
  491. public function mappingToExternalEntities() {
  492. $attributes = [
  493. 'old_id int(11) unsigned NOT NULL,',
  494. 'type varchar(50) NOT NULL,',
  495. 'new_id int(11) unsigned NOT NULL,',
  496. 'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,',
  497. 'PRIMARY KEY (old_id, type),',
  498. 'KEY new_id (new_id)',
  499. ];
  500. return $this->sqlify(__FUNCTION__, $attributes);
  501. }
  502. public function log() {
  503. $attributes = [
  504. 'id bigint(20) unsigned NOT NULL AUTO_INCREMENT,',
  505. 'name varchar(255),',
  506. 'level int(11),',
  507. 'message longtext,',
  508. 'created_at timestamp DEFAULT CURRENT_TIMESTAMP,',
  509. 'PRIMARY KEY (id)',
  510. ];
  511. return $this->sqlify(__FUNCTION__, $attributes);
  512. }
  513. public function userFlags() {
  514. $attributes = [
  515. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  516. 'user_id bigint(20) NOT NULL,',
  517. 'name varchar(50) NOT NULL,',
  518. 'value varchar(255),',
  519. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  520. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  521. 'PRIMARY KEY (id),',
  522. 'UNIQUE KEY user_id_name (user_id, name)',
  523. ];
  524. return $this->sqlify(__FUNCTION__, $attributes);
  525. }
  526. public function featureFlags() {
  527. $attributes = [
  528. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  529. 'name varchar(100) NOT NULL,',
  530. 'value tinyint(1),',
  531. 'created_at timestamp NULL,', // must be NULL, see comment at the top
  532. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  533. 'PRIMARY KEY (id),',
  534. 'UNIQUE KEY name (name)',
  535. ];
  536. return $this->sqlify(__FUNCTION__, $attributes);
  537. }
  538. public function dynamicSegmentFilters() {
  539. $attributes = [
  540. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  541. 'segment_id int(11) unsigned NOT NULL,',
  542. 'created_at timestamp NULL,',
  543. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  544. 'filter_data longblob,',
  545. 'PRIMARY KEY (id),',
  546. 'KEY segment_id (segment_id)',
  547. ];
  548. return $this->sqlify(__FUNCTION__, $attributes);
  549. }
  550. public function userAgents() {
  551. $attributes = [
  552. 'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
  553. 'hash varchar(32) UNIQUE NOT NULL, ',
  554. 'user_agent text NOT NULL, ',
  555. 'created_at timestamp NULL,',
  556. 'updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
  557. 'PRIMARY KEY (id)',
  558. ];
  559. return $this->sqlify(__FUNCTION__, $attributes);
  560. }
  561. private function sqlify($model, $attributes) {
  562. $table = $this->prefix . Helpers::camelCaseToUnderscore($model);
  563. $sql = [];
  564. $sql[] = "CREATE TABLE " . $table . " (";
  565. $sql = array_merge($sql, $attributes);
  566. $sql[] = ") " . $this->charsetCollate . ";";
  567. return implode("\n", $sql);
  568. }
  569. private function updateNullInUnsubscribeStats() {
  570. global $wpdb;
  571. // perform once for versions below or equal to 3.47.6
  572. if (version_compare($this->settings->get('db_version', '3.47.6'), '3.47.6', '>')) {
  573. return false;
  574. }
  575. $query = "
  576. ALTER TABLE `{$this->prefix}statistics_unsubscribes`
  577. CHANGE `newsletter_id` `newsletter_id` int(11) unsigned NULL,
  578. CHANGE `queue_id` `queue_id` int(11) unsigned NULL;
  579. ";
  580. $wpdb->query($query);
  581. return true;
  582. }
  583. /**
  584. * This method adds updated_at column to scheduled_task_subscribers for users with old MySQL..
  585. * Updated_at was added after created_at column and created_at used to have default CURRENT_TIMESTAMP.
  586. * Since MySQL versions below 5.6.5 allow only one column with CURRENT_TIMESTAMP as default per table
  587. * and db_delta doesn't remove default values we need to perform this change manually..
  588. * @return bool
  589. */
  590. private function fixScheduledTasksSubscribersTimestampColumns() {
  591. // skip the migration if the DB version is higher than 3.63.0 or is not set (a new install)
  592. if (version_compare($this->settings->get('db_version', '3.63.1'), '3.63.0', '>')) {
  593. return false;
  594. }
  595. global $wpdb;
  596. $scheduledTasksSubscribersTable = "{$this->prefix}scheduled_task_subscribers";
  597. // Remove default CURRENT_TIMESTAMP from created_at
  598. $updateCreatedAtQuery = "
  599. ALTER TABLE `$scheduledTasksSubscribersTable`
  600. CHANGE `created_at` `created_at` timestamp NULL;
  601. ";
  602. $wpdb->query($updateCreatedAtQuery);
  603. // Add updated_at column in case it doesn't exist
  604. $updatedAtColumnExists = $wpdb->get_results("
  605. SELECT COLUMN_NAME
  606. FROM INFORMATION_SCHEMA.COLUMNS
  607. WHERE table_name = '$scheduledTasksSubscribersTable' AND column_name = 'updated_at';
  608. ");
  609. if (empty($updatedAtColumnExists)) {
  610. $addUpdatedAtQuery = "
  611. ALTER TABLE `$scheduledTasksSubscribersTable`
  612. ADD `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
  613. ";
  614. $wpdb->query($addUpdatedAtQuery);
  615. }
  616. return true;
  617. }
  618. private function removeDeprecatedStatisticsIndexes(): bool {
  619. global $wpdb;
  620. // skip the migration if the DB version is higher than 3.67.1 or is not set (a new install)
  621. if (version_compare($this->settings->get('db_version', '3.67.1'), '3.67.1', '>')) {
  622. return false;
  623. }
  624. $dbName = Env::$dbName;
  625. $statisticsTables = [
  626. "{$this->prefix}statistics_clicks",
  627. "{$this->prefix}statistics_opens",
  628. ];
  629. foreach ($statisticsTables as $statisticsTable) {
  630. $oldStatisticsIndexExists = $wpdb->get_results("
  631. SELECT DISTINCT INDEX_NAME
  632. FROM INFORMATION_SCHEMA.STATISTICS
  633. WHERE TABLE_SCHEMA = '{$dbName}'
  634. AND TABLE_NAME = '$statisticsTable'
  635. AND INDEX_NAME='newsletter_id_subscriber_id'
  636. ");
  637. if (!empty($oldStatisticsIndexExists)) {
  638. $dropIndexQuery = "
  639. ALTER TABLE `{$statisticsTable}`
  640. DROP INDEX `newsletter_id_subscriber_id`
  641. ";
  642. $wpdb->query($dropIndexQuery);
  643. }
  644. }
  645. return true;
  646. }
  647. }