WJ_Stats

Class Stats.

Defined (1)

The class is defined in the following location(s).

/classes/WJ_Stats.php  
  1. class WJ_Stats extends WYSIJA_object { 
  2.  
  3. public $email_id = null; 
  4. public $user_id = null; 
  5. public $clicked_url = ''; 
  6. public $is_preview = false; 
  7.  
  8. /** 
  9. * Email object 
  10. * @var Wysija_email 
  11. */ 
  12. private $_email; 
  13.  
  14. /** 
  15. * User object 
  16. * @var Wysija_user 
  17. */ 
  18. private $_user; 
  19.  
  20. private $_url_id = null; 
  21. private $_unique_click = false; 
  22. public $decoded_url = ''; 
  23.  
  24.  
  25. /** 
  26. * load some global variables into the class 
  27. */ 
  28. function __construct() { 
  29. if (!empty($_REQUEST['email_id'])) 
  30. $this->email_id = (int) $_REQUEST['email_id']; 
  31. if (!empty($_REQUEST['user_id'])) 
  32. $this->user_id = (int) $_REQUEST['user_id']; 
  33. if (!empty($_REQUEST['demo'])) 
  34. $this->is_preview = true; 
  35. $this->_get_clicked_url(); 
  36.  
  37. // $this->_user can be empty, it's helpful in case an email was sent as a preview to a non-existing user, then a link is opened from there 
  38. $this->_user = WYSIJA::get('user', 'model')->getOne(false, (int)$this->user_id); 
  39.  
  40. $this->_email = WYSIJA::get('email', 'model')->getOne(false, (int)$this->email_id); 
  41. if (empty($this->_email)) 
  42. exit; 
  43.  
  44. // consider to get / insert URL record here 
  45. // $this->_url = $this->_get_url($this->clicked_url); 
  46.  
  47. /** 
  48. * Get user object 
  49. * @param type $user_id 
  50. */ 
  51. public function get_user() { 
  52. return $this->_user; 
  53.  
  54. /** 
  55. * Get email object 
  56. * @param type $user_id 
  57. */ 
  58. public function get_email() { 
  59. return $this->_email; 
  60.  
  61. /** 
  62. * count the action as an open and display the empty picture 
  63. */ 
  64. public function subscriber_opened() { 
  65. if (!$this->_is_open_action()) 
  66. exit; 
  67.  
  68. $model_email_user_stat = WYSIJA::get('email_user_stat', 'model'); 
  69. $model_email_user_stat->reset(); 
  70.  
  71. // update status to 1 and set opened_at time 
  72. // only if the status = 0 
  73. $model_email_user_stat->update( 
  74. array('status' => 1, 'opened_at' => time()),  
  75. array('email_id' => $this->email_id, 'user_id' => $this->user_id, 'status' => 0) 
  76. ); 
  77.  
  78. $this->_update_user(array('last_opened' => time())); 
  79.  
  80. header('Cache-Control: no-store, no-cache, must-revalidate'); 
  81. header('Cache-Control: post-check=0, pre-check=0', false); 
  82. header('Pragma: no-cache'); 
  83.  
  84. if (empty($picture)) 
  85. $picture = WYSIJA_DIR_IMG . 'statpicture.png'; 
  86. $handle = fopen($picture, 'r'); 
  87.  
  88. if (!$handle) 
  89. exit; 
  90. header('Content-type: image/png'); 
  91. $contents = fread($handle, filesize($picture)); 
  92. fclose($handle); 
  93. echo $contents; 
  94. exit; 
  95.  
  96. public function subscriber_clicked() { 
  97. if (!$this->_is_click_action()) 
  98. exit; 
  99.  
  100. if ($this->email_id && $this->is_preview === false) { //if not email_id that means it is an email preview 
  101. $this->_record_url(); 
  102.  
  103. $this->_record_user_url(); 
  104.  
  105. $this->_record_url_mail(); 
  106.  
  107. $redirect = $this->_record_email_user_stat(); 
  108. if (!empty($redirect)) 
  109. $this->decoded_url = $redirect; 
  110. $this->_update_user(array('last_clicked' => time())); 
  111. }else { 
  112. $this->_get_url_preview(); 
  113. //sometimes this will be a life saver :) 
  114. $this->decoded_url = str_replace('&', '&', $this->decoded_url); 
  115. if ($this->is_browser_link($this->decoded_url)) { 
  116. $this->decoded_url = $this->attach_user($this->decoded_url); 
  117. return $this->decoded_url; 
  118.  
  119. /** 
  120. * Attach user_id as a param of an url 
  121. * @param string $url 
  122. * @param int $user_id 
  123. * @return string 
  124. */ 
  125. protected function attach_user($url) { 
  126. if (!empty($this->_user) && !empty($this->_user['user_id'])) { 
  127. $url_components = parse_url($url); 
  128. $arr_params = array(); 
  129. if (!empty($url_components['query'])) 
  130. parse_str($url_components['query'], $arr_params); 
  131. if (empty($arr_params['user_id'])) { 
  132. $url .= ((strpos($url, '?') !== false) ? '&' : '?'); 
  133. $url .= 'user_id='.$this->_user['user_id']; 
  134. return $url; 
  135.  
  136. /** 
  137. * record entry into the table email_user_url 
  138. */ 
  139. private function _record_user_url() { 
  140. //look for email_user_url entry and insert if not exists 
  141. $model_email_user_url = WYSIJA::get('email_user_url', 'model'); 
  142. $data_email_user_url = array('email_id' => $this->email_id, 'user_id' => $this->user_id, 'url_id' => $this->_url_id); 
  143. $email_user_url_array = $model_email_user_url->getOne(false, $data_email_user_url); 
  144.  
  145. if (!$email_user_url_array && $this->email_id > 0 && $this->user_id > 0 && $this->_url_id > 0) { 
  146. //we need to insert in email_user_url 
  147. $model_email_user_url->reset(); 
  148. $query_EmailUserUrl = 'INSERT IGNORE INTO [wysija]email_user_url (`email_id` , `user_id`, `url_id`) '; 
  149. $query_EmailUserUrl .= 'VALUES (' . $this->email_id . ', ' . $this->user_id . ', ' . $this->_url_id . ')'; 
  150.  
  151. $model_email_user_url->query($query_EmailUserUrl); 
  152.  
  153. //$modelEmailUserUrl->insert($dataEmailUserUrl); 
  154. $this->_unique_click = true; 
  155.  
  156. //increment stats counter on email_user_url clicked 
  157. $model_email_user_url = WYSIJA::get('email_user_url', 'model'); 
  158. $model_email_user_url->update(array('clicked_at' => time(), 'number_clicked' => '[increment]'), $data_email_user_url); 
  159.  
  160. /** 
  161. * record entry into the table url_mail 
  162. */ 
  163. private function _record_url_mail() { 
  164. //look for url_mail entry and insert if not exists 
  165. $model_url_mail = WYSIJA::get('url_mail', 'model'); 
  166. $data_url_mail = array('email_id' => $this->email_id, 'url_id' => $this->_url_id); 
  167. $urlMailObj = $model_url_mail->getOne(false, $data_url_mail); 
  168. if (!$urlMailObj) { 
  169. //we need to insert in url_mail 
  170. $model_url_mail->reset(); 
  171. $model_url_mail->insert($data_url_mail); 
  172.  
  173. $data_update = array('total_clicked' => '[increment]'); 
  174. if (!$this->_unique_click) 
  175. $data_update['unique_clicked'] = '[increment]'; 
  176. //increment stats counter on url_mail clicked 
  177. $model_url_mail->update($data_update, $data_url_mail); 
  178.  
  179. /** 
  180. * record entry into the table email_user_stat 
  181. */ 
  182. private function _record_email_user_stat() { 
  183. // clicked status 
  184. $status_email_user_stat = 2; 
  185.  
  186. // this is the system url case (unsubscribe, view in browser and subscriptions) 
  187. if (in_array($this->clicked_url, array('[unsubscribe_link]', '[subscriptions_link]', '[view_in_browser_link]'))) { 
  188. $this->subscriberClass = WYSIJA::get('user', 'model'); 
  189. $this->subscriberClass->getFormat = OBJECT; 
  190.  
  191. //check if the security hash is passed to insure privacy 
  192. $receiver = $link = false; 
  193. if (isset($_REQUEST['hash'])) { 
  194. if ($_REQUEST['hash'] == md5(AUTH_KEY . $this->clicked_url . $this->user_id)) { 
  195. $receiver = $this->subscriberClass->getOne(array('user_id' => $this->user_id)); 
  196. } else { 
  197. die('Security check failure.'); 
  198. } else { 
  199. //link is not valid anymore 
  200. //propose to resend the newsletter with good links ? 
  201. $link = $this->subscriberClass->old_get_new_link_for_expired_links($this->user_id, $this->email_id); 
  202.  
  203.  
  204. switch ($this->clicked_url) { 
  205. case '[unsubscribe_link]': 
  206. // we need to make sure that this link belongs to that user 
  207. if ($receiver) { 
  208. $link = $this->subscriberClass->getUnsubLink($receiver, true); 
  209. // unsubscribe status 
  210. $status_email_user_stat = 3; 
  211. break; 
  212. case '[subscriptions_link]': 
  213. if ($receiver) { 
  214. $link = $this->subscriberClass->getEditsubLink($receiver, true); 
  215. break; 
  216. case '[view_in_browser_link]': 
  217. $model_email = WYSIJA::get('email', 'model'); 
  218. $data_email = $model_email->getOne(false, array('email_id' => $this->email_id)); 
  219. $helper_email = WYSIJA::get('email', 'helper'); 
  220. $link = $helper_email->getVIB($data_email); 
  221. break; 
  222.  
  223. //if the subscriber already exists in the DB we will have a link 
  224. if ($link) { 
  225. $this->decoded_url = $link; 
  226. } else { 
  227. //the subscriber doesn't appear in the DB we can redirect to the web version 
  228. $this->decoded_url = $this->_get_browser_link(); 
  229.  
  230. return $this->decoded_url; 
  231. } else { 
  232. // this is the standard non-system url case 
  233. if (strpos($this->decoded_url, 'http://') === false && strpos($this->decoded_url, 'https://') === false) { 
  234. $this->decoded_url = 'http://' . $this->decoded_url; 
  235.  
  236. // check that there is no broken unsubscribe link such as http://[unsubscribe_link] 
  237. if (strpos($this->decoded_url, '[unsubscribe_link]') !== false) { 
  238. $this->subscriberClass = WYSIJA::get('user', 'model'); 
  239. $this->subscriberClass->getFormat = OBJECT; 
  240. $receiver = $this->subscriberClass->getOne($this->user_id); 
  241. $this->decoded_url = $this->subscriberClass->getUnsubLink($receiver, true); 
  242.  
  243. if (strpos($this->decoded_url, '[view_in_browser_link]') !== false) { 
  244. $link = $this->_get_browser_link(); 
  245. $this->decoded_url = $link; 
  246.  
  247. $data_update = array(); 
  248.  
  249. // check if we already have a record 
  250. $model_email_user_stat = WYSIJA::get('email_user_stat', 'model'); 
  251. $exists = $model_email_user_stat->getOne(false, array('equal' => array('email_id' => $this->email_id, 'user_id' => $this->user_id), 'less' => array('status' => $status_email_user_stat))); 
  252.  
  253. // fix "opened_at" value in case the "opened" status was not properly recorded (blocked images) 
  254. if(is_array($exists) && array_key_exists('opened_at', $exists) && (int)$exists['opened_at'] === 0) { 
  255. // set opened at in case it was not recorded 
  256. $data_update['opened_at'] = time(); 
  257.  
  258. $model_email_user_stat->reset(); 
  259. $model_email_user_stat->colCheck = false; 
  260.  
  261. // set new status 
  262. $data_update['status'] = $status_email_user_stat; 
  263.  
  264. // update email user stat 
  265. $model_email_user_stat->update($data_update, array('equal' => array('email_id' => $this->email_id, 'user_id' => $this->user_id), 'less' => array('status' => $status_email_user_stat))); 
  266.  
  267. /** 
  268. * update the user last_clicked or last_opened parameter 
  269. * @param type $data_update 
  270. */ 
  271. private function _update_user($data_update) { 
  272. if (!empty($data_update)) { 
  273. $model_user = WYSIJA::get('user', 'model'); 
  274. $model_user->update($data_update, array('user_id' => $this->user_id)); 
  275.  
  276. /** 
  277. * when the email sent is a preview email, we just need to emulate actions, no need to count stats or to unsubscribe user etc ... 
  278. * @return boolean 
  279. */ 
  280. private function _get_url_preview() { 
  281. // we're in the case of an email preview 
  282. if (in_array($this->clicked_url, array('[unsubscribe_link]', '[subscriptions_link]', '[view_in_browser_link]'))) { 
  283. $model_user = WYSIJA::get('user', 'model'); 
  284. $model_user->getFormat = OBJECT; 
  285. $user_object = $model_user->getOne(false, array('wpuser_id' => get_current_user_id())); 
  286. switch ($this->clicked_url) { 
  287. case '[unsubscribe_link]': 
  288. $link = $model_user->getConfirmLink($user_object, 'unsubscribe', false, true) . '&demo=1'; 
  289.  
  290. break; 
  291. case '[subscriptions_link]': 
  292. $link = $model_user->getConfirmLink($user_object, 'subscriptions', false, true) . '&demo=1'; 
  293. break; 
  294. case 'view_in_browser_link': 
  295. case '[view_in_browser_link]': 
  296. if (!$this->email_id) 
  297. $this->email_id = $_REQUEST['id']; 
  298.  
  299. $link = $this->_get_browser_link(); 
  300. break; 
  301.  
  302. $this->decoded_url = $link; 
  303. }else { 
  304. if (strpos($this->decoded_url, 'http://') === false && strpos($this->decoded_url, 'https://') === false) 
  305. $this->decoded_url = 'http://' . $this->decoded_url; 
  306. return true; 
  307.  
  308. /** 
  309. * construct the view in your browser link 
  310. * @return type 
  311. */ 
  312. private function _get_browser_link() { 
  313. $params_url = array( 
  314. 'wysija-page' => 1,  
  315. 'controller' => 'email',  
  316. 'action' => 'view',  
  317. 'email_id' => $this->email_id,  
  318. 'user_id' => 0 
  319. ); 
  320. $config = WYSIJA::get('config', 'model'); 
  321. return WYSIJA::get_permalink($config->getValue('confirm_email_link'), $params_url); 
  322.  
  323. /** 
  324. * Detect if the current link is a browser link 
  325. * @param string $url 
  326. * @return boolean 
  327. */ 
  328. protected function is_browser_link($url) { 
  329. $flag = false; 
  330. $helper_toolbox = WYSIJA::get('toolbox', 'helper'); 
  331. if ($helper_toolbox->is_internal_link($url)) { 
  332. $url_components = parse_url($url); 
  333. if (!empty($url_components['query'])) { 
  334. $params = array(); 
  335. parse_str($url_components['query'], $params); 
  336. if (!empty($params['controller']) && strtolower($params['controller']) == 'email' 
  337. && !empty($params['action']) && strtolower($params['action']) == 'view' 
  338. ) { 
  339. $flag = true; 
  340. return $flag; 
  341.  
  342. /** 
  343. * check that this is a valid open action 
  344. * @return boolean 
  345. */ 
  346. private function _is_open_action() { 
  347. if (empty($this->email_id) || empty($this->user_id)) 
  348. return false; 
  349.  
  350. return true; 
  351.  
  352. /** 
  353. * check that this is a valid click action 
  354. * @return boolean 
  355. */ 
  356. private function _is_click_action() { 
  357. if ((empty($this->email_id) || empty($this->user_id) || empty($this->clicked_url)) && $this->is_preview === false) 
  358. return false; 
  359.  
  360. return true; 
  361.  
  362. /** 
  363. * record the url into the db if not recorded already 
  364. * @return boolean 
  365. */ 
  366. private function _record_url() { 
  367. //look for url entry and insert if not exists 
  368. $model_url = WYSIJA::get('url', 'model'); 
  369. $url_found = $model_url->getOne(false, array('url' => $this->clicked_url)); 
  370.  
  371. if ($url_found) { 
  372. // we need to keep it 
  373. $this->_url_id = $url_found['url_id']; 
  374. } else { 
  375. // we need to record in database 
  376. $this->_url_id = $model_url->insert(array('url' => $this->clicked_url)); 
  377.  
  378. /** 
  379. * get/alter clicked url based on the global parameters 
  380. */ 
  381. private function _get_clicked_url() { 
  382.  
  383. if (isset($_REQUEST['urlencoded'])) { 
  384. $this->clicked_url = $_REQUEST['urlencoded']; 
  385. } elseif (isset($_REQUEST['urlpassed'])) { 
  386. $this->clicked_url = $_REQUEST['urlpassed']; 
  387.  
  388. // make sure the url is or is not base64 encoded, some server cannot handle long url or url with encoded parameter which is the default behaviour 
  389. if (isset($_REQUEST['no64'])) { 
  390. $this->decoded_url = $this->clicked_url; 
  391. } else { 
  392. $this->clicked_url = $this->decoded_url = base64_decode($this->clicked_url); 
  393.  
  394. if (strpos($this->clicked_url, 'utm_source') !== false) { 
  395. $this->clicked_url = $this->_clean_params_url(array('utm_source', 'utm_campaign', 'utm_medium'), $this->clicked_url); 
  396. return true; 
  397.  
  398. /** 
  399. * remove any params from a url 
  400. * @param array $params_to_remove 
  401. * @param string $url 
  402. * @return string 
  403. */ 
  404. private function _clean_params_url($params_to_remove = array(), $url = '') { 
  405. if (!$url) { 
  406. return $url; 
  407.  
  408. $url_splitted = explode('?', $url); 
  409. $params_in_url = array(); 
  410.  
  411. // lets parse the parameters of the url 
  412. parse_str($url_splitted[1], $params_in_url); 
  413.  
  414. foreach ($params_to_remove as $param_key) { 
  415. unset($params_in_url[$param_key]); 
  416.  
  417. // let's use the base of that url to rebuild it without the cleaned out parameters. 
  418. $new_url = $url_splitted[0]; 
  419.  
  420. // when there are params left other than the ones we cleaned out of the url, we stick them back together 
  421. if($params_in_url) { 
  422. $new_url .= '?'; 
  423. $i = 0; 
  424. foreach ($params_in_url as $k => $v) { 
  425. if ($i > 0) { 
  426. $new_url .= '&'; 
  427.  
  428. // parse_str keeps the ampersand as a html number, let's clean it out so that the url look good when recorded 
  429. $new_url .= str_replace($k, '&', '') . '=' . $v; 
  430. $i++; 
  431.  
  432. return $new_url; 
  433.