PageRenderTime 35ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-content/plugins/wp-invoice/core/wpi_functions.php

https://bitbucket.org/sanders_nick/my-maxi-skirt
PHP | 3033 lines | 1883 code | 498 blank | 652 comment | 444 complexity | 255ba36bcefeac09f578acca1ffeb4b1 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-1.0, GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. *
  4. * Handles all functions
  5. *
  6. */
  7. setlocale(LC_MONETARY, 'en_US');
  8. class WPI_Functions {
  9. /**
  10. * PHP function to echoing a message to JS console
  11. * Ported from WP-Property
  12. *
  13. * @since 3.0.3
  14. */
  15. function console_log($text = false) {
  16. global $wpi_settings;
  17. if(isset($wpi_settings['developer_mode']) && $wpi_settings['developer_mode'] != 'true') {
  18. return;
  19. }
  20. if(empty($text)) {
  21. return;
  22. }
  23. if(is_array($text) || is_object($text)) {
  24. $text = str_replace("\n", '', print_r($text, true));
  25. }
  26. //** Cannot use quotes */
  27. $text = str_replace('"', '-', $text);
  28. add_filter('wp_footer', create_function('$nothing,$echo_text = "'. $text .'"', 'echo \'<script type="text/javascript">if(typeof console == "object"){console.log("\' . $echo_text . \'");}</script>\'; '));
  29. add_filter('admin_footer', create_function('$nothing,$echo_text = "'. $text .'"', 'echo \'<script type="text/javascript">if(typeof console == "object"){console.log("\' . $echo_text . \'");}</script>\'; '));
  30. }
  31. /**
  32. * Retreives API key from UD servers.
  33. *
  34. * @todo Add loggin functionality so user can reference some log to see why key may not be updating.
  35. * @since 3.0.2
  36. *
  37. */
  38. function get_api_key($args = false){
  39. $defaults = array(
  40. 'return_all' => false,
  41. 'force_check' => false
  42. );
  43. $args = wp_parse_args( $args, $defaults );
  44. //** check if API key already exists */
  45. $ud_api_key = get_option('ud_api_key');
  46. //** if key exists, and we are not focing a check, return what we have */
  47. if($ud_api_key && !$args['force_check']) {
  48. return $ud_api_key;
  49. }
  50. $blogname = urlencode(str_replace(array('http://', 'https://'), '', get_bloginfo('url')));
  51. $system = 'wpi';
  52. $wpp_version = WP_INVOICE_VERSION_NUM;
  53. $check_url = "http://updates.usabilitydynamics.com/key_generator.php?system=$system&site=$blogname&system_version=$wpp_version";
  54. $response = @wp_remote_get($check_url);
  55. if(!$response) {
  56. return false;
  57. }
  58. //** Check for errors */
  59. if(is_object($response) && !empty($response->errors)) {
  60. /*
  61. foreach($response->errors as $errors) {
  62. $error_string .= implode(",", $errors);
  63. UD_F::log("API Check Error: " . $error_string);
  64. }
  65. */
  66. return false;
  67. }
  68. //** Quit if failture */
  69. if($response['response']['code'] != '200') {
  70. return false;
  71. }
  72. $response['body'] = trim($response['body']);
  73. //** If return is not in MD5 format, it is an error */
  74. if(strlen($response['body']) != 40) {
  75. if($args['return']) {
  76. return $response['body'];
  77. } else {
  78. /* UD_F::log("API Check Error: " . sprintf(__('An error occured during premium feature check: <b>%s</b>.','wpp'), $response['body'])); */
  79. return false;
  80. }
  81. }
  82. //** update wpi_key is DB */
  83. update_option('ud_api_key', $response['body']);
  84. // Go ahead and return, it should just be the API key
  85. return $response['body'];
  86. }
  87. /**
  88. * Function for performing a wpi_object search
  89. *
  90. *
  91. * @todo This function is not ready at all, it doesn't do any searching, just returns all invoices for testing datatables
  92. * @since 3.0
  93. *
  94. */
  95. static function query($search_vars = false) {
  96. global $wpdb;
  97. $sort_by = " ORDER BY post_modified DESC ";
  98. /** Start our SQL */
  99. $sql = "SELECT * FROM {$wpdb->posts} AS p WHERE post_type = 'wpi_object' ";
  100. if (!empty($search_vars)) {
  101. if (is_string($search_vars)) {
  102. $args = array();
  103. parse_str($search_vars, $args);
  104. $search_vars = $args;
  105. }
  106. /*
  107. $use_status_filter = false;
  108. if ( !empty( $search_vars['status'] ) ) {
  109. $use_status_filter = true;
  110. }
  111. */
  112. foreach ($search_vars as $primary_key => $key_terms) {
  113. //** Handle search_string differently, it applies to all meta values */
  114. if ($primary_key == 's') {
  115. /* First, go through the posts table */
  116. $tofind = strtolower($key_terms);
  117. $sql .= " AND (";
  118. $sql .= " p.ID IN (SELECT ID FROM {$wpdb->posts} WHERE LOWER(post_title) LIKE '%$tofind%')";
  119. /* Now go through the post meta table */
  120. $sql .= " OR p.ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE LOWER(meta_value) LIKE '%$tofind%')";
  121. $sql .= ")";
  122. continue;
  123. }
  124. // Type
  125. if ($primary_key == 'type') {
  126. if (empty($key_terms)) {
  127. continue;
  128. }
  129. if (is_array($key_terms)) {
  130. $key_terms = implode("','", $key_terms);
  131. }
  132. $sql .= " AND ";
  133. $sql .= " p.ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'type' AND meta_value IN ('{$key_terms}'))";
  134. continue;
  135. }
  136. // Status
  137. if ($primary_key == 'status') {
  138. if (empty($key_terms)) {
  139. continue;
  140. }
  141. if (is_array($key_terms)) {
  142. $sql .= " AND (";
  143. $i = 0;
  144. foreach ($key_terms as $term) {
  145. if (empty($term)) {
  146. continue;
  147. }
  148. if ($i > 0) {
  149. $sql .= " OR ";
  150. }
  151. $sql .= " post_status = '{$term}' ";
  152. $i++;
  153. }
  154. $sql .= ")";
  155. }
  156. }
  157. /*
  158. if ( !$use_status_filter ) {
  159. $sql .= " AND ( post_status = 'active' ) ";
  160. }
  161. */
  162. // Recipient
  163. if ($primary_key == 'recipient') {
  164. if (empty($key_terms)) {
  165. continue;
  166. }
  167. $user = get_user_by('id', $key_terms);
  168. $sql .= " AND ";
  169. $sql .= " p.ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'user_email' AND meta_value = '{$user->user_email}')";
  170. continue;
  171. }
  172. // Sorting
  173. if ($primary_key == 'sorting') {
  174. $sort_by = " ORDER BY {$key_terms['order_by']} {$key_terms['sort_dir']} ";
  175. }
  176. /* Date */
  177. if ($primary_key == 'm') {
  178. if (empty($key_terms) || (int) $key_terms == 0) {
  179. continue;
  180. }
  181. $key_terms = '' . preg_replace('|[^0-9]|', '', $key_terms);
  182. $sql .= " AND YEAR(post_date)=" . substr($key_terms, 0, 4);
  183. if (strlen($key_terms) > 5) {
  184. $sql .= " AND MONTH(post_date)=" . substr($key_terms, 4, 2);
  185. }
  186. if (strlen($key_terms) > 7) {
  187. $sql .= " AND DAYOFMONTH(post_date)=" . substr($key_terms, 6, 2);
  188. }
  189. if (strlen($key_terms) > 9) {
  190. $sql .= " AND HOUR(post_date)=" . substr($key_terms, 8, 2);
  191. }
  192. if (strlen($key_terms) > 11) {
  193. $sql .= " AND MINUTE(post_date)=" . substr($key_terms, 10, 2);
  194. }
  195. if (strlen($key_terms) > 13) {
  196. $sql .= " AND SECOND(post_date)=" . substr($key_terms, 12, 2);
  197. }
  198. }
  199. }
  200. }
  201. $sql = $sql . $sort_by;
  202. //echo $sql;
  203. $results = $wpdb->get_results($sql);
  204. return $results;
  205. }
  206. /**
  207. *
  208. * @global object $wpdb
  209. * @param array $search_vars
  210. * @param string $interval
  211. * @return mixed
  212. * @author odokienko@UD
  213. */
  214. static function get_sales_by ($search_vars = false, $interval="weekly") {
  215. global $wpdb;
  216. switch ($interval){
  217. case "weekly":
  218. $interval_function = 'WEEK';
  219. break;
  220. case 'daily':
  221. $interval_function = 'DAYOFYEAR';
  222. break;
  223. case 'monthly':
  224. default:
  225. $interval_function = 'MONTH';
  226. break;
  227. }
  228. if (!empty($search_vars)) {
  229. if (is_string($search_vars)) {
  230. $args = array();
  231. parse_str($search_vars, $args);
  232. $search_vars = $args;
  233. }
  234. foreach ($search_vars as $primary_key => $key_terms) {
  235. //** Handle search_string differently, it applies to all meta values */
  236. if ($primary_key == 's' && !empty($key_terms)) {
  237. /* First, go through the posts table */
  238. $tofind = strtolower($key_terms);
  239. $sql .= " AND (";
  240. $sql .= " LOWER(`{$wpdb->posts}`.post_title) LIKE '%$tofind%'";
  241. /* Now go through the post meta table */
  242. $sql .= " OR
  243. `{$wpdb->posts}`.ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE LOWER(meta_value) LIKE '%$tofind%')";
  244. $sql .= ")";
  245. continue;
  246. }
  247. // Type
  248. if ($primary_key == 'type') {
  249. if (empty($key_terms)) {
  250. continue;
  251. }
  252. if (is_array($key_terms)) {
  253. $key_terms = implode("','", $key_terms);
  254. }
  255. $sql .= " AND ";
  256. $sql .= " `{$wpdb->posts}`.ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'type' AND meta_value IN ('{$key_terms}'))";
  257. continue;
  258. }
  259. // Status
  260. if ($primary_key == 'status') {
  261. if (empty($key_terms)) {
  262. continue;
  263. }
  264. if (is_array($key_terms)) {
  265. $sql .= " AND (";
  266. $i = 0;
  267. foreach ($key_terms as $term) {
  268. if (empty($term)) {
  269. continue;
  270. }
  271. if ($i > 0) {
  272. $sql .= " OR ";
  273. }
  274. $sql .= " `{$wpdb->posts}`.post_status = '{$term}' ";
  275. $i++;
  276. }
  277. $sql .= ")";
  278. }
  279. }
  280. /*
  281. if ( !$use_status_filter ) {
  282. $sql .= " AND ( post_status = 'active' ) ";
  283. }
  284. */
  285. // Recipient
  286. if ($primary_key == 'recipient') {
  287. if (empty($key_terms)) {
  288. continue;
  289. }
  290. $user = get_user_by('id', $key_terms);
  291. $sql .= " AND ";
  292. $sql .= " `{$wpdb->posts}`.ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'user_email' AND meta_value = '{$user->user_email}')";
  293. continue;
  294. }
  295. /* Date */
  296. if ($primary_key == 'm') {
  297. if (empty($key_terms) || (int) $key_terms == 0) {
  298. continue;
  299. }
  300. $key_terms = '' . preg_replace('|[^0-9]|', '', $key_terms);
  301. $sql .= " AND YEAR(`{$wpdb->posts}`.post_date)=" . substr($key_terms, 0, 4);
  302. if (strlen($key_terms) > 5) {
  303. $sql .= " AND MONTH(`{$wpdb->posts}`.post_date)=" . substr($key_terms, 4, 2);
  304. }
  305. if (strlen($key_terms) > 7) {
  306. $sql .= " AND DAYOFMONTH(`{$wpdb->posts}`.post_date)=" . substr($key_terms, 6, 2);
  307. }
  308. if (strlen($key_terms) > 9) {
  309. $sql .= " AND HOUR(`{$wpdb->posts}`.post_date)=" . substr($key_terms, 8, 2);
  310. }
  311. if (strlen($key_terms) > 11) {
  312. $sql .= " AND MINUTE(`{$wpdb->posts}`.post_date)=" . substr($key_terms, 10, 2);
  313. }
  314. if (strlen($key_terms) > 13) {
  315. $sql .= " AND SECOND(`{$wpdb->posts}`.post_date)=" . substr($key_terms, 12, 2);
  316. }
  317. }
  318. }
  319. }
  320. $date_table = "
  321. select @maxDate - interval (a.a+(10*b.a)+(100*c.a)) day aDate from
  322. (select 0 as a union all select 1 union all select 2 union all select 3
  323. union all select 4 union all select 5 union all select 6 union all
  324. select 7 union all select 8 union all select 9) a, /*10 day range*/
  325. (select 0 as a union all select 1 union all select 2 union all select 3
  326. union all select 4 union all select 5 union all select 6 union all
  327. select 7 union all select 8 union all select 9) b, /*100 day range*/
  328. (select 0 as a union all select 1 union all select 2 union all select 3
  329. union all select 4 union all select 5 union all select 6 union all
  330. select 7 union all select 8 union all select 9) c, /*1000 day range*/
  331. (select
  332. @minDate := date_format(FROM_UNIXTIME((select min(time) from `{$wpdb->prefix}wpi_object_log` mn join {$wpdb->posts} on (mn.object_id=`{$wpdb->posts}`.id and `{$wpdb->posts}`.post_type = 'wpi_object' {$sql}))),'%Y-%m-%d'),
  333. @maxDate := date_format(FROM_UNIXTIME((select max(time) from `{$wpdb->prefix}wpi_object_log` mx join {$wpdb->posts} on (mx.object_id=`{$wpdb->posts}`.id and `{$wpdb->posts}`.post_type = 'wpi_object' {$sql}))),'%Y-%m-%d')
  334. ) e
  335. ";
  336. $sql = "
  337. SELECT distinct
  338. YEAR(datetable.aDate) as int_year,
  339. {$interval_function}(datetable.aDate) int_erval,
  340. sum(COALESCE(if (payment.action='refund',payment.value*-1,payment.value),0)) as sum_interval
  341. FROM ($date_table) datetable
  342. left join `{$wpdb->prefix}wpi_object_log` as payment on (
  343. datetable.aDate=date_format(FROM_UNIXTIME(payment.time),'%Y-%m-%d')
  344. and (
  345. payment.object_id in (
  346. select `{$wpdb->posts}`.id from `{$wpdb->posts}` where `{$wpdb->posts}`.post_type = 'wpi_object' {$sql}
  347. )
  348. )
  349. AND (payment.action = 'add_payment' or payment.action = 'refund')
  350. AND payment.attribute = 'balance'
  351. )
  352. WHERE datetable.aDate between @minDate and @maxDate
  353. GROUP BY 1,2
  354. ";
  355. $results = $wpdb->get_results($sql);
  356. return $results;
  357. }
  358. /**
  359. * Get Search filter fields
  360. *
  361. * @global array $wpi_settings
  362. * @global object $wpdb
  363. * @return type
  364. */
  365. function get_search_filters() {
  366. global $wpi_settings, $wpdb;
  367. $filters = array();
  368. $default = array(array(
  369. 'key' => 'all',
  370. 'label' => __('All'),
  371. 'amount' => 0
  372. ));
  373. if (isset($wpi_settings['types'])) {
  374. $f = $default;
  375. $i = 1;
  376. $all = 0;
  377. foreach ($wpi_settings['types'] as $key => $value) {
  378. $amount = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = 'type' AND meta_value = '{$key}'");
  379. $all = $all + $amount;
  380. if ($amount > 0) {
  381. $f[$i]['key'] = $key;
  382. $f[$i]['label'] = $value['label'];
  383. $f[$i]['amount'] = $amount;
  384. $i++;
  385. }
  386. }
  387. if ($all > 0) {
  388. $f[0]['amount'] = $all;
  389. $filters['type'] = $f;
  390. }
  391. // If there is only 1 type - hide Types option
  392. if ($i == 2) {
  393. unset($filters['type']);
  394. }
  395. }
  396. if (!empty($wpi_settings['statuses'])) {
  397. $f = array();
  398. $amount = 0;
  399. $i = 1;
  400. $all = 0;
  401. foreach ($wpi_settings['statuses'] as $status) {
  402. $amount = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_status = '{$status}' AND post_type = 'wpi_object'");
  403. $all = $all + $amount;
  404. if ($amount > 0) {
  405. $f[$i]['key'] = $status;
  406. $f[$i]['label'] = strtoupper(substr($status, 0, 1)) . substr($status, 1);
  407. $f[$i]['amount'] = $amount;
  408. $i++;
  409. }
  410. }
  411. if ($all > 0) {
  412. $filters['status'] = $f;
  413. }
  414. }
  415. return $filters;
  416. }
  417. /**
  418. * Convert a string to a url-like slug
  419. *
  420. * @param type $slug
  421. * @return type
  422. */
  423. function slug_to_label($slug = false) {
  424. if (!$slug)
  425. return;
  426. $slug = str_replace("_", " ", $slug);
  427. $slug = ucwords($slug);
  428. return $slug;
  429. }
  430. /**
  431. * Convert a string into a number. Allow invoice ID to be passed for currency symbol localization
  432. *
  433. * @global array $wpi_settings
  434. * @param type $amount
  435. * @param type $invoice_id
  436. * @return type
  437. */
  438. static function currency_format($amount, $invoice_id = false) {
  439. global $wpi_settings;
  440. if ($invoice_id) {
  441. $invoice = get_invoice($invoice_id);
  442. }
  443. $currency_symbol = !empty($wpi_settings['currency']['symbol'][$invoice['default_currency_code']]) ? $wpi_settings['currency']['symbol'][$invoice['default_currency_code']] : '$';
  444. $amount = (float) $amount;
  445. $thousands_separator_symbol = !isset( $wpi_settings['thousands_separator_symbol'] )?',':($wpi_settings['thousands_separator_symbol'] == '0'?'':$wpi_settings['thousands_separator_symbol']);
  446. return $currency_symbol . number_format($amount, 2, '.', $thousands_separator_symbol);
  447. }
  448. /**
  449. * Run numbers for reporting purposes.
  450. *
  451. * @global object $wpdb
  452. * @global type $wpi_reports
  453. * @return type
  454. */
  455. static function run_reports() {
  456. global $wpdb, $wpi_reports;
  457. //* Get all invoices */
  458. $invoice_ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'wpi_object' ");
  459. $totals = array();
  460. $objects = array();
  461. $r = array(
  462. 'line_item_counts' => array()
  463. );
  464. foreach ($invoice_ids as $post_id) {
  465. $wpi_invoice_object = new WPI_Invoice();
  466. $wpi_invoice_object->load_invoice("id=$post_id");
  467. $objects[$post_id] = $wpi_invoice_object->data;
  468. }
  469. foreach ($objects as $object) {
  470. //** Total paid invoices per client */
  471. if ($object['post_status'] == 'paid') {
  472. $r['collected_client_value'][$object['user_email']] = !empty($r['client_value'][$object['user_email']]) ? $r['client_value'][$object['user_email']] : 0 + $object['subtotal'] + $object['total_tax'] - $object['total_discount'];
  473. $r['total_paid'][] = $objects[$object['ID']]['subtotal'] + $objects[$object['ID']]['total_tax'] - $objects[$object['ID']]['total_discount'];;
  474. foreach ($object['itemized_list'] as $list_item) {
  475. $r['collected_line_items'][$list_item['name']] = !empty($r['collected_line_items'][$list_item['name']]) ? $r['collected_line_items'][$list_item['name']] : 0 + $list_item['line_total_after_tax'];
  476. if (!empty($r['line_item_counts'][$list_item['name']])) {
  477. $r['line_item_counts'][$list_item['name']]++;
  478. } else {
  479. $r['line_item_counts'][$list_item['name']] = 1;
  480. }
  481. }
  482. }
  483. if ($object['post_status'] == 'active') {
  484. $r['uncollected_client_value'][$object['user_email']] = !empty($r['uncollected_client_value'][$object['user_email']]) ? $r['uncollected_client_value'][$object['user_email']] : 0 + $object['subtotal'] + $object['total_tax'] - $object['total_discount'];
  485. $r['total_unpaid'][] = $objects[$object['ID']]['subtotal'] + $objects[$object['ID']]['total_tax'] - $objects[$object['ID']]['total_discount'];
  486. }
  487. }
  488. if (isset($r['collected_line_items']) && is_array($r['collected_line_items'])) {
  489. arsort($r['collected_line_items']);
  490. }
  491. if (isset($r['uncollected_client_value']) && is_array($r['uncollected_client_value'])) {
  492. arsort($r['uncollected_client_value']);
  493. }
  494. if (isset($r['collected_client_value']) && is_array($r['collected_client_value'])) {
  495. arsort($r['collected_client_value']);
  496. }
  497. if (isset($r['total_paid']) && is_array($r['total_paid'])) {
  498. $r['total_paid'] = array_sum($r['total_paid']);
  499. }
  500. if (isset($r['total_unpaid']) && is_array($r['total_unpaid'])) {
  501. $r['total_unpaid'] = array_sum($r['total_unpaid']);
  502. }
  503. $wpi_reports = $r;
  504. return $r;
  505. }
  506. /**
  507. * Check if theme-specific stylesheet exists.
  508. *
  509. * get_option('template') seems better choice than get_option('stylesheet'), which returns the current theme's slug
  510. * which is a problem when a child theme is used. We want the parent theme's slug.
  511. *
  512. * @since 3.0
  513. *
  514. */
  515. static function has_theme_specific_stylesheet() {
  516. $theme_slug = get_option('template');
  517. if (file_exists(WPI_Path . "/core/template/theme-specific/{$theme_slug}.css")) {
  518. return true;
  519. }
  520. return false;
  521. }
  522. /**
  523. * Check if Payment method allowed
  524. * @param String $param
  525. * @return bool
  526. */
  527. function is_true($param) {
  528. if (empty($param))
  529. return false;
  530. return ( $param == 'true' || $param == 'on' || $param == 'yes' ) ? true : false;
  531. }
  532. /**
  533. * Fixes billing structure
  534. * @param array $wpi_settings_billings
  535. * @param array &$invoice_billings
  536. */
  537. function merge_billings($wpi_settings_billings, $invoice_billings) {
  538. if (!isset($invoice_billings) || !is_array($invoice_billings)) {
  539. $invoice_billings = array();
  540. }
  541. if (is_array($wpi_settings_billings)) {
  542. foreach ($wpi_settings_billings as $key => $value) {
  543. // TODO: Refactor on|yes|true off|no|false
  544. // WPI_Functions::is_true() used temporary
  545. if (!WPI_Functions::is_true($value['allow'])) {
  546. unset($invoice_billings[$key]);
  547. } else {
  548. if (!empty($invoice_billings[$key])) {
  549. if (!isset($invoice_billings[$key]['name'])) {
  550. $invoice_billings[$key]['name'] = $value['name'];
  551. }
  552. if (!isset($invoice_billings[$key]['allow'])) {
  553. $invoice_billings[$key]['allow'] = $value['allow'];
  554. }
  555. if (!isset($invoice_billings[$key]['default_option'])) {
  556. $invoice_billings[$key]['default_option'] = $value['default_option'];
  557. }
  558. if (!empty($value['settings'])) {
  559. foreach ($value['settings'] as $setting_key => $setting_value) {
  560. foreach ($setting_value as $setting_key_field => $setting_value_field) {
  561. if (!isset($invoice_billings[$key]['settings'][$setting_key][$setting_key_field])) {
  562. $invoice_billings[$key]['settings'][$setting_key][$setting_key_field] = $setting_value_field;
  563. }
  564. }
  565. }
  566. }
  567. } else {
  568. $invoice_billings[$key] = $value;
  569. }
  570. }
  571. }
  572. }
  573. }
  574. function set_default_payment_method($wpi_settings_billings, $invoice_data) {
  575. $settings_dpm = '';
  576. if (!empty($wpi_settings_billings) && is_array($wpi_settings_billings)) {
  577. foreach ($wpi_settings_billings as $method => $value) {
  578. if ($value['default_option'] == 'true') {
  579. $settings_dpm = $method;
  580. }
  581. }
  582. }
  583. $invoice_data['default_payment_method'] = $settings_dpm;
  584. }
  585. /**
  586. * Returns an array of users
  587. * Used for user-email auto-completion.
  588. * @uses $wpdb
  589. * @since 3.0
  590. * @return array. Users List
  591. *
  592. */
  593. function build_user_array() {
  594. global $wpdb;
  595. return $wpdb->get_results("SELECT display_name,user_email,ID FROM {$wpdb->prefix}users", ARRAY_A);
  596. }
  597. /**
  598. * Handle user data updating
  599. *
  600. * Typically called when saving an invoice.
  601. * @since 3.0
  602. */
  603. function update_user($userdata) {
  604. $user_id = email_exists($userdata['user_email']);
  605. if ($user_id) {
  606. $userdata['ID'] = $user_id;
  607. }
  608. if (empty($userdata['ID']) && empty($userdata['user_email'])) {
  609. return false;
  610. }
  611. if ($user_id) {
  612. $user_id = wp_update_user($userdata);
  613. } else {
  614. if (empty($userdata['user_login'])) {
  615. if (!empty($userdata['first_name']) && !empty($userdata['last_name'])) {
  616. $userdata['display_name'] = $userdata['first_name'] . ' ' . $userdata['last_name'];
  617. } else {
  618. }
  619. }
  620. $userdata['user_login'] = $userdata['user_email'];
  621. if(empty($userdata['user_pass'])) {
  622. $userdata['user_pass'] = wp_generate_password( 12, false);
  623. }
  624. $user_id = wp_insert_user($userdata);
  625. }
  626. // Prevent entering of wrong phone number to avoid errors on front-end
  627. if (!preg_match('/\A[\d.+?]{0,3}-[\d.+?]{0,3}-[\d.+?]{0,4}\Z/si', $userdata['phonenumber'])) {
  628. if (preg_match('/\A[\d.+?]{0,10}\Z/si', $userdata['phonenumber'])) {
  629. $phonenumber = $userdata['phonenumber'];
  630. $userdata['phonenumber'] = substr($phonenumber, 0, 3) . '-' . substr($phonenumber, 3, 3) . '-' . substr($phonenumber, 6, 4);
  631. } else {
  632. $userdata['phonenumber'] = '';
  633. }
  634. }
  635. if (!is_object($user_id) && $user_id > 0) {
  636. /* Update user's meta data */
  637. $non_meta_data = array(
  638. 'ID',
  639. 'first_name',
  640. 'last_name',
  641. 'nickname',
  642. 'description',
  643. 'user_pass',
  644. 'user_email',
  645. 'user_url',
  646. 'user_nicename',
  647. 'display_name',
  648. 'user_registered',
  649. 'role'
  650. );
  651. foreach ($userdata as $key => $value) {
  652. if (!in_array($key, $non_meta_data)) {
  653. update_user_meta($user_id, $key, $value);
  654. }
  655. }
  656. return $user_id;
  657. }
  658. return $user_id;
  659. }
  660. /**
  661. * Add itemized charge like itemized list item
  662. *
  663. * @param int $invoice_id
  664. * @param string $name
  665. * @param float $amount
  666. * @param float $tax
  667. * @return array
  668. */
  669. function add_itemized_charge($invoice_id, $name, $amount, $tax) {
  670. $post_id = wpi_invoice_id_to_post_id($invoice_id);
  671. $charge_items = get_post_meta($post_id, 'itemized_charges', true);
  672. $new_item = array(
  673. 'name' => $name,
  674. 'amount' => $amount,
  675. 'tax' => $tax,
  676. 'before_tax' => $amount,
  677. 'after_tax' => $amount + ($amount / 100 * $tax)
  678. );
  679. if (!empty($charge_items)) {
  680. $charge_items[] = $new_item;
  681. } else {
  682. $charge_items[0] = $new_item;
  683. }
  684. update_post_meta($post_id, 'itemized_charges', $charge_items);
  685. return end($charge_items);
  686. }
  687. /**
  688. * Loads invoice variables into post if it is a wpi_object
  689. *
  690. * @hooked_into setup_postdata()
  691. * @uses $wpdb
  692. * @since 3.0
  693. *
  694. */
  695. function the_post(&$post) {
  696. global $post;
  697. if ($post->post_type == 'wpi_object') {
  698. $this_invoice = new WPI_Invoice();
  699. $invoice_id = $post->ID;
  700. $this_invoice->load_invoice("id=$invoice_id");
  701. $t_post = (array) $post;
  702. $t_data = (array) $this_invoice->data;
  703. $t_post = WPI_Functions::array_merge_recursive_distinct($t_post, $t_data);
  704. $post = (object) $t_post;
  705. }
  706. }
  707. function objectToArray($object) {
  708. if (!is_object($object) && !is_array($object)) {
  709. return $object;
  710. }
  711. if (is_object($object)) {
  712. $object = get_object_vars($object);
  713. }
  714. return array_map(array('WPI_functions', 'objectToArray'), $object);
  715. }
  716. /**
  717. Generates a slug
  718. */
  719. function generateSlug($title) {
  720. $slug = preg_replace("/[^a-zA-Z0-9 ]/", "", $title);
  721. $slug = str_replace(" ", "_", $slug);
  722. return $slug;
  723. }
  724. /**
  725. * Figure out current page for front-end AJAX function
  726. */
  727. function current_page() {
  728. $pageURL = 'http';
  729. if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") {
  730. $pageURL .= "s";
  731. }
  732. $pageURL .= "://";
  733. if ($_SERVER["SERVER_PORT"] != "80") {
  734. $pageURL .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
  735. } else {
  736. $pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
  737. }
  738. return $pageURL;
  739. }
  740. /**
  741. Get users paid and pending invoices
  742. */
  743. function get_user_invoices($args) {
  744. global $wpdb;
  745. $defaults = array('user_id' => false, 'status' => false);
  746. extract(wp_parse_args($args, $defaults), EXTR_SKIP);
  747. // User email and id are the same thing
  748. if (!$user_id && isset($user_email))
  749. $user_id = $user_email;
  750. // If nothing is set, nothing we can do
  751. if (!isset($user_id))
  752. return;
  753. $users_invoices = $wpdb->get_col("
  754. SELECT post_id
  755. FROM {$wpdb->postmeta} postmeta
  756. JOIN {$wpdb->posts} posts ON posts.ID = postmeta.post_id
  757. WHERE postmeta.meta_key = 'user_email'
  758. AND postmeta.meta_value = '" . $user_id . "'
  759. AND posts.post_type = 'wpi_object'
  760. AND posts.post_status = '$status'
  761. ");
  762. // Nothing found
  763. if (!is_array($users_invoices))
  764. return false;
  765. $return = array();
  766. foreach ($users_invoices as $post_id) {
  767. $invoice_id = wpi_post_id_to_invoice_id($post_id);
  768. $this_invoice = new WPI_Invoice();
  769. $this_invoice->load_invoice("id={$invoice_id}");
  770. if (!empty($status) && $status != $this_invoice->data['post_status'])
  771. continue;
  772. // Do not include quotes
  773. if ($this_invoice->data['type'] != 'invoice') {
  774. continue;
  775. }
  776. $return[] = $this_invoice;
  777. }
  778. return $return;
  779. }
  780. /**
  781. Add message to notice queve
  782. */
  783. function add_message($message, $type = 'good', $class = '') {
  784. global $wpi_messages;
  785. if (!is_array($wpi_messages))
  786. $wpi_messages = array();
  787. array_push($wpi_messages, array('message' => $message, 'type' => $type, 'class' => $class));
  788. }
  789. /**
  790. Display messages in queve
  791. */
  792. function print_messages() {
  793. global $wpi_messages;
  794. if (count($wpi_messages) < 1)
  795. return;
  796. $update_messages = array();
  797. $warning_messages = array();
  798. echo "<div id='wpi_message_stack'>";
  799. foreach ($wpi_messages as $message) {
  800. if ($message['type'] == 'good') {
  801. array_push($update_messages, array('message' => $message['message'], 'class' => $message['class']));
  802. }
  803. if ($message['type'] == 'bad') {
  804. array_push($warning_messages, array('message' => $message['message'], 'class' => $message['class']));
  805. }
  806. }
  807. if (count($update_messages) > 0) {
  808. echo "<div class='wpi_message wpi_yellow_notification'>";
  809. foreach ($update_messages as $u_message)
  810. echo "<div class='wpi_message_holder {$message['class']}' >{$u_message['message']}</div>";
  811. echo "</div>";
  812. }
  813. if (count($warning_messages) > 0) {
  814. echo "<div class='wpi_message wpi_red_notification'>";
  815. foreach ($warning_messages as $w_message)
  816. echo "<div class='wpi_message_holder {$w_message['class']}' >{$w_message['message']}</div>";
  817. echo "</div>";
  818. }
  819. echo "</div>";
  820. }
  821. /**
  822. * Checks if particular template exists in the template folder
  823. *
  824. * @global array $wpi_settings
  825. * @param type $template
  826. * @return type
  827. */
  828. function wpi_use_custom_template($template) {
  829. global $wpi_settings;
  830. /* if custom templates are turned off, don't bother checking */
  831. if (!isset($wpi_settings['use_custom_templates']) || $wpi_settings['use_custom_templates'] != 'yes') {
  832. return false;
  833. }
  834. if (file_exists($wpi_settings['frontend_template_path'] . "$template")) {
  835. return true;
  836. }
  837. return false;
  838. }
  839. /**
  840. * Determine WPI front-end template path
  841. *
  842. * @global array $wpi_settings
  843. * @return type
  844. */
  845. function template_path() {
  846. global $wpi_settings;
  847. $use_custom_templates = false;
  848. if (file_exists(STYLESHEETPATH . "/wpi/")) {
  849. return STYLESHEETPATH . "/wpi/";
  850. }
  851. }
  852. /**
  853. * Display invoice status formatted for back-end
  854. *
  855. * @param type $invoice_id
  856. */
  857. function get_status($invoice_id) {
  858. $this_invoice = new WPI_Invoice();
  859. $this_invoice->load_invoice("id=$invoice_id");
  860. if (is_array($this_invoice->data['log'])) {
  861. foreach (array_reverse($this_invoice->data['log']) as $event) {
  862. if (empty($event['text'])) {
  863. continue;
  864. }
  865. ?>
  866. <tr class="wpi_event_<?php echo $event['action']; ?> <?php if ($event['action'] == 'add_charge' || $event['action'] == 'do_adjustment')
  867. echo "wpi_not_for_recurring"; ?>">
  868. <th><?php echo date(get_option('time_format') . ' ' . get_option('date_format'), $event['time']); ?> </th>
  869. <td><?php echo $event['text']; ?></td>
  870. </tr>
  871. <?php
  872. }
  873. } else {
  874. ?>
  875. <tr class="wpi_event_error">
  876. <th colspan='2'>No log entries.</th>
  877. </tr>
  878. <?php
  879. }
  880. }
  881. /**
  882. * Render itemized charges list
  883. *
  884. * @param int $post_id
  885. */
  886. function get_charges($post_id) {
  887. $charges_list = get_post_meta($post_id, 'itemized_charges', true);
  888. $result = '';
  889. ob_start();
  890. if (!empty($charges_list)) {
  891. foreach ($charges_list as $key => $value) {
  892. ?>
  893. <li class="wp_invoice_itemized_charge_row clearfix" id="wp_invoice_itemized_charge_row_<?php echo $key; ?>">
  894. <span class="id hidden"><?php echo $key; ?></span>
  895. <div class="flexible_width_holder">
  896. <div class="flexible_width_holder_content">
  897. <span class="row_delete">&nbsp;</span>
  898. <input class="item_name input_field" name="wpi_invoice[itemized_charges][<?php echo $key; ?>][name]" value="<?php echo stripslashes($value['name']); ?>" />
  899. </div>
  900. </div>
  901. <span class="fixed_width_holder">
  902. <span class="row_amount">
  903. <input autocomplete="off" value="<?php echo stripslashes($value['amount']); ?>" name="wpi_invoice[itemized_charges][<?php echo $key; ?>][amount]" id="amount_item_<?php echo $key; ?>" class="item_amount input_field">
  904. </span>
  905. <span class="row_charge_tax">
  906. <input autocomplete="off" value="<?php echo stripslashes($value['tax']); ?>" name="wpi_invoice[itemized_charges][<?php echo $key; ?>][tax]" id="charge_tax_item_<?php echo $key; ?>" class="item_charge_tax input_field">
  907. </span>
  908. <span class="row_total" id="total_item_<?php echo $key; ?>" ><?php echo $value['after_tax']; ?></span>
  909. </span>
  910. </li>
  911. <?php
  912. }
  913. $result .= ob_get_contents();
  914. ob_end_clean();
  915. }
  916. echo $result;
  917. }
  918. /**
  919. * Returns highest invoice ID
  920. *
  921. * @global object $wpdb
  922. * @return longint
  923. * @author korotkov@ud
  924. */
  925. function get_highest_custom_id() {
  926. global $wpdb;
  927. $invoices = get_posts(
  928. array(
  929. 'post_type' => 'wpi_object',
  930. 'numberposts' => 0,
  931. 'post_status' => 'any'
  932. )
  933. );
  934. if (!count($invoices)) {
  935. return false;
  936. }
  937. $id_array = array();
  938. foreach ($invoices as $invoice) {
  939. $id_array[] = get_post_meta($invoice->ID, 'invoice_id', true);
  940. /** Get custom IDs too */
  941. $custom_id = get_post_meta($invoice->ID, 'custom_id', true);
  942. if ( $custom_id ) {
  943. $id_array[] = $custom_id;
  944. }
  945. }
  946. return @max($id_array);
  947. }
  948. /**
  949. * Removes empty values from array
  950. *
  951. * @param array $array
  952. * @return array
  953. * @author korotkov@ud
  954. */
  955. function remove_blank_values($array) {
  956. if (!is_array($array))
  957. return false;
  958. foreach ($array as $key => $value) {
  959. if (!empty($value))
  960. $return[$key] = $value;
  961. }
  962. return $return;
  963. }
  964. /**
  965. * Run when a plugin is being activated
  966. * Handles the task of migrating from old version of WPI to new
  967. */
  968. function Activate() {
  969. global $wpdb, $wpi_settings;
  970. /* check if scheduler already sheduled */
  971. if(!wp_next_scheduled('wpi_hourly_event')){
  972. /* Setup WPI schedule to handle recurring invoices */
  973. wp_schedule_event(time(), 'hourly', 'wpi_hourly_event');
  974. }
  975. if(!wp_next_scheduled('wpi_update')){
  976. /* Scheduling daily update event */
  977. wp_schedule_event(time(), 'daily', 'wpi_update');
  978. }
  979. WPI_Functions::log(__("Schedule created with plugin activation.", WPI));
  980. /* Try to create new schema tables */
  981. WPI_Functions::create_new_schema_tables();
  982. /* Get previous activated version */
  983. $current_version = get_option('wp_invoice_version');
  984. /* If no version found at all, we do new install */
  985. if ($current_version == WP_INVOICE_VERSION_NUM) {
  986. WPI_Functions::log(__("Plugin activated. No older versions found, installing version ", WPI) . WP_INVOICE_VERSION_NUM . ".");
  987. } else if ((int) $current_version < 3) {
  988. /* Determine if legacy data exist */
  989. WPI_Legacy::init();
  990. WPI_Functions::log(__("Plugin activated.", WPI));
  991. }
  992. /* Update version */
  993. update_option('wp_invoice_version', WP_INVOICE_VERSION_NUM);
  994. WPI_Functions::check_for_premium_features();
  995. update_option('wpi_activation_time', time());
  996. }
  997. function Deactivate() {
  998. wp_clear_scheduled_hook('wpi_hourly_event');
  999. wp_clear_scheduled_hook('wpi_update');
  1000. WPI_Functions::log(__("Plugin deactivated.", WPI));
  1001. }
  1002. /**
  1003. * Called by profile_update action/hook
  1004. * Used to save profile settings for WP Users.
  1005. * @param int $user_id User ID
  1006. * @param object $old_user_data old value.
  1007. * @return bool True on successful update, false on failure.
  1008. */
  1009. function save_update_profile($user_id, $old_user_data) {
  1010. global $wpi_settings;
  1011. if (empty($user_id) || $user_id == 0) {
  1012. return false;
  1013. }
  1014. $custom_user_information = apply_filters('wpi_user_information', $wpi_settings['user_meta']['custom']);
  1015. $user_information = array_merge($wpi_settings['user_meta']['required'], $custom_user_information);
  1016. // On Adding/Editing Invoice user data exists in ['wpi_invoice']['user_data']
  1017. $data = !empty($_POST['wpi_invoice']['user_data']) ? $_POST['wpi_invoice']['user_data'] : $_POST;
  1018. if (!is_array($data)) {
  1019. return false;
  1020. }
  1021. foreach ($user_information as $field_id => $field_name) {
  1022. if (isset($data[$field_id])) {
  1023. update_user_meta($user_id, $field_id, $data[$field_id]);
  1024. }
  1025. }
  1026. }
  1027. /**
  1028. * Called by profile_update action/hook
  1029. * If user_email changed then it updates user invoices with new user_email
  1030. * @param type $user_id
  1031. * @param type $old_user_data
  1032. * @return boolean
  1033. * @author odokienko@UD
  1034. */
  1035. function protect_user_invoices($user_id, $old_user_data) {
  1036. global $wpdb;
  1037. if (empty($user_id) || $user_id == 0) {
  1038. return false;
  1039. }
  1040. $userdata = get_userdata($user_id);
  1041. if ($userdata->user_email != $old_user_data->user_email){
  1042. $wpdb->query("
  1043. UPDATE {$wpdb->postmeta} postmeta, {$wpdb->posts} posts
  1044. SET postmeta.meta_value = '" . $userdata->user_email . "'
  1045. WHERE posts.ID = postmeta.post_id
  1046. AND postmeta.meta_key = 'user_email'
  1047. AND postmeta.meta_value = '" . $old_user_data->user_email . "'
  1048. AND posts.post_type = 'wpi_object'
  1049. ");
  1050. WPI_Functions::console_log('WPI_Invoice::protect_user_invoices() '.__('user_email changed.', WPI));
  1051. }
  1052. }
  1053. /*
  1054. * Set Custom Screen Options
  1055. */
  1056. function wpi_screen_options() {
  1057. global $current_screen;
  1058. $output = '';
  1059. switch ($current_screen->id) {
  1060. case 'toplevel_page_wpi_main':
  1061. break;
  1062. case 'invoice_page_wpi_page_manage_invoice':
  1063. $output .= '
  1064. <div id="wpi_screen_meta" class="metabox-prefs">
  1065. <label for="wpi_itemized-list-tax">
  1066. <input type="checkbox" ' . (get_user_option('wpi_ui_display_itemized_tax') == 'true' ? 'checked="checked"' : '') . ' value="" id="wpi_itemized-list-tax" name="wpi_ui_display_itemized_tax" class="non-metabox-option">
  1067. Row Tax</label>
  1068. </div>';
  1069. break;
  1070. }
  1071. return $output;
  1072. }
  1073. /**
  1074. * Called by template_redirect to validate whether an invoice should be displayed
  1075. */
  1076. function validate_page_hash($md5_invoice_id) {
  1077. global $wpdb, $wpi_settings, $post, $invoice_id;
  1078. $invoice_id = $wpdb->get_var("SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key='invoice_id' AND MD5(meta_value) = '{$md5_invoice_id}'");
  1079. if (!$invoice_id) {
  1080. return false;
  1081. }
  1082. if ($wpi_settings['web_invoice_page'] != $post->ID) {
  1083. return false;
  1084. }
  1085. // Verify HTTPS. If its enforced, but not active, we reload page, and do the process again
  1086. //print_r( $_SERVER );
  1087. if (!function_exists('wp_https_redirect')) {
  1088. session_start();
  1089. if (!isset($_SESSION['https'])) {
  1090. if ( $wpi_settings['force_https'] == 'true' && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")) {
  1091. $_SESSION['https'] = 1;
  1092. header("Location: https://" . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
  1093. @session_destroy();
  1094. exit;
  1095. } else {
  1096. if (session_id() != '')
  1097. @session_destroy();
  1098. }
  1099. }
  1100. //Added to see how the invoice looks once it is created...
  1101. if ($wpi_settings['force_https'] == 'false') {
  1102. if (session_id() != '')
  1103. @session_destroy();
  1104. // Nothing should be done here, this function is simply for validating.
  1105. // If we got this far, means invoice_id and page are validated, and HTTPS is NOT enforced
  1106. return true;
  1107. //print $_SERVER['SERVER_NAME']; print $_SERVER['REQUEST_URI']; die;
  1108. //header("Location: http://" . $_SERVER['SERVER_NAME']'/wp-login.php');
  1109. //header("Location: http://localhost/wordpress/wp-login.php");
  1110. //exit;
  1111. }
  1112. }
  1113. // 5. Validation passed
  1114. return true;
  1115. }
  1116. /**
  1117. Displayed how many days it has been since a certain date.
  1118. */
  1119. function days_since($date1, $return_number = false) {
  1120. if (empty($date1))
  1121. return "";
  1122. if (is_array($date1))
  1123. $date1 = $date1[year] . "-" . $date1[month] . "-" . $date1[day];
  1124. $date2 = date("Y-m-d");
  1125. $date1 = date("Y-m-d", strtotime($date1));
  1126. // determine if future or past
  1127. if (strtotime($date2) < strtotime($date1))
  1128. $future = true;
  1129. $difference = abs(strtotime($date2) - strtotime($date1));
  1130. $days = round(((($difference / 60) / 60) / 24), 0);
  1131. if ($return_number)
  1132. return $days;
  1133. if ($days == 0) {
  1134. return __('Today', WPI);
  1135. } elseif ($days == 1) {
  1136. return($future ? __(" Tomorrow ", WPI) : __(" Yesterday ", WPI));
  1137. } elseif ($days > 1 && $days <= 6) {
  1138. return ($future ? __sprintf(__(" in %s days ", WPI), $days) : " ".$days." ".__("days ago", WPI));
  1139. } elseif ($days > 6) {
  1140. return date(get_option('date_format'), strtotime($date1));
  1141. }
  1142. }
  1143. /**
  1144. * Render money in format.
  1145. *
  1146. * @refactoring korotkov@ud
  1147. * @global array $wpi_settings
  1148. * @param type $number
  1149. * @return type
  1150. */
  1151. function money_format( $number ) {
  1152. global $wpi_settings;
  1153. return number_format( (float)$number, 2, '.', $wpi_settings['thousands_separator_symbol']?$wpi_settings['thousands_separator_symbol']:'' );
  1154. }
  1155. /**
  1156. We use this to merge two arrays.
  1157. Used when loading default billing data, and then being updated by invoice-specific data
  1158. Awesome function from http://us2.php.net/manual/en/function.array-merge-recursive.php
  1159. */
  1160. function &array_merge_recursive_distinct() {
  1161. $aArrays = func_get_args();
  1162. $aMerged = $aArrays[0];
  1163. for ($i = 1; $i < count($aArrays); $i++) {
  1164. if (is_array($aArrays[$i])) {
  1165. foreach ($aArrays[$i] as $key => $val) {
  1166. if (is_array($aArrays[$i][$key])) {
  1167. $aMerged[$key] = (isset($aMerged[$key]) && is_array($aMerged[$key]) ) ? WPI_Functions::array_merge_recursive_distinct($aMerged[$key], $aArrays[$i][$key]) : $aArrays[$i][$key];
  1168. } else {
  1169. $aMerged[$key] = $val;
  1170. }
  1171. }
  1172. }
  1173. }
  1174. return $aMerged;
  1175. }
  1176. /** @TODO: Update it to show Settings page link */
  1177. function set_plugin_page_settings_link($links) {
  1178. /* $settings_link = "<a href='{$core->options['links']['settings_page']}'>Settings</a>";
  1179. array_unshift($links, $settings_link); */
  1180. return $links;
  1181. }
  1182. // Checks whether all plugin tables exist via tables_exist function
  1183. function check_tables() {
  1184. global $wpdb;
  1185. if (!WPI_Functions::tables_exist()) {
  1186. $message = __("The plugin database tables are gone, deactivate and reactivate plugin to re-create them.", WPI);
  1187. }
  1188. WPI_UI::error_message($message);
  1189. }
  1190. function tables_exist() {
  1191. global $wpdb;
  1192. if (!$wpdb->query("SHOW TABLES LIKE '{$wpdb->base_prefix}wpi_object_log';"))
  1193. return false;
  1194. return true;
  1195. }
  1196. // Used for displaying variables in the UI, mostly for debugging
  1197. function qc($what, $force = false) {
  1198. global $wp_invoice_debug;
  1199. if (is_array($what)) {
  1200. $what = WPI_Functions::pretty_print_r($what, false);
  1201. }
  1202. if (is_array($what) || is_string($what)) { // this way we don't try and show classess
  1203. if ($wp_invoice_debug || $force) {
  1204. ?>
  1205. <div class="ui-state-error ui-corner-all" style="padding: 0 .7em;">
  1206. <p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>
  1207. <span class="message_content"><?php echo $what; ?></span></p>
  1208. </div>
  1209. <?php
  1210. }
  1211. } else {
  1212. ?>
  1213. <div class="ui-state-error ui-corner-all" style="padding: 0 .7em;">
  1214. <p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>
  1215. <span class="message_content"><pre><?php print_r($what); ?></pre></span></p>
  1216. </div>
  1217. <?php
  1218. }
  1219. // Add QC Message to Log
  1220. //WPI_Functions::log($what)
  1221. }
  1222. // Logs events and saved them into WordPress option 'wpi_log'
  1223. // This function is intended to ease troubleshooting later
  1224. function log($what) {
  1225. $wpi_log = get_option('wpi_log');
  1226. // If no session log created yet, create one
  1227. if (!is_array($wpi_log)) {
  1228. $wpi_log = array();
  1229. array_push($wpi_log, array(time(), "Log Started."));
  1230. }
  1231. // Insert event into session
  1232. array_push($wpi_log, array(time(), $what));
  1233. update_option('wpi_log', $wpi_log);
  1234. return true;
  1235. }
  1236. // Alternative to print_r
  1237. function pretty_print_r($data, $echo = false) {
  1238. // Clean $_REQUEST array
  1239. $result = '';
  1240. if ($data == $_REQUEST) {
  1241. foreach ($data as $key => $value) {
  1242. $pattern = "/PHPSESSID|ui-tab/";
  1243. if (preg_match($pattern, $key)) {
  1244. unset($data[$key]);
  1245. }
  1246. }
  1247. }
  1248. if (is_array($data)) { //If the given variable is an array, print using the print_r function.
  1249. $result .= "<pre class='wpi_class_pre'>\n";
  1250. $result .= print_r($data, true);
  1251. $result .= "</pre>";
  1252. } elseif (is_object($data)) {
  1253. $result .= "<pre>\n";
  1254. var_dump($data, true);
  1255. $result .= "</pre>";
  1256. } else {
  1257. $result .= "=========&gt; ";
  1258. $result .= var_dump($data, true);
  1259. $result .= " &lt;=========";
  1260. }
  1261. if ($echo == false)
  1262. return $result;
  1263. $echo;
  1264. }
  1265. function check_settings() {
  1266. global $wpi_settings;
  1267. if ($wpi_settings['web_invoice_page'] == '') {
  1268. $message .= __('Invoice page not selected. ', WPI);
  1269. $message .= __("Visit ", WPI) . "<a href='admin.php?page=wpi_page_settings'>".__('settings page', WPI)."</a>" . __(" to configure.", WPI);
  1270. }
  1271. if (!function_exists('curl_exec'))
  1272. $message .= __("cURL is not turned on on your server, credit card processing will not work. If you have access to your php.ini file, activate <b>extension=php_curl.dll</b>.", WPI);
  1273. WPI_UI::error_message($message);
  1274. }
  1275. function settings_action() {
  1276. if(isset($_REQUEST['wpi_settings']) ) {
  1277. if ($_REQUEST['page'] == 'wpi_page_settings') {
  1278. unset($_REQUEST);
  1279. wp_redirect(admin_url("admin.php?page=wpi_page_settings&message=updated"));
  1280. exit;
  1281. }
  1282. }
  1283. }
  1284. /**
  1285. Handles saving and updating
  1286. Can also handle AJAX save/update function
  1287. */
  1288. function save_invoice($invoice, $args = '') {
  1289. //die( json_encode($invoice) );
  1290. /* Set function additional params */
  1291. $defaults = array(
  1292. 'type' => 'default'
  1293. );
  1294. extract(wp_parse_args($args, $defaults), EXTR_SKIP);
  1295. if ($type != 'import') {
  1296. if (!wp_verify_nonce($_REQUEST['nonce'], 'wpi-update-invoice')) {
  1297. die('Security check');
  1298. }
  1299. }
  1300. /* Init New Invoice object from passed variables */
  1301. $ni = new WPI_Invoice();
  1302. $ni->set("ID={$invoice['ID']}");
  1303. $ni->set("invoice_id={$invoice['invoice_id']}");
  1304. //$ni->set("terms_acceptance_required={$invoice['terms_acceptance_required']}");
  1305. $ni->set("subject={$invoice['subject']}");
  1306. $ni->set("description={$invoice['description']}");
  1307. //$ni->set("watermark={$invoice['meta']['watermark']}");
  1308. if ($invoice['deposit'] == 'on' || $invoice['deposit'] == 'true') {
  1309. $ni->set("deposit_amount={$invoice['deposit_amount']}");
  1310. } else {
  1311. $ni->set("deposit_amount=0");
  1312. }
  1313. $ni->set("due_date_year={$invoice['due_date_year']}");
  1314. $ni->set("due_date_month={$invoice['due_date_month']}");
  1315. $ni->set("due_date_day={$invoice['due_date_day']}");
  1316. $ni->set("default_currency_code={$invoice['default_currency_code']}");
  1317. if (!empty($invoice['meta']['terms'])) {
  1318. $ni->set("terms={$invoice['meta']['terms']}");
  1319. }
  1320. $ni->set("tax={$invoice['meta']['tax']}");
  1321. $ni->set("custom_id={$invoice['meta']['custom_id']}");
  1322. /**
  1323. * DETECTING INVOICE TYPE
  1324. * (Changes for ability to use premium feature Quotes)
  1325. *
  1326. * @author Anton Korotkov
  1327. *
  1328. * There are three available types by default:
  1329. * - invoice
  1330. * - recurring
  1331. */
  1332. // 'invoice' is by default
  1333. $invoice_type = 'invoice';
  1334. // If $invoice object has type definition then use it
  1335. if ( !empty( $invoice['type'] ) ) {
  1336. $invoice_type = $invoice['type'];
  1337. }
  1338. // Save status of invoice (quote or not quote)
  1339. if(isset ($invoice['quote'])) {
  1340. if($invoice['quote'] == "on") {
  1341. $ni->set("status=quote");
  1342. $ni->set("is_quote=true");
  1343. $invoice_type = 'quote';
  1344. } else {
  1345. $ni->set("status=null");
  1346. }
  1347. }
  1348. // But if recurring settings are defined then invoice type should be recurring
  1349. if ($invoice['recurring']['active'] == 'on' && !empty($invoice['recurring']['cycles'])) {
  1350. $ni->create_schedule("unit={$invoice['recurring']['unit']}&length={$invoice['recurring']['length']}&cycles={$invoice['recurring']['cycles']}&send_invoice_automatically={$invoice['recurring']['send_invoice_automatically']}&start_date[month]={$invoice['recurring']['start_date']['month']}&start_date[day]={$invoice['recurring']['start_date']['day']}&start_date[year]={$invoice['recurring']['start_date']['year']}");
  1351. $invoice_type = 'recurring';
  1352. }
  1353. // Finally set invoice type
  1354. $ni->set("type=$invoice_type");
  1355. /* Set invoice status */
  1356. $status = (!empty($invoice['post_status']) ? $invoice['post_status'] : 'active');
  1357. $ni->set("post_status={$status}");
  1358. /* Add discounts if exist */
  1359. if (is_array($invoice['meta']['discount'])) {
  1360. foreach ($invoice['meta']['discount'] as $discount) {
  1361. if (!empty($discount['name']) && !empty($discount['amount'])) {
  1362. $ni->add_discount("name={$discount['name']}&type={$discount['type']}&amount={$discount['amount']}");
  1363. }
  1364. }
  1365. }
  1366. if (!empty($invoice['client_change_payment_method'])) {
  1367. $ni->set("client_change_payment_method={$invoice['client_change_payment_method']}");
  1368. }
  1369. $ni->set("default_payment_method={$invoice['default_payment_method']}");
  1370. $ni->set("tax_method={$invoice['tax_method']}");
  1371. // It's bad idea to clear log, because all neccessary data such as payment information exist there
  1372. //$ni->admin("clear_log={$invoice['admin']['clear_log']}");
  1373. /* Manually set billing settings due to the complexity of the hierarchy */
  1374. $ni->data['billing'] = !empty($invoice['billing']) ? $invoice['billing'] : array();
  1375. /* Add line items */
  1376. foreach ($invoice['itemized_list'] as $line_item) {
  1377. $ni->line_item("name={$line_item['name']}&description={$line_item['description']}&quantity={$line_item['quantity']}&price={$line_item['price']}&tax_rate={$line_item['tax']}");
  1378. }
  1379. /* Add line items for charges */
  1380. if (!empty($invoice['itemized_charges'])) {
  1381. foreach ($invoice['itemized_charges'] as $charge_item) {
  1382. $ni->line_charge("name={$charge_item['name']}&amount={$charge_item['amount']}&tax={$charge_item['tax']}");
  1383. }
  1384. }
  1385. /*
  1386. * Save Invoice Object to DB and update user
  1387. * (trimming is a precaution because it could cause problems in inserted in DB w/ whitespace on end)
  1388. */
  1389. $ni->set("user_email=" . trim($invoice['user_data']['user_email']));
  1390. if ($type != 'import') {
  1391. WPI_Functions::update_user($invoice['user_data']);
  1392. }
  1393. $invoice_id = $ni->save_invoice();
  1394. if ($invoice_id) {
  1395. return $invoice_id;
  1396. } else {
  1397. return false;
  1398. }
  1399. }
  1400. function SendNotificationInvoice() {
  1401. $ni = new WPI_Invoice();
  1402. $ni->SendNotificationLog($_REQUEST);
  1403. }
  1404. /**
  1405. * It's a callback function. It calls on a option "wpi_options" changes
  1406. *
  1407. * @param mixed $new_value
  1408. * @param mixed $old_value (do not used)
  1409. * @return $new_value
  1410. * @author odokienko@UD
  1411. */
  1412. function pre_update_option_wpi_options($new_value,$old_value){
  1413. global $wpdb;
  1414. $default_currency_code = $new_value['currency']["default_currency_code"];
  1415. $protected_currencies = array();
  1416. /* check for curency with default_currency_code*/
  1417. $protected_currencies[] = $default_currency_code;
  1418. /* check for currencies that are already used in invoices */
  1419. $results = $wpdb->get_results("
  1420. SELECT DISTINCT `{$wpdb->prefix}postmeta`.meta_value
  1421. FROM `{$wpdb->posts}`
  1422. JOIN `{$wpdb->prefix}postmeta` ON ( `{$wpdb->posts}`.id = `{$wpdb->prefix}postmeta`.post_id )
  1423. WHERE `{$wpdb->posts}`.post_type = 'wpi_object'
  1424. AND `{$wpdb->prefix}postmeta`.meta_key = 'default_currency_code'
  1425. ");
  1426. foreach($results as $curr_row){
  1427. $protected_currencies[] = $curr_row->meta_value;
  1428. }
  1429. foreach ($protected_currencies as $curr){
  1430. if (empty($new_value['currency']['types'][$curr])){
  1431. $new_value['currency']['types'][$curr] = $old_value['currency']['types'][$curr];
  1432. }
  1433. if (empty($new_value['currency']['symbol'][$curr])){
  1434. $new_value['currency']['symbol'][$curr] = $old_value['currency']['symbol'][$curr];
  1435. }
  1436. }
  1437. foreach ($new_value['currency']['symbol'] as &$symbol){
  1438. $symbol = base64_encode($symbol);
  1439. }
  1440. /**
  1441. * and checking the option "use_wp_crm_to_send_notifications". If it is set to true
  1442. * than retrieves an option 'wp_crm_settings' and check it for containing
  1443. * a default WPI notification templates and add them if necessary
  1444. */
  1445. if(!empty($new_value['use_wp_crm_to_send_notifications'])){
  1446. $wp_crm_settings = get_option('wp_crm_settings');
  1447. $update_needed = false;
  1448. if (empty($wp_crm_settings['notifications']['wpi_send_thank_you_email'])){
  1449. $wp_crm_settings['notifications']['wpi_send_thank_you_email']['subject'] = __('Invoice #[invoice_id] has been paid', 'wp_crm');
  1450. $wp_crm_settings['notifications']['wpi_send_thank_you_email']['to'] = '[user_email]';
  1451. $wp_crm_settings['notifications']['wpi_send_thank_you_email']['send_from'] = '[business_name] <[from]>';
  1452. $wp_crm_settings['notifications']['wpi_send_thank_you_email']['message'] = __("Dear [user_name],\n[business_name] has received your payment for the invoice.\n\nYou can overview invoice status and payment history by clicking this link:\n[permalink]\n\nThank you very much for your patronage.\n\nBest regards,\n[business_name] ([from])", 'wp_crm');
  1453. $wp_crm_settings['notifications']['wpi_send_thank_you_email']['fire_on_action'] = array('wpi_send_thank_you_email');
  1454. $update_needed = true;
  1455. }
  1456. if(empty($wp_crm_settings['notifications']['wpi_cc_thank_you_email'])){
  1457. $wp_crm_settings['notifications']['wpi_cc_thank_you_email']['subject'] = __('Invoice #[invoice_id] has been paid by [user_name]', 'wp_crm');
  1458. $wp_crm_settings['notifications']['wpi_cc_thank_you_email']['to'] = '[admin_email]';
  1459. $wp_crm_settings['notifications']['wpi_cc_thank_you_email']['send_from'] = '[business_name] <[from]>';
  1460. $wp_crm_settings['notifications']['wpi_cc_thank_you_email']['message'] = __("[user_name] has paid invoice #[invoice_id].\n[invoice_title]\nTotal payments: [default_currency_code] [total_payments] of [default_currency_code] [total].\n\nYou can overview invoice status and payment history by clicking this link:\n[permalink]\n\nUser information:\n\nID: [user_id]\nName: [user_name]\nEmail: [user_email]\n\n--------------------\n[site]", 'wp_crm');
  1461. $wp_crm_settings['notifications']['wpi_cc_thank_you_email']['fire_on_action'] = array('wpi_cc_thank_you_email');
  1462. $update_needed = true;
  1463. }
  1464. if(empty($wp_crm_settings['notifications']['wpi_send_invoice_creator_email'])){
  1465. $wp_crm_settings['notifications']['wpi_send_invoice_creator_email']['subject'] = __('Invoice #[invoice_id] has been paid by [user_name]', 'wp_crm');
  1466. $wp_crm_settings['notifications']['wpi_send_invoice_creator_email']['to'] = '[creator_email]';
  1467. $wp_crm_settings['notifications']['wpi_send_invoice_creator_email']['send_from'] = '[business_name] <[from]>';
  1468. $wp_crm_settings['notifications']['wpi_send_invoice_creator_email']['message'] = __("Dear [creator_name],\n[user_name] has paid invoice #[invoice_id].\n\n[invoice_title]\nTotal payments: [default_currency_code] [total_payments] of [default_currency_code] [total].\n\nYou can overview invoice status and payment history by clicking this link:\n[permalink]\n\nUser information:\n\nID: [user_id]\nName: [user_name]\nEmail: [user_email]\n\n--------------------\n[site]", 'wp_crm');
  1469. $wp_crm_settings['notifications']['wpi_send_invoice_creator_email']['fire_on_action'] = array('wpi_send_invoice_creator_email');
  1470. $update_needed = true;
  1471. }
  1472. if($update_needed){
  1473. update_option('wp_crm_settings', $wp_crm_settings);
  1474. }
  1475. }
  1476. return $new_value;
  1477. }
  1478. /**
  1479. * It's a callback function. It calls on a option "wpi_options" get_option
  1480. *
  1481. * @param mixed $new_value
  1482. * @param mixed $old_value (do not used)
  1483. * @return $new_value
  1484. * @author odokienko@UD
  1485. */
  1486. function option_wpi_options($value){
  1487. if (empty($value['currency']['symbol'])) return $value;
  1488. foreach ($value['currency']['symbol'] as $key => $symbol){
  1489. $value['currency']['symbol'][$key] = base64_decode($symbol);
  1490. }
  1491. return $value;
  1492. }
  1493. /**
  1494. * Creates post type.
  1495. *
  1496. * Ran everytime.
  1497. *
  1498. * @since 3.0
  1499. *
  1500. */
  1501. function register_post_type() {
  1502. global $wpdb, $wpi_settings, $wp_properties;
  1503. $wpi_settings['statuses'] = array();
  1504. $labels = array(
  1505. 'name' => __('Invoices', WPI),
  1506. 'singular_name' => __('Invoice', WPI),
  1507. 'add_new' => __('Add New', WPI),
  1508. 'add_new_item' => __('Add New Invoice', WPI),
  1509. 'edit_item' => __('Edit Invoice', WPI),
  1510. 'new_item' => __('New Invoice', WPI),
  1511. 'view_item' => __('View Invoice', WPI),
  1512. 'search_items' => __('Search Invoices', WPI),
  1513. 'not_found' => __('No invoices found', WPI),
  1514. 'not_found_in_trash' => __('No invoices found in Trash', WPI),
  1515. 'parent_item_colon' => ''
  1516. );
  1517. // Register custom post types
  1518. register_post_type('wpi_object', array(
  1519. 'labels' => $labels,
  1520. 'singular_label' => __('Invoice', WPI),
  1521. 'public' => false,
  1522. 'show_ui' => false,
  1523. '_builtin' => false,
  1524. '_edit_link' => $wpi_settings['links']['manage_invoice'] . '&wpi[existing_invoice][invoice_id]=%d',
  1525. 'capability_type' => 'post',
  1526. 'hierarchical' => false,
  1527. 'rewrite' => array('slug' => $wp_properties['configuration']['base_slug']),
  1528. 'query_var' => $wp_properties['configuration']['base_slug'],
  1529. 'supports' => array('title', 'editor', 'thumbnail'),
  1530. 'menu_icon' => WPI_URL . "/core/css/images/wp_invoice.png"
  1531. ));
  1532. register_post_status('archived', array(
  1533. 'label' => _x('Archived', 'wpi_object'),
  1534. 'public' => false,
  1535. '_builtin' => false,
  1536. 'label_count' => _n_noop('Archived <span class="count">(%s)</span>', 'Archived <span class="count">(%s)</span>'),
  1537. ));
  1538. $wpi_settings['statuses'][] = 'archived';
  1539. register_post_status('active', array(
  1540. 'label' => _x('Active', 'wpi_object'),
  1541. 'public' => false,
  1542. '_builtin' => false,
  1543. 'label_count' => _n_noop('Due Invoices <span class="count">(%s)</span>', 'Due Invoices <span class="count">(%s)</span>'),
  1544. ));
  1545. $wpi_settings['statuses'][] = 'active';
  1546. register_post_status('paid', array(
  1547. 'label' => _x('Paid', 'wpi_object'),
  1548. 'public' => false,
  1549. '_builtin' => false,
  1550. 'label_count' => _n_noop('Paid <span class="count">(%s)</span>', 'Paid <span class="count">(%s)</span>'),
  1551. ));
  1552. $wpi_settings['statuses'][] = 'paid';
  1553. register_post_status('trash', array(
  1554. 'label' => _x('Trash', 'wpi_object'),
  1555. 'public' => false,
  1556. '_builtin' => false,
  1557. 'label_count' => _n_noop('Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>'),
  1558. ));
  1559. $wpi_settings['statuses'][] = 'trash';
  1560. register_post_status('pending', array(
  1561. 'label' => _x('Pending', 'wpi_object'),
  1562. 'public' => false,
  1563. '_builtin' => false,
  1564. 'label_count' => _n_noop('Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>'),
  1565. ));
  1566. $wpi_settings['statuses'][] = 'pending';
  1567. register_post_status('refund', array(
  1568. 'label' => _x('Refund', 'wpi_object'),
  1569. 'public' => false,
  1570. '_builtin' => false,
  1571. 'label_count' => _n_noop('Refund <span class="count">(%s)</span>', 'Refund <span class="count">(%s)</span>'),
  1572. ));
  1573. $wpi_settings['statuses'][] = 'refund';
  1574. do_action('wpi_register_object');
  1575. }
  1576. /**
  1577. * Creates WPI 3.0 Database Schema
  1578. *
  1579. * Creates
  1580. *
  1581. * @uses $wpdb
  1582. * @since 3.0
  1583. *
  1584. */
  1585. function create_new_schema_tables() {
  1586. global $wpdb;
  1587. require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
  1588. dbDelta("CREATE TABLE {$wpdb->base_prefix}wpi_object_log (
  1589. ID mediumint(9) NOT NULL auto_increment,
  1590. blog_id mediumint(9) NOT NULL,
  1591. object_id mediumint(9) NOT NULL,
  1592. user_id mediumint(9) NOT NULL,
  1593. attribute varchar(255) collate utf8_unicode_ci NOT NULL,
  1594. action varchar(255) collate utf8_unicode_ci NOT NULL,
  1595. value varchar(255) collate utf8_unicode_ci NOT NULL,
  1596. text text collate utf8_unicode_ci NOT NULL,
  1597. time bigint(11) NOT NULL default '0',
  1598. UNIQUE KEY id (ID),
  1599. KEY time (time),
  1600. KEY object_id (object_id),
  1601. KEY user_id (user_id),
  1602. KEY event_type (action)
  1603. ) ");
  1604. WPI_Functions::log("Installation SQL queries ran.");
  1605. }
  1606. /**
  1607. * This function loads our payment gateways
  1608. * @since 3.0
  1609. */
  1610. function load_gateways() {
  1611. global $wpi_settings;
  1612. $default_headers = array(
  1613. 'Name' => __('Name', 'wpi_gateway'),
  1614. 'Version' => __('Version', 'wpi_gateway'),
  1615. 'Description' => __('Description', 'wpi_gateway')
  1616. );
  1617. if (!is_dir(WPI_Gateways_Path)) {
  1618. return;
  1619. }
  1620. if ($premium_dir = opendir(WPI_Gateways_Path)) {
  1621. if(file_exists(WPI_Gateways_Path . "/index.php")) {
  1622. if(WP_DEBUG) {
  1623. include_once(WPI_Gateways_Path . "/index.php");
  1624. } else {
  1625. @include_once(WPI_Gateways_Path . "/index.php");
  1626. }
  1627. }
  1628. while (false !== ($file = readdir($premium_dir))) {
  1629. if ($file == 'index.php')
  1630. continue;
  1631. if (end(explode(".", $file)) == 'php') {
  1632. $slug = str_replace(array('.php'), '', $file);
  1633. if (substr($slug, 0, 6) == "class_") {
  1634. $t = split("class_", $slug);
  1635. $slug = $t[1];
  1636. }
  1637. $plugin_data = @get_file_data(WPI_Gateways_Path . "/" . $file, $default_headers, 'plugin');
  1638. $wpi_settings['installed_gateways'][$slug]['name'] = $plugin_data['Name'];
  1639. $wpi_settings['installed_gateways'][$slug]['version'] = $plugin_data['Version'];
  1640. $wpi_settings['installed_gateways'][$slug]['description'] = $plugin_data['Description'];
  1641. if(WP_DEBUG) {
  1642. include_once(WPI_Gateways_Path . "/" . $file);
  1643. } else {
  1644. @include_once(WPI_Gateways_Path . "/" . $file);
  1645. }
  1646. // Disable plugin if class does not exists - file is empty
  1647. if (!class_exists($slug)) {
  1648. unset($wpi_settings['installed_gateways'][$slug]);
  1649. } else {
  1650. /** Initialize the object, then update the billing permissions to show whats in the object */
  1651. eval("\$wpi_settings['installed_gateways']['" . $slug . "']['object'] = new " . $slug . "();");
  1652. }
  1653. }
  1654. }
  1655. /** Sync our options */
  1656. WPI_Gateway_Base::sync_billing_objects();
  1657. }
  1658. }
  1659. /**
  1660. * Check for premium features and load them
  1661. * @since 3.0
  1662. */
  1663. function load_premium() {
  1664. global $wpi_settings;
  1665. $default_headers = array(
  1666. 'Name' => __('Name', WPI),
  1667. 'Version' => __('Version', WPI),
  1668. 'Description' => __('Description', WPI),
  1669. 'Minimum Core Version' => __('Minimum Core Version', WPI)
  1670. );
  1671. $wpi_settings['installed_features'] = array();
  1672. if (!is_dir(WPI_Premium))
  1673. return;
  1674. if ($premium_dir = opendir(WPI_Premium)) {
  1675. if (file_exists(WPI_Premium . "/index.php"))
  1676. @include_once(WPI_Premium . "/index.php");
  1677. while (false !== ($file = readdir($premium_dir))) {
  1678. if ($file == 'index.php')
  1679. continue;
  1680. if (end(@explode(".", $file)) == 'php') {
  1681. $plugin_slug = str_replace(array('.php'), '', $file);
  1682. if (substr($plugin_slug, 0, 6) == "class_") {
  1683. $t = split("class_", $plugin_slug);
  1684. $plugin_slug = $t[1];
  1685. }
  1686. $plugin_data = @get_file_data(WPI_Premium . "/" . $file, $default_headers, 'plugin');
  1687. $wpi_settings['installed_features'][$plugin_slug]['name'] = $plugin_data['Name'];
  1688. $wpi_settings['installed_features'][$plugin_slug]['version'] = $plugin_data['Version'];
  1689. $wpi_settings['installed_features'][$plugin_slug]['description'] = $plugin_data['Description'];
  1690. if($plugin_data['Minimum Core Version']) {
  1691. $wpi_settings['installed_features'][$plugin_slug]['minimum_wpi_version'] = $plugin_data['Minimum Core Version'];
  1692. }
  1693. //** If feature has a Minimum Core Version and it is more than current version - we do not load **/
  1694. $feature_requires_upgrade = (!empty($wpi_settings['installed_features'][$plugin_slug]['minimum_wpi_version']) && (version_compare(WP_INVOICE_VERSION_NUM, $wpi_settings['installed_features'][$plugin_slug]['minimum_wpi_version']) < 0) ? true : false);
  1695. if($feature_requires_upgrade) {
  1696. //** Disable feature if it requires a higher WPI version**/
  1697. $wpi_settings['installed_features'][$plugin_slug]['disabled'] = 'true';
  1698. $wpi_settings['installed_features'][$plugin_slug]['needs_higher_wpi_version'] = 'true';
  1699. } else {
  1700. $wpi_settings['installed_features'][$plugin_slug]['needs_higher_wpi_version'] = 'false';
  1701. //$wpi_settings['installed_features'][$plugin_slug]['disabled'] = 'false';
  1702. }
  1703. // Check if the plugin is disabled
  1704. if (empty($wpi_settings['installed_features'][$plugin_slug]['disabled'])) {
  1705. $wpi_settings['installed_features'][$plugin_slug]['disabled'] = 'false';
  1706. }
  1707. if ($wpi_settings['installed_features'][$plugin_slug]['disabled'] != 'true') {
  1708. include_once(WPI_Premium . "/" . $file);
  1709. // Disable plugin if class does not exists - file is empty
  1710. if (!class_exists($plugin_slug))
  1711. unset($wpi_settings['installed_features'][$plugin_slug]);
  1712. else
  1713. $wpi_settings['installed_features'][$plugin_slug]['disabled'] = 'false';
  1714. } else {
  1715. // Feature not loaded because it is disabled
  1716. }
  1717. }
  1718. }
  1719. }
  1720. }
  1721. /**
  1722. * Run manually when a version mismatch is detected.
  1723. *
  1724. * Holds official current version designation.
  1725. * Called in admin_init hook.
  1726. *
  1727. **/
  1728. function manual_activation() {
  1729. $installed_ver = get_option( "wp_invoice_version" );
  1730. $wpi_version = WP_INVOICE_VERSION_NUM;
  1731. if(@version_compare($installed_ver, $wpi_version) == '-1') {
  1732. //** We are upgrading */
  1733. //** Update option to latest version so this isn't run on next admin page load */
  1734. update_option( "wp_invoice_version", $wpi_version );
  1735. //** Try to create new schema tables */
  1736. WPI_Functions::create_new_schema_tables();
  1737. //** Get premium features on activation */
  1738. WPI_Functions::check_for_premium_features();
  1739. }
  1740. return;
  1741. }
  1742. /**
  1743. * Checks for, and downloads, any premium features from TCT servers
  1744. *
  1745. * @uses $wpdb
  1746. * @since 3.0
  1747. *
  1748. */
  1749. function check_for_premium_features($return = false) {
  1750. global $wpi_settings;
  1751. $blogname = get_bloginfo('url');
  1752. $blogname = urlencode(str_replace(array('http://', 'https://'), '', $blogname));
  1753. $system = 'wpi';
  1754. $wpi_version = WP_INVOICE_VERSION_NUM;
  1755. $api_key = WPI_Functions::get_api_key(array('force_check' => true, 'return' => true));
  1756. if(empty($api_key) || strlen($api_key) != 40) {
  1757. if($return) {
  1758. if(empty($api_key)) {
  1759. $api_key = __("The API key could not be generated.", WPI);
  1760. }
  1761. return sprintf(__('An error occured during premium feature check: <b>%s</b>.',WPI), $api_key);
  1762. } else {
  1763. return;
  1764. }
  1765. }
  1766. $check_url = "http://updates.usabilitydynamics.com/?system={$system}&site={$blogname}&system_version={$wpi_version}&api_key={$api_key}";
  1767. $response = @wp_remote_get($check_url);
  1768. if (!$response) {
  1769. return;
  1770. }
  1771. // Check for errors
  1772. if (is_object($response) && !empty($response->errors)) {
  1773. foreach ($response->errors as $update_errrors) {
  1774. $error_string .= implode(",", $update_errrors);
  1775. WPI_Functions::log("Feature Update Error: " . $error_string);
  1776. }
  1777. if ($return) {
  1778. return sprintf(__('An error occured during premium feature check: <b> %s </b>.', WPI), $error_string);
  1779. }
  1780. return;
  1781. }
  1782. //** Quit if failure */
  1783. if ($response['response']['code'] != '200') {
  1784. return;
  1785. }
  1786. $response = @json_decode($response['body']);
  1787. if (is_object($response->available_features)) {
  1788. $response->available_features = WPI_Functions::objectToArray($response->available_features);
  1789. //** Update the database */
  1790. $wpi_settings = get_option('wpi_options');
  1791. $wpi_settings['available_features'] = WPI_Functions::objectToArray($response->available_features);
  1792. update_option('wpi_options', $wpi_settings);
  1793. } // available_features
  1794. if ($response->features == 'eligible' && $wpi_settings['disable_automatic_feature_update'] != 'true') {
  1795. // Try to create directory if it doesn't exist
  1796. if (!is_dir(WPI_Premium)) {
  1797. @mkdir(WPI_Premium, 0755);
  1798. }
  1799. // If didn't work, we quit
  1800. if (!is_dir(WPI_Premium)) {
  1801. return;
  1802. }
  1803. // Save code
  1804. if (is_object($response->code)) {
  1805. foreach ($response->code as $code) {
  1806. $filename = $code->filename;
  1807. $php_code = $code->code;
  1808. $version = $code->version;
  1809. //** Check version */
  1810. $default_headers = array(
  1811. 'Name' => __('Feature Name', WPI),
  1812. 'Version' => __('Version', WPI),
  1813. 'Description' => __('Description', WPI)
  1814. );
  1815. $current_file = @get_file_data(WPI_Premium . "/" . $filename, $default_headers, 'plugin');
  1816. if (@version_compare($current_file[Version], $version) == '-1') {
  1817. $this_file = WPI_Premium . "/" . $filename;
  1818. $fh = @fopen($this_file, 'w');
  1819. if ($fh) {
  1820. fwrite($fh, $php_code);
  1821. fclose($fh);
  1822. if ($current_file[Version]) {
  1823. //UD_F::log(sprintf(__('WP-Invoice Premium Feature: %s updated to version %s from %s.', WPI), $code->name, $version, $current_file[Version]));
  1824. } else {
  1825. //UD_F::log(sprintf(__('WP-Invoice Premium Feature: %s updated to version %s.', WPI), $code->name, $version));
  1826. }
  1827. $updated_features[] = $code->name;
  1828. }
  1829. } else {
  1830. }
  1831. }
  1832. }
  1833. }
  1834. // Update settings
  1835. //WPI_Functions::settings_action(true);
  1836. if ($return && $wpi_settings['disable_automatic_feature_update'] == 'true') {
  1837. return __('Update ran successfully but no features were downloaded because the setting is disabled. Enable it in the "Main" tab.', WPI);
  1838. } elseif ($return) {
  1839. return __('Update ran successfully.', WPI);
  1840. }
  1841. }
  1842. /**
  1843. * Check if premium feature is installed or not
  1844. * @param string $slug. Slug of premium feature
  1845. * @return boolean.
  1846. */
  1847. function check_premium($slug) {
  1848. global $wpi_settings;
  1849. if(empty($wpi_settings['installed_features'][$slug]['version'])) {
  1850. return false;
  1851. }
  1852. $file = WPI_Premium . "/" . $slug . ".php";
  1853. $default_headers = array(
  1854. 'Name' => __('Name',WPI),
  1855. 'Version' => __('Version',WPI),
  1856. 'Description' => __('Description',WPI)
  1857. );
  1858. $plugin_data = @get_file_data( $file , $default_headers, 'plugin' );
  1859. if(!is_array($plugin_data) || empty($plugin_data['Version'])) {
  1860. return false;
  1861. }
  1862. return true;
  1863. }
  1864. /**
  1865. * Logs an action
  1866. *
  1867. * @since 3.0
  1868. */
  1869. function log_event($object_id, $attribute, $action, $value, $text = '', $time = false) {
  1870. global $wpdb, $current_user, $blog_id;
  1871. if (!$time) {
  1872. $time = time();
  1873. }
  1874. $wpdb->show_errors();
  1875. $wpdb->insert($wpdb->base_prefix . 'wpi_object_log', array(
  1876. 'object_id' => $object_id,
  1877. 'user_id' => $current_user->ID,
  1878. 'attribute' => $attribute,
  1879. 'action' => $action,
  1880. 'value' => $value,
  1881. 'text' => $text,
  1882. 'time' => $time,
  1883. 'blog_id' => $blog_id
  1884. ));
  1885. if($wpdb->insert_id) {
  1886. return $wpdb->insert_id;
  1887. } else {
  1888. return false;
  1889. }
  1890. }
  1891. /**
  1892. * Detect browser
  1893. *
  1894. * @global bool $is_lynx
  1895. * @global bool $is_gecko
  1896. * @global bool $is_IE
  1897. * @global bool $is_opera
  1898. * @global bool $is_NS4
  1899. * @global bool $is_safari
  1900. * @global bool $is_chrome
  1901. * @global bool $is_iphone
  1902. *
  1903. * @author korotkov@ud
  1904. * @return array
  1905. */
  1906. function browser() {
  1907. global $is_lynx, $is_gecko, $is_IE, $is_opera, $is_NS4, $is_safari, $is_chrome, $is_iphone;
  1908. if ($is_lynx)
  1909. $classes['name'] = 'lynx';
  1910. elseif ($is_gecko)
  1911. $classes['name'] = 'gecko';
  1912. elseif ($is_opera)
  1913. $classes['name'] = 'opera';
  1914. elseif ($is_NS4)
  1915. $classes['name'] = 'ns4';
  1916. elseif ($is_safari)
  1917. $classes['name'] = 'safari';
  1918. elseif ($is_chrome)
  1919. $classes['name'] = 'chrome';
  1920. elseif ($is_IE) {
  1921. $classes['name'] = 'ie';
  1922. if (preg_match('/MSIE ([0-9]+)([a-zA-Z0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $browser_version))
  1923. $classes['version'] = $browser_version[1];
  1924. } else {
  1925. $classes['name'] = 'unknown';
  1926. }
  1927. if ($is_iphone) {
  1928. $classes['name'] = 'iphone';
  1929. }
  1930. if (stristr($_SERVER['HTTP_USER_AGENT'], "mac")) {
  1931. $classes['sys'] = 'osx';
  1932. } elseif (stristr($_SERVER['HTTP_USER_AGENT'], "linux")) {
  1933. $classes['sys'] = 'linux';
  1934. } elseif (stristr($_SERVER['HTTP_USER_AGENT'], "windows")) {
  1935. $classes['sys'] = 'windows';
  1936. }
  1937. return $classes;
  1938. }
  1939. /**
  1940. * Revalidate all the invoices
  1941. *
  1942. * @author korotkov@ud
  1943. * @global object $wpdb
  1944. */
  1945. function total_revalidate() {
  1946. global $wpdb;
  1947. /** Recalculate all invoices */
  1948. $invoices = $wpdb->get_col("
  1949. SELECT ID
  1950. FROM {$wpdb->posts}
  1951. WHERE post_type = 'wpi_object'
  1952. ");
  1953. foreach ($invoices as $post_id) {
  1954. $invoice_id = wpi_post_id_to_invoice_id($post_id);
  1955. $this_invoice = new WPI_Invoice();
  1956. $this_invoice->load_invoice("id={$invoice_id}");
  1957. $this_invoice->save_invoice();
  1958. }
  1959. }
  1960. /**
  1961. * Returns WP-CRM attributes list
  1962. *
  1963. * @global object $wp_crm
  1964. * @return array
  1965. * @author korotkov@ud
  1966. */
  1967. function get_wpi_crm_attributes() {
  1968. /** If WP-CRM not installed */
  1969. if ( !class_exists('WP_CRM_Core') ) return;
  1970. global $wp_crm;
  1971. $attributes = array();
  1972. if ( !empty( $wp_crm['data_structure']['attributes'] ) ) {
  1973. foreach( $wp_crm['data_structure']['attributes'] as $slug => $attribute ) {
  1974. if ( !empty( $attribute['wp_invoice'] ) && $attribute['wp_invoice'] == 'true' ) {
  1975. $attributes[ $slug ] = $attribute;
  1976. }
  1977. }
  1978. }
  1979. return $attributes;
  1980. }
  1981. /**
  1982. * WP-CRM custom fields procces filter
  1983. *
  1984. * @param array $current_fields
  1985. * @param string $name
  1986. * @return array
  1987. * @author korotkov@ud
  1988. */
  1989. function wpi_crm_custom_fields( $current_fields, $name ) {
  1990. $attributes = self::get_wpi_crm_attributes();
  1991. if ( empty( $attributes ) ) return $current_fields;
  1992. foreach( $attributes as $attr_key => $attr_value ) {
  1993. $current_fields['customer_information'][ $attr_key ] = array(
  1994. 'type' => 'text',
  1995. 'class' => 'text-input',
  1996. 'name' => $name.'['.$attr_key.']',
  1997. 'label' => __( $attr_value['title'], WPI )
  1998. );
  1999. }
  2000. return $current_fields;
  2001. }
  2002. /**
  2003. * Filter CRM actions list
  2004. * @param array $current
  2005. * @author odokienko@UD
  2006. * @return array
  2007. */
  2008. function wpi_crm_custom_notification($current) {
  2009. foreach( WPI_Core::$crm_notification_actions as $action_key => $action_name ) {
  2010. $current[$action_key] = $action_name;
  2011. }
  2012. return $current;
  2013. }
  2014. /**
  2015. * Create label for user activity stream attribute
  2016. *
  2017. * @version 1.0
  2018. * @author odokienko@UD
  2019. */
  2020. function wp_crm_entry_type_label($attr,$entity) {
  2021. global $wp_crm;
  2022. switch ($attr){
  2023. case "wpi_notification":
  2024. $attr = __("WP-Invoice Notification");
  2025. break;
  2026. }
  2027. return $attr;
  2028. }
  2029. /**
  2030. * Detects if at least one PF is installed
  2031. *
  2032. * @global array $wpi_settings
  2033. * @return bool
  2034. * @author korotkov@ud
  2035. */
  2036. static function has_installed_premium_features() {
  2037. global $wpi_settings;
  2038. if ( empty( $wpi_settings['installed_features'] ) ) return false;
  2039. foreach ( $wpi_settings['available_features'] as $feature_key => $feature ) {
  2040. if ( array_key_exists( $feature_key, $wpi_settings['installed_features'] ) && class_exists( $feature_key ) ) {
  2041. return true;
  2042. }
  2043. }
  2044. return false;
  2045. }
  2046. /**
  2047. * Changing of Mail From for Notifications.
  2048. *
  2049. * @global array $wpi_settings
  2050. * @return string
  2051. */
  2052. function notification_mail_from() {
  2053. global $wpi_settings;
  2054. $email = empty( $wpi_settings['mail_from_user_email'] ) ? "wordpress@".strtolower($_SERVER['SERVER_NAME']) : $wpi_settings['mail_from_user_email'];
  2055. return $email;
  2056. }
  2057. /**
  2058. * Changing of Mail From Name for Notifications.
  2059. *
  2060. * @global array $wpi_settings
  2061. * @return type
  2062. */
  2063. function notification_mail_from_name() {
  2064. global $wpi_settings;
  2065. $sendername = empty( $wpi_settings['mail_from_sender_name'] ) ? "WordPress" : stripslashes($wpi_settings['mail_from_sender_name']);
  2066. return $sendername;
  2067. }
  2068. /**
  2069. * Promotional links notice
  2070. *
  2071. * @global type $wp_properties
  2072. * @author korotkov@ud
  2073. */
  2074. function promotional_notice() {
  2075. global $wpi_settings;
  2076. $hour = 3600;
  2077. $day = 24*$hour;
  2078. $show_after = 2*$day;
  2079. $activation_date = (int)get_option( 'wpi_activation_time' );
  2080. $now = time();
  2081. if ( empty( $wpi_settings['installed_features'] ) && $now - $activation_date > $show_after ) {
  2082. $screen = get_current_screen();
  2083. if ( $screen->id == 'invoice_page_wpi_page_settings' ) :
  2084. ?>
  2085. <div class="updated <?php wpp_css( 'admin_notice::promotional_notice', 'wpi_promotional_notice' ) ?>">
  2086. <div class="<?php wpp_css( 'admin_notice::promotional_notice::top', 'wpi_promotional_notice_top_line' ) ?>">
  2087. <?php echo sprintf( __('Find out how to <a target="_blank" href="%s">Extend</a> your <a target="_blank" href="%s">WP-Invoice</a> plugin', WPI), 'https://usabilitydynamics.com/products/wp-invoice/premium-features/', 'https://usabilitydynamics.com/products/wp-invoice/' ); ?>
  2088. </div>
  2089. <div class="<?php wpp_css( 'admin_notice::promotional_notice::bottom', 'wpi_promotional_notice_bottom_line' ) ?>">
  2090. <a target="_blank" href="https://usabilitydynamics.com/products/wp-invoice/premium-features/"><?php _e( 'Premium Features', WPI ); ?></a>
  2091. |
  2092. <a target="_blank" href="https://usabilitydynamics.com/forums/"><?php _e( 'Support Forum', WPI ); ?></a>
  2093. |
  2094. <a target="_blank" href="https://usabilitydynamics.com/products/wp-invoice/#documentation-tutorials"><?php _e( 'User Guide', WPI ); ?></a>
  2095. </div>
  2096. </div>
  2097. <?php
  2098. endif;
  2099. }
  2100. }
  2101. }
  2102. /**
  2103. * Draw users dropdown list
  2104. *
  2105. * @global object $wpdb
  2106. * @param string $post_type
  2107. * @param string $select_name
  2108. * @param bool $return_users
  2109. * @return bool|nothing
  2110. * @author korotkov@ud
  2111. */
  2112. function wpi_invoice_users_dropdown($post_type, $select_name, $return_users=false) {
  2113. global $wpdb;
  2114. switch ($post_type) {
  2115. case 'wpi_object':
  2116. $results = $wpdb->get_results($wpdb->prepare("
  2117. SELECT u.ID, pm.meta_value
  2118. FROM {$wpdb->posts} AS p
  2119. JOIN {$wpdb->prefix}postmeta AS pm ON pm.post_id = p.ID AND pm.meta_key = 'user_email'
  2120. JOIN {$wpdb->users} AS u ON u.user_email = pm.meta_value
  2121. WHERE post_type= %s
  2122. ", $post_type), ARRAY_N);
  2123. break;
  2124. }
  2125. if (empty($results)) {
  2126. return false;
  2127. }
  2128. $users = array();
  2129. foreach ($results as $result) {
  2130. $users[] = $result[0];
  2131. }
  2132. if ($return_users)
  2133. return $users;
  2134. $selected = isset($_GET['recipient']) ? (int) $_GET['recipient'] : 0;
  2135. if (!empty($users)) {
  2136. wp_dropdown_users(
  2137. array(
  2138. 'include' => $users,
  2139. 'show_option_all' => __('Show all users', WPI),
  2140. 'selected' => $selected,
  2141. 'name' => $select_name
  2142. )
  2143. );
  2144. }
  2145. }
  2146. /**
  2147. * Mark invoice as Paid
  2148. *
  2149. * @param int $invoice_id
  2150. * @param bool $check_balance
  2151. * @return bool
  2152. * @author korotkov@ud
  2153. */
  2154. function wp_invoice_mark_as_paid($invoice_id, $check_balance=false) {
  2155. if ($check_balance) {
  2156. if (wpi_is_full_paid_invoice($invoice_id)) {
  2157. $post_id = wpi_invoice_id_to_post_id($invoice_id);
  2158. wp_update_post(
  2159. array(
  2160. 'ID' => $post_id,
  2161. 'post_status' => 'paid'
  2162. )
  2163. );
  2164. WPI_Functions::log_event($post_id, 'invoice', 'update', '', __('Payment status: Complete', WPI));
  2165. return true;
  2166. } else {
  2167. $post_id = wpi_invoice_id_to_post_id($invoice_id);
  2168. wp_update_post(
  2169. array(
  2170. 'ID' => $post_id,
  2171. 'post_status' => 'active'
  2172. )
  2173. );
  2174. return true;
  2175. }
  2176. } else {
  2177. $post_id = wpi_invoice_id_to_post_id($invoice_id);
  2178. wp_update_post(
  2179. array(
  2180. 'ID' => $post_id,
  2181. 'post_status' => 'paid'
  2182. )
  2183. );
  2184. WPI_Functions::log_event($post_id, 'invoice', 'update', '', __('Payment status: Complete', WPI));
  2185. return true;
  2186. }
  2187. }
  2188. /**
  2189. * Mark invoice as Pending (for PayPal IPN)
  2190. *
  2191. * @param int $invoice_id
  2192. * @author korotkov@ud
  2193. */
  2194. function wp_invoice_mark_as_pending($invoice_id) {
  2195. $post_id = wpi_invoice_id_to_post_id($invoice_id);
  2196. wp_update_post(
  2197. array(
  2198. 'ID' => $post_id,
  2199. 'post_status' => 'pending'
  2200. )
  2201. );
  2202. /** Mark invoice as processed by IPN (used for trashing abandoned SPC transactions) */
  2203. update_post_meta($post_id, 'processed_by_ipn', 'true');
  2204. WPI_Functions::log_event($post_id, 'invoice', 'update', '', __('Pending', WPI));
  2205. }
  2206. /**
  2207. * Determine if invoice is paid full
  2208. *
  2209. * @global object $wpdb
  2210. * @param int $invoice_id
  2211. * @return bool
  2212. * @author korotkov@ud
  2213. */
  2214. function wpi_is_full_paid_invoice($invoice_id) {
  2215. global $wpdb;
  2216. $invoice_obj = new WPI_Invoice();
  2217. $invoice_obj->load_invoice("id={$invoice_id}");
  2218. $object_id = wpi_invoice_id_to_post_id($invoice_id);
  2219. $payment_history = $wpdb->get_results("SELECT * FROM {$wpdb->base_prefix}wpi_object_log WHERE object_id = '{$object_id}' AND action = 'add_payment'", ARRAY_A);
  2220. $paid_amount = 0;
  2221. foreach ($payment_history as $payment) {
  2222. $paid_amount += abs($payment['value']);
  2223. }
  2224. return $paid_amount >= ( $invoice_obj->data['subtotal'] - $invoice_obj->data['total_discount'] );
  2225. }
  2226. /**
  2227. * Formates amount
  2228. *
  2229. * @param number $amount
  2230. * @return string
  2231. */
  2232. function wp_invoice_currency_format($amount) {
  2233. global $wpi_settings;
  2234. $thousands_separator_symbol = !isset( $wpi_settings['thousands_separator_symbol'] )?',':($wpi_settings['thousands_separator_symbol'] == '0'?'':$wpi_settings['thousands_separator_symbol']);
  2235. if ($amount) {
  2236. return number_format($amount, 2, '.', $thousands_separator_symbol);
  2237. } else {
  2238. return $amount;
  2239. }
  2240. }
  2241. /**
  2242. * Emails user after payment is done
  2243. *
  2244. * @param array $invoice
  2245. * @author korotkov@ud
  2246. * @refactoring odokienko@UD
  2247. */
  2248. function wp_invoice_send_email_receipt($invoice,$notification_data) {
  2249. global $wpi_settings;
  2250. $subject = sprintf(__("Invoice #%s has been paid", WPI), $notification_data['invoice_id']);
  2251. $headers = array(
  2252. "From: {$notification_data['business_name']} <{$notification_data['from']}>\r\n",
  2253. "Content-Type: text/html"
  2254. );
  2255. $message = sprintf(
  2256. __("Dear %1s,<br>%2s has received your payment for the invoice.<br><br>You can overview invoice status and payment history by clicking this link:<br>%3s<br><br>Thank you very much for your patronage.<br><br>Best regards,<br>%4s (%5s)", WPI),
  2257. $notification_data['user_name'],
  2258. $notification_data['business_name'],
  2259. $notification_data['permalink'],
  2260. $notification_data['business_name'],
  2261. $notification_data['from']
  2262. );
  2263. /**
  2264. * @todo add condition witch will be look at this option odokienko@UD */
  2265. if ( function_exists('wp_crm_send_notification') && !empty($wpi_settings['use_wp_crm_to_send_notifications']) && $wpi_settings['use_wp_crm_to_send_notifications'] == 'true') {
  2266. wp_crm_send_notification( 'wpi_send_thank_you_email', $notification_data );
  2267. //** Add message to user activity stream */
  2268. wp_crm_add_to_user_log( $notification_data['user_id'], sprintf(__("WP-Invoice: Message with subject '%1s' was sent", WPI),$subject),false,array('attribute'=>'wpi_notification'));
  2269. }else{
  2270. $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
  2271. $subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8');
  2272. if (wp_mail("{$notification_data['user_name']} <{$notification_data['user_email']}>", $subject, $message, implode("\r\n",(array)$headers) . "\r\n")) {
  2273. WPI_Functions::log_event($notification_data['invoice_id'], 'invoice', 'emailed', '', __('Receipt eMailed', WPI));
  2274. }
  2275. }
  2276. return $message;
  2277. }
  2278. /**
  2279. * Emails merchant after payment is done
  2280. *
  2281. * @global array $wpi_settings
  2282. * @param array $invoice
  2283. * @author korotkov@UD
  2284. *
  2285. * @refactoring odokienko@UD
  2286. */
  2287. function wp_invoice_send_me_notification($invoice,$notification_data) {
  2288. global $wpi_settings;
  2289. $headers = array(
  2290. "From: {$notification_data['business_name']} <{$notification_data['from']}>\r\n",
  2291. "Content-Type: text/html"
  2292. );
  2293. $subject = sprintf(__("Invoice #%s has been paid", WPI), $notification_data['invoice_id']);
  2294. $message = sprintf(
  2295. __("%1s has paid invoice #%2s.<br><br>%3s<br>Total payments: %4s %5s of %6s %7s.<br><br>You can overview invoice status and payment history by clicking this link:<br>%8s<br><br>User information:<br><br>ID: %9s<br>Name: %10s<br>Email: %11s<br><br>--------------------<br>%12s", WPI),
  2296. $notification_data['user_name'],
  2297. $notification_data['invoice_id'],
  2298. $notification_data['invoice_title'],
  2299. $notification_data['default_currency_code'],
  2300. $notification_data['total_payments'],
  2301. $notification_data['default_currency_code'],
  2302. $notification_data['total'],
  2303. $notification_data['permalink'],
  2304. $notification_data['user_id'],
  2305. $notification_data['user_name'],
  2306. $notification_data['user_email'],
  2307. $notification_data['site']
  2308. );
  2309. if ( function_exists('wp_crm_send_notification') && !empty($wpi_settings['use_wp_crm_to_send_notifications']) && $wpi_settings['use_wp_crm_to_send_notifications'] == 'true') {
  2310. wp_crm_send_notification( 'wpi_cc_thank_you_email', $notification_data );
  2311. //** Add message to user activity stream */
  2312. wp_crm_add_to_user_log( $notification_data['admin_id'], sprintf(__("WP-Invoice: Message with subject '%1s' was sent", WPI),$subject),false,array('attribute'=>'wpi_notification'));
  2313. }else{
  2314. $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
  2315. $subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8');
  2316. wp_mail("{$notification_data['admin_name']} <{$notification_data['admin_email']}>", $subject, $message, implode("\r\n",(array)$headers) . "\r\n");
  2317. }
  2318. }
  2319. /**
  2320. * Sends notification to invoice creator
  2321. *
  2322. * @global array $wpi_settings
  2323. * @param array $invoice
  2324. * @author korotkov@UD
  2325. *
  2326. * @refactoring odokienko@UD
  2327. */
  2328. function wp_invoice_send_creator_notification($invoice,$notification_data) {
  2329. global $wpi_settings;
  2330. $headers = array(
  2331. "From: {$notification_data['business_name']} <{$notification_data['from']}>\r\n",
  2332. "Content-Type: text/html"
  2333. );
  2334. $subject = sprintf(__("Invoice #%s has been paid", WPI), $notification_data['invoice_id']);
  2335. $message = sprintf(
  2336. __("Hello %1s,<br><br>%2s has paid invoice #%3s.<br><br>%4s<br>Total payments: %5s %6s of %7s %8s.<br><br>You can overview invoice status and payment history by clicking this link:<br>%9s<br><br>User information:<br><br>ID: %10s<br>Name: %11s<br>Email: %12s<br><br>--------------------<br>%13s", WPI),
  2337. $notification_data['creator_name'],
  2338. $notification_data['user_name'],
  2339. $notification_data['invoice_id'],
  2340. $notification_data['invoice_title'],
  2341. $notification_data['default_currency_code'],
  2342. $notification_data['total_payments'],
  2343. $notification_data['default_currency_code'],
  2344. $notification_data['total'],
  2345. $notification_data['permalink'],
  2346. $notification_data['user_id'],
  2347. $notification_data['user_name'],
  2348. $notification_data['user_email'],
  2349. $notification_data['site']
  2350. );
  2351. if ( function_exists('wp_crm_send_notification') && !empty($wpi_settings['use_wp_crm_to_send_notifications']) && $wpi_settings['use_wp_crm_to_send_notifications'] == 'true' ) {
  2352. wp_crm_send_notification( 'wpi_send_invoice_creator_email', $notification_data );
  2353. //** Add message to user activity stream */
  2354. wp_crm_add_to_user_log( $notification_data['creator_id'], sprintf(__("WP-Invoice: Message with subject '%1s' was sent", WPI),$subject),false,array('attribute'=>'wpi_notification'));
  2355. }else{
  2356. $message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
  2357. $subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8');
  2358. wp_mail("{$notification_data['creator_name']} <{$notification_data['creator_email']}>", $subject, $message, implode("\r\n",(array)$headers) . "\r\n");
  2359. }
  2360. }
  2361. /**
  2362. * Sends required notifications
  2363. * @global array $wpi_settings
  2364. * @param array $invoice
  2365. * @author korotkov@UD
  2366. * @refactoring odokienko@UD
  2367. */
  2368. function send_notification( $invoice ) {
  2369. global $wpi_settings;
  2370. if ( (!empty($wpi_settings['send_thank_you_email']) && $wpi_settings['send_thank_you_email'] == 'true') ||
  2371. (!empty($wpi_settings['cc_thank_you_email']) && $wpi_settings['cc_thank_you_email'] == 'true') ||
  2372. (!empty($wpi_settings['send_invoice_creator_email']) && $wpi_settings['send_invoice_creator_email'] == 'true' ) ) {
  2373. $paid_invoice = new WPI_Invoice();
  2374. $paid_invoice->load_invoice("id={$invoice['invoice_id']}");
  2375. $invoice = $paid_invoice->data;
  2376. $notification_data['invoice_id'] = (!empty($invoice['custom_id'])) ? $invoice['custom_id'] : $invoice['invoice_id'];
  2377. $notification_data['invoice_title']= $invoice['post_title'];
  2378. $notification_data['from'] = stripslashes(get_option('admin_email'));
  2379. $notification_data['permalink'] = get_invoice_permalink($invoice['invoice_id']);
  2380. $notification_data['business_name'] = $wpi_settings['business_name'];
  2381. $notification_data['site'] = stripslashes($wpi_settings['business_name']);
  2382. $notification_data['user_email'] = $invoice['user_data']['user_email'];
  2383. $notification_data['user_name'] = wpi_get_user_display_name($invoice);
  2384. $notification_data['user_id'] = $invoice['user_data']['ID'];
  2385. $admin = get_user_by_email(get_option('admin_email'));
  2386. $notification_data['admin_email'] = stripslashes( $admin->user_email );
  2387. $notification_data['admin_id'] = $admin->ID;
  2388. $notification_data['admin_name'] = stripslashes( $admin->display_name );
  2389. $creator = get_userdata( $invoice['post_author'] );
  2390. $notification_data['creator_email']= stripslashes( $creator->user_email );
  2391. $notification_data['creator_name'] = stripslashes( $creator->display_name );
  2392. $notification_data['creator_id'] = $creator->ID ;
  2393. $notification_data['total'] = $invoice['subtotal'] - $invoice['total_discount'] + $invoice['total_tax'];
  2394. $notification_data['default_currency_code'] = $invoice['default_currency_code'];
  2395. $notification_data['total_payments'] = $invoice['total_payments'];
  2396. //** If we are going to change our Mail From */
  2397. if ( !empty($wpi_settings['change_mail_from']) && $wpi_settings['change_mail_from'] == 'true' ) {
  2398. add_filter('wp_mail_from', array('WPI_Functions', 'notification_mail_from'));
  2399. add_filter('wp_mail_from_name', array('WPI_Functions', 'notification_mail_from_name'));
  2400. }
  2401. /** Email client */
  2402. if (!empty($wpi_settings['send_thank_you_email']) && $wpi_settings['send_thank_you_email'] == 'true') {
  2403. wp_invoice_send_email_receipt($invoice,$notification_data);
  2404. }
  2405. /** Email site admin */
  2406. if (!empty($wpi_settings['cc_thank_you_email']) && $wpi_settings['cc_thank_you_email'] == 'true') {
  2407. wp_invoice_send_me_notification($invoice,$notification_data);
  2408. }
  2409. /** Email invoice creator */
  2410. if ( !empty( $wpi_settings['send_invoice_creator_email'] ) && $wpi_settings['send_invoice_creator_email'] == 'true' ){
  2411. wp_invoice_send_creator_notification($invoice,$notification_data);
  2412. }
  2413. remove_filter('wp_mail_from', array('WPI_Functions', 'notification_mail_from'));
  2414. remove_filter('wp_mail_from_name', array('WPI_Functions', 'notification_mail_from_name'));
  2415. }
  2416. }
  2417. /**
  2418. * Returns display_name from invoice if exists or from userdata
  2419. *
  2420. * @param type $invoice
  2421. * @return type
  2422. * @author korotkov@ud
  2423. */
  2424. function wpi_get_user_display_name( $invoice ) {
  2425. /** If display name exists, return it */
  2426. if ( !empty( $invoice['user_data']['display_name'] ) ) {
  2427. return $invoice['user_data']['display_name'];
  2428. }
  2429. /** Get current user data and return it */
  2430. $user = get_userdata( $invoice['user_data']['ID'] );
  2431. return $user->display_name;
  2432. }
  2433. /**
  2434. * This function checks to see if a plugin is installed
  2435. * @param string $slug The class name of the plugin
  2436. * @return bool Whether or not its installed
  2437. * @since 3.0
  2438. */
  2439. function wpi_feature_installed($slug) {
  2440. global $wpi_settings;
  2441. if (is_array($wpi_settings['installed_features'][$slug]) && !$wpi_settings['installed_features'][$slug]['disabled']) {
  2442. return true;
  2443. }
  2444. return false;
  2445. }
  2446. /**
  2447. * Shows business information on front-end
  2448. */
  2449. function wp_invoice_show_business_information() {
  2450. $core = WPI_Core::getInstance();
  2451. $business_info['name'] = $core->Settings->options['business_name'];
  2452. $business_info['address'] = $core->Settings->options['business_address'];
  2453. $business_info['phone'] = $core->Settings->options['business_phone'];
  2454. ?>
  2455. <div id="invoice_business_info" class="clearfix">
  2456. <p class="invoice_page_subheading"><strong>Bill From:</strong></p>
  2457. <p class="wp_invoice_bi wp_invoice_business_name"><?php echo $business_info['name']; ?></p>
  2458. <p class="wp_invoice_bi wp_invoice_business_address"><?php echo $business_info['address']; ?></p>
  2459. <p class="wp_invoice_bi wp_invoice_business_phone"><?php echo $business_info['phone']; ?></p>
  2460. </div>
  2461. <?php
  2462. }
  2463. /**
  2464. * Returns due date in format
  2465. *
  2466. * @param type $invoice
  2467. * @return type
  2468. * @author korotkov@ud
  2469. */
  2470. function get_due_date( $invoice ) {
  2471. if ( !empty( $invoice['due_date_year'] ) && !empty( $invoice['due_date_month'] ) && !empty( $invoice['due_date_day'] ) ) {
  2472. return date( get_option('date_format'), strtotime( $invoice['due_date_day'].'-'.$invoice['due_date_month'].'-'.$invoice['due_date_year'] ) );
  2473. }
  2474. return false;
  2475. }
  2476. /**
  2477. * Find a full diff between two arrays.
  2478. *
  2479. * @param array $array1
  2480. * @param array $array2
  2481. * @return array
  2482. * @author korotkov@ud
  2483. */
  2484. function wpi_multi_array_diff($array1, $array2) {
  2485. $ret = array();
  2486. foreach ($array1 as $k => $v) {
  2487. if (!isset($array2[$k])) $ret[$k] = $v;
  2488. else if (is_array($v) && is_array($array2[$k])) {
  2489. $u = wpi_multi_array_diff($v, $array2[$k]);
  2490. if ( !empty($u) ) {
  2491. $ret[$k] = $u;
  2492. }
  2493. } else if ( $v != $array2[$k] ) {
  2494. $ret[$k] = $v;
  2495. }
  2496. }
  2497. return $ret;
  2498. }
  2499. /**
  2500. * @author korotkov@ud
  2501. * @param array $data
  2502. * <b>Example:</b><br>
  2503. * <pre>
  2504. * array(
  2505. * 'venue' => 'wpi_authorize',
  2506. * 'amount' => '100.00',
  2507. * 'payer_email' => 'john.smith@gmail.com',
  2508. * 'payer_first_name' => 'John',
  2509. * 'payer_last_name' => 'Smith',
  2510. * 'cc_number' => '411111111111111',
  2511. * 'cc_expiration' => '0412',
  2512. * 'cc_code' => '356',
  2513. * 'items' => array(
  2514. * array(
  2515. * 'name' => 'Name 1',
  2516. * 'description' => 'Item 1',
  2517. * 'quantity' => 1,
  2518. * 'price' => '10.00'
  2519. * ),
  2520. * array(
  2521. * 'name' => 'Name 2',
  2522. * 'description' => 'Item 2',
  2523. * 'quantity' => 2,
  2524. * 'price' => '10.00'
  2525. * )
  2526. * )
  2527. * )
  2528. * </pre>
  2529. * @return array
  2530. */
  2531. function wpi_process_transaction($data) {
  2532. $wpa = new WPI_Payment_Api();
  2533. return $wpa->process_transaction($data);
  2534. }