module.php 78 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455
  1. <?php
  2. defined('ABSPATH') || exit;
  3. class TNP_Composer {
  4. static $block_dirs = array();
  5. static function register_block($dir) {
  6. // Checks
  7. if (!file_exists($dir . '/block.php')) {
  8. $error = new WP_Error('1', 'block.php missing on folder ' . $dir);
  9. NewsletterEmails::instance()->logger->error($error);
  10. return $error;
  11. }
  12. self::$block_dirs[] = $dir;
  13. return true;
  14. }
  15. }
  16. /**
  17. * @property int $id The list unique identifier
  18. * @property string $name The list name
  19. * @property bool $forced If the list must be added to every new subscriber
  20. * @property int $status When and how the list is visible to the subscriber - see constants
  21. * @property bool $checked If it must be pre-checked on subscription form
  22. * @property array $languages The list of language used to pre-assign this list
  23. */
  24. abstract class TNP_List {
  25. const STATUS_PRIVATE = 0;
  26. const STATUS_PUBLIC = 2;
  27. const STATUS_PROFILE_ONLY = 1;
  28. const STATUS_HIDDEN = 3; // Public but never show (can be set with a hidden form field)
  29. }
  30. /**
  31. * @property int $id The list unique identifier
  32. * @property string $name The list name
  33. * @property int $status When and how the list is visible to the subscriber - see constants
  34. */
  35. abstract class TNP_Profile {
  36. const STATUS_PRIVATE = 0;
  37. const STATUS_PUBLIC = 2;
  38. const STATUS_PROFILE_ONLY = 1;
  39. const STATUS_HIDDEN = 3; // Public but never show (can be set with a hidden form field)
  40. }
  41. /**
  42. * @property int $id The subscriber unique identifier
  43. * @property string $email The subscriber email
  44. * @property string $name The subscriber name or first name
  45. * @property string $surname The subscriber last name
  46. * @property string $status The subscriber status
  47. * @property string $language The subscriber language code 2 chars lowercase
  48. */
  49. abstract class TNP_User {
  50. const STATUS_CONFIRMED = 'C';
  51. const STATUS_NOT_CONFIRMED = 'S';
  52. const STATUS_UNSUBSCRIBED = 'U';
  53. const STATUS_BOUNCED = 'B';
  54. }
  55. /**
  56. * @property int $id The subscriber unique identifier
  57. * @property string $subject The subscriber email
  58. * @property string $message The subscriber name or first name
  59. * @property string $track The subscriber last name
  60. * @property array $options The subscriber status
  61. * */
  62. abstract class TNP_Email {
  63. }
  64. /**
  65. * @property string $id Theme identifier
  66. * @property string $dir Absolute path to the theme folder
  67. * @property string $name Theme name
  68. */
  69. class TNP_Theme {
  70. var $dir;
  71. var $name;
  72. public function get_defaults() {
  73. @include $this->dir . '/theme-defaults.php';
  74. if (!isset($theme_defaults) || !is_array($theme_defaults))
  75. return array();
  76. return $theme_defaults;
  77. }
  78. }
  79. class NewsletterAddon {
  80. var $logger;
  81. var $admin_logger;
  82. var $name;
  83. var $options;
  84. var $version;
  85. public function __construct($name, $version = '0.0.0') {
  86. $this->name = $name;
  87. $this->version = $version;
  88. if (is_admin()) {
  89. $old_version = get_option('newsletter_' . $name . '_version');
  90. if ($version != $old_version) {
  91. $this->upgrade($old_version === false);
  92. update_option('newsletter_' . $name . '_version', $version, false);
  93. }
  94. }
  95. add_action('newsletter_init', array($this, 'init'));
  96. }
  97. function upgrade($first_install = false) {
  98. }
  99. function init() {
  100. }
  101. /**
  102. *
  103. * @return NewsletterLogger
  104. */
  105. function get_logger() {
  106. if (!$this->logger) {
  107. $this->logger = new NewsletterLogger($this->name);
  108. }
  109. return $this->logger;
  110. }
  111. function get_admin_logger() {
  112. if (!$this->admin_logger) {
  113. $this->admin_logger = new NewsletterLogger($this->name . '-admin');
  114. }
  115. return $this->admin_logger;
  116. }
  117. function setup_options() {
  118. if ($this->options)
  119. return;
  120. $this->options = get_option('newsletter_' . $this->name, array());
  121. }
  122. function save_options($options) {
  123. update_option('newsletter_' . $this->name, $options);
  124. $this->setup_options();
  125. }
  126. function merge_defaults($defaults) {
  127. $options = get_option('newsletter_' . $this->name, array());
  128. $options = array_merge($defaults, $options);
  129. $this->save_options($options);
  130. }
  131. /**
  132. * @global wpdb $wpdb
  133. * @param string $query
  134. */
  135. function query($query) {
  136. global $wpdb;
  137. $r = $wpdb->query($query);
  138. if ($r === false) {
  139. $logger = $this->get_logger();
  140. $logger->fatal($query);
  141. $logger->fatal($wpdb->last_error);
  142. }
  143. return $r;
  144. }
  145. }
  146. class NewsletterMailerAddon extends NewsletterAddon {
  147. var $enabled = false;
  148. function __construct($name, $version = '0.0.0') {
  149. parent::__construct($name, $version);
  150. $this->setup_options();
  151. $this->enabled = !empty($this->options['enabled']);
  152. }
  153. function init() {
  154. parent::init();
  155. add_action('newsletter_register_mailer', function () {
  156. if ($this->enabled) {
  157. Newsletter::instance()->register_mailer($this->get_mailer());
  158. }
  159. });
  160. }
  161. /**
  162. *
  163. * @return NewsletterMailer
  164. */
  165. function get_mailer() {
  166. return null;
  167. }
  168. function get_last_run() {
  169. return get_option('newsletter_' . $this->name . '_last_run', 0);
  170. }
  171. function save_last_run($time) {
  172. update_option('newsletter_' . $this->name . '_last_run', $time);
  173. }
  174. function save_options($options) {
  175. parent::save_options($options);
  176. $this->enabled = !empty($options['enabled']);
  177. }
  178. static function get_test_message($to, $subject = '') {
  179. $message = new TNP_Mailer_Message();
  180. $message->to = $to;
  181. $message->to_name = '';
  182. $message->body = "<!DOCTYPE html>\n";
  183. $message->body .= "This is the rich text (HTML) version of a test message.</p>\n";
  184. $message->body .= "This is a <strong>bold text</strong></p>\n";
  185. $message->body .= "This is a <a href='http://www.thenewsletterplugin.com'>link to www.thenewsletterplugin.com</a></p>\n";
  186. $message->body_text = 'This is the TEXT version of a test message. You should see this message only if you email client does not support the rich text (HTML) version.';
  187. $message->headers['X-Newsletter-Email-Id'] = '0';
  188. if (empty($subject)) {
  189. $message->subject = '[' . get_option('blogname') . '] Test message from Newsletter (' . date(DATE_ISO8601) . ')';
  190. } else {
  191. $message->subject = $subject;
  192. }
  193. $message->from = Newsletter::instance()->options['sender_email'];
  194. $message->from_name = Newsletter::instance()->options['sender_name'];
  195. return $message;
  196. }
  197. function get_test_messages($to, $count) {
  198. $messages = array();
  199. for ($i = 0; $i < $count; $i++) {
  200. $messages[] = self::get_test_message($to, '[' . get_option('blogname') . '] Test message ' . ($i + 1) . ' from Newsletter (' . date(DATE_ISO8601) . ')');
  201. }
  202. return $messages;
  203. }
  204. }
  205. /**
  206. */
  207. class NewsletterMailer {
  208. const ERROR_GENERIC = '1';
  209. const ERROR_FATAL = '2';
  210. /* @var NewsletterLogger */
  211. var $logger;
  212. var $name;
  213. var $options;
  214. private $delta;
  215. protected $batch_size = 1;
  216. public function __construct($name, $options = array()) {
  217. $this->name = $name;
  218. $this->options = $options;
  219. }
  220. public function get_name() {
  221. return $this->name;
  222. }
  223. public function get_description() {
  224. return $this->name;
  225. }
  226. public function get_batch_size() {
  227. return $this->batch_size;
  228. }
  229. function send_with_stats($message) {
  230. $this->delta = microtime(true);
  231. $r = $this->send($message);
  232. $this->delta = microtime(true) - $this->delta;
  233. return $r;
  234. }
  235. /**
  236. *
  237. * @param TNP_Mailer_Message $message
  238. * @return bool|WP_Error
  239. */
  240. public function send($message) {
  241. $message->error = 'No mailing system available';
  242. return new WP_Error(self::ERROR_FATAL, 'No mailing system available');
  243. }
  244. public function send_batch_with_stats($messages) {
  245. $this->delta = microtime(true);
  246. $r = $this->send_batch($messages);
  247. $this->delta = microtime(true) - $this->delta;
  248. return $r;
  249. }
  250. function get_capability() {
  251. return (int) (3600 * $this->batch_size / $this->delta);
  252. }
  253. /**
  254. *
  255. * @param TNP_Mailer_Message[] $messages
  256. * @return bool|WP_Error
  257. */
  258. public function send_batch($messages) {
  259. // We should not get there is the batch size is one, the caller should use "send()". We can get
  260. // there if the array of messages counts to one, since could be the last of a series of chunks.
  261. if ($this->batch_size == 1 || count($messages) == 1) {
  262. //$this->get_logger()->debug('Caso 1 messaggio');
  263. $last_result = true;
  264. foreach ($messages as $message) {
  265. $r = $this->send($message);
  266. if (is_wp_error($r)) {
  267. $last_result = $r;
  268. }
  269. }
  270. return $last_result;
  271. }
  272. // We should always get there
  273. if (count($messages) <= $this->batch_size) {
  274. //$this->get_logger()->debug('Caso batch esatto');
  275. return $this->send_chunk($messages);
  276. }
  277. //$this->get_logger()->debug('Caso bach troppo grande');
  278. // We should not get here, since it is not optimized
  279. $chunks = array_chunk($message, $this->batch_size);
  280. $last_result = true;
  281. foreach ($chunks as $chunk) {
  282. $r = $this->send_chunk($chunk);
  283. if (is_wp_error($r)) {
  284. $last_result = $r;
  285. }
  286. }
  287. return $last_result;
  288. }
  289. protected function send_chunk($messages) {
  290. $last_result = true;
  291. foreach ($messages as $message) {
  292. $r = $this->send($message);
  293. if (is_wp_error($r)) {
  294. $last_result = $r;
  295. }
  296. }
  297. return $last_result;
  298. }
  299. /**
  300. *
  301. * @return NewsletterLogger
  302. */
  303. function get_logger() {
  304. if ($this->logger) {
  305. return $this->logger;
  306. }
  307. $this->logger = new NewsletterLogger('mailer-' . $this->name);
  308. return $this->logger;
  309. }
  310. /**
  311. *
  312. * @param TNP_Mailer_Message $message
  313. * @return bool|WP_Error
  314. */
  315. public function enqueue(TNP_Mailer_Message $message) {
  316. // Optimization when there is no queue
  317. if ($this->queue_max == 0) {
  318. $r = $this->send($message);
  319. return $r;
  320. }
  321. $this->queue[] = $message;
  322. if (count($this->queue) >= $this->queue_max) {
  323. return $this->flush();
  324. }
  325. return true;
  326. }
  327. public function flush() {
  328. $undelivered = array();
  329. foreach ($this->queue as $message) {
  330. $r = $this->deliver($message);
  331. if (is_wp_error($r)) {
  332. $message->error = $r;
  333. $undelivered[] = $message;
  334. }
  335. }
  336. $this->queue = array();
  337. if ($undelivered) {
  338. return new WP_Error(self::ERROR_GENERAL, 'Error while flushing messages', $undelivered);
  339. }
  340. return true;
  341. }
  342. /**
  343. * Original mail function simulation for compatibility.
  344. * @deprecated
  345. *
  346. * @param string $to
  347. * @param string $subject
  348. * @param array $message
  349. * @param array $headers
  350. * @param bool $enqueue
  351. * @param type $from Actually ignored
  352. * @return type
  353. */
  354. public function mail($to, $subject, $message, $headers = null, $enqueue = false, $from = false) {
  355. $mailer_message = new TNP_Mailer_Message();
  356. $mailer_message->to = $to;
  357. $mailer_message->subject = $subject;
  358. $mailer_message->headers = $headers;
  359. $mailer_message->body = $message['html'];
  360. $mailer_message->body_text = $message['text'];
  361. if ($enqueue) {
  362. return !is_wp_error($this->enqueue($mailer_message));
  363. }
  364. return !is_wp_error($this->send($mailer_message));
  365. }
  366. function save_last_run($time) {
  367. update_option($this->prefix . '_last_run', $time);
  368. }
  369. function get_last_run() {
  370. return (int) get_option($this->prefix . '_last_run', 0);
  371. }
  372. }
  373. /**
  374. * @property string $to
  375. * @property string $subject
  376. * @property string $body
  377. * @property array $headers
  378. * @property string $from
  379. * @property string $from_name
  380. */
  381. class TNP_Mailer_Message {
  382. var $to_name = '';
  383. var $headers = array();
  384. var $user_id = 0;
  385. var $email_id = 0;
  386. var $error = '';
  387. var $subject = '';
  388. var $body = '';
  389. var $body_text = '';
  390. }
  391. class NewsletterModule {
  392. /**
  393. * @var NewsletterLogger
  394. */
  395. var $logger;
  396. /**
  397. * @var NewsletterStore
  398. */
  399. var $store;
  400. /**
  401. * The main module options
  402. * @var array
  403. */
  404. var $options;
  405. /**
  406. * @var string The module name
  407. */
  408. var $module;
  409. /**
  410. * The module version
  411. * @var string
  412. */
  413. var $version;
  414. var $old_version;
  415. /**
  416. * Prefix for all options stored on WordPress options table.
  417. * @var string
  418. */
  419. var $prefix;
  420. /**
  421. * @var NewsletterThemes
  422. */
  423. var $themes;
  424. var $components;
  425. function __construct($module, $version, $module_id = null, $components = array()) {
  426. $this->module = $module;
  427. $this->version = $version;
  428. $this->prefix = 'newsletter_' . $module;
  429. array_unshift($components, '');
  430. $this->components = $components;
  431. $this->logger = new NewsletterLogger($module);
  432. $this->options = $this->get_options();
  433. $this->store = NewsletterStore::singleton();
  434. //$this->logger->debug($module . ' constructed');
  435. // Version check
  436. if (is_admin()) {
  437. $this->old_version = get_option($this->prefix . '_version', '0.0.0');
  438. if ($this->old_version == '0.0.0') {
  439. require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
  440. $this->first_install();
  441. update_option($this->prefix . "_first_install_time", time(), FALSE);
  442. }
  443. if (strcmp($this->old_version, $this->version) != 0) {
  444. require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
  445. $this->logger->info('Version changed from ' . $this->old_version . ' to ' . $this->version);
  446. // Do all the stuff for this version change
  447. $this->upgrade();
  448. update_option($this->prefix . '_version', $this->version);
  449. }
  450. add_action('admin_menu', array($this, 'admin_menu'));
  451. }
  452. }
  453. /**
  454. *
  455. * @global wpdb $wpdb
  456. * @param string $query
  457. */
  458. function query($query) {
  459. global $wpdb;
  460. $this->logger->debug($query);
  461. $r = $wpdb->query($query);
  462. if ($r === false) {
  463. $this->logger->fatal($wpdb->last_error);
  464. }
  465. return $r;
  466. }
  467. function get_results($query) {
  468. global $wpdb;
  469. $r = $wpdb->get_results($query);
  470. if ($r === false) {
  471. $logger = $this->get_logger();
  472. $logger->fatal($query);
  473. $logger->fatal($wpdb->last_error);
  474. }
  475. return $r;
  476. }
  477. /**
  478. *
  479. * @global wpdb $wpdb
  480. * @param string $table
  481. * @param array $data
  482. */
  483. function insert($table, $data) {
  484. global $wpdb;
  485. $this->logger->debug("inserting into table $table");
  486. $r = $wpdb->insert($table, $data);
  487. if ($r === false) {
  488. $this->logger->fatal($wpdb->last_error);
  489. }
  490. }
  491. function first_install() {
  492. $this->logger->debug('First install');
  493. }
  494. /**
  495. * Does a basic upgrade work, checking if the options is already present and if not (first
  496. * installation), recovering the defaults, saving them on database and initializing the
  497. * internal $options.
  498. */
  499. function upgrade() {
  500. foreach ($this->components as $component) {
  501. $this->logger->debug('Upgrading component ' . $component);
  502. $this->init_options($component);
  503. }
  504. }
  505. function init_options($component = '', $autoload = true) {
  506. global $wpdb;
  507. $default_options = $this->get_default_options($component);
  508. $options = $this->get_options($component);
  509. $options = array_merge($default_options, $options);
  510. $this->save_options($options, $component, $autoload);
  511. }
  512. function upgrade_query($query) {
  513. global $wpdb, $charset_collate;
  514. $this->logger->info('upgrade_query> Executing ' . $query);
  515. $suppress_errors = $wpdb->suppress_errors(true);
  516. $wpdb->query($query);
  517. if ($wpdb->last_error) {
  518. $this->logger->debug($wpdb->last_error);
  519. }
  520. $wpdb->suppress_errors($suppress_errors);
  521. }
  522. /** Returns a prefix to be used for option names and other things which need to be uniquely named. The parameter
  523. * "sub" should be used when a sub name is needed for another set of options or like.
  524. *
  525. * @param string $sub
  526. * @return string The prefix for names
  527. */
  528. function get_prefix($sub = '', $language = '') {
  529. return $this->prefix . (!empty($sub) ? '_' : '') . $sub . (!empty($language) ? '_' : '') . $language;
  530. }
  531. /**
  532. * Returns the options of a module, if not found an empty array.
  533. */
  534. function get_options($sub = '', $language = '') {
  535. $options = get_option($this->get_prefix($sub, $language), array());
  536. // Protection against scarmled database...
  537. if (!is_array($options)) {
  538. $options = array();
  539. }
  540. if ($language) {
  541. $main_options = get_option($this->get_prefix($sub));
  542. // Protection against scarmled database...
  543. if (!is_array($main_options))
  544. $main_options = array();
  545. //$options = array_merge($main_options, array_filter($options));
  546. $options = array_merge($main_options, $options);
  547. }
  548. return $options;
  549. }
  550. function get_default_options($sub = '') {
  551. if (!empty($sub)) {
  552. $sub = '-' . $sub;
  553. }
  554. $file = NEWSLETTER_DIR . '/' . $this->module . '/defaults' . $sub . '.php';
  555. if (file_exists($file)) {
  556. @include $file;
  557. }
  558. if (!isset($options) || !is_array($options)) {
  559. return array();
  560. }
  561. return $options;
  562. }
  563. function reset_options($sub = '') {
  564. $this->save_options(array_merge($this->get_options($sub), $this->get_default_options($sub)), $sub);
  565. return $this->get_options($sub);
  566. }
  567. /**
  568. * Saves the module options (or eventually a subset names as per parameter $sub). $options
  569. * should be an array (even if it can work with non array options.
  570. * The internal module options variable IS initialized with those new options only for the main
  571. * options (empty $sub parameter).
  572. * If the options contain a "theme" value, the theme-related options contained are saved as well
  573. * (used by some modules).
  574. *
  575. * @param array $options
  576. * @param string $sub
  577. */
  578. function save_options($options, $sub = '', $autoload = null, $language = '') {
  579. update_option($this->get_prefix($sub, $language), $options, $autoload);
  580. if (empty($sub) && empty($language)) {
  581. $this->options = $options;
  582. if (isset($this->themes) && isset($options['theme'])) {
  583. $this->themes->save_options($options['theme'], $options);
  584. }
  585. }
  586. }
  587. function delete_options($sub = '') {
  588. delete_option($this->get_prefix($sub));
  589. if (empty($sub)) {
  590. $this->options = array();
  591. }
  592. }
  593. function merge_options($options, $sub = '', $language = '') {
  594. if (!is_array($options)) {
  595. $options = array();
  596. }
  597. $old_options = $this->get_options($sub, $language);
  598. $this->save_options(array_merge($old_options, $options), $sub, null, $language);
  599. }
  600. function backup_options($sub) {
  601. $options = $this->get_options($sub);
  602. update_option($this->get_prefix($sub) . '_backup', $options, false);
  603. }
  604. function get_last_run($sub = '') {
  605. return get_option($this->get_prefix($sub) . '_last_run', 0);
  606. }
  607. /**
  608. * Save the module last run value. Used to store a timestamp for some modules,
  609. * for example the Feed by Mail module.
  610. *
  611. * @param int $time Unix timestamp (as returned by time() for example)
  612. * @param string $sub Sub module name (default empty)
  613. */
  614. function save_last_run($time, $sub = '') {
  615. update_option($this->get_prefix($sub) . '_last_run', $time);
  616. }
  617. /**
  618. * Sums $delta seconds to the last run time.
  619. * @param int $delta Seconds
  620. * @param string $sub Sub module name (default empty)
  621. */
  622. function add_to_last_run($delta, $sub = '') {
  623. $time = $this->get_last_run($sub);
  624. $this->save_last_run($time + $delta, $sub);
  625. }
  626. /**
  627. * Checks if the semaphore of that name (for this module) is still red. If it is active the method
  628. * returns false. If it is not active, it will be activated for $time seconds.
  629. *
  630. * Since this method activate the semaphore when called, it's name is a bit confusing.
  631. *
  632. * @param string $name Sempahore name (local to this module)
  633. * @param int $time Max time in second this semaphore should stay red
  634. * @return boolean False if the semaphore is red and you should not proceed, true is it was not active and has been activated.
  635. */
  636. function check_transient($name, $time) {
  637. if ($time < 60)
  638. $time = 60;
  639. //usleep(rand(0, 1000000));
  640. if (($value = get_transient($this->get_prefix() . '_' . $name)) !== false) {
  641. list($t, $v) = explode(';', $value, 2);
  642. $this->logger->error('Blocked by transient ' . $this->get_prefix() . '_' . $name . ' set ' . (time() - $t) . ' seconds ago by ' . $v);
  643. return false;
  644. }
  645. //$ip = ''; //gethostbyname(gethostname());
  646. $value = time() . ";" . ABSPATH . ';' . gethostname();
  647. set_transient($this->get_prefix() . '_' . $name, $value, $time);
  648. return true;
  649. }
  650. function delete_transient($name = '') {
  651. delete_transient($this->get_prefix() . '_' . $name);
  652. }
  653. /** Returns a random token of the specified size (or 10 characters if size is not specified).
  654. *
  655. * @param int $size
  656. * @return string
  657. */
  658. static function get_token($size = 10) {
  659. return substr(md5(rand()), 0, $size);
  660. }
  661. /**
  662. * Adds query string parameters to an URL checing id there are already other parameters.
  663. *
  664. * @param string $url
  665. * @param string $qs The part of query-string to add (param1=value1&param2=value2...)
  666. * @param boolean $amp If the method must use the &amp; instead of the plain & (default true)
  667. * @return string
  668. */
  669. static function add_qs($url, $qs, $amp = true) {
  670. if (strpos($url, '?') !== false) {
  671. if ($amp)
  672. return $url . '&amp;' . $qs;
  673. else
  674. return $url . '&' . $qs;
  675. } else
  676. return $url . '?' . $qs;
  677. }
  678. /**
  679. * Returns the email address normalized, lowercase with no spaces. If it's not a valid email
  680. * returns false.
  681. */
  682. static function normalize_email($email) {
  683. if (!is_string($email))
  684. return false;
  685. $email = strtolower(trim($email));
  686. if (!is_email($email)) {
  687. return false;
  688. }
  689. //$email = apply_filters('newsletter_normalize_email', $email);
  690. return $email;
  691. }
  692. static function normalize_name($name) {
  693. $name = str_replace(';', ' ', $name);
  694. $name = strip_tags($name);
  695. return $name;
  696. }
  697. static function normalize_sex($sex) {
  698. $sex = trim(strtolower($sex));
  699. if ($sex != 'f' && $sex != 'm') {
  700. $sex = 'n';
  701. }
  702. return $sex;
  703. }
  704. static function is_email($email, $empty_ok = false) {
  705. if (!is_string($email))
  706. return false;
  707. $email = strtolower(trim($email));
  708. if ($email == '') {
  709. return $empty_ok;
  710. }
  711. if (!is_email($email)) {
  712. return false;
  713. }
  714. // TODO: To be moved on the subscription module and make configurable
  715. if (strpos($email, 'mailinator.com') !== false) {
  716. return false;
  717. }
  718. if (strpos($email, 'guerrillamailblock.com') !== false) {
  719. return false;
  720. }
  721. if (strpos($email, 'emailtemporanea.net') !== false) {
  722. return false;
  723. }
  724. return true;
  725. }
  726. /**
  727. * Converts a GMT date from mysql (see the posts table columns) into a timestamp.
  728. *
  729. * @param string $s GMT date with format yyyy-mm-dd hh:mm:ss
  730. * @return int A timestamp
  731. */
  732. static function m2t($s) {
  733. // TODO: use the wordpress function I don't remember the name
  734. $s = explode(' ', $s);
  735. $d = explode('-', $s[0]);
  736. $t = explode(':', $s[1]);
  737. return gmmktime((int) $t[0], (int) $t[1], (int) $t[2], (int) $d[1], (int) $d[2], (int) $d[0]);
  738. }
  739. static function format_date($time) {
  740. if (empty($time)) {
  741. return '-';
  742. }
  743. return gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
  744. }
  745. static function format_time_delta($delta) {
  746. $days = floor($delta / (3600 * 24));
  747. $hours = floor(($delta % (3600 * 24)) / 3600);
  748. $minutes = floor(($delta % 3600) / 60);
  749. $seconds = floor(($delta % 60));
  750. $buffer = $days . ' days, ' . $hours . ' hours, ' . $minutes . ' minutes, ' . $seconds . ' seconds';
  751. return $buffer;
  752. }
  753. /**
  754. * Formats a scheduler returned "next execution" time, managing negative or false values. Many times
  755. * used in conjuction with "last run".
  756. *
  757. * @param string $name The scheduler name
  758. * @return string
  759. */
  760. static function format_scheduler_time($name) {
  761. $time = wp_next_scheduled($name);
  762. if ($time === false) {
  763. return 'No next run scheduled';
  764. }
  765. $delta = $time - time();
  766. // If less 10 minutes late it can be a cron problem but now it is working
  767. if ($delta < 0 && $delta > -600) {
  768. return 'Probably running now';
  769. } else if ($delta <= -600) {
  770. return 'It seems the cron system is not working. Reload the page to see if this message change.';
  771. }
  772. return 'Runs in ' . self::format_time_delta($delta);
  773. }
  774. static function date($time = null, $now = false, $left = false) {
  775. if (is_null($time)) {
  776. $time = time();
  777. }
  778. if ($time == false) {
  779. $buffer = 'none';
  780. } else {
  781. $buffer = gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
  782. }
  783. if ($now) {
  784. $buffer .= ' (now: ' . gmdate(get_option('date_format') . ' ' .
  785. get_option('time_format'), time() + get_option('gmt_offset') * 3600);
  786. $buffer .= ')';
  787. }
  788. if ($left) {
  789. $buffer .= ', ' . gmdate('H:i:s', $time - time()) . ' left';
  790. }
  791. return $buffer;
  792. }
  793. /**
  794. * Return an array of array with on first element the array of recent post and on second element the array
  795. * of old posts.
  796. *
  797. * @param array $posts
  798. * @param int $time
  799. */
  800. static function split_posts(&$posts, $time = 0) {
  801. if ($time < 0) {
  802. return array_chunk($posts, ceil(count($posts) / 2));
  803. }
  804. $result = array(array(), array());
  805. if (empty($posts))
  806. return $result;
  807. foreach ($posts as &$post) {
  808. if (self::is_post_old($post, $time))
  809. $result[1][] = $post;
  810. else
  811. $result[0][] = $post;
  812. }
  813. return $result;
  814. }
  815. static function is_post_old(&$post, $time = 0) {
  816. return self::m2t($post->post_date_gmt) <= $time;
  817. }
  818. static function get_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
  819. global $post;
  820. if (empty($post_id))
  821. $post_id = $post->ID;
  822. if (empty($post_id))
  823. return $alternative;
  824. $image_id = function_exists('get_post_thumbnail_id') ? get_post_thumbnail_id($post_id) : false;
  825. if ($image_id) {
  826. $image = wp_get_attachment_image_src($image_id, $size);
  827. return $image[0];
  828. } else {
  829. $attachments = get_children(array('post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID'));
  830. if (empty($attachments)) {
  831. return $alternative;
  832. }
  833. foreach ($attachments as $id => $attachment) {
  834. $image = wp_get_attachment_image_src($id, $size);
  835. return $image[0];
  836. }
  837. }
  838. }
  839. /**
  840. * Cleans up a text containing url tags with appended the absolute URL (due to
  841. * the editor behavior) moving back them to the simple form.
  842. */
  843. static function clean_url_tags($text) {
  844. $text = str_replace('%7B', '{', $text);
  845. $text = str_replace('%7D', '}', $text);
  846. // Only tags which are {*_url}
  847. $text = preg_replace("/[\"']http[^\"']+(\\{[^\\}]+_url\\})[\"']/i", "\"\\1\"", $text);
  848. return $text;
  849. }
  850. function get_styles() {
  851. $list = array('' => 'none');
  852. $dir = NEWSLETTER_DIR . '/' . $this->module . '/styles';
  853. $handle = @opendir($dir);
  854. if ($handle !== false) {
  855. while ($file = readdir($handle)) {
  856. if ($file == '.' || $file == '..')
  857. continue;
  858. if (substr($file, -4) != '.css')
  859. continue;
  860. $list[$file] = substr($file, 0, strlen($file) - 4);
  861. }
  862. closedir($handle);
  863. }
  864. $dir = WP_CONTENT_DIR . '/extensions/newsletter/' . $this->module . '/styles';
  865. if (is_dir($dir)) {
  866. $handle = @opendir($dir);
  867. if ($handle !== false) {
  868. while ($file = readdir($handle)) {
  869. if ($file == '.' || $file == '..')
  870. continue;
  871. if (isset($list[$file]))
  872. continue;
  873. if (substr($file, -4) != '.css')
  874. continue;
  875. $list[$file] = substr($file, 0, strlen($file) - 4);
  876. }
  877. closedir($handle);
  878. }
  879. }
  880. return $list;
  881. }
  882. function get_style_url($style) {
  883. if (is_file(WP_CONTENT_DIR . '/extensions/newsletter/' . $this->module . '/styles/' . $style))
  884. return WP_CONTENT_URL . '/extensions/newsletter/' . $this->module . '/styles/' . $style;
  885. else
  886. return plugins_url('newsletter') . '/' . $this->module . '/styles/' . $style;
  887. }
  888. function admin_menu() {
  889. }
  890. function add_menu_page($page, $title, $capability = '') {
  891. if (!Newsletter::instance()->is_allowed())
  892. return;
  893. $name = 'newsletter_' . $this->module . '_' . $page;
  894. add_submenu_page('newsletter_main_index', $title, $title, 'exist', $name, array($this, 'menu_page'));
  895. }
  896. function add_admin_page($page, $title) {
  897. if (!Newsletter::instance()->is_allowed())
  898. return;
  899. $name = 'newsletter_' . $this->module . '_' . $page;
  900. $name = apply_filters('newsletter_admin_page', $name);
  901. add_submenu_page(null, $title, $title, 'exist', $name, array($this, 'menu_page'));
  902. }
  903. function sanitize_file_name($name) {
  904. return preg_replace('/[^a-z_\\-]/i', '', $name);
  905. }
  906. function menu_page() {
  907. global $plugin_page, $newsletter, $wpdb;
  908. $parts = explode('_', $plugin_page, 3);
  909. $module = $this->sanitize_file_name($parts[1]);
  910. $page = $this->sanitize_file_name($parts[2]);
  911. $page = str_replace('_', '-', $page);
  912. $file = NEWSLETTER_DIR . '/' . $module . '/' . $page . '.php';
  913. require $file;
  914. }
  915. function get_admin_page_url($page) {
  916. return admin_url('admin.php') . '?page=newsletter_' . $this->module . '_' . $page;
  917. }
  918. /** Returns all the emails of the give type (message, feed, followup, ...) and in the given format
  919. * (default as objects). Return false on error or at least an empty array. Errors should never
  920. * occur.
  921. *
  922. * @global wpdb $wpdb
  923. * @param string $type
  924. * @return boolean|array
  925. */
  926. function get_emails($type = null, $format = OBJECT) {
  927. global $wpdb;
  928. if ($type == null) {
  929. $list = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " order by id desc", $format);
  930. } else {
  931. $type = (string) $type;
  932. $list = $wpdb->get_results($wpdb->prepare("select * from " . NEWSLETTER_EMAILS_TABLE . " where type=%s order by id desc", $type), $format);
  933. }
  934. if ($wpdb->last_error) {
  935. $this->logger->error($wpdb->last_error);
  936. return false;
  937. }
  938. if (empty($list)) {
  939. return array();
  940. }
  941. return $list;
  942. }
  943. /**
  944. * Retrieves an email from DB and unserialize the options.
  945. *
  946. * @param mixed $id
  947. * @param string $format
  948. */
  949. function get_email($id, $format = OBJECT) {
  950. $email = $this->store->get_single(NEWSLETTER_EMAILS_TABLE, $id, $format);
  951. if (!$email) {
  952. return null;
  953. }
  954. if ($format == OBJECT) {
  955. $email->options = maybe_unserialize($email->options);
  956. if (!is_array($email->options)) {
  957. $email->options = array();
  958. }
  959. if (empty($email->query)) {
  960. $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
  961. }
  962. } else if ($format == ARRAY_A) {
  963. $email['options'] = maybe_unserialize($email['options']);
  964. if (!is_array($email['options'])) {
  965. $email['options'] = array();
  966. }
  967. if (empty($email['query'])) {
  968. $email['query'] = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
  969. }
  970. }
  971. return $email;
  972. }
  973. /**
  974. * Save an email and provide serialization, if needed, of $email['options'].
  975. */
  976. function save_email($email, $return_format = OBJECT) {
  977. if (is_object($email)) {
  978. $email = (array) $email;
  979. }
  980. if (isset($email['subject'])) {
  981. if (mb_strlen($email['subject'], 'UTF-8') > 250) {
  982. $email['subject'] = mb_substr($email['subject'], 0, 250, 'UTF-8');
  983. }
  984. }
  985. if (isset($email['options']) && is_array($email['options'])) {
  986. $email['options'] = serialize($email['options']);
  987. }
  988. $email = $this->store->save(NEWSLETTER_EMAILS_TABLE, $email, $return_format);
  989. if ($return_format == OBJECT) {
  990. $email->options = maybe_unserialize($email->options);
  991. if (!is_array($email->options))
  992. $email->options = array();
  993. } else if ($return_format == ARRAY_A) {
  994. $email['options'] = maybe_unserialize($email['options']);
  995. if (!is_array($email['options']))
  996. $email['options'] = array();
  997. }
  998. return $email;
  999. }
  1000. function get_email_from_request() {
  1001. if (isset($_REQUEST['nek'])) {
  1002. list($id, $token) = @explode('-', $_REQUEST['nek'], 2);
  1003. } else {
  1004. return null;
  1005. }
  1006. $email = $this->get_email($id);
  1007. return $email;
  1008. }
  1009. /**
  1010. *
  1011. * @global wpdb $wpdb
  1012. * @param int|array $id
  1013. * @return boolean
  1014. */
  1015. function delete_email($id) {
  1016. global $wpdb;
  1017. $r = $this->store->delete(NEWSLETTER_EMAILS_TABLE, $id);
  1018. if ($r !== false) {
  1019. // $id could be an array if IDs
  1020. $id = (array) $id;
  1021. foreach ($id as $email_id) {
  1022. $wpdb->delete(NEWSLETTER_STATS_TABLE, array('email_id' => $email_id));
  1023. $wpdb->delete(NEWSLETTER_SENT_TABLE, array('email_id' => $email_id));
  1024. }
  1025. }
  1026. return $r;
  1027. }
  1028. function get_email_field($id, $field_name) {
  1029. return $this->store->get_field(NEWSLETTER_EMAILS_TABLE, $id, $field_name);
  1030. }
  1031. function get_email_status_slug($email) {
  1032. $email = (object) $email;
  1033. if ($email->status == 'sending' && $email->send_on > time()) {
  1034. return 'scheduled';
  1035. }
  1036. return $email->status;
  1037. }
  1038. function get_email_status_label($email) {
  1039. $email = (object) $email;
  1040. $status = $this->get_email_status_slug($email);
  1041. switch ($status) {
  1042. case 'sending':
  1043. return __('Sending', 'newsletter');
  1044. case 'scheduled':
  1045. return __('Scheduled', 'newsletter');
  1046. case 'sent':
  1047. return __('Sent', 'newsletter');
  1048. case 'paused':
  1049. return __('Paused', 'newsletter');
  1050. case 'new':
  1051. return __('Draft', 'newsletter');
  1052. default:
  1053. return ucfirst($email->status);
  1054. }
  1055. }
  1056. function show_email_status_label($email) {
  1057. echo '<span class="tnp-email-status-', $this->get_email_status_slug($email), '">', esc_html($this->get_email_status_label($email)), '</span>';
  1058. }
  1059. function get_email_progress($email, $format = 'percent') {
  1060. return $email->total > 0 ? intval($email->sent / $email->total * 100) : 0;
  1061. }
  1062. function show_email_progress_bar($email, $attrs = array()) {
  1063. $email = (object) $email;
  1064. $attrs = array_merge(array('format' => 'percent', 'numbers' => false, 'scheduled' => false), $attrs);
  1065. if ($email->status == 'sending' && $email->send_on > time()) {
  1066. if ($attrs['scheduled']) {
  1067. echo '<span class="tnp-progress-date">', $this->format_date($email->send_on), '</span>';
  1068. }
  1069. } else if ($email->status == 'new') {
  1070. echo '';
  1071. } else {
  1072. $percent = $this->get_email_progress($email);
  1073. $label = $percent;
  1074. if ($attrs['format'] == 'numbers') {
  1075. $label = $email->sent . ' ' . __('of', 'newsletter') . ' ' . $email->total;
  1076. }
  1077. echo '<div class="tnp-progress ', $email->status, '">';
  1078. echo '<div class="tnp-progress-bar" role="progressbar" style="width: ', $percent, '%;">&nbsp;', $percent, '%&nbsp;</div>';
  1079. echo '</div>';
  1080. if ($attrs['numbers']) {
  1081. echo '<div class="tnp-progress-numbers">', $email->sent, ' ', __('of', 'newsletter'), ' ', $email->total, '</div>';
  1082. }
  1083. }
  1084. }
  1085. function get_email_type_label($type) {
  1086. // Is an email?
  1087. if (is_object($type))
  1088. $type = $type->type;
  1089. $label = apply_filters('newsletter_email_type', '', $type);
  1090. if (!empty($label))
  1091. return $label;
  1092. switch ($type) {
  1093. case 'followup':
  1094. return 'Followup';
  1095. case 'message':
  1096. return 'Standard Newsletter';
  1097. case 'feed':
  1098. return 'Feed by Mail';
  1099. }
  1100. if (strpos($type, 'automated') === 0) {
  1101. list($a, $id) = explode('_', $type);
  1102. return 'Automated Channel ' . $id;
  1103. }
  1104. return ucfirst($type);
  1105. }
  1106. function get_email_progress_label($email) {
  1107. if ($email->status == 'sent' || $email->status == 'sending') {
  1108. return $email->sent . ' ' . __('of', 'newsletter') . ' ' . $email->total;
  1109. }
  1110. return '-';
  1111. }
  1112. /**
  1113. * Returns the email unique key
  1114. * @param TNP_User $user
  1115. * @return string
  1116. */
  1117. function get_email_key($email) {
  1118. if (!isset($email->token)) {
  1119. return $email->id . '-';
  1120. }
  1121. return $email->id . '-' . $email->token;
  1122. }
  1123. /** Searches for a user using the nk parameter or the ni and nt parameters. Tries even with the newsletter cookie.
  1124. * If found, the user object is returned or null.
  1125. * The user is returned without regards to his status that should be checked by caller.
  1126. *
  1127. * @return TNP_User
  1128. */
  1129. function check_user($context = '') {
  1130. global $wpdb;
  1131. $user = null;
  1132. if (isset($_REQUEST['nk'])) {
  1133. list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
  1134. } else if (isset($_COOKIE['newsletter'])) {
  1135. list ($id, $token) = @explode('-', $_COOKIE['newsletter'], 2);
  1136. }
  1137. if (isset($id)) {
  1138. $user = $this->get_user($id);
  1139. if ($user) {
  1140. if ($context == 'preconfirm') {
  1141. if ($token != md5($user->token)) {
  1142. $user = null;
  1143. }
  1144. } else {
  1145. if ($token != $user->token) {
  1146. $user = null;
  1147. }
  1148. }
  1149. }
  1150. }
  1151. if ($user == null && is_user_logged_in()) {
  1152. $user = $this->get_user_by_wp_user_id(get_current_user_id());
  1153. }
  1154. return $user;
  1155. }
  1156. /** Returns the user identify by an id or an email. If $id_or_email is an object or an array, it is assumed it contains
  1157. * the "id" attribute or key and that is used to load the user.
  1158. *
  1159. * @global type $wpdb
  1160. * @param string|int|object|array $id_or_email
  1161. * @param string $format
  1162. * @return TNP_User
  1163. */
  1164. function get_user($id_or_email, $format = OBJECT) {
  1165. global $wpdb;
  1166. if (empty($id_or_email))
  1167. return null;
  1168. // To simplify the reaload of a user passing the user it self.
  1169. if (is_object($id_or_email)) {
  1170. $id_or_email = $id_or_email->id;
  1171. } else if (is_array($id_or_email)) {
  1172. $id_or_email = $id_or_email['id'];
  1173. }
  1174. $id_or_email = strtolower(trim($id_or_email));
  1175. if (is_numeric($id_or_email)) {
  1176. $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where id=%d limit 1", $id_or_email), $format);
  1177. } else {
  1178. $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where email=%s limit 1", $id_or_email), $format);
  1179. }
  1180. if ($wpdb->last_error) {
  1181. $this->logger->error($wpdb->last_error);
  1182. return null;
  1183. }
  1184. return $r;
  1185. }
  1186. /**
  1187. * Returns the user unique key
  1188. * @param TNP_User $user
  1189. * @return string
  1190. */
  1191. function get_user_key($user, $context = '') {
  1192. if ($context == 'preconfirm') {
  1193. return $user->id . '-' . md5($user->token);
  1194. }
  1195. return $user->id . '-' . $user->token;
  1196. }
  1197. function get_user_status_label($user) {
  1198. switch ($user->status) {
  1199. case 'S': return __('NOT CONFIRMED', 'newsletter');
  1200. break;
  1201. case 'C': return __('CONFIRMED', 'newsletter');
  1202. break;
  1203. case 'U': return __('UNSUBSCRIBED', 'newsletter');
  1204. break;
  1205. case 'B': return __('BOUNCED', 'newsletter');
  1206. break;
  1207. }
  1208. return '';
  1209. }
  1210. /**
  1211. * Return the user identified by the "nk" parameter (POST or GET).
  1212. * If no user can be found or the token is not matching, returns null.
  1213. * If die_on_fail is true it dies instead of return null.
  1214. *
  1215. * @param bool $die_on_fail
  1216. * @return TNP_User
  1217. */
  1218. function get_user_from_request($die_on_fail = false, $context = '') {
  1219. $id = 0;
  1220. if (isset($_REQUEST['nk'])) {
  1221. list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
  1222. }
  1223. $user = $this->get_user($id);
  1224. if ($user == null) {
  1225. if ($die_on_fail) {
  1226. die(__('No subscriber found.', 'newsletter'));
  1227. } else {
  1228. return null;
  1229. }
  1230. }
  1231. if ($context == 'preconfirm') {
  1232. $user_token = md5($user->token);
  1233. } else {
  1234. $user_token = $user->token;
  1235. }
  1236. if ($token != $user_token) {
  1237. if ($die_on_fail) {
  1238. die(__('No subscriber found.', 'newsletter'));
  1239. } else {
  1240. return null;
  1241. }
  1242. }
  1243. return $user;
  1244. }
  1245. /**
  1246. * @param string $language The language for the list labels (it does not affect the lists returned)
  1247. * @return TNP_Profile[]
  1248. */
  1249. function get_profiles($language = '') {
  1250. static $profiles = array();
  1251. if (isset($profiles[$language])) {
  1252. return $profiles[$language];
  1253. }
  1254. $profiles[$language] = array();
  1255. $data = NewsletterSubscription::instance()->get_options('profile', $language);
  1256. for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
  1257. if (empty($data['profile_' . $i])) {
  1258. continue;
  1259. }
  1260. $profile = new stdClass();
  1261. $profile->name = $data['profile_' . $i];
  1262. $profile->id = $i;
  1263. $profile->status = (int) $data['profile_' . $i . '_status'];
  1264. $profiles[$language][] = $profile;
  1265. }
  1266. return $profiles[$language];
  1267. }
  1268. /**
  1269. * @param string $language The language for the list labels (it does not affect the lists returned)
  1270. * @return TNP_List[]
  1271. */
  1272. function get_lists($language = '') {
  1273. static $lists = array();
  1274. if (isset($lists[$language])) {
  1275. return $lists[$language];
  1276. }
  1277. $lists[$language] = array();
  1278. $data = NewsletterSubscription::instance()->get_options('lists', $language);
  1279. for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
  1280. if (empty($data['list_' . $i])) {
  1281. continue;
  1282. }
  1283. $list = new stdClass();
  1284. $list->name = $data['list_' . $i];
  1285. $list->id = $i;
  1286. $list->forced = !empty($data['list_' . $i . '_forced']);
  1287. $list->status = (int) $data['list_' . $i . '_status'];
  1288. $list->checked = !empty($data['list_' . $i . '_checked']);
  1289. if (empty($data['list_' . $i . '_languages'])) {
  1290. $list->languages = array();
  1291. } else {
  1292. $list->languages = $data['list_' . $i . '_languages'];
  1293. }
  1294. $lists[$language][] = $list;
  1295. }
  1296. return $lists[$language];
  1297. }
  1298. /**
  1299. *
  1300. * @return TNP_List[]
  1301. */
  1302. function get_lists_public($language = '') {
  1303. static $lists = array();
  1304. if (isset($lists[$language])) {
  1305. return $lists[$language];
  1306. }
  1307. $lists[$language] = array();
  1308. $all = $this->get_lists($language);
  1309. foreach ($all as $list) {
  1310. if ($list->status == TNP_List::STATUS_PRIVATE) {
  1311. continue;
  1312. }
  1313. $lists[$language][] = $list;
  1314. }
  1315. return $lists[$language];
  1316. }
  1317. /**
  1318. * Lists to be shown on subscription form.
  1319. *
  1320. * @return TNP_List[]
  1321. */
  1322. function get_lists_for_subscription($language = '') {
  1323. static $lists = array();
  1324. if (isset($lists[$language])) {
  1325. return $lists[$language];
  1326. }
  1327. $lists[$language] = array();
  1328. $all = $this->get_lists($language);
  1329. foreach ($all as $list) {
  1330. if ($list->status != TNP_List::STATUS_PUBLIC || $list->forced) {
  1331. continue;
  1332. }
  1333. $lists[$language][] = $list;
  1334. }
  1335. return $lists[$language];
  1336. }
  1337. /**
  1338. * Returns the lists to be shown in the profile page.
  1339. *
  1340. * @return TNP_List[]
  1341. */
  1342. function get_lists_for_profile($language = '') {
  1343. static $lists = array();
  1344. if (isset($lists[$language])) {
  1345. return $lists[$language];
  1346. }
  1347. $lists[$language] = array();
  1348. $all = $this->get_lists($language);
  1349. foreach ($all as $list) {
  1350. if ($list->status == TNP_List::STATUS_PRIVATE || $list->status == TNP_List::STATUS_HIDDEN) {
  1351. continue;
  1352. }
  1353. $lists[$language][] = $list;
  1354. }
  1355. return $lists[$language];
  1356. }
  1357. /**
  1358. * Returns a list as an object (with the same signature of TNP_List)
  1359. *
  1360. * @param int $id
  1361. * @return TNP_List
  1362. */
  1363. function get_list($id, $language = '') {
  1364. $id = (int) $id;
  1365. if (!$id) {
  1366. return null;
  1367. }
  1368. $data = NewsletterSubscription::instance()->get_options('lists', $language);
  1369. $list = new stdClass();
  1370. $list->name = $data['list_' . $id];
  1371. $list->id = $id;
  1372. $list->forced = !empty($data['list_' . $id . '_forced']);
  1373. $list->status = (int) $data['list_' . $id . '_status'];
  1374. $list->checked = !empty($data['list_' . $id . '_checked']);
  1375. return $list;
  1376. }
  1377. /**
  1378. * NEVER CHANGE THIS METHOD SIGNATURE, USER BY THIRD PARTY PLUGINS.
  1379. *
  1380. * Saves a new user on the database. Return false if the email (that must be unique) is already
  1381. * there. For a new users set the token and creation time if not passed.
  1382. *
  1383. * @param array $user
  1384. * @return TNP_User|array|boolean Returns the subscriber reloaded from DB in the specified format. Flase on failure (duplicate email).
  1385. */
  1386. function save_user($user, $return_format = OBJECT) {
  1387. if (is_object($user)) {
  1388. $user = (array) $user;
  1389. }
  1390. if (empty($user['id'])) {
  1391. $existing = $this->get_user($user['email']);
  1392. if ($existing != null) {
  1393. return false;
  1394. }
  1395. if (empty($user['token'])) {
  1396. $user['token'] = NewsletterModule::get_token();
  1397. }
  1398. }
  1399. // Due to the unique index on email field, this can fail.
  1400. return $this->store->save(NEWSLETTER_USERS_TABLE, $user, $return_format);
  1401. }
  1402. /**
  1403. * Updates the user last activity timestamp.
  1404. *
  1405. * @global wpdb $wpdb
  1406. * @param TNP_User $user
  1407. */
  1408. function update_user_last_activity($user) {
  1409. global $wpdb;
  1410. $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set last_activity=%d where id=%d limit 1", time(), $user->id));
  1411. }
  1412. function update_user_ip($user, $ip) {
  1413. global $wpdb;
  1414. // Only if changed
  1415. $r = $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set ip=%s, geo=0 where ip<>%s and id=%d limit 1", $ip, $ip, $user->id));
  1416. }
  1417. /**
  1418. * Finds single style blocks and adds a style attribute to every HTML tag with a class exactly matching the rules in the style
  1419. * block. HTML tags can use the attribute "inline-class" to exact match a style rules if they need a composite class definition.
  1420. *
  1421. * @param string $content
  1422. * @param boolean $strip_style_blocks
  1423. * @return string
  1424. */
  1425. function inline_css($content, $strip_style_blocks = false) {
  1426. // CSS
  1427. $matches = array();
  1428. // "s" skips line breaks
  1429. $styles = preg_match('|<style>(.*?)</style>|s', $content, $matches);
  1430. if (isset($matches[1])) {
  1431. $style = str_replace(array("\n", "\r"), '', $matches[1]);
  1432. $rules = array();
  1433. preg_match_all('|\s*\.(.*?)\{(.*?)\}\s*|s', $style, $rules);
  1434. //print_r($rules);
  1435. for ($i = 0; $i < count($rules[1]); $i++) {
  1436. $class = trim($rules[1][$i]);
  1437. $value = trim($rules[2][$i]);
  1438. $value = preg_replace('|\s+|', ' ', $value);
  1439. $content = str_replace('class="' . $class . '"', 'class="' . $class . '" style="' . $value . '"', $content);
  1440. $content = str_replace('inline-class="' . $class . '"', 'style="' . $value . '"', $content);
  1441. }
  1442. }
  1443. if ($strip_style_blocks) {
  1444. return trim(preg_replace('|<style>.*?</style>|s', '', $content));
  1445. } else {
  1446. return $content;
  1447. }
  1448. }
  1449. /**
  1450. * Returns a list of users marked as "test user".
  1451. * @return TNP_User[]
  1452. */
  1453. function get_test_users() {
  1454. return $this->store->get_all(NEWSLETTER_USERS_TABLE, "where test=1");
  1455. }
  1456. /**
  1457. * Deletes a subscriber and cleans up all the stats table with his correlated data.
  1458. *
  1459. * @global wpdb $wpdb
  1460. * @param int|id[] $id
  1461. */
  1462. function delete_user($id) {
  1463. global $wpdb;
  1464. $id = (array) $id;
  1465. foreach ($id as $user_id) {
  1466. $user = $this->get_user($user_id);
  1467. if ($user) {
  1468. $r = $this->store->delete(NEWSLETTER_USERS_TABLE, $user_id);
  1469. $wpdb->delete(NEWSLETTER_STATS_TABLE, array('user_id' => $user_id));
  1470. $wpdb->delete(NEWSLETTER_SENT_TABLE, array('user_id' => $user_id));
  1471. do_action('newsletter_user_deleted', $user);
  1472. }
  1473. }
  1474. return count($id);
  1475. }
  1476. /**
  1477. * Add to a destination URL the parameters to identify the user, the email and to show
  1478. * an alert message, if required. The parameters are then managed by the [newsletter] shortcode.
  1479. *
  1480. * @param string $url If empty the standard newsletter page URL is used (usually it is empty, but sometime a custom URL has been specified)
  1481. * @param string $message_key The message identifier
  1482. * @param TNP_User|int $user
  1483. * @param TNP_Email|int $email
  1484. * @param string $alert An optional alter message to be shown. Does not work with custom URLs
  1485. * @return string The final URL with parameters
  1486. */
  1487. function build_message_url($url = '', $message_key = '', $user = null, $email = null, $alert = '') {
  1488. $params = 'nm=' . urlencode($message_key);
  1489. $language = '';
  1490. if ($user) {
  1491. if (!is_object($user)) {
  1492. $user = $this->get_user($user);
  1493. }
  1494. if ($message_key == 'confirmation') {
  1495. $params .= '&nk=' . urlencode($this->get_user_key($user, 'preconfirm'));
  1496. } else {
  1497. $params .= '&nk=' . urlencode($this->get_user_key($user));
  1498. }
  1499. $language = $this->get_user_language($user);
  1500. }
  1501. if ($email) {
  1502. if (!is_object($email)) {
  1503. $email = $this->get_email($email);
  1504. }
  1505. $params .= '&nek=' . urlencode($this->get_email_key($email));
  1506. }
  1507. if ($alert) {
  1508. $params .= '&alert=' . urlencode($alert);
  1509. }
  1510. if (empty($url)) {
  1511. $url = Newsletter::instance()->get_newsletter_page_url($language);
  1512. }
  1513. return self::add_qs($url, $params, false);
  1514. }
  1515. /**
  1516. * Builds a standard Newsletter action URL for the specified action.
  1517. *
  1518. * @param string $action
  1519. * @param TNP_User $user
  1520. * @param TNP_Email $email
  1521. * @return string
  1522. */
  1523. function build_action_url($action, $user = null, $email = null) {
  1524. $url = $this->add_qs($this->get_home_url(), 'na=' . urlencode($action));
  1525. //$url = $this->add_qs(admin_url('admin-ajax.php'), 'action=newsletter&na=' . urlencode($action));
  1526. if ($user) {
  1527. $url .= '&nk=' . urlencode($this->get_user_key($user));
  1528. }
  1529. if ($email) {
  1530. $url .= '&nek=' . urlencode($this->get_email_key($email));
  1531. }
  1532. return $url;
  1533. }
  1534. function get_subscribe_url() {
  1535. return $this->build_action_url('s');
  1536. }
  1537. function clean_stats_table() {
  1538. global $wpdb;
  1539. $this->logger->info('Cleaning up stats table');
  1540. $this->query("delete s from `{$wpdb->prefix}newsletter_stats` s left join `{$wpdb->prefix}newsletter` u on s.user_id=u.id where u.id is null");
  1541. $this->query("delete s from `{$wpdb->prefix}newsletter_stats` s left join `{$wpdb->prefix}newsletter_emails` e on s.email_id=e.id where e.id is null");
  1542. }
  1543. function clean_sent_table() {
  1544. global $wpdb;
  1545. $this->logger->info('Cleaning up sent table');
  1546. $this->query("delete s from `{$wpdb->prefix}newsletter_sent` s left join `{$wpdb->prefix}newsletter` u on s.user_id=u.id where u.id is null");
  1547. $this->query("delete s from `{$wpdb->prefix}newsletter_sent` s left join `{$wpdb->prefix}newsletter_emails` e on s.email_id=e.id where e.id is null");
  1548. }
  1549. function clean_user_logs_table() {
  1550. //global $wpdb;
  1551. }
  1552. function clean_tables() {
  1553. $this->clean_sent_table();
  1554. $this->clean_stats_table();
  1555. $this->clean_user_logs_table();
  1556. }
  1557. function anonymize_ip($ip) {
  1558. if (empty($ip)) {
  1559. return $ip;
  1560. }
  1561. $parts = explode('.', $ip);
  1562. array_pop($parts);
  1563. return implode('.', $parts) . '.0';
  1564. }
  1565. function process_ip($ip) {
  1566. $option = Newsletter::instance()->options['ip'];
  1567. if (empty($option)) {
  1568. return $ip;
  1569. }
  1570. if ($option == 'anonymize') {
  1571. return $this->anonymize_ip($ip);
  1572. }
  1573. return '';
  1574. }
  1575. function anonymize_user($id) {
  1576. global $wpdb;
  1577. $user = $this->get_user($id);
  1578. if (!$user) {
  1579. return null;
  1580. }
  1581. $user->name = '';
  1582. $user->surname = '';
  1583. $user->ip = $this->anonymize_ip($user->ip);
  1584. for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
  1585. $field = 'profile_' . $i;
  1586. $user->$field = '';
  1587. }
  1588. // [TODO] Status?
  1589. $user->status = TNP_User::STATUS_UNSUBSCRIBED;
  1590. $user->email = $user->id . '@anonymi.zed';
  1591. $user = $this->save_user($user);
  1592. return $user;
  1593. }
  1594. /**
  1595. * Changes a user status. Accept a user object, user id or user email.
  1596. *
  1597. * @param TNP_User $user
  1598. * @param string $status
  1599. * @return TNP_User
  1600. */
  1601. function set_user_status($user, $status) {
  1602. global $wpdb;
  1603. $this->logger->debug('Status change to ' . $status . ' of subscriber ' . $user->id . ' from ' . $_SERVER['REQUEST_URI']);
  1604. $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set status=%s where id=%d limit 1", $status, $user->id));
  1605. return $this->get_user($user);
  1606. }
  1607. /**
  1608. *
  1609. * @global wpdb $wpdb
  1610. * @param TNP_User $user
  1611. * @return TNP_User
  1612. */
  1613. function refresh_user_token($user) {
  1614. global $wpdb;
  1615. $token = $this->get_token();
  1616. $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set token=%s where id=%d limit 1", $token, $user->id));
  1617. return $this->get_user($user);
  1618. }
  1619. /**
  1620. * Create a log entry with the meaningful user data.
  1621. *
  1622. * @global wpdb $wpdb
  1623. * @param TNP_User $user
  1624. * @param string $source
  1625. * @return type
  1626. */
  1627. function add_user_log($user, $source = '') {
  1628. global $wpdb;
  1629. $lists = $this->get_lists_public();
  1630. foreach ($lists as $list) {
  1631. $field_name = 'list_' . $list->id;
  1632. $data[$field_name] = $user->$field_name;
  1633. }
  1634. $data['status'] = $user->status;
  1635. $ip = $this->get_remote_ip();
  1636. $ip = $this->process_ip($ip);
  1637. $this->store->save($wpdb->prefix . 'newsletter_user_logs', array('ip' => $ip, 'user_id' => $user->id, 'source' => $source, 'created' => time(), 'data' => json_encode($data)));
  1638. }
  1639. /**
  1640. *
  1641. * @global wpdb $wpdb
  1642. * @param TNP_User $user
  1643. * @param int $list
  1644. * @param type $value
  1645. */
  1646. function set_user_list($user, $list, $value) {
  1647. global $wpdb;
  1648. $list = (int) $list;
  1649. $value = $value ? 1 : 0;
  1650. $r = $wpdb->update(NEWSLETTER_USERS_TABLE, array('list_' . $list => $value), array('id' => $user->id));
  1651. }
  1652. function set_user_field($id, $field, $value) {
  1653. $this->store->set_field(NEWSLETTER_USERS_TABLE, $id, $field, $value);
  1654. }
  1655. function set_user_wp_user_id($user_id, $wp_user_id) {
  1656. $this->store->set_field(NEWSLETTER_USERS_TABLE, $user_id, 'wp_user_id', $wp_user_id);
  1657. }
  1658. /**
  1659. *
  1660. * @param int $wp_user_id
  1661. * @param string $format
  1662. * @return TNP_User
  1663. */
  1664. function get_user_by_wp_user_id($wp_user_id, $format = OBJECT) {
  1665. return $this->store->get_single_by_field(NEWSLETTER_USERS_TABLE, 'wp_user_id', $wp_user_id, $format);
  1666. }
  1667. /**
  1668. * Returns the user language IF there is a supported mutilanguage plugin installed.
  1669. * @param TNP_User $user
  1670. * @return string Language code or empty
  1671. */
  1672. function get_user_language($user) {
  1673. if ($user && $this->is_multilanguage()) {
  1674. return $user->language;
  1675. }
  1676. return '';
  1677. }
  1678. /**
  1679. * Replaces every possible Newsletter tag ({...}) in a piece of text or HTML.
  1680. *
  1681. * @global wpdb $wpdb
  1682. * @param string $text
  1683. * @param mixed $user Can be an object, associative array or id
  1684. * @param mixed $email Can be an object, associative array or id
  1685. * @param type $referrer
  1686. * @return type
  1687. */
  1688. function replace($text, $user = null, $email = null, $referrer = null) {
  1689. global $wpdb;
  1690. static $home_url = false;
  1691. if (!$home_url) {
  1692. $home_url = home_url('/');
  1693. }
  1694. //$this->logger->debug('Replace start');
  1695. if ($user !== null && !is_object($user)) {
  1696. if (is_array($user)) {
  1697. $user = (object) $user; //$this->get_user($user['id']);
  1698. } else if (is_numeric($user)) {
  1699. $user = $this->get_user($user);
  1700. } else {
  1701. $user = null;
  1702. }
  1703. }
  1704. if ($email !== null && !is_object($email)) {
  1705. if (is_array($email)) {
  1706. $email = (object) $email; //$this->get_user($user['id']);
  1707. } else if (is_numeric($email)) {
  1708. $email = $this->get_email($email);
  1709. } else {
  1710. $email = null;
  1711. }
  1712. }
  1713. $text = apply_filters('newsletter_replace', $text, $user, $email);
  1714. $text = $this->replace_url($text, 'BLOG_URL', $home_url);
  1715. $text = $this->replace_url($text, 'HOME_URL', $home_url);
  1716. $text = str_replace('{blog_title}', html_entity_decode(get_bloginfo('name')), $text);
  1717. $text = str_replace('{blog_description}', get_option('blogdescription'), $text);
  1718. $text = $this->replace_date($text);
  1719. if ($user) {
  1720. $nk = $this->get_user_key($user);
  1721. $options_profile = NewsletterSubscription::instance()->get_options('profile', $this->get_user_language($user));
  1722. $text = str_replace('{email}', $user->email, $text);
  1723. $name = apply_filters('newsletter_replace_name', $user->name, $user);
  1724. if (empty($name)) {
  1725. $text = str_replace(' {name}', '', $text);
  1726. $text = str_replace('{name}', '', $text);
  1727. } else {
  1728. $text = str_replace('{name}', $name, $text);
  1729. }
  1730. switch ($user->sex) {
  1731. case 'm': $text = str_replace('{title}', $options_profile['title_male'], $text);
  1732. break;
  1733. case 'f': $text = str_replace('{title}', $options_profile['title_female'], $text);
  1734. break;
  1735. case 'n': $text = str_replace('{title}', $options_profile['title_none'], $text);
  1736. break;
  1737. default:
  1738. $text = str_replace('{title}', '', $text);
  1739. }
  1740. $text = str_replace('{surname}', $user->surname, $text);
  1741. $text = str_replace('{last_name}', $user->surname, $text);
  1742. $full_name = trim($user->name . ' ' . $user->surname);
  1743. if (empty($full_name)) {
  1744. $text = str_replace(' {full_name}', '', $text);
  1745. $text = str_replace('{full_name}', '', $text);
  1746. } else {
  1747. $text = str_replace('{full_name}', $full_name, $text);
  1748. }
  1749. $text = str_replace('{token}', $user->token, $text);
  1750. $text = str_replace('%7Btoken%7D', $user->token, $text);
  1751. $text = str_replace('{id}', $user->id, $text);
  1752. $text = str_replace('%7Bid%7D', $user->id, $text);
  1753. $text = str_replace('{ip}', $user->ip, $text);
  1754. $text = str_replace('{key}', $nk, $text);
  1755. $text = str_replace('%7Bkey%7D', $nk, $text);
  1756. for ($i = 1; $i < NEWSLETTER_PROFILE_MAX; $i++) {
  1757. $p = 'profile_' . $i;
  1758. $text = str_replace('{profile_' . $i . '}', $user->$p, $text);
  1759. }
  1760. $base = (empty($this->options_main['url']) ? get_option('home') : $this->options_main['url']);
  1761. $id_token = '&amp;ni=' . $user->id . '&amp;nt=' . $user->token;
  1762. $nek = false;
  1763. if ($email) {
  1764. $nek = $this->get_email_key($email);
  1765. $text = str_replace('{email_id}', $email->id, $text);
  1766. $text = str_replace('{email_key}', $nek, $text);
  1767. $text = str_replace('{email_subject}', $email->subject, $text);
  1768. $text = $this->replace_url($text, 'EMAIL_URL', $this->build_action_url('v', $user) . '&id=' . $email->id);
  1769. }
  1770. $text = $this->replace_url($text, 'SUBSCRIPTION_CONFIRM_URL', $this->build_action_url('c', $user));
  1771. $text = $this->replace_url($text, 'ACTIVATION_URL', $this->build_action_url('v', $user));
  1772. // Obsolete.
  1773. $text = $this->replace_url($text, 'FOLLOWUP_SUBSCRIPTION_URL', self::add_qs($base, 'nm=fs' . $id_token));
  1774. $text = $this->replace_url($text, 'FOLLOWUP_UNSUBSCRIPTION_URL', self::add_qs($base, 'nm=fu' . $id_token));
  1775. $text = $this->replace_url($text, 'UNLOCK_URL', $this->build_action_url('ul', $user));
  1776. } else {
  1777. $text = $this->replace_url($text, 'SUBSCRIPTION_CONFIRM_URL', '#');
  1778. $text = $this->replace_url($text, 'ACTIVATION_URL', '#');
  1779. $text = $this->replace_url($text, 'UNSUBSCRIPTION_CONFIRM_URL', '#');
  1780. $text = $this->replace_url($text, 'UNSUBSCRIPTION_URL', '#');
  1781. }
  1782. if (strpos($text, '{subscription_form}') !== false) {
  1783. $text = str_replace('{subscription_form}', NewsletterSubscription::instance()->get_subscription_form($referrer), $text);
  1784. } else {
  1785. for ($i = 1; $i <= 10; $i++) {
  1786. if (strpos($text, "{subscription_form_$i}") !== false) {
  1787. $text = str_replace("{subscription_form_$i}", NewsletterSubscription::instance()->get_form($i), $text);
  1788. break;
  1789. }
  1790. }
  1791. }
  1792. // Company info
  1793. // TODO: Move to another module
  1794. $options = Newsletter::instance()->get_options('info');
  1795. $text = str_replace('{company_address}', $options['footer_contact'], $text);
  1796. $text = str_replace('{company_name}', $options['footer_title'], $text);
  1797. //$this->logger->debug('Replace end');
  1798. return $text;
  1799. }
  1800. function replace_date($text) {
  1801. $text = str_replace('{date}', date_i18n(get_option('date_format')), $text);
  1802. // Date processing
  1803. $x = 0;
  1804. while (($x = strpos($text, '{date_', $x)) !== false) {
  1805. $y = strpos($text, '}', $x);
  1806. if ($y === false)
  1807. continue;
  1808. $f = substr($text, $x + 6, $y - $x - 6);
  1809. $text = substr($text, 0, $x) . date_i18n($f) . substr($text, $y + 1);
  1810. }
  1811. return $text;
  1812. }
  1813. function replace_url($text, $tag, $url) {
  1814. static $home = false;
  1815. if (!$home) {
  1816. $home = trailingslashit(home_url());
  1817. }
  1818. $tag_lower = strtolower($tag);
  1819. $text = str_replace('http://{' . $tag_lower . '}', $url, $text);
  1820. $text = str_replace('https://{' . $tag_lower . '}', $url, $text);
  1821. $text = str_replace($home . '{' . $tag_lower . '}', $url, $text);
  1822. $text = str_replace($home . '%7B' . $tag_lower . '%7D', $url, $text);
  1823. $text = str_replace('{' . $tag_lower . '}', $url, $text);
  1824. $text = str_replace('%7B' . $tag_lower . '%7D', $url, $text);
  1825. $url_encoded = urlencode($url);
  1826. $text = str_replace('%7B' . $tag_lower . '_encoded%7D', $url_encoded, $text);
  1827. $text = str_replace('{' . $tag_lower . '_encoded}', $url_encoded, $text);
  1828. // for compatibility
  1829. $text = str_replace($home . $tag, $url, $text);
  1830. return $text;
  1831. }
  1832. public static function antibot_form_check($captcha = false) {
  1833. if (strtolower($_SERVER['REQUEST_METHOD']) != 'post') {
  1834. return false;
  1835. }
  1836. if (!isset($_POST['ts']) || time() - $_POST['ts'] > 60) {
  1837. return false;
  1838. }
  1839. if ($captcha) {
  1840. $n1 = (int) $_POST['n1'];
  1841. if (empty($n1)) {
  1842. return false;
  1843. }
  1844. $n2 = (int) $_POST['n2'];
  1845. if (empty($n2)) {
  1846. return false;
  1847. }
  1848. $n3 = (int) $_POST['n3'];
  1849. if ($n1 + $n2 != $n3) {
  1850. return false;
  1851. }
  1852. }
  1853. return true;
  1854. }
  1855. public static function request_to_antibot_form($submit_label = 'Continue...', $captcha = false) {
  1856. header('Content-Type: text/html;charset=UTF-8');
  1857. header('X-Robots-Tag: noindex,nofollow,noarchive');
  1858. header('Cache-Control: no-cache,no-store,private');
  1859. echo "<!DOCTYPE html>\n";
  1860. echo '<html><head>'
  1861. . '<style type="text/css">'
  1862. . '.tnp-captcha {text-align: center; margin: 200px auto 0 auto !important; max-width: 300px !important; padding: 10px !important; font-family: "Open Sans", sans-serif; background: #ECF0F1; border-radius: 5px; padding: 50px !important; border: none !important;}'
  1863. . 'p {text-align: center; padding: 10px; color: #7F8C8D;}'
  1864. . 'input[type=text] {width: 50px; padding: 10px 10px; border: none; border-radius: 2px; margin: 0px 5px;}'
  1865. . 'input[type=submit] {text-align: center; border: none; padding: 10px 15px; font-family: "Open Sans", sans-serif; background-color: #27AE60; color: white; cursor: pointer;}'
  1866. . '</style>'
  1867. . '</head><body>';
  1868. echo '<form method="post" action="https://www.domain.tld" id="form">';
  1869. echo '<div style="width: 1px; height: 1px; overflow: hidden">';
  1870. foreach ($_REQUEST as $name => $value) {
  1871. if ($name == 'submit')
  1872. continue;
  1873. if (is_array($value)) {
  1874. foreach ($value as $element) {
  1875. echo '<input type="text" name="';
  1876. echo esc_attr($name);
  1877. echo '[]" value="';
  1878. echo esc_attr(stripslashes($element));
  1879. echo '">';
  1880. }
  1881. } else {
  1882. echo '<input type="hidden" name="', esc_attr($name), '" value="', esc_attr(stripslashes($value)), '">';
  1883. }
  1884. }
  1885. if (isset($_SERVER['HTTP_REFERER'])) {
  1886. echo '<input type="hidden" name="nhr" value="' . esc_attr($_SERVER['HTTP_REFERER']) . '">';
  1887. }
  1888. echo '<input type="hidden" name="ts" value="' . time() . '">';
  1889. echo '</div>';
  1890. if ($captcha) {
  1891. echo '<div class="tnp-captcha">';
  1892. echo '<p>', __('Math question', 'newsletter'), '</p>';
  1893. echo '<input type="text" name="n1" value="', rand(1, 9), '" readonly style="width: 50px">';
  1894. echo '+';
  1895. echo '<input type="text" name="n2" value="', rand(1, 9), '" readonly style="width: 50px">';
  1896. echo '=';
  1897. echo '<input type="text" name="n3" value="?" style="width: 50px">';
  1898. echo '<br><br>';
  1899. echo '<input type="submit" value="', esc_attr($submit_label), '">';
  1900. echo '</div>';
  1901. }
  1902. echo '<noscript><input type="submit" value="';
  1903. echo esc_attr($submit_label);
  1904. echo '"></noscript></form>';
  1905. echo '<script>';
  1906. echo 'document.getElementById("form").action="' . home_url('/') . '";';
  1907. if (!$captcha) {
  1908. echo 'document.getElementById("form").submit();';
  1909. }
  1910. echo '</script>';
  1911. echo '</body></html>';
  1912. die();
  1913. }
  1914. static function extract_body($html) {
  1915. $x = stripos($html, '<body');
  1916. if ($x !== false) {
  1917. $x = strpos($html, '>', $x);
  1918. $y = strpos($html, '</body>');
  1919. return substr($html, $x + 1, $y - $x - 1);
  1920. } else {
  1921. return $html;
  1922. }
  1923. }
  1924. /** Returns a percentage as string */
  1925. static function percent($value, $total) {
  1926. if ($total == 0)
  1927. return '-';
  1928. return sprintf("%.2f", $value / $total * 100) . '%';
  1929. }
  1930. /** Returns a percentage as integer value */
  1931. static function percentValue($value, $total) {
  1932. if ($total == 0)
  1933. return 0;
  1934. return round($value / $total * 100);
  1935. }
  1936. /**
  1937. * Takes in a variable and checks if object, array or scalar and return the integer representing
  1938. * a database record id.
  1939. *
  1940. * @param mixed $var
  1941. * @return in
  1942. */
  1943. static function to_int_id($var) {
  1944. if (is_object($var)) {
  1945. return (int) $var->id;
  1946. }
  1947. if (is_array($var)) {
  1948. return (int) $var['id'];
  1949. }
  1950. return (int) $var;
  1951. }
  1952. static function to_array($text) {
  1953. $text = trim($text);
  1954. if (empty($text)) {
  1955. return array();
  1956. }
  1957. $text = preg_split("/\\r\\n/", $text);
  1958. $text = array_map('trim', $text);
  1959. $text = array_map('strtolower', $text);
  1960. $text = array_filter($text);
  1961. return $text;
  1962. }
  1963. static function sanitize_ip($ip) {
  1964. if (empty($ip))
  1965. return '';
  1966. return preg_replace('/[^0-9a-fA-F:., ]/', '', $ip);
  1967. }
  1968. static function get_remote_ip() {
  1969. return self::sanitize_ip($_SERVER['REMOTE_ADDR']);
  1970. }
  1971. static function get_signature($text) {
  1972. $key = NewsletterStatistics::instance()->options['key'];
  1973. return md5($text . $key);
  1974. }
  1975. static function check_signature($text, $signature) {
  1976. if (empty($signature))
  1977. return false;
  1978. $key = NewsletterStatistics::instance()->options['key'];
  1979. return md5($text . $key) === $signature;
  1980. }
  1981. static function get_home_url() {
  1982. static $url = false;
  1983. if (!$url) {
  1984. $url = home_url('/');
  1985. }
  1986. return $url;
  1987. }
  1988. static function clean_eol($text) {
  1989. $text = str_replace("\r\n", "\n", $text);
  1990. $text = str_replace("\r", "\n", $text);
  1991. $text = str_replace("\n", "\r\n", $text);
  1992. return $text;
  1993. }
  1994. /**
  1995. * Return the current language code. Optionally, if a user is passed and it has a language
  1996. * the user language is returned.
  1997. * If there is no language available, an empty string is returned.
  1998. *
  1999. * @param TNP_User $user
  2000. * @return string The language code
  2001. */
  2002. function get_current_language($user = null) {
  2003. global $TRP_LANGUAGE, $current_user;
  2004. // TODO: Check if the blog is multilanguage?
  2005. if ($user && $user->language) {
  2006. return $user->language;
  2007. }
  2008. if (class_exists('SitePress')) {
  2009. $current_language = apply_filters('wpml_current_language', '');
  2010. if ($current_language == 'all') {
  2011. $current_language = '';
  2012. }
  2013. return $current_language;
  2014. }
  2015. if (function_exists('pll_current_language')) {
  2016. return pll_current_language();
  2017. }
  2018. $current_language = apply_filters('newsletter_current_language', '');
  2019. return $current_language;
  2020. }
  2021. function get_default_language() {
  2022. if (class_exists('SitePress')) {
  2023. return $current_language = apply_filters('wpml_current_language', '');
  2024. } else if (function_exists('pll_default_language')) {
  2025. return pll_default_language();
  2026. } else if (class_exists('TRP_Translate_Press')) {
  2027. // TODO: Find the default language
  2028. }
  2029. return '';
  2030. }
  2031. function is_all_languages() {
  2032. return $this->get_current_language() == '';
  2033. }
  2034. function is_default_language() {
  2035. return $this->get_current_language() == $this->get_default_language();
  2036. }
  2037. /**
  2038. * Returns an array of languages with key the language code and value the language name.
  2039. * An empty array is returned if no language is available.
  2040. */
  2041. function get_languages() {
  2042. $language_options = array();
  2043. if (class_exists('SitePress')) {
  2044. $languages = apply_filters('wpml_active_languages', null);
  2045. foreach ($languages as $language) {
  2046. $language_options[$language['language_code']] = $language['translated_name'];
  2047. }
  2048. return $language_options;
  2049. } else if (function_exists('icl_get_languages')) {
  2050. $languages = icl_get_languages();
  2051. foreach ($languages as $code => $language) {
  2052. $language_options[$code] = $language['native_name'];
  2053. }
  2054. return $language_options;
  2055. }
  2056. return apply_filters('newsletter_languages', $language_options);
  2057. }
  2058. function get_language_label($language) {
  2059. $languages = $this->get_languages();
  2060. if (isset($languages[$language]))
  2061. return $languages[$language];
  2062. return '';
  2063. }
  2064. function switch_language($language) {
  2065. if (class_exists('SitePress')) {
  2066. if (empty($language))
  2067. $language = 'all';
  2068. do_action('wpml_switch_language', $language);
  2069. return;
  2070. }
  2071. }
  2072. function is_multilanguage() {
  2073. return class_exists('SitePress') || function_exists('pll_default_language') || class_exists('TRP_Translate_Press');
  2074. }
  2075. function get_posts($filters = array(), $language = '') {
  2076. $current_language = $this->get_current_language();
  2077. // Language switch for WPML
  2078. if ($language) {
  2079. if (class_exists('SitePress')) {
  2080. $this->switch_language($language);
  2081. $filters['suppress_filters'] = false;
  2082. }
  2083. if (class_exists('Polylang')) {
  2084. $filters['lang'] = $language;
  2085. }
  2086. }
  2087. $posts = get_posts($filters);
  2088. if ($language) {
  2089. if (class_exists('SitePress')) {
  2090. $this->switch_language($current_language);
  2091. }
  2092. }
  2093. return $posts;
  2094. }
  2095. }
  2096. /**
  2097. * Kept for compatibility.
  2098. *
  2099. * @param type $post_id
  2100. * @param type $size
  2101. * @param type $alternative
  2102. * @return type
  2103. */
  2104. function nt_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
  2105. return NewsletterModule::get_post_image($post_id, $size, $alternative);
  2106. }
  2107. function newsletter_get_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
  2108. echo NewsletterModule::get_post_image($post_id, $size, $alternative);
  2109. }
  2110. /**
  2111. * Accepts a post or a post ID.
  2112. *
  2113. * @param WP_Post $post
  2114. */
  2115. function newsletter_the_excerpt($post, $words = 30) {
  2116. $post = get_post($post);
  2117. $excerpt = $post->post_excerpt;
  2118. if (empty($excerpt)) {
  2119. $excerpt = $post->post_content;
  2120. $excerpt = strip_shortcodes($excerpt);
  2121. $excerpt = wp_strip_all_tags($excerpt, true);
  2122. }
  2123. echo '<p>' . wp_trim_words($excerpt, $words) . '</p>';
  2124. }