PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/system_remove/modules/avisota/AvisotaUpdate.php

https://github.com/avisota/contao-core
PHP | 1052 lines | 831 code | 117 blank | 104 comment | 139 complexity | dd6d70b18f9351fa4089f364faf6903e MD5 | raw file
  1. <?php
  2. /**
  3. * Avisota newsletter and mailing system
  4. * Copyright © 2016 Sven Baumann
  5. *
  6. * PHP version 5
  7. *
  8. * @copyright way.vision 2015
  9. * @author Sven Baumann <baumann.sv@gmail.com>
  10. * @package avisota/contao-core
  11. * @license LGPL-3.0+
  12. * @filesource
  13. */
  14. /**
  15. * Class AvisotaUpdate
  16. *
  17. * @copyright way.vision 2015
  18. * @author Sven Baumann <baumann.sv@gmail.com>
  19. * @package avisota/contao-core
  20. */
  21. class AvisotaUpdate extends BackendModule
  22. {
  23. /**
  24. * Updates
  25. */
  26. public static $updates = array
  27. (
  28. '0.4.5' => array('required' => true),
  29. '1.5.0' => array('required' => true),
  30. '1.5.1' => array('required' => true),
  31. '2.0.0-u1' => array('required' => true),
  32. '2.0.0-u2' => array(),
  33. '2.0.0-u3' => array()
  34. );
  35. /**
  36. * @var AvisotaUpdate
  37. */
  38. protected static $instance = null;
  39. public static function getInstance()
  40. {
  41. if (self::$instance === null) {
  42. self::$instance = new AvisotaUpdate();
  43. }
  44. return self::$instance;
  45. }
  46. /**
  47. * @var Database
  48. */
  49. protected $Database;
  50. /**
  51. * Template file
  52. *
  53. * @var string
  54. */
  55. protected $strTemplate = 'be_avisota_update';
  56. public function hasUpdates()
  57. {
  58. foreach (self::$updates as $version => $updates) {
  59. $methodName = 'check' . preg_replace('#[^\w]#', '_', $version);
  60. if ($this->$methodName()) {
  61. return true;
  62. }
  63. }
  64. return false;
  65. }
  66. /**
  67. * Generate the backend module.
  68. *
  69. * @return string
  70. */
  71. public function generate()
  72. {
  73. $this->loadLanguageFile('avisota_update');
  74. if ($this->Input->post('FORM_SUBMIT') == 'avisota_update') {
  75. // on db update, redirect to er client
  76. if ($this->Input->post('dbupdate')) {
  77. $this->redirect('contao/main.php?do=repository_manager&update=database');
  78. }
  79. // check for updates
  80. if ($this->Input->post('update')) {
  81. $versions = $this->Input->post('update');
  82. $version = array_shift($versions);
  83. try {
  84. if ($this->runUpdate($version)) {
  85. $_SESSION['TL_INFO'][] = $GLOBALS['TL_LANG']['avisota_update']['updateSuccess'];
  86. }
  87. else {
  88. array_unshift($versions, $version);
  89. $_SESSION['TL_ERROR'][] = $GLOBALS['TL_LANG']['avisota_update']['updateFailed'];
  90. }
  91. }
  92. catch (Exception $e) {
  93. array_unshift($versions, $version);
  94. $_SESSION['TL_ERROR'][] = $e->getMessage();
  95. }
  96. if (count($versions)) {
  97. $_SESSION['TL_INFO'][] = $GLOBALS['TL_LANG']['avisota_update']['moreUpdates'];
  98. $_SESSION['AUTORUN_UPDATES'] = $versions;
  99. }
  100. else {
  101. unset($_SESSION['AUTORUN_UPDATES']);
  102. }
  103. }
  104. $this->reload();
  105. }
  106. if ($this->Environment->isAjaxRequest) {
  107. $version = $this->Input->get('update');
  108. if ($this->runUpdate($version)) {
  109. header('Content-Type: text/plain');
  110. echo $GLOBALS['TL_LANG']['avisota_update']['updateSuccess'];
  111. exit;
  112. }
  113. header("HTTP/1.0 500 Internal Server Error");
  114. header('Content-Type: text/plain');
  115. echo $GLOBALS['TL_LANG']['avisota_update']['updateFailed'];
  116. exit;
  117. }
  118. $GLOBALS['TL_JAVASCRIPT']['avisota_update'] = 'assets/avisota/core/css/avisota_update.js';
  119. return parent::generate();
  120. }
  121. /**
  122. * Compile the current element
  123. */
  124. protected function compile()
  125. {
  126. $this->Template->updates = self::$updates;
  127. $versions = array();
  128. $statuses = array();
  129. foreach (self::$updates as $version => $updates) {
  130. $methodName = 'check' . preg_replace('#[^\w]#', '_', $version);
  131. $statuses[$version] = $this->$methodName();
  132. $shortVersion = preg_replace('#^(\d+\.\d+\.\d+).*$#', '$1', $version);
  133. $versions[$shortVersion] = (isset($versions[$shortVersion]) ? $versions[$shortVersion]
  134. : false) || $statuses[$version];
  135. }
  136. $this->Template->status = $statuses;
  137. uksort($versions, 'version_compare');
  138. $lastVersion = '0.3.x';
  139. foreach ($versions as $version => $requireUpdate) {
  140. if ($requireUpdate) {
  141. break;
  142. }
  143. $lastVersion = $version;
  144. }
  145. $this->Template->previous = $lastVersion;
  146. }
  147. protected function runUpdate($version)
  148. {
  149. if (isset(self::$updates[$version])) {
  150. $methodName = 'upgrade' . preg_replace('#[^\w]#', '_', $version);
  151. return $this->$methodName();
  152. }
  153. $this->log('Try to run illegal update to version ' . $version . '!', 'AvisotaUpdate::update', TL_ERROR);
  154. throw new Exception('Try to run illegal update to version ' . $version . '!');
  155. }
  156. protected function check0_4_5()
  157. {
  158. return \Database::getInstance()->tableExists('orm_avisota_message_content')
  159. && !\Database::getInstance()->fieldExists('cell', 'orm_avisota_message_content');
  160. }
  161. /**
  162. * Database upgrade to 0.4.5
  163. */
  164. protected function upgrade0_4_5()
  165. {
  166. try {
  167. if (\Database::getInstance()->tableExists('orm_avisota_message_content')) {
  168. if (!\Database::getInstance()->fieldExists('cell', 'orm_avisota_message_content')) {
  169. \Database::getInstance()
  170. ->execute("ALTER TABLE orm_avisota_message_content ADD cell varchar(32) NOT NULL default ''");
  171. }
  172. \Database::getInstance()
  173. ->prepare("UPDATE orm_avisota_message_content SET cell=? WHERE cell=?")
  174. ->execute('body', '');
  175. }
  176. }
  177. catch (Exception $e) {
  178. $this->log($e->getMessage() . "\n" . $e->getTraceAsString(), 'AvisotaRunonce::upgrade_0_4_5', TL_ERROR);
  179. return false;
  180. }
  181. return true;
  182. }
  183. /**
  184. * Database upgrade to 1.5.0
  185. */
  186. protected function check1_5_0()
  187. {
  188. return \Database::getInstance()->tableExists('orm_avisota_message_outbox')
  189. && (!\Database::getInstance()->tableExists('orm_avisota_message_outbox_recipient') ||
  190. !\Database::getInstance()->fieldExists('tstamp', 'orm_avisota_message_outbox') ||
  191. \Database::getInstance()->fieldExists('token', 'orm_avisota_message_outbox')
  192. && \Database::getInstance()->fieldExists('email', 'orm_avisota_message_outbox')
  193. && \Database::getInstance()->fieldExists('send', 'orm_avisota_message_outbox')
  194. && \Database::getInstance()->fieldExists('source', 'orm_avisota_message_outbox')
  195. && \Database::getInstance()->fieldExists('failed', 'orm_avisota_message_outbox'));
  196. }
  197. /**
  198. * Database upgrade to 1.5.0
  199. */
  200. protected function upgrade1_5_0()
  201. {
  202. try {
  203. if (\Database::getInstance()->tableExists('orm_avisota_message_outbox')) {
  204. if (!\Database::getInstance()->tableExists('orm_avisota_message_outbox_recipient')) {
  205. // create outbox recipient table
  206. \Database::getInstance()->execute(
  207. "CREATE TABLE `orm_avisota_message_outbox_recipient` (
  208. `id` int(10) unsigned NOT NULL auto_increment,
  209. `pid` int(10) unsigned NOT NULL default '0',
  210. `tstamp` int(10) unsigned NOT NULL default '0',
  211. `email` varchar(255) NOT NULL default '',
  212. `domain` varchar(255) NOT NULL default '',
  213. `recipientID` int(10) unsigned NOT NULL default '0',
  214. `source` varchar(255) NOT NULL default '',
  215. `sourceID` int(10) unsigned NOT NULL default '0',
  216. `send` int(10) unsigned NOT NULL default '0',
  217. `failed` char(1) NOT NULL default '',
  218. `error` blob NULL,
  219. PRIMARY KEY (`id`),
  220. KEY `pid` (`pid`),
  221. KEY `email` (`email`),
  222. KEY `domain` (`domain`),
  223. KEY `send` (`send`),
  224. KEY `source` (`source`),
  225. KEY `sourceID` (`sourceID`)
  226. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
  227. );
  228. }
  229. // make sure the tstamp field exists
  230. if (!\Database::getInstance()->fieldExists('tstamp', 'orm_avisota_message_outbox')) {
  231. \Database::getInstance()
  232. ->execute(
  233. "ALTER TABLE orm_avisota_message_outbox ADD tstamp int(10) unsigned NOT NULL default '0'"
  234. );
  235. }
  236. // split the outbox table data
  237. if (\Database::getInstance()->fieldExists('token', 'orm_avisota_message_outbox')
  238. && \Database::getInstance()->fieldExists('email', 'orm_avisota_message_outbox')
  239. && \Database::getInstance()->fieldExists('send', 'orm_avisota_message_outbox')
  240. && \Database::getInstance()->fieldExists('source', 'orm_avisota_message_outbox')
  241. && \Database::getInstance()->fieldExists('failed', 'orm_avisota_message_outbox')
  242. ) {
  243. $outbox = \Database::getInstance()
  244. ->execute("SELECT DISTINCT pid,token FROM orm_avisota_message_outbox");
  245. $newsletterDataSets = $outbox->fetchAllAssoc();
  246. if (count($newsletterDataSets)) {
  247. $outboxeIds = array();
  248. // create the outboxes
  249. foreach ($newsletterDataSets as $newsletterData) {
  250. if ($newsletterData['token']) {
  251. $time = \Database::getInstance()
  252. ->prepare(
  253. "SELECT IF (tstamp, tstamp, send) as time FROM (SELECT MIN(tstamp) as tstamp, MIN(send) as send FROM orm_avisota_message_outbox WHERE token=? GROUP BY token) t"
  254. )
  255. ->execute($newsletterData['token'])
  256. ->time;
  257. $outboxeIds[$newsletterData['token']] = \Database::getInstance()
  258. ->prepare("INSERT INTO orm_avisota_message_outbox SET pid=?, tstamp=?")
  259. ->execute($newsletterData['pid'], $time)
  260. ->insertId;
  261. }
  262. }
  263. // move the recipients
  264. foreach ($outboxeIds as $token => $outboxId) {
  265. \Database::getInstance()
  266. ->prepare(
  267. "INSERT INTO orm_avisota_message_outbox_recipient (pid,tstamp,email,domain,send,source,sourceID,failed)
  268. SELECT
  269. ?,
  270. tstamp,
  271. email,
  272. SUBSTRING(email, LOCATE('@', email)+1) as domain,
  273. send,
  274. SUBSTRING(source, 1, LOCATE(':', source)-1) as source,
  275. SUBSTRING(source, LOCATE(':', source)+1) as sourceID,
  276. failed
  277. FROM orm_avisota_message_outbox
  278. WHERE token=?"
  279. )
  280. ->execute($outboxId, $token);
  281. }
  282. // update recipientID
  283. $recipient = \Database::getInstance()
  284. ->execute("SELECT * FROM orm_avisota_message_outbox_recipient WHERE recipientID=0");
  285. while ($recipient->next()) {
  286. switch ($recipient->source) {
  287. case 'list':
  288. $resultSet = \Database::getInstance()
  289. ->prepare("SELECT id FROM orm_avisota_recipient WHERE email=? AND pid=?")
  290. ->execute($recipient->email, $recipient->sourceID);
  291. break;
  292. case 'mgroup':
  293. $resultSet = \Database::getInstance()
  294. ->prepare("SELECT id FROM tl_member WHERE email=?")
  295. ->execute($recipient->email);
  296. break;
  297. default:
  298. continue;
  299. }
  300. if ($resultSet->next()) {
  301. \Database::getInstance()
  302. ->prepare(
  303. "UPDATE orm_avisota_message_outbox_recipient SET recipientID=? WHERE id=?"
  304. )
  305. ->execute($resultSet->id, $recipient->id);
  306. }
  307. }
  308. // delete old entries from outbox
  309. \Database::getInstance()
  310. ->execute(
  311. "DELETE FROM orm_avisota_message_outbox WHERE id NOT IN (" . implode(
  312. ',',
  313. $outboxeIds
  314. ) . ")"
  315. );
  316. // delete old fields
  317. foreach (array('token', 'email', 'send', 'source', 'failed') as $fieldName) {
  318. if (\Database::getInstance()->fieldExists($fieldName, 'orm_avisota_message_outbox')) {
  319. \Database::getInstance()->execute('ALTER TABLE orm_avisota_message_outbox DROP ' . $fieldName);
  320. }
  321. }
  322. }
  323. }
  324. }
  325. }
  326. catch (Exception $e) {
  327. $this->log($e->getMessage() . "\n" . $e->getTraceAsString(), 'AvisotaRunonce::upgrade_1_5_0', TL_ERROR);
  328. return false;
  329. }
  330. return true;
  331. }
  332. protected function check1_5_1()
  333. {
  334. return \Database::getInstance()->tableExists('orm_avisota_statistic_raw_recipient_link')
  335. && !\Database::getInstance()->fieldExists('real_url', 'orm_avisota_statistic_raw_recipient_link')
  336. && \Database::getInstance()->executeUncached(
  337. "SELECT * FROM orm_avisota_statistic_raw_recipient_link WHERE (real_url='' OR ISNULL(real_url)) AND url REGEXP 'email=[^…]' LIMIT 1"
  338. )->numRows
  339. || \Database::getInstance()->tableExists('orm_avisota_statistic_raw_link')
  340. && \Database::getInstance()->execute(
  341. "SELECT * FROM orm_avisota_statistic_raw_link WHERE url REGEXP '&#x?[0-9]+;' LIMIT 1"
  342. )->numRows;
  343. }
  344. /**
  345. * Database upgrade to 1.5.1
  346. */
  347. protected function upgrade1_5_1()
  348. {
  349. try {
  350. if (\Database::getInstance()->tableExists('orm_avisota_statistic_raw_recipient_link')) {
  351. $this->import('AvisotaStatic', 'Static');
  352. // make sure the real_url field exists
  353. if (!\Database::getInstance()->fieldExists('real_url', 'orm_avisota_statistic_raw_recipient_link')) {
  354. \Database::getInstance()
  355. ->execute("ALTER TABLE orm_avisota_statistic_raw_recipient_link ADD real_url blob NULL");
  356. }
  357. // temporary caches
  358. $newsletterCache = array();
  359. $categoryCache = array();
  360. $unsubscribeCache = array();
  361. // links that are reduced
  362. $links = array();
  363. $link = \Database::getInstance()
  364. ->executeUncached(
  365. "SELECT * FROM orm_avisota_statistic_raw_recipient_link WHERE (real_url='' OR ISNULL(real_url)) AND url REGEXP 'email=[^…]'"
  366. );
  367. while ($link->next()) {
  368. $newsletter = false;
  369. $category = false;
  370. $unsubscribeUrl = false;
  371. if (isset($newsletterCache[$link->pid])) {
  372. $newsletter = $newsletterCache[$link->pid];
  373. }
  374. else {
  375. $newsletter = \Database::getInstance()
  376. ->prepare("SELECT * FROM orm_avisota_message WHERE id=?")
  377. ->execute($link->pid);
  378. if ($newsletter->next()) {
  379. $newsletter = $newsletterCache[$link->pid] = (object) $newsletter->row();
  380. }
  381. else {
  382. $newsletter = $newsletterCache[$link->pid] = false;
  383. }
  384. }
  385. if ($newsletter) {
  386. if (isset($categoryCache[$newsletter->pid])) {
  387. $category = $categoryCache[$newsletter->pid];
  388. }
  389. else {
  390. $category = \Database::getInstance()
  391. ->prepare("SELECT * FROM orm_avisota_message_category WHERE id=?")
  392. ->execute($newsletter->pid);
  393. if ($category->next()) {
  394. $category = $categoryCache[$newsletter->pid] = (object) $category->row();
  395. }
  396. else {
  397. $category = $categoryCache[$newsletter->pid] = false;
  398. }
  399. }
  400. }
  401. if ($category) {
  402. if (isset($unsubscribeCache[$link->recipient])) {
  403. $unsubscribeUrl = $unsubscribeCache[$link->recipient];
  404. }
  405. else {
  406. $recipientData = array('email' => $link->recipient);
  407. $this->Static->set($category, $newsletter, $recipientData);
  408. $unsubscribeUrl = $unsubscribeCache[$link->recipient] = $this->replaceInsertTags(
  409. '{{newsletter::unsubscribe_url}}'
  410. );
  411. }
  412. }
  413. if ($unsubscribeUrl && $unsubscribeUrl == $link->url) {
  414. // create a new (real) url
  415. $realUrl = $link->url;
  416. $url = preg_replace('#email=[^&]*#', 'email=…', $link->url);
  417. // update the recipient-less-link
  418. if (!$links[$url]) {
  419. \Database::getInstance()
  420. ->prepare("UPDATE orm_avisota_statistic_raw_link SET url=? WHERE id=?")
  421. ->execute($url, $link->linkID);
  422. $links[$url] = $link->linkID;
  423. }
  424. // or delete if there is allready a link with this url
  425. else {
  426. \Database::getInstance()
  427. ->prepare("DELETE FROM orm_avisota_statistic_raw_link WHERE id=?")
  428. ->execute($link->linkID);
  429. }
  430. // update the recipient-link
  431. \Database::getInstance()
  432. ->prepare(
  433. "UPDATE orm_avisota_statistic_raw_recipient_link SET linkID=?, url=?, real_url=? WHERE id=?"
  434. )
  435. ->execute($links[$url], $url, $realUrl, $link->id);
  436. // update link hit
  437. \Database::getInstance()
  438. ->prepare(
  439. "UPDATE orm_avisota_statistic_raw_link_hit SET linkID=? WHERE linkID=? AND recipientLinkID=?"
  440. )
  441. ->execute($links[$url], $link->linkID, $link->id);
  442. }
  443. }
  444. }
  445. }
  446. catch (Exception $e) {
  447. $this->log($e->getMessage() . "\n" . $e->getTraceAsString(), 'AvisotaRunonce::upgrade_1_5_1()', TL_ERROR);
  448. return false;
  449. }
  450. try {
  451. if (\Database::getInstance()->tableExists('orm_avisota_statistic_raw_link')) {
  452. // cache url->id
  453. $cache = array();
  454. // find and clean html entities encoded urls
  455. $link = \Database::getInstance()->execute(
  456. "SELECT * FROM orm_avisota_statistic_raw_link WHERE url REGEXP '&#x?[0-9]+;'"
  457. );
  458. while ($link->next()) {
  459. // decorde url
  460. $url = html_entity_decode($link->url);
  461. // search cache
  462. if (isset($cache[$link->pid][$url])) {
  463. $linkId = $cache[$link->pid][$url];
  464. }
  465. // or search existing record
  466. else {
  467. $existingLink = \Database::getInstance()
  468. ->prepare("SELECT * FROM orm_avisota_statistic_raw_link WHERE pid=? AND url=?")
  469. ->executeUncached($link->pid, $url);
  470. if ($existingLink->next()) {
  471. // use existing record
  472. $linkId = $existingLink->id;
  473. }
  474. else {
  475. // insert new record
  476. $linkId = \Database::getInstance()
  477. ->prepare("INSERT INTO orm_avisota_statistic_raw_link (pid,tstamp,url) VALUES (?, ?, ?)")
  478. ->executeUncached($link->pid, $link->tstamp, $url)
  479. ->insertId;
  480. }
  481. // set cache
  482. $cache[$link->pid][$url] = $linkId;
  483. }
  484. // update recipient link
  485. \Database::getInstance()
  486. ->prepare("UPDATE orm_avisota_statistic_raw_recipient_link SET linkId=? WHERE linkId=?")
  487. ->execute($linkId, $link->id);
  488. // delete old record
  489. \Database::getInstance()
  490. ->prepare("DELETE FROM orm_avisota_statistic_raw_link WHERE id=?")
  491. ->execute($link->id);
  492. $this->log('Cleaned html encoded url "' . $url . '"', 'AvisotaRunonce::upgrade1_5_1()', TL_INFO);
  493. }
  494. }
  495. }
  496. catch (Exception $e) {
  497. $this->log($e->getMessage() . "\n" . $e->getTraceAsString(), 'AvisotaRunonce::upgrade1_5_1()', TL_ERROR);
  498. return false;
  499. }
  500. return true;
  501. }
  502. protected function check2_0_0_u1()
  503. {
  504. return \Database::getInstance()->tableExists('orm_avisota_recipient_list')
  505. && !\Database::getInstance()->tableExists('orm_avisota_mailing_list')
  506. || \Database::getInstance()->tableExists('orm_avisota_recipient_list')
  507. && \Database::getInstance()->tableExists('orm_avisota_mailing_list')
  508. && \Database::getInstance()->execute("SELECT COUNT(id) AS c FROM orm_avisota_recipient_list")->c > 0
  509. && \Database::getInstance()->execute("SELECT COUNT(id) AS c FROM orm_avisota_mailing_list")->c == 0;
  510. }
  511. protected function upgrade2_0_0_u1()
  512. {
  513. try {
  514. if (!\Database::getInstance()->tableExists('orm_avisota_mailing_list')) {
  515. \Database::getInstance()->query(
  516. "CREATE TABLE `orm_avisota_mailing_list` (
  517. `id` int(10) unsigned NOT NULL auto_increment,
  518. `tstamp` int(10) unsigned NOT NULL default '0',
  519. `title` varchar(255) NOT NULL default '',
  520. `alias` varbinary(128) NOT NULL default '',
  521. `viewOnlinePage` int(10) unsigned NOT NULL default '0',
  522. PRIMARY KEY (`id`)
  523. ) ENGINE=MyISAM DEFAULT CHARSET=utf8"
  524. );
  525. }
  526. if (!\Database::getInstance()->tableExists('orm_avisota_recipient_to_mailing_list')) {
  527. \Database::getInstance()->query(
  528. "CREATE TABLE `orm_avisota_recipient_to_mailing_list` (
  529. `recipient` int(10) unsigned NOT NULL default '0',
  530. `list` int(10) unsigned NOT NULL default '0',
  531. `confirmationSent` int(10) unsigned NOT NULL default '0',
  532. `reminderSent` int(10) unsigned NOT NULL default '0',
  533. `reminderCount` int(1) unsigned NOT NULL default '0',
  534. `confirmed` char(1) NOT NULL default '',
  535. `token` char(8) NOT NULL default '',
  536. PRIMARY KEY (`recipient`, `list`)
  537. ) ENGINE=MyISAM DEFAULT CHARSET=utf8"
  538. );
  539. }
  540. if (\Database::getInstance()->tableExists('orm_avisota_recipient_list')) {
  541. // create mailing lists from recipient lists
  542. \Database::getInstance()->query(
  543. "INSERT INTO orm_avisota_mailing_list (id, tstamp, title, alias, viewOnlinePage)
  544. SELECT id, tstamp, title, alias, viewOnlinePage FROM orm_avisota_recipient_list"
  545. );
  546. // insert subscriptions into relation table
  547. \Database::getInstance()->query(
  548. "INSERT INTO orm_avisota_recipient_to_mailing_list (recipient, list, confirmed, confirmationSent, reminderSent, reminderCount, token)
  549. SELECT id, pid, confirmed, addedOn, 0, IF(notification, notification, 0), token FROM orm_avisota_recipient"
  550. );
  551. // fetch recipients that are multiple
  552. $recipientDataSets = array();
  553. $recipient = \Database::getInstance()
  554. ->execute(
  555. "SELECT (SELECT COUNT(email) FROM orm_avisota_recipient r2 WHERE r1.email=r2.email) AS c, r1.*
  556. FROM orm_avisota_recipient r1
  557. HAVING c>1
  558. ORDER BY email,tstamp
  559. LIMIT 1000"
  560. );
  561. while ($recipient->next()) {
  562. // convert email to lowercase
  563. $recipient->email = strtolower($recipient->email);
  564. // set first existence
  565. if (!isset($recipientDataSets[$recipient->email])) {
  566. $recipientDataSets[$recipient->email] = $recipient->row();
  567. $recipientDataSets[$recipient->email]['ids'] = array($recipient->id);
  568. $recipientDataSets[$recipient->email]['pids'] = array($recipient->pid);
  569. }
  570. // update fields
  571. else {
  572. $recipientData = & $recipientDataSets[$recipient->email];
  573. // delete duplicate recipient, but use its data
  574. if (in_array($recipient->pid, $recipientDataSets[$recipient->email]['pids'])) {
  575. \Database::getInstance()
  576. ->prepare("DELETE FROM orm_avisota_recipient WHERE id=?")
  577. ->execute($recipient->id);
  578. }
  579. else {
  580. $recipientData['ids'][] = $recipient->id;
  581. $recipientData['pids'][] = $recipient->pid;
  582. }
  583. foreach ($recipient->row() as $field => $value) {
  584. // skip some fields
  585. if ($field == 'id' || $field == 'pid' || $field == 'tstamp' || $field == 'email' || $field == 'confirmed' || $field == 'token' || $field == 'notification') {
  586. continue;
  587. }
  588. // use the lowest value of addedOn
  589. else if ($field == 'addedOn') {
  590. if ($recipientData['addedOn'] > $value && $value > 0 || $recipientData['addedOn'] == 0) {
  591. $recipientData['addedOn'] = $value;
  592. }
  593. }
  594. // update value if previous value is empty or current value is newer
  595. else if (!empty($value) && (empty($recipientData[$field]) || $recipientData['tstamp'] < $recipient->tstamp)) {
  596. $recipientData[$field] = $value;
  597. }
  598. }
  599. if ($recipientData['tstamp'] < $recipient->tstamp) {
  600. $recipientData['tstamp'] = $recipient->tstamp;
  601. }
  602. }
  603. }
  604. foreach ($recipientDataSets as &$recipientData) {
  605. // update subscription
  606. \Database::getInstance()
  607. ->query(
  608. "UPDATE orm_avisota_recipient_to_mailing_list
  609. SET recipient=" . $recipientData['id'] . "
  610. WHERE recipient IN (" . implode(',', $recipientData['ids']) . ")"
  611. );
  612. // delete waste rows
  613. \Database::getInstance()
  614. ->query(
  615. "DELETE FROM orm_avisota_recipient
  616. WHERE id!=" . $recipientData['id'] . " AND id IN (" . implode(
  617. ',',
  618. $recipientData['ids']
  619. ) . ")"
  620. );
  621. // unset fields that are just virtual
  622. unset($recipientData['c'], $recipientData['ids'], $recipientData['pids']);
  623. // update row
  624. \Database::getInstance()
  625. ->prepare("UPDATE orm_avisota_recipient %s WHERE id=?")
  626. ->set($recipientData)
  627. ->execute($recipientData['id']);
  628. }
  629. // reload if there are more
  630. if ($recipient->numRows == 1000) {
  631. $this->reload();
  632. }
  633. }
  634. }
  635. catch (Exception $e) {
  636. $this->log($e->getMessage() . "\n" . $e->getTraceAsString(), 'AvisotaRunonce::upgrade2_0_0_u1()', TL_ERROR);
  637. return false;
  638. }
  639. return true;
  640. }
  641. protected function check2_0_0_u2()
  642. {
  643. return \Database::getInstance()->tableExists('orm_avisota_message_category')
  644. && (!\Database::getInstance()->tableExists('orm_avisota_transport')
  645. || !\Database::getInstance()->fieldExists('transportMode', 'orm_avisota_message_category')
  646. || \Database::getInstance()->execute(
  647. "SELECT COUNT(id) AS c FROM orm_avisota_message_category WHERE transportMode=''"
  648. )->c > 0);
  649. }
  650. protected function upgrade2_0_0_u2()
  651. {
  652. try {
  653. if (\Database::getInstance()->tableExists('orm_avisota_message_category')) {
  654. if (!\Database::getInstance()->tableExists('orm_avisota_transport')) {
  655. \Database::getInstance()->query(
  656. "CREATE TABLE `orm_avisota_transport` (
  657. `id` int(10) unsigned NOT NULL auto_increment,
  658. `tstamp` int(10) unsigned NOT NULL default '0',
  659. `type` varchar(255) NOT NULL default '',
  660. `title` varchar(255) NOT NULL default '',
  661. `sender` varchar(128) NOT NULL default '',
  662. `senderName` varchar(128) NOT NULL default '',
  663. `replyTo` varchar(128) NOT NULL default '',
  664. `replyToName` varchar(128) NOT NULL default '',
  665. `swiftUseSmtp` char(23) NOT NULL default '',
  666. `swiftSmtpHost` varchar(255) NOT NULL default '',
  667. `swiftSmtpUser` varchar(255) NOT NULL default '',
  668. `swiftSmtpPass` varchar(255) NOT NULL default '',
  669. `swiftSmtpEnc` char(3) NOT NULL default '',
  670. `swiftSmtpPort` int(5) unsigned NOT NULL default '25',
  671. PRIMARY KEY (`id`)
  672. ) ENGINE=MyISAM DEFAULT CHARSET=utf8"
  673. );
  674. }
  675. if (!\Database::getInstance()->fieldExists('transportMode', 'orm_avisota_message_category')) {
  676. \Database::getInstance()->query(
  677. "ALTER TABLE `orm_avisota_message_category` ADD `transportMode` char(22) NOT NULL default ''"
  678. );
  679. }
  680. if (!\Database::getInstance()->fieldExists('transport', 'orm_avisota_message_category')) {
  681. \Database::getInstance()->query(
  682. "ALTER TABLE `orm_avisota_message_category` ADD `transport` int(10) unsigned NOT NULL default '0'"
  683. );
  684. }
  685. if (\Database::getInstance()->fieldExists('useSMTP', 'orm_avisota_message_category')) {
  686. $category = \Database::getInstance()
  687. ->execute(
  688. "SELECT GROUP_CONCAT(id) AS ids, useSMTP, smtpHost, smtpUser, smtpPass, smtpPort, smtpEnc, sender, senderName
  689. FROM orm_avisota_message_category
  690. WHERE transportMode=''
  691. GROUP BY useSMTP, smtpHost, smtpUser, smtpPass, smtpPort, smtpEnc, sender, senderName"
  692. );
  693. while ($category->next()) {
  694. $transport = array(
  695. 'tstamp' => time(),
  696. 'type' => 'swift',
  697. 'title' => 'Swift Transport' . ($category->useSMTP
  698. ? (' (' . ($category->smtpUser
  699. ? $category->smtpUser . '@' : '') . $category->smtpHost . ')') : ''),
  700. 'swiftUseSmtp' => $category->useSMTP ? 'swiftSmtpOn' : 'swiftSmtpSystemSettings',
  701. 'swiftSmtpHost' => $category->smtpHost,
  702. 'swiftSmtpUser' => $category->smtpUser,
  703. 'swiftSmtpPass' => $category->smtpPass,
  704. 'swiftSmtpEnc' => $category->smtpEnc,
  705. 'sender' => $category->sender,
  706. 'senderName' => $category->senderName
  707. );
  708. // create new transport
  709. $transportId = \Database::getInstance()
  710. ->prepare("INSERT INTO orm_avisota_transport %s")
  711. ->set($transport)
  712. ->execute()
  713. ->insertId;
  714. // update categories to use the transport
  715. \Database::getInstance()
  716. ->query(
  717. "UPDATE orm_avisota_message_category SET transportMode='byCategory', transport=" . $transportId . " WHERE id IN (" . $category->ids . ")"
  718. );
  719. }
  720. }
  721. }
  722. }
  723. catch (Exception $e) {
  724. $this->log($e->getMessage() . "\n" . $e->getTraceAsString(), 'AvisotaRunonce::upgrade2_0_0_u2()', TL_ERROR);
  725. return false;
  726. }
  727. return true;
  728. }
  729. protected function check2_0_0_u3()
  730. {
  731. return \Database::getInstance()->tableExists('orm_avisota_message') &&
  732. \Database::getInstance()->execute(
  733. "SELECT COUNT(id) AS c FROM orm_avisota_message WHERE recipients LIKE '%list-%' OR recipients LIKE '%mgroup-%'"
  734. )->c > 0;
  735. }
  736. protected function upgrade2_0_0_u3()
  737. {
  738. try {
  739. if (\Database::getInstance()->tableExists('orm_avisota_recipient_list')) {
  740. if (!\Database::getInstance()->tableExists('orm_avisota_recipient_source')) {
  741. \Database::getInstance()->query(
  742. "CREATE TABLE `orm_avisota_recipient_source` (
  743. `id` int(10) unsigned NOT NULL auto_increment,
  744. `sorting` int(10) unsigned NOT NULL default '0',
  745. `tstamp` int(10) unsigned NOT NULL default '0',
  746. `type` varchar(255) NOT NULL default '',
  747. `title` varchar(255) NOT NULL default '',
  748. `integratedBy` char(32) NOT NULL default '',
  749. `integratedMailingLists` blob NULL,
  750. `integratedAllowSingleListSelection` char(1) NOT NULL default '',
  751. `integratedAllowSingleSelection` char(1) NOT NULL default '',
  752. `integratedDetails` varchar(255) NOT NULL default '',
  753. `integratedFilterByColumns` blob NULL,
  754. `memberBy` char(32) NOT NULL default '',
  755. `memberMailingLists` blob NULL,
  756. `memberAllowSingleMailingListSelection` char(1) NOT NULL default '',
  757. `memberGroups` blob NULL,
  758. `memberAllowSingleGroupSelection` char(1) NOT NULL default '',
  759. `memberAllowSingleSelection` char(1) NOT NULL default '',
  760. `memberFilterByColumns` blob NULL,
  761. `filter` char(1) NOT NULL default '',
  762. `disable` char(1) NOT NULL default '',
  763. PRIMARY KEY (`id`)
  764. ) ENGINE=MyISAM DEFAULT CHARSET=utf8"
  765. );
  766. }
  767. if (!\Database::getInstance()->fieldExists('recipientsMode', 'orm_avisota_message_category')) {
  768. \Database::getInstance()->query(
  769. "ALTER TABLE `orm_avisota_message_category` ADD `recipientsMode` char(22) NOT NULL default ''"
  770. );
  771. }
  772. if (!\Database::getInstance()->fieldExists('recipients', 'orm_avisota_message_category')) {
  773. \Database::getInstance()->query("ALTER TABLE `orm_avisota_message_category` ADD `recipients` blob NULL");
  774. }
  775. $sources = array();
  776. $sourcesByNewsletter = array();
  777. $newslettersByCategory = array();
  778. $sourcesByCategory = array();
  779. $newsletter = \Database::getInstance()
  780. ->execute(
  781. "SELECT id, pid, recipients
  782. FROM orm_avisota_message
  783. WHERE recipients LIKE '%list-%' OR recipients LIKE '%mgroup-%'"
  784. );
  785. while ($newsletter->next()) {
  786. if (!isset($newslettersByCategory[$newsletter->pid])) {
  787. $newslettersByCategory[$newsletter->pid] = array($newsletter->id);
  788. }
  789. else {
  790. $newslettersByCategory[$newsletter->pid][] = $newsletter->id;
  791. }
  792. $recipients = deserialize($newsletter->recipients, true);
  793. foreach ($recipients as $recipientIdentifier) {
  794. // create a new source, if none exists for this
  795. if (!isset($sources[$recipientIdentifier])) {
  796. list($type, $id) = explode('-', $recipientIdentifier, 2);
  797. switch ($type) {
  798. case 'list':
  799. $list = \Database::getInstance()
  800. ->prepare("SELECT title FROM orm_avisota_recipient_list WHERE id=?")
  801. ->execute($id);
  802. if (!$list->next()) {
  803. $this->log(
  804. 'Recipient list ID ' . $id . ' does not exists (anymore), skipping while convert into recipient source!',
  805. 'AvisotaUpdate::update2_0_0_u3()',
  806. TL_ERROR
  807. );
  808. continue;
  809. }
  810. $sourceData = array(
  811. 'type' => 'integrated',
  812. 'title' => $list->title,
  813. 'integratedBy' => 'integratedByMailingLists',
  814. 'integratedMailingLists' => serialize(array($id)),
  815. 'integratedDetails' => $GLOBALS['TL_CONFIG']['avisota_merge_member_details']
  816. ? 'integrated_member_details' : 'integrated_details'
  817. );
  818. break;
  819. case 'mgroup':
  820. $group = \Database::getInstance()
  821. ->prepare("SELECT name FROM tl_member_group WHERE id=?")
  822. ->execute($id);
  823. if (!$group->next()) {
  824. $this->log(
  825. 'Member group ID ' . $id . ' does not exists (anymore), skipping while convert into recipient source!',
  826. 'AvisotaUpdate::update2_0_0_u3()',
  827. TL_ERROR
  828. );
  829. continue;
  830. }
  831. $sourceData = array(
  832. 'type' => 'member',
  833. 'title' => $group->name,
  834. 'memberBy' => 'memberByGroups',
  835. 'memberGroups' => serialize(array($id))
  836. );
  837. break;
  838. default:
  839. $this->log(
  840. 'Unknown recipient type "' . $type . '", could not convert into recipient source!',
  841. 'AvisotaUpdate::update2_0_0_u3()',
  842. TL_ERROR
  843. );
  844. continue;
  845. }
  846. $sourceData['sorting'] = \Database::getInstance()
  847. ->executeUncached('SELECT MAX(sorting) AS sorting FROM orm_avisota_recipient_source')
  848. ->sorting;
  849. $sourceData['sorting'] = $sourceData['sorting'] ? $sourceData['sorting'] * 2 : 128;
  850. $sourceData['tstamp'] = time();
  851. $sourceId = \Database::getInstance()
  852. ->prepare("INSERT INTO orm_avisota_recipient_source %s")
  853. ->set($sourceData)
  854. ->execute()
  855. ->insertId;
  856. $sources[$recipientIdentifier] = $sourceId;
  857. }
  858. else {
  859. $sourceId = $sources[$recipientIdentifier];
  860. }
  861. // remember which newsletter use which source
  862. if (!isset($sourcesByNewsletter[$newsletter->id])) {
  863. $sourcesByNewsletter[$newsletter->id] = array($sourceId);
  864. }
  865. else {
  866. $sourcesByNewsletter[$newsletter->id][] = $sourceId;
  867. }
  868. }
  869. }
  870. // break down newsletter sources to category
  871. foreach ($newslettersByCategory as $categoryId => $newsletterIds) {
  872. $sourcesByCategory[$categoryId] = array();
  873. foreach ($newsletterIds as $newsletterId) {
  874. $tmp = $sourcesByNewsletter[$newsletterId];
  875. sort($tmp);
  876. $sourcesByCategory[$categoryId][] = implode(',', $tmp);
  877. }
  878. $sourcesByCategory[$categoryId] = array_unique($sourcesByCategory[$categoryId]);
  879. // all newsletters use the same sources
  880. if (count($sourcesByCategory[$categoryId]) == 1) {
  881. $tmp = explode(',', array_shift($sourcesByCategory[$categoryId]));
  882. foreach ($tmp as $k => $v) {
  883. $tmp[$k] = $v . ':*';
  884. }
  885. \Database::getInstance()
  886. ->prepare(
  887. "UPDATE orm_avisota_message_category SET recipientsMode=?, recipients=? WHERE id=?"
  888. )
  889. ->execute('byCategory', serialize($tmp), $categoryId);
  890. \Database::getInstance()
  891. ->query(
  892. "UPDATE orm_avisota_message SET recipients='' WHERE id IN (" . implode(
  893. ',',
  894. $newsletterIds
  895. ) . ")"
  896. );
  897. }
  898. // every newsletter use its own source
  899. else {
  900. \Database::getInstance()
  901. ->prepare("UPDATE orm_avisota_message_category SET recipientsMode=? WHERE id=?")
  902. ->execute('byNewsletter', $categoryId);
  903. // update each newsletter
  904. foreach ($newsletterIds as $newsletterId) {
  905. $tmp = $sourcesByNewsletter[$newsletterId];
  906. foreach ($tmp as $k => $v) {
  907. $tmp[$k] = $v . ':*';
  908. }
  909. \Database::getInstance()
  910. ->prepare("UPDATE orm_avisota_message SET recipients=? WHERE id=?")
  911. ->execute(serialize($tmp), $newsletterId);
  912. }
  913. }
  914. }
  915. }
  916. }
  917. catch (Exception $e) {
  918. $this->log($e->getMessage() . "\n" . $e->getTraceAsString(), 'AvisotaRunonce::upgrade2_0_0_u3()', TL_ERROR);
  919. return false;
  920. }
  921. return true;
  922. }
  923. public function hookMysqlMultiTriggerCreate($triggerName, $trigger, $return)
  924. {
  925. if ($trigger->table == 'orm_avisota_recipient') {
  926. $return['ALTER_CHANGE'][] = 'DELETE FROM orm_avisota_recipient_to_mailing_list';
  927. $return['ALTER_CHANGE'][] = 'INSERT INTO orm_avisota_recipient_to_mailing_list (recipient, list) SELECT r.id, l.id FROM orm_avisota_recipient r INNER JOIN orm_avisota_mailing_list l ON FIND_IN_SET(l.id, r.lists)';
  928. }
  929. if ($trigger->table == 'tl_member') {
  930. $return['ALTER_CHANGE'][] = 'DELETE FROM tl_member_to_mailing_list';
  931. $return['ALTER_CHANGE'][] = 'INSERT INTO tl_member_to_mailing_list (member, list) SELECT m.id, l.id FROM tl_member m INNER JOIN orm_avisota_mailing_list l ON FIND_IN_SET(l.id, m.avisota_lists)';
  932. }
  933. return $return;
  934. }
  935. }