AntonLV 10 years ago
parent
commit
ebbf205e00
88 changed files with 5322 additions and 27 deletions
  1. 5 0
      .buildpath
  2. 28 0
      .project
  3. 12 0
      .settings/.jsdtscope
  4. 10 0
      .settings/org.eclipse.core.resources.prefs
  5. 3 0
      .settings/org.eclipse.php.core.prefs
  6. 1 0
      .settings/org.eclipse.wst.jsdt.ui.superType.container
  7. 1 0
      .settings/org.eclipse.wst.jsdt.ui.superType.name
  8. 3 1
      install/sql/system.sql
  9. 5 1
      modules/boonex/english/data/langs/system/en.xml
  10. 163 0
      modules/boonex/market_api/classes/BxMarketApiConfig.php
  11. 58 0
      modules/boonex/market_api/classes/BxMarketApiDb.php
  12. 79 0
      modules/boonex/market_api/classes/BxMarketApiFormAdd.php
  13. 111 0
      modules/boonex/market_api/classes/BxMarketApiFormAddClients.php
  14. 93 0
      modules/boonex/market_api/classes/BxMarketApiFormPass.php
  15. 195 0
      modules/boonex/market_api/classes/BxMarketApiGridKands.php
  16. 69 0
      modules/boonex/market_api/classes/BxMarketApiGridKandsClients.php
  17. 828 0
      modules/boonex/market_api/classes/BxMarketApiModule.php
  18. 23 0
      modules/boonex/market_api/classes/BxMarketApiPageKands.php
  19. 67 0
      modules/boonex/market_api/classes/BxMarketApiResponse.php
  20. 34 0
      modules/boonex/market_api/classes/BxMarketApiTemplate.php
  21. 75 0
      modules/boonex/market_api/install/config.php
  22. 20 0
      modules/boonex/market_api/install/installer.php
  23. 26 0
      modules/boonex/market_api/install/langs/en.xml
  24. 37 0
      modules/boonex/market_api/install/sql/disable.sql
  25. 91 0
      modules/boonex/market_api/install/sql/enable.sql
  26. 25 0
      modules/boonex/market_api/install/sql/install.sql
  27. 11 0
      modules/boonex/market_api/install/sql/uninstall.sql
  28. 21 0
      modules/boonex/market_api/js/kands.js
  29. 18 0
      modules/boonex/market_api/request.php
  30. 3 0
      modules/boonex/market_api/template/css/kands.css
  31. BIN
      modules/boonex/market_api/template/images/icons/std-mi.png
  32. BIN
      modules/boonex/market_api/template/images/icons/std-pi.png
  33. BIN
      modules/boonex/market_api/template/images/icons/std-si.png
  34. BIN
      modules/boonex/market_api/template/images/icons/std-wi.png
  35. 15 0
      modules/boonex/market_api/template/kands_add_form.html
  36. 17 0
      modules/boonex/market_api/template/kands_add_form_clients.html
  37. 17 0
      modules/boonex/market_api/template/kands_pass_form.html
  38. 19 0
      modules/boonex/paypal_payflow/classes/BxPfwCart.php
  39. 215 0
      modules/boonex/paypal_payflow/classes/BxPfwConfig.php
  40. 143 0
      modules/boonex/paypal_payflow/classes/BxPfwDb.php
  41. 19 0
      modules/boonex/paypal_payflow/classes/BxPfwDetails.php
  42. 134 0
      modules/boonex/paypal_payflow/classes/BxPfwExpressCheckout.php
  43. 65 0
      modules/boonex/paypal_payflow/classes/BxPfwHostedCheckout.php
  44. 88 0
      modules/boonex/paypal_payflow/classes/BxPfwLog.php
  45. 312 0
      modules/boonex/paypal_payflow/classes/BxPfwModule.php
  46. 19 0
      modules/boonex/paypal_payflow/classes/BxPfwOrders.php
  47. 32 0
      modules/boonex/paypal_payflow/classes/BxPfwPageCart.php
  48. 27 0
      modules/boonex/paypal_payflow/classes/BxPfwPageDetails.php
  49. 29 0
      modules/boonex/paypal_payflow/classes/BxPfwPageHistory.php
  50. 32 0
      modules/boonex/paypal_payflow/classes/BxPfwPageOrders.php
  51. 169 0
      modules/boonex/paypal_payflow/classes/BxPfwPayPal.php
  52. 15 0
      modules/boonex/paypal_payflow/classes/BxPfwProfileFields.php
  53. 22 0
      modules/boonex/paypal_payflow/classes/BxPfwProvider.php
  54. 258 0
      modules/boonex/paypal_payflow/classes/BxPfwRecurringBilling.php
  55. 53 0
      modules/boonex/paypal_payflow/classes/BxPfwSecureToken.php
  56. 268 0
      modules/boonex/paypal_payflow/classes/BxPfwTemplate.php
  57. 70 0
      modules/boonex/paypal_payflow/install/config.php
  58. 25 0
      modules/boonex/paypal_payflow/install/installer.php
  59. 164 0
      modules/boonex/paypal_payflow/install/langs/en.php
  60. 164 0
      modules/boonex/paypal_payflow/install/langs/ru.php
  61. 214 0
      modules/boonex/paypal_payflow/install/sql/install.sql
  62. 47 0
      modules/boonex/paypal_payflow/install/sql/uninstall.sql
  63. 7 0
      modules/boonex/paypal_payflow/js/_cart.js
  64. 32 0
      modules/boonex/paypal_payflow/js/_orders.js
  65. 4 0
      modules/boonex/paypal_payflow/log/.gitignore
  66. 12 0
      modules/boonex/paypal_payflow/request.php
  67. 27 0
      modules/boonex/paypal_payflow/template/css/_cart.css
  68. 11 0
      modules/boonex/paypal_payflow/template/css/_orders.css
  69. 35 0
      modules/boonex/paypal_payflow/template/ec_confirm.html
  70. 1 0
      modules/boonex/paypal_payflow/template/hc_credit_card.html
  71. 20 0
      modules/boonex/paypal_payflow/template/history_orders.html
  72. 14 0
      modules/boonex/paypal_payflow/template/history_orders_block.html
  73. BIN
      modules/boonex/paypal_payflow/template/images/icons/std-mi.png
  74. BIN
      modules/boonex/paypal_payflow/template/images/icons/std-pi.png
  75. BIN
      modules/boonex/paypal_payflow/template/images/icons/std-si.png
  76. BIN
      modules/boonex/paypal_payflow/template/images/icons/std-wi.png
  77. 21 0
      modules/boonex/paypal_payflow/template/rb_processed_order.html
  78. 6 2
      modules/boonex/russian/data/langs/system/ru.xml
  79. BIN
      phing.phar
  80. BIN
      plugins/ffmpeg/ffmpeg_win.exe
  81. 132 0
      pricing.html
  82. 35 0
      print.php
  83. 8 11
      studio/classes/BxDolStudioDesigner.php
  84. 9 0
      studio/classes/BxDolStudioDesignerQuery.php
  85. 9 6
      studio/js/designer.js
  86. 15 6
      studio/template/scripts/BxBaseStudioDesigner.php
  87. 40 0
      template/splash.bkp.html
  88. 117 0
      test.php

+ 5 - 0
.buildpath

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<buildpath>
+	<buildpathentry kind="src" path=""/>
+	<buildpathentry kind="con" path="org.eclipse.php.core.LANGUAGE"/>
+</buildpath>

+ 28 - 0
.project

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dolphin</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.dltk.core.scriptbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.php.core.PHPNature</nature>
+		<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
+	</natures>
+</projectDescription>

+ 12 - 0
.settings/.jsdtscope

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path=""/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
+		<attributes>
+			<attribute name="hide" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
+	<classpathentry kind="output" path=""/>
+</classpath>

+ 10 - 0
.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,10 @@
+#Mon Jun 27 11:24:30 GMT+03:00 2016
+eclipse.preferences.version=1
+encoding//modules/boonex/air/data/template/system/_header.html=UTF-8
+encoding//modules/boonex/ocean/data/template/system/_header.html=UTF-8
+encoding//studio/template/_header.html=UTF-8
+encoding//studio/template/page_2.html=UTF-8
+encoding//template/_header.html=UTF-8
+encoding//template/page_150.html=UTF-8
+encoding//template/page_2.html=UTF-8
+encoding//template/page_44.html=UTF-8

+ 3 - 0
.settings/org.eclipse.php.core.prefs

@@ -0,0 +1,3 @@
+#Fri Dec 18 11:01:21 GMT+03:00 2015
+eclipse.preferences.version=1
+include_path=0;/trident.me

+ 1 - 0
.settings/org.eclipse.wst.jsdt.ui.superType.container

@@ -0,0 +1 @@
+org.eclipse.wst.jsdt.launching.baseBrowserLibrary

+ 1 - 0
.settings/org.eclipse.wst.jsdt.ui.superType.name

@@ -0,0 +1 @@
+Window

+ 3 - 1
install/sql/system.sql

@@ -1031,7 +1031,9 @@ CREATE TABLE `sys_injections` (
 
 INSERT INTO `sys_injections`(`name`, `page_index`, `key`, `type`, `data`, `replace`, `active`) VALUES
 ('update_cache', 150, 'injection_head', 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:17:"get_cache_updater";s:6:"params";a:0:{}s:5:"class";s:19:"TemplStudioLauncher";}', 0, 1),
-('live_updates', 0, 'injection_head', 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:4:"init";s:6:"params";a:0:{}s:5:"class";s:24:"TemplLiveUpdatesServices";}', 0, 1);
+('live_updates', 0, 'injection_head', 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:4:"init";s:6:"params";a:0:{}s:5:"class";s:24:"TemplLiveUpdatesServices";}', 0, 1),
+('sys_head', 0, 'injection_head', 'text', '', 0, 1),
+('sys_body', 0, 'injection_footer', 'text', '', 0, 1);
 
 -- --------------------------------------------------------
 

+ 5 - 1
modules/boonex/english/data/langs/system/en.xml

@@ -587,7 +587,7 @@
 	<string name="_adm_dsg_err_remove_old_icon"><![CDATA[Cannot remove current icon.]]></string>
 	<string name="_adm_dsg_err_remove_old_logo"><![CDATA[Cannot remove current logo image.]]></string>
 	<string name="_adm_dsg_err_save"><![CDATA[Cannot save: ]]></string>
-	<string name="_adm_dsg_err_save_file"><![CDATA[Cannot save changes in the file.]]></string>
+	<string name="_adm_dsg_err_save_changes"><![CDATA[Cannot save changes.]]></string>
 	<string name="_adm_dsg_scs_save"><![CDATA[The changes were successfully saved]]></string>
 	<string name="_adm_dsg_txt_alt_text"><![CDATA[Logo Text]]></string>
 	<string name="_adm_dsg_txt_cover_common"><![CDATA[Default pages cover]]></string>
@@ -601,6 +601,10 @@
 	<string name="_adm_dsg_txt_upload_icon"><![CDATA[Upload New Site Icon]]></string>
 	<string name="_adm_dsg_txt_upload_image"><![CDATA[Upload New Logo Image]]></string>
 	<string name="_adm_dsg_txt_upload_image_preview"><![CDATA[Current Logo Image]]></string>
+	<string name="_adm_dsg_txt_inj_head"><![CDATA[&lt;HEAD&gt; injection]]></string>
+	<string name="_adm_dsg_txt_inj_head_inf"><![CDATA[Code to insert before closing HEAD tag]]></string>
+	<string name="_adm_dsg_txt_inj_body"><![CDATA[&lt;BODY&gt; injection]]></string>
+	<string name="_adm_dsg_txt_inj_body_inf"><![CDATA[Code to insert before closing BODY tag]]></string>
 	<string name="_adm_err_cannot_process_action"><![CDATA[Cannot process action. It does not have an associated handler.]]></string>
 	<string name="_adm_err_form_view_iu_delete"><![CDATA[Cannot remove current image.]]></string>
 	<string name="_adm_err_form_view_iu_save"><![CDATA[Cannot save: ]]></string>

+ 163 - 0
modules/boonex/market_api/classes/BxMarketApiConfig.php

@@ -0,0 +1,163 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxMarketApiConfig extends BxBaseModGeneralConfig
+{
+	protected $_aHtmlIds;
+	
+    protected $_sAnimationEffect;
+    protected $_iAnimationSpeed;
+
+    protected $_sSessionKeysPrefix;
+
+	protected $_aCurrency;
+	protected $_aMarket;
+
+	protected $_iMainSellerId;
+	protected $_sMainSellerSbsProvider;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'file-text col-red3',
+
+        	// database tables
+            'TABLE_LICENSES' => $aModule['db_prefix'] . 'licenses',
+
+        	// database fields
+            'FIELD_ID' => 'id',
+        	'FIELD_LICENSE_ID' => 'license_id',
+            'FIELD_PROFILE_ID' => 'profile_id',
+        	'FIELD_TYPE' => 'type',
+
+        	// page URIs
+            'URL_KANDS_MANAGE' => 'page.php?i=kands-manage',
+
+        	// some params
+            'PARAM_PLAIN_DESCRIPTION_CHARS' => 'bx_market_api_plain_description_chars',
+
+			// objects
+			'OBJECT_GRID_KANDS' => 'bx_market_api_kands',
+        	'OBJECT_GRID_KANDS_CLIENTS' => 'bx_market_api_kands_clients',
+
+        	// 'OAuth' module
+        	'OAUTH' => 'bx_oauth',
+
+        	// Parent 'Market' module 
+        	'MARKET' => 'bx_market',
+
+        	// Parent 'Market' database fields
+            'MFIELD_ID' => 'id',
+            'MFIELD_AUTHOR' => 'author',
+            'MFIELD_ADDED' => 'added',
+            'MFIELD_CHANGED' => 'changed',
+        	'MFIELD_NAME' => 'name',
+            'MFIELD_TITLE' => 'title',
+            'MFIELD_TEXT' => 'text',
+        	'MFIELD_CATEGORY' => 'cat',
+        	'MFIELD_PRICE_SINGLE' => 'price_single',
+        	'MFIELD_PRICE_RECURRING' => 'price_recurring',
+        	'MFIELD_DURATION_RECURRING' => 'duration_recurring',
+        	'MFIELD_THUMB' => 'thumb',
+        	'MFIELD_PACKAGE' => 'package',
+        	'MFIELD_COMMENTS' => 'comments',
+	        'MFIELD_VIEWS' => 'views',
+	        'MFIELD_VOTES' => 'votes',
+        	'MFIELD_RATE' => 'rate',
+        	'MFIELD_FILE_VERSION' => 'version',
+
+	        // Parent 'Market' objects
+	        'MOBJECT_VOTES' => 'bx_market',
+        	'MOBJECT_CATEGORY' => 'bx_market_cats',
+
+	        // page URIs
+        	'MURL_VIEW_ENTRY' => 'page.php?i=view-product&id=',
+        );
+
+        $this->_aJsClasses = array(
+        	'kands' => 'BxMarketApiKands',
+        );
+
+        $this->_aJsObjects = array(
+        	'kands' => 'oBxMarketApiKands',
+        );
+
+        $this->_aHtmlIds = array(
+        	'field_user' => 'bx-market-api-user',
+        	'field_user_id' => 'bx-market-api-user-id'
+        );
+
+		$this->_sAnimationEffect = 'fade';
+        $this->_iAnimationSpeed = 'slow';
+
+        $this->_sSessionKeysPrefix = 'bx_uno_';
+
+        $oPayments = BxDolPayments::getInstance();
+        $this->_aCurrency = array(
+        	'code' => $oPayments->getOption('default_currency_code'),
+        	'sign' => $oPayments->getOption('default_currency_sign')
+        );
+
+        $this->_aMarket = BxDolModuleQuery::getInstance()->getModuleByName($this->CNF['MARKET']);
+
+        $this->_iMainSellerId = 2;
+        $this->_sMainSellerSbsProvider = 'chargebee';
+    }
+
+	public function getHtmlIds($sKey = '')
+    {
+        if(empty($sKey))
+            return $this->_aHtmlIds;
+
+        return isset($this->_aHtmlIds[$sKey]) ? $this->_aHtmlIds[$sKey] : '';
+    }
+
+    public function getAnimationEffect()
+    {
+        return $this->_sAnimationEffect;
+    }
+
+    public function getAnimationSpeed()
+    {
+        return $this->_iAnimationSpeed;
+    }
+
+    public function getSessionKeysPrefix()
+    {
+    	return $this->_sSessionKeysPrefix;
+    }
+
+	public function getCurrency()
+    {
+    	return $this->_aCurrency;
+    }
+
+    public function getMarketModuleId()
+    {
+    	return !empty($this->_aMarket) && is_array($this->_aMarket) ? (int)$this->_aMarket['id'] : 0;
+    }
+
+    public function getMarketMainSellerId()
+    {
+    	return $this->_iMainSellerId;
+    }
+
+    public function getMarketMainSellerSbsProvider()
+    {
+    	return $this->_sMainSellerSbsProvider;
+    }
+}
+
+/** @} */

+ 58 - 0
modules/boonex/market_api/classes/BxMarketApiDb.php

@@ -0,0 +1,58 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+/*
+ * Module database queries
+ */
+class BxMarketApiDb extends BxBaseModGeneralDb
+{
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+
+	public function getLicense($aParams = array())
+    {
+    	$CNF = &$this->_oConfig->CNF;
+    	$aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
+
+    	$sJoinClause = $sWhereClause = "";
+        switch($aParams['type']) {
+            case 'license_id_profile_id':
+            	$aMethod['name'] = 'getRow';
+            	$aMethod['params'][1] = array(
+                	'license_id' => $aParams['license_id'],
+            		'profile_id' => $aParams['profile_id']
+                );
+
+                $sWhereClause = " AND `tl`.`" . $CNF['FIELD_LICENSE_ID'] . "`=:license_id AND `tl`.`" . $CNF['FIELD_PROFILE_ID'] . "`=:profile_id";
+                break;
+
+			case 'profile_id_type':
+				$aMethod['params'][1] = array(
+            		'profile_id' => $aParams['profile_id'],
+					'type' => $aParams['type']
+                );
+
+				$sWhereClause = " AND `tl`.`" . $CNF['FIELD_PROFILE_ID'] . "`=:profile_id AND `tl`.`" . $CNF['FIELD_TYPE'] . "`=:type";
+                break;
+        }
+
+        $aMethod['params'][0] = "SELECT
+        		`tl`.*
+            FROM `" . $this->_oConfig->CNF['TABLE_LICENSES'] . "` AS `tl`" . $sJoinClause . "
+            WHERE 1" . $sWhereClause;
+
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+}
+
+/** @} */

+ 79 - 0
modules/boonex/market_api/classes/BxMarketApiFormAdd.php

@@ -0,0 +1,79 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxMarketApiFormAdd extends BxTemplFormView
+{
+    function __construct()
+    {
+        $aCustomForm = array(
+            'form_attrs' => array(
+                'id' => 'bx-market-api-kands-add',
+                'name' => 'bx-market-api-kands-add',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?' . bx_encode_url_params($_GET, array('ids', '_r')),
+                'method' => 'post',
+            ),
+            'params' => array (
+                'db' => array(
+                    'submit_name' => 'client_add',
+                ),
+            ),
+            'inputs' => array(
+                'title' => array(
+                    'type' => 'text',
+                    'name' => 'title',
+                    'caption' => _t('_Title'),
+                    'required' => true,
+                    'checker' => array (
+                        'func' => 'avail',
+                        'error' => _t ('_sys_adm_form_err_required_field'),
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'redirect_uri' => array(
+                    'type' => 'text',
+                    'name' => 'redirect_uri',
+                    'caption' => _t('_bx_market_api_client_url'),
+                    'required' => true,
+                    'checker' => array (
+                        'func' => 'avail',
+                        'error' => _t ('_sys_adm_form_err_required_field'),
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'Submit' => array (
+                    'type' => 'input_set',
+                    0 => array (
+                        'type' => 'submit',
+                        'name' => 'client_add',
+                        'value' => _t('_Submit'),
+                    ),
+                    1 => array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('Close'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-active').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    ),
+                ),
+            ),
+        );
+
+        parent::__construct ($aCustomForm);
+    }
+}
+
+/** @} */

+ 111 - 0
modules/boonex/market_api/classes/BxMarketApiFormAddClients.php

@@ -0,0 +1,111 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxMarketApiFormAddClients extends BxTemplFormView
+{
+	protected $MODULE;
+	protected $_oModule;
+
+    function __construct()
+    {
+    	$this->MODULE = 'bx_market_api';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+        $aCustomForm = array(
+            'form_attrs' => array(
+                'id' => 'bx-market-api-kands-add',
+                'name' => 'bx-market-api-kands-add',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?' . bx_encode_url_params($_GET, array('ids', '_r')),
+                'method' => 'post',
+            ),
+            'params' => array (
+                'db' => array(
+                    'submit_name' => 'client_add',
+                ),
+            ),
+            'inputs' => array(
+            	'user_id' => array(
+                    'type' => 'hidden',
+                    'name' => 'user_id',
+            		'attrs' => array(
+            			'id' => $this->_oModule->_oConfig->getHtmlIds('field_user_id'),
+            		),
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+            	'user' => array(
+                    'type' => 'text',
+                    'name' => 'user',
+                    'caption' => _t('_bx_market_api_user_id'),
+                    'required' => true,
+                	'attrs' => array(
+            			'id' => $this->_oModule->_oConfig->getHtmlIds('field_user'),
+            		),
+                    'checker' => array (
+                        'func' => 'avail',
+                        'error' => _t ('_sys_adm_form_err_required_field'),
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text',
+                    'name' => 'title',
+                    'caption' => _t('_Title'),
+                    'required' => true,
+                    'checker' => array (
+                        'func' => 'avail',
+                        'error' => _t ('_sys_adm_form_err_required_field'),
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'redirect_uri' => array(
+                    'type' => 'text',
+                    'name' => 'redirect_uri',
+                    'caption' => _t('_bx_market_api_client_url'),
+                    'required' => true,
+                    'checker' => array (
+                        'func' => 'avail',
+                        'error' => _t ('_sys_adm_form_err_required_field'),
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'Submit' => array (
+                    'type' => 'input_set',
+                    0 => array (
+                        'type' => 'submit',
+                        'name' => 'client_add',
+                        'value' => _t('_Submit'),
+                    ),
+                    1 => array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('Close'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-active').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    ),
+                ),
+            ),
+        );
+
+        parent::__construct ($aCustomForm);
+    }
+}
+
+/** @} */

+ 93 - 0
modules/boonex/market_api/classes/BxMarketApiFormPass.php

@@ -0,0 +1,93 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxMarketApiFormPass extends BxTemplFormView
+{
+	protected $MODULE;
+	protected $_oModule;
+
+    function __construct($iId)
+    {
+    	$this->MODULE = 'bx_market_api';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+        $aCustomForm = array(
+            'form_attrs' => array(
+                'id' => 'bx-market-api-kands-pass',
+                'name' => 'bx-market-api-kands-pass',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?' . bx_encode_url_params($_GET, array('ids', '_r')),
+                'method' => 'post',
+            ),
+            'params' => array (
+                'db' => array(
+                    'submit_name' => 'client_pass',
+                ),
+            ),
+            'inputs' => array(
+            	'id' => array(
+            		'type' => 'hidden',
+            		'name' => 'id',
+            		'value' => $iId,
+            		'db' => array (
+                        'pass' => 'Int',
+                    ),
+            	),
+            	'user_id' => array(
+                    'type' => 'hidden',
+                    'name' => 'user_id',
+            		'attrs' => array(
+            			'id' => $this->_oModule->_oConfig->getHtmlIds('field_user_id'),
+            		),
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'user' => array(
+                    'type' => 'text',
+                    'name' => 'user',
+                    'caption' => _t('_bx_market_api_user_id'),
+                    'required' => true,
+                	'attrs' => array(
+            			'id' => $this->_oModule->_oConfig->getHtmlIds('field_user'),
+            		),
+                    'checker' => array (
+                        'func' => 'avail',
+                        'error' => _t ('_sys_adm_form_err_required_field'),
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'Submit' => array (
+                    'type' => 'input_set',
+                    0 => array (
+                        'type' => 'submit',
+                        'name' => 'client_pass',
+                        'value' => _t('_Submit'),
+                    ),
+                    1 => array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('Close'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-active').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    ),
+                ),
+            ),
+        );
+
+        parent::__construct ($aCustomForm);
+    }
+}
+
+/** @} */

+ 195 - 0
modules/boonex/market_api/classes/BxMarketApiGridKands.php

@@ -0,0 +1,195 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxMarketApiGridKands extends BxTemplGrid
+{
+	protected static $LENGTH_ID = 10;
+	protected static $LENGTH_SECRET = 32;
+
+	protected $MODULE;
+	protected $_oModule;
+
+	protected $_sFormClass;
+	protected $_sFormClassPass;
+	protected $_sFormTemplate;
+	protected $_iUserId;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_market_api';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+    	if(!$oTemplate)
+			$oTemplate = $this->_oModule->_oTemplate;
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sFormClass = 'FormAdd';
+        $this->_sFormClassPass = 'FormPass';
+        $this->_sFormTemplate = 'kands_add_form.html';
+        $this->_sFormTemplatePass = 'kands_pass_form.html';
+
+        $this->_iUserId = bx_get_logged_profile_id();
+    }
+
+	public function getCode($isDisplayHeader = true)
+    {
+    	return $this->_oTemplate->getJsCode('kands') . parent::getCode($isDisplayHeader);
+    }
+
+    public function performActionAdd()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        bx_import($this->_sFormClass, $this->_oModule->_aModule);
+        $sForm = $this->_oModule->_oConfig->getClassPrefix() . $this->_sFormClass;
+        $oForm = new $sForm();
+        $oForm->initChecker();
+
+        if ($oForm->isSubmittedAndValid()) {
+			$iNewId = BxDolService::call($CNF['OAUTH'], 'add_client', array(array(
+				'title' => $oForm->getCleanValue('title'),
+				'client_id' => strtolower(genRndPwd(self::$LENGTH_ID, false)),
+				'client_secret' => strtolower(genRndPwd(self::$LENGTH_SECRET, false)),
+				'redirect_uri' => $oForm->getCleanValue('redirect_uri'),
+				'scope' => 'market',
+				'parent_id' => $this->_getKeyParentId($oForm), 
+				'user_id' => $this->_getKeyUserId($oForm)
+			)));
+
+            if($iNewId)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iNewId);
+            else
+                $aRes = array('msg' => _t('_error occured'));
+
+            return echoJson($aRes);
+        }
+
+		echoJson(array('popup' => array('html' => BxTemplFunctions::getInstance()->transBox('', $this->_oTemplate->parseHtmlByName($this->_sFormTemplate, array(
+			'js_object' => $this->_oModule->_oConfig->getJsObject('kands'),
+			'form' => $oForm->getCode(),
+			'form_id' => $oForm->aFormAttrs['id'],
+			'object' => $this->_sObject
+		))), 'options' => array('closeOnOuterClick' => false))));
+    }
+
+	public function performActionPass()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId) {
+                echoJson(array());
+                exit;
+            }
+
+            $aIds = array($iId);
+        }
+
+        $iId = $aIds[0];
+
+        bx_import($this->_sFormClassPass, $this->_oModule->_aModule);
+        $sForm = $this->_oModule->_oConfig->getClassPrefix() . $this->_sFormClassPass;
+        $oForm = new $sForm($iId);
+        $oForm->initChecker();
+
+        if ($oForm->isSubmittedAndValid()) {
+        	$aSet = array('user_id' => $oForm->getCleanValue('user_id'));
+        	$aWhere = array('id' => $iId);
+            if(BxDolService::call($CNF['OAUTH'], 'update_clients_by', array($aSet, $aWhere)))
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
+            else
+                $aRes = array('msg' => _t('_error occured'));
+
+            return echoJson($aRes);
+        }
+
+		echoJson(array('popup' => array('html' => BxTemplFunctions::getInstance()->transBox('', $this->_oTemplate->parseHtmlByName($this->_sFormTemplatePass, array(
+			'js_object' => $this->_oModule->_oConfig->getJsObject('kands'),
+			'form' => $oForm->getCode(),
+			'form_id' => $oForm->aFormAttrs['id'],
+			'object' => $this->_sObject
+		))), 'options' => array('closeOnOuterClick' => false))));
+    }
+
+	public function performActionDelete()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        $iAffected = 0;
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            echoJson(array());
+            exit;
+        }
+
+        $aIdsAffected = array ();
+        foreach($aIds as $iId) {
+            if(!BxDolService::call($CNF['OAUTH'], 'delete_clients_by', array(array('id' => $iId))))
+                continue;
+
+            $aIdsAffected[] = $iId;
+            $iAffected++;
+        }
+
+        echoJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t('_error occured')));
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+
+        $this->_oTemplate->addJs(array(
+        	'jquery.form.min.js',
+        	'jquery-ui/jquery.ui.widget.min.js',
+        	'jquery-ui/jquery.ui.menu.min.js',
+        	'jquery-ui/jquery.ui.autocomplete.min.js',
+        	'kands.js'
+        ));
+
+        $this->_oTemplate->addCss(array(
+        	'kands.css'
+        ));
+
+        $oForm = new BxTemplFormView(array());
+        $oForm->addCssJs();
+    }
+
+	protected function _getActionPass($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+		if($this->_oModule->checkAllowedPass($aRow) !== CHECK_ACTION_RESULT_ALLOWED)
+			return '';
+
+    	return $this->_getActionDefault($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+	protected function _getDataArray($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+		$this->_aOptions['source'] = BxDolService::call($CNF['OAUTH'], 'get_clients_by', array(array('type' => 'user_id', 'user_id' => $this->_iUserId)));
+
+        return parent::_getDataArray($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+
+    protected function _getKeyParentId(&$oForm)
+    {
+    	return 0;
+    }
+
+    protected function _getKeyUserId(&$oForm)
+    {
+    	return $this->_iUserId;
+    }
+}
+
+/** @} */

+ 69 - 0
modules/boonex/market_api/classes/BxMarketApiGridKandsClients.php

@@ -0,0 +1,69 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+require_once('BxMarketApiGridKands.php');
+
+class BxMarketApiGridKandsClients extends BxMarketApiGridKands
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_market_api';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+    	if(!$oTemplate)
+			$oTemplate = $this->_oModule->_oTemplate;
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sFormClass = 'FormAddClients';
+        $this->_sFormTemplate = 'kands_add_form_clients.html';
+
+        $this->_iUserId = bx_get_logged_profile_id();
+    }
+
+	protected function _getCellUserId($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$oProfile = BxDolProfile::getInstance($mixedValue);
+        if (!$oProfile) 
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+    	$sProfile = $oProfile->getDisplayName();
+
+        $mixedValue = $this->_oTemplate->parseHtmlByName('bx_a.html', array(
+            'href' => $oProfile->getUrl(),
+            'title' => bx_html_attribute($sProfile),
+            'bx_repeat:attrs' => array(),
+        	'content' => $sProfile
+        ));
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+	protected function _getDataArray($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+		$this->_aOptions['source'] = BxDolService::call($CNF['OAUTH'], 'get_clients_by', array(array('type' => 'parent_id', 'parent_id' => $this->_iUserId)));
+
+        return BxTemplGrid::_getDataArray($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+
+	protected function _getKeyParentId(&$oForm)
+    {
+    	return $this->_iUserId;
+    }
+
+    protected function _getKeyUserId(&$oForm)
+    {
+    	return $oForm->getCleanValue('user_id');
+    }
+}
+
+/** @} */

+ 828 - 0
modules/boonex/market_api/classes/BxMarketApiModule.php

@@ -0,0 +1,828 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+define('BX_MARKET_API_LICENSE_TYPE_NETWORK', 'network');
+
+/**
+ * MarketApi module
+ */
+class BxMarketApiModule extends BxBaseModGeneralModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+	public function actionGetUsers()
+    {
+        $sTerm = bx_get('term');
+
+        $aResult = BxDolService::call('system', 'profiles_search', array($sTerm), 'TemplServiceProfiles');
+
+        echoJson($aResult);
+    }
+
+    public function actionJsonBrowseAll()
+    {
+		$aParams = $this->getBrowseParams();
+
+		$aResults = $this->getBrowseProducts('latest', $aParams);
+		echoJson($aResults);
+    }
+
+	public function actionJsonBrowseFeatured()
+    {
+		$aParams = $this->getBrowseParams();
+		$aParams['value'] = $this->_oConfig->getMarketMainSellerId();
+
+       	$aResults = $this->getBrowseProducts('featured', $aParams);
+       	echoJson($aResults);
+    }
+
+	public function actionJsonBrowseSelected()
+    {
+		$aParams = $this->getBrowseParams();
+		$aParams['selected'] = array();
+		if(bx_get('products') !== false)
+			$aParams['selected'] = unserialize(base64_decode(bx_get('products')));
+
+		$aResults = $this->getBrowseProducts('selected', $aParams);
+		echoJson($aResults);
+    }
+
+	public function actionJsonBrowsePurchased()
+    {
+		$aParams = $this->getBrowseParams();
+		$aParams['short'] = true;
+		if(empty($aParams['client']) && !empty($aParams['key']))
+			$aParams['client'] = $this->_getClientByKey($aParams['key']);
+
+		if(empty($aParams['client']))
+			return echoJson(array());
+
+		$aResults = $this->getBrowseProducts('purchased', $aParams);
+		echoJson($aResults);
+    }
+
+	public function actionJsonBrowseByCategory()
+    {
+		$aParams = $this->getBrowseParams();
+
+        $aCatName2Id = array(
+        	'templates' => 36, 
+        	'translations' => 38
+        );
+
+        $aCategories = array_keys(BxDolFormQuery::getDataItems($this->_oConfig->CNF['MOBJECT_CATEGORY']));
+        foreach($aCatName2Id as $sCat => $iId)
+        	unset($aCategories[$iId]);
+
+        $aCatName2Id['extensions'] = $aCategories;
+
+		$aParams['value'] = $aCatName2Id[$aParams['value']];
+		$aResults = $this->getBrowseProducts('category', $aParams);
+		echoJson($aResults);
+    }
+
+	public function actionJsonBrowseByTag()
+    {
+		$aParams = $this->getBrowseParams();
+
+		$aResults = $this->getBrowseProducts('tag', $aParams);
+		echoJson($aResults);
+    }
+
+	public function actionJsonBrowseByVendor()
+    {
+		$aParams = $this->getBrowseParams();
+
+        $aResults = $this->getBrowseProducts('vendor', $aParams);
+        echoJson($aResults);
+    }
+
+	public function actionJsonBrowseByKeyword()
+    {
+		$aParams = $this->getBrowseParams();
+
+		$aResults = $this->getBrowseProducts('keyword', $aParams);
+		echoJson($aResults);
+    }
+	public function actionJsonBrowseUpdates()
+    {
+		$aResults = array(); 
+
+		$sProducts = bx_get('products') !== false ? bx_get('products') : '';
+		if(empty($sProducts))
+			return echoJson($aResults);
+
+		$aProducts = unserialize(base64_decode($sProducts));
+
+		$aParams = $this->getBrowseParams();
+		if(empty($aParams['key']))
+			return echoJson($aResults);
+
+		if(empty($aParams['client']))
+			$aParams['client'] = $this->_getClientByKey($aParams['key']);
+
+		$aResults = $this->getBrowseUpdates($aProducts, $aParams, false);
+		echoJson($aResults);
+    }
+
+	public function actionJsonGetProductById()
+    {
+		$aResults = $this->getEntryBy('id');
+		echoJson($aResults);
+    }
+	public function actionJsonGetProductByUri()
+    {
+    	//TODO: check where it's used. Need to recreated because there is no URI anymore.
+		echoJson(array());
+    }
+	public function actionJsonGetProductByName()
+    {
+		$aResults = $this->getEntryBy('name');
+		echoJson($aResults);
+    }
+	public function actionJsonDownloadUpdate()
+    {
+		$aResults = array();
+
+		$sKey = bx_get('key') !== false ? bx_get('key') : '';
+		$sProduct = bx_get('product') !== false ? bx_get('product') : '';
+		if(empty($sProduct))
+			return echoJson($aResults);
+
+		$aProduct = unserialize(base64_decode($sProduct));
+
+		$iClient = $this->_getClientByKey($sKey);
+		if(empty($iClient))
+			return echoJson($aResults);
+
+		$aUpdate = $this->_getUpdate($iClient, $aProduct, $sKey, false);
+		if(empty($aUpdate) || empty($aUpdate['file_id']))
+			return echoJson($aResults);
+
+		$aResults = $this->_getFile((int)$aUpdate['file_id']);
+		echoJson($aResults);
+    }
+
+	public function actionPurchase($iVendor)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+    	$sSessionKeysPrefix = $this->_oConfig->getSessionKeysPrefix();
+
+		if(empty($iVendor) || bx_get('products') === false)
+			$this->_oTemplate->displayNoData('Wrong incomming paramaters. Please report.');
+
+		$iModule = $this->_oConfig->getMarketModuleId();
+
+		$bUser = isLogged();
+		$sSid = bx_process_input(bx_get('sid'));
+
+		$oSession = BxDolSession::getInstance();
+		if($bUser && $oSession->isValue($sSessionKeysPrefix . 'sid') && $oSession->getValue($sSessionKeysPrefix . 'sid') != $sSid) {
+			$bUser = false;
+			bx_logout();
+		}
+
+		$sProducts = bx_process_input(bx_get('products'));
+		$aProducts = explode(',', base64_decode($sProducts));
+
+		$aPurchases = array();
+		foreach($aProducts as $iProduct) {
+			$aProduct = BxDolService::call($CNF['MARKET'], 'get_entries_by', array(array('type' => 'id', 'value' => $iProduct)));
+
+			$aPurchases[] = array($iModule, $aProduct[$CNF['MFIELD_AUTHOR']], $aProduct[$CNF['MFIELD_ID']], 1);
+		}
+
+		$oSession->setValue($sSessionKeysPrefix . 'sid', $sSid);
+		$oSession->setValue($sSessionKeysPrefix . 'purchase', serialize($aPurchases));
+		$oSession->setValue($sSessionKeysPrefix . 'vendor', $iVendor);
+		if(isset($_SERVER["HTTP_REFERER"]))
+			$oSession->setValue($sSessionKeysPrefix . 'referer', bx_append_url_params($_SERVER["HTTP_REFERER"], array('vendor' => $iVendor, 'products' => $sProducts)));
+
+		if($bUser)
+			$this->addToCart();
+
+		header('Location: ' . BX_DOL_URL_ROOT . 'page/login');
+		exit;
+    }
+
+    public function actionSubscribe($iVendor, $iItem)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+    	$sSessionKeysPrefix = $this->_oConfig->getSessionKeysPrefix();
+
+    	if(empty($iVendor) || empty($iItem))
+			$this->_oTemplate->displayNoData('Wrong incomming paramaters. Please report.');
+
+		$iModule = $this->_oConfig->getMarketModuleId();
+
+		$bUser = isLogged();
+		$sSid = bx_process_input(bx_get('sid'));
+
+		$oSession = BxDolSession::getInstance();
+		if($bUser && $oSession->isValue($sSessionKeysPrefix . 'sid') && $oSession->getValue($sSessionKeysPrefix . 'sid') != $sSid) {
+			$bUser = false;
+			bx_logout();
+		}
+
+		$oSession->setValue($sSessionKeysPrefix . 'sid', $sSid);
+		$oSession->setValue($sSessionKeysPrefix . 'subscribe', serialize(array($iModule, $iVendor, $iItem, 1)));
+		if(isset($_SERVER["HTTP_REFERER"]))
+			$oSession->setValue($sSessionKeysPrefix . 'referer', bx_append_url_params($_SERVER["HTTP_REFERER"], array('page' => 'checkout', 'vendor' => $iVendor, 'products' => '')));
+
+		if($bUser)
+			$this->subscribe();
+
+		header('Location: ' . BX_DOL_URL_ROOT . 'page/login');
+		exit;
+    }
+
+	public function serviceBrowsePurchased()
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+		$aParams = $this->getBrowseParams();
+		if(empty($aParams['key']) || empty($aParams['domain']))
+			return _t('_bx_market_api_err_wrong_data');
+
+		$aClient = $this->_getOauthClientByKey($aParams['key']);
+		if(empty($aClient) || !is_array($aClient))
+			return _t('_bx_market_api_err_wrong_data');
+
+		if(strcmp($aParams['domain'], $this->getBrowseParamsDomain($aClient['redirect_uri'])) != 0)
+			return _t('_bx_market_api_err_ks_used');
+
+		$aParams['parent'] = (int)$aClient['parent_id'];
+		$aParams['client'] = (int)$aClient['user_id'];
+		$aParams['products'] = bx_get('products') !== false ? unserialize(base64_decode(bx_get('products'))) : array();
+
+		$aResults = $this->getBrowseProducts('purchased', $aParams);
+		if(empty($aParams['parent']))
+			return $aResults;
+
+		$iProfile = (int)$aParams['parent'];
+		$oProfile = BxDolProfile::getInstance($iProfile);
+		if(!$oProfile)
+			return $aResults;
+
+		$aLicenses = $this->_oDb->getLicense(array('type' => 'profile_id_type', 'profile_id' => $iProfile, 'type' => BX_MARKET_API_LICENSE_TYPE_NETWORK));
+		if(empty($aLicenses) || !is_array($aLicenses))
+			return $aResults;
+
+		foreach($aLicenses as $aLicense) {
+			$aLicenseInfo = BxDolService::call($CNF['MARKET'], 'get_license', array(array('type' => 'id', 'id' => $aLicense[$CNF['FIELD_LICENSE_ID']])));
+
+			$aResults = array_merge($aResults, $this->getBrowseProducts('granted', array(
+				'value' => $this->_oConfig->getMarketMainSellerId(),
+				'license' => $aLicenseInfo
+			)));
+		}
+
+		return $aResults;
+    }
+
+	public function serviceBrowseUpdates()
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+		$aParams = $this->getBrowseParams();
+		if(empty($aParams['key']))
+			return _t('_bx_market_api_err_wrong_data');
+
+		$aClient = $this->_getOauthClientByKey($aParams['key']);
+		if(empty($aClient) || !is_array($aClient))
+			return _t('_bx_market_api_err_wrong_data');
+
+		$aParams['client'] = (int)$aClient['user_id'];
+		$aProducts = bx_get('products') !== false ? unserialize(base64_decode(bx_get('products'))) : array();
+
+		return $this->getBrowseUpdates($aProducts, $aParams, true);
+    }
+
+	public function serviceDownloadFile()
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$sKey = bx_process_input(bx_get('key'), BX_DATA_TEXT);
+    	$aClient = $this->_getOauthClientByKey($sKey);
+		if(empty($aClient) || !is_array($aClient))
+			return _t('_bx_market_api_err_wrong_data');
+
+		$iClient = (int)$aClient['user_id'];
+		$iFile = (int)bx_get('file_id');
+
+		$aEntry = BxDolService::call($CNF['MARKET'], 'get_entries_by', array(array(
+			'type' => 'file_id',
+			'value' => $iFile
+		)));
+		if(empty($aEntry))
+			return array();
+
+		$aFile = $this->_getFile($iFile);
+		$bFree = (float)$aEntry[$CNF['MFIELD_PRICE_SINGLE']] == 0 && (float)$aEntry[$CNF['MFIELD_PRICE_RECURRING']] == 0;
+		if(!empty($aFile) && !empty($sKey) && !$bFree && !in_array($aEntry['id'], $this->_getGranted($sKey))) {
+			$aLicense = BxDolService::call($CNF['MARKET'], 'get_license', array(array(
+				'type' => 'profile_id_file_id_key', 
+				'profile_id' => $iClient,
+				'file_id' => $iFile,
+				'key' => $sKey
+			)));
+	        if(empty($aLicense))
+	        	return array();
+
+			if(empty($aLicense['domain']))
+				BxDolService::call($CNF['MARKET'], 'update_license', array(array('domain' => $sKey), array('id' => $aLicense['id'])));
+		}
+
+		return $aFile;
+    }
+
+    public function serviceGetBlockKandsManage()
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+	    $sMenu = '';
+	    $sGrid = 'OBJECT_GRID_KANDS';
+
+		$aLicenses = $this->_oDb->getLicense(array('type' => 'profile_id_type', 'profile_id' => bx_get_logged_profile_id(), 'type' => BX_MARKET_API_LICENSE_TYPE_NETWORK));
+		if(!empty($aLicenses) && is_array($aLicenses)) {
+			$sUriParam = 'clients';
+			$bClients = (int)bx_get($sUriParam) == 1;
+			if($bClients)
+				$sGrid = 'OBJECT_GRID_KANDS_CLIENTS';
+
+			$sLink = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($CNF['URL_KANDS_MANAGE']);
+            $oMenu = new BxTemplMenu(array(
+            	'template' => 'menu_vertical.html', 
+            	'menu_items' => array(
+					array('id' => 'manage-myself', 'name' => 'manage-myself', 'class' => '', 'link' => $sLink, 'target' => '_self', 'title' => _t('_bx_market_api_menu_item_title_kands_manage_myself'), 'active' => 1),
+					array('id' => 'manage-clients', 'name' => 'manage-clients', 'class' => '', 'link' => bx_append_url_params($sLink, array($sUriParam => 1)), 'target' => '_self', 'title' => _t('_bx_market_api_menu_item_title_kands_manage_clients'), 'active' => 1)
+				)
+            ), $this->_oTemplate);
+            $oMenu->setSelected($this->_aModule['name'], 'manage-' . ($bClients ? 'clients' : 'myself'));
+            $sMenu = $oMenu->getCode();
+		}
+
+    	$oGrid = BxDolGrid::getObjectInstance($CNF[$sGrid]);
+        if($oGrid)
+            return array(
+            	'content' => $oGrid->getCode(),
+            	'menu' => $sMenu
+            );
+
+        $this->_oTemplate->displayPageNotFound();
+    }
+
+	public function checkAllowedPass($aDataEntry, $isPerformAction = false)
+    {
+    	$iProfile = bx_get_logged_profile_id();
+    	if($aDataEntry['user_id'] != $iProfile)
+    		return CHECK_ACTION_RESULT_NOT_ALLOWED;
+
+        if(isAdmin())
+        	return CHECK_ACTION_RESULT_ALLOWED;
+
+        // check ACL
+        $aCheck = checkActionModule($iProfile, 'pass', $this->getName(), $isPerformAction);
+        if($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+			return $aCheck[CHECK_ACTION_MESSAGE];
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function addToCart()
+    {
+    	$sSessionKeysPrefix = $this->_oConfig->getSessionKeysPrefix();
+
+    	$oSession = BxDolSession::getInstance();
+    	$sSid = $oSession->getValue($sSessionKeysPrefix . 'sid');
+		$sPurchase = $oSession->getUnsetValue($sSessionKeysPrefix . 'purchase');
+		$iVendor = (int)$oSession->getUnsetValue($sSessionKeysPrefix . 'vendor');
+		if(empty($sSid) || empty($sPurchase) || empty($iVendor))
+			return;
+
+		$oPayment = BxDolPayments::getInstance();
+
+		$aProducts = unserialize($sPurchase);
+		foreach($aProducts as $aProduct) {
+			list($iModuleId, $iVendorId, $iItemId, $iItemCount) = $aProduct;
+			$oPayment->addToCart($iVendorId, $iModuleId, $iItemId, $iItemCount);
+		}
+
+		header('Location: ' . $oPayment->getCartUrl($iVendor));
+		exit;
+    }
+
+	public function subscribe()
+    {
+		$sSessionKeysPrefix = $this->_oConfig->getSessionKeysPrefix();
+
+    	$oSession = BxDolSession::getInstance();
+    	$sSid = $oSession->getValue($sSessionKeysPrefix . 'sid');
+		$sSubscribe = $oSession->getUnsetValue($sSessionKeysPrefix . 'subscribe');
+		if(empty($sSid) || empty($sSubscribe))
+			return;
+
+		list($iModuleId, $iVendorId, $iItemId, $iItemCount) = unserialize($sSubscribe);
+		$sVendorProvider = $this->_oConfig->getMarketMainSellerSbsProvider();
+
+		header('Location: ' . BxDolPayments::getInstance()->getSubscribeUrl($iVendorId, $sVendorProvider, $iModuleId, $iItemId, $iItemCount));
+		exit;
+    }
+
+    public function redirectAfterPayment()
+    {
+    	$sSessionKeysPrefix = $this->_oConfig->getSessionKeysPrefix();
+
+    	$oSession = BxDolSession::getInstance();
+    	$sSid = $oSession->getUnsetValue($sSessionKeysPrefix . 'sid');
+    	$sReferer = $oSession->getUnsetValue($sSessionKeysPrefix . 'referer');
+    	if(empty($sSid) || empty($sReferer))
+			return;
+
+		header('Location: ' . $sReferer);
+		exit;
+    }
+
+	protected function getEntryBy($sType)
+	{
+		$CNF = &$this->_oConfig->CNF;
+
+		if(!in_array($sType, array('id', 'name')))
+			return array();
+
+    	$aEntry = BxDolService::call($CNF['MARKET'], 'get_entry_by', array($sType, ($sType == 'id' ? (int)bx_get('value') : bx_process_input(bx_get('value'), BX_DATA_TEXT))));
+    	if(empty($aEntry))
+    		return array();
+
+		$iClient = 0;
+		$oClient = null;
+		if(bx_get('client') !== false) {
+			$iClient = (int)bx_get('client');
+			$oClient = BxDolProfile::getInstance($iClient);
+		}
+
+		$sKey = '';
+		if(bx_get('key') !== false)
+			$sKey = bx_process_input(bx_get('key'), BX_DATA_TEXT);
+
+		$oAuthor = BxDolProfile::getInstance($aEntry[$CNF['MFIELD_AUTHOR']]);
+
+		$aCurrency = $this->_oConfig->getCurrency();
+
+		$bFree = (float)$aEntry[$CNF['MFIELD_PRICE_SINGLE']] == 0 && (float)$aEntry[$CNF['MFIELD_PRICE_RECURRING']] == 0;
+		$bPurchased = $oClient ? BxDolService::call($CNF['MARKET'], 'has_license', array($iClient, $aEntry[$CNF['MFIELD_ID']])) || in_array($aEntry[$CNF['MFIELD_ID']], $this->_getGranted($sKey)) : false;
+		$bDownloadable = ($bFree || $bPurchased) && (int)$aEntry[$CNF['MFIELD_PACKAGE']] != 0;
+
+		$sViewUrl = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($CNF['MURL_VIEW_ENTRY'] . $aEntry[$CNF['MFIELD_ID']]);
+
+		$sCategory = $sCategoryUrl = '';
+		$iCategory = (int)$aEntry[$CNF['MFIELD_CATEGORY']];
+		if($iCategory != 0) {
+			$oCategory = BxDolCategory::getObjectInstance($CNF['MOBJECT_CATEGORY']);
+			if($oCategory) {
+				$aCategories = BxDolFormQuery::getDataItems($CNF['MOBJECT_CATEGORY']);
+
+				$sCategory = isset($aCategories[$iCategory]) ? $aCategories[$iCategory] : '';
+				$sCategoryUrl = $oCategory->getCategoryUrl($iCategory);
+			}
+		}
+
+		$aFile = BxDolService::call($CNF['MARKET'], 'get_file', array($aEntry[$CNF['MFIELD_PACKAGE']]));
+
+		$aScreenshots = array(); 
+		$aImages = BxDolService::call($CNF['MARKET'], 'get_screenshots', array($aEntry[$CNF['MFIELD_ID']]));
+		foreach($aImages as $aImage) {
+			$aScreenshots[] = array(
+				'id' => $aImage['img_id'], 
+				'small' => $aImage['url_sm'],
+				'big' => $aImage['url_bg']
+			);
+		}
+
+    	return array(
+    		'id' => $aEntry[$CNF['MFIELD_ID']],
+    		'name' => $aEntry[$CNF['MFIELD_NAME']],
+	    	'title' => $aEntry[$CNF['MFIELD_TITLE']],
+	    	'url' => $sViewUrl,
+	    	'description' => $aEntry[$CNF['MFIELD_TEXT']],
+    		'description_plain' => BxTemplFunctions::getInstance()->getStringWithLimitedLength(strip_tags($aEntry[$CNF['MFIELD_TEXT']]), (int)getParam($CNF['PARAM_PLAIN_DESCRIPTION_CHARS'])),
+	    	'author_id' => $aEntry[$CNF['MFIELD_AUTHOR']],
+	    	'author_name' => $oAuthor->getDisplayName(),
+	    	'author_url' => $oAuthor->getUrl(),
+    		'author_currency_code' => $aCurrency['code'],
+    		'author_currency_sign' => $aCurrency['sign'],
+	    	'created' => $aEntry[$CNF['MFIELD_ADDED']],
+	    	'updated' => $aEntry[$CNF['MFIELD_CHANGED']],
+    		'version' => $aFile[$CNF['MFIELD_FILE_VERSION']],
+	    	//--- Single price related fields
+	    	'price_single' => $aEntry[$CNF['MFIELD_PRICE_SINGLE']],
+    		'discount_single' => array(), //TODO: need to have Discounted Price in Market if we need this feature at all.		$aEntry['entry_price_full'] != 0 && $aEntry['entry_price_full'] != $aEntry['entry_price_discounted'] ? $this->rScript->_prepareDiscount($aEntry['entry_price_full'], $aEntry['entry_price_discounted']) : array(),
+	    	//--- Recurring price related fields
+    		'price_recurring' => $aEntry[$CNF['MFIELD_PRICE_RECURRING']],
+    		'duration_recurring' => $aEntry[$CNF['MFIELD_DURATION_RECURRING']],
+    		'category' => $sCategory,
+    		'category_url' => $sCategoryUrl,
+    		'tags' => '', //TODO: integrate tags
+    		'reviews_cnt' => $aEntry[$CNF['MFIELD_COMMENTS']],
+    		'reviews_url' => $sViewUrl . '#cmts-anchor-bx-market-' . $aEntry[$CNF['MFIELD_ID']],
+    		'views_cnt' => $aEntry[$CNF['MFIELD_VIEWS']],
+    		'votes_cnt' => $aEntry[$CNF['MFIELD_VOTES']],
+    		'rate' => $aEntry[$CNF['MFIELD_RATE']],
+    		'screenshots' => $aScreenshots,
+			'is_file' => $bDownloadable ? 1 : 0,
+			'file_id' => $bDownloadable ? $aEntry[$CNF['MFIELD_PACKAGE']] : 0,
+    		'is_free' => $bFree ? 1 : 0,
+			'is_purchased' => $bPurchased ? 1 : 0,
+    	);
+    }
+
+    protected function getBrowseParams()
+    {
+    	$aParams = array();
+
+    	if(bx_get('value') !== false)
+			$aParams['value'] = bx_process_input(bx_get('value'), BX_DATA_TEXT);
+
+    	if(bx_get('order_by') !== false)
+			$aParams['order_by'] = bx_process_input(bx_get('order_by'), BX_DATA_TEXT);
+		if(bx_get('order_trend') !== false)
+        	$aParams['order_trend'] = bx_process_input(bx_get('order_trend'), BX_DATA_TEXT);
+
+		$aParams['start'] = bx_get('start') !== false ? (int)bx_get('start') : 0;
+        $aParams['per_page'] = bx_get('per_page') !== false ? (int)bx_get('per_page') : 10;
+
+        $aParams['key'] = bx_get('key') !== false ? bx_process_input(bx_get('key'), BX_DATA_TEXT) : '';
+        $aParams['key_assigned'] = bx_get('key_assigned') !== false ? (int)bx_get('key_assigned') : 0;
+
+        $aParams['parent'] = bx_get('parent') !== false ? (int)bx_get('parent') : 0;
+        $aParams['client'] = bx_get('client') !== false ? (int)bx_get('client') : 0;
+
+        $aParams['domain'] = $this->getBrowseParamsDomain();
+
+        return $aParams;
+    }
+
+    protected function getBrowseParamsDomain($sDomain = '')
+    {
+    	if(empty($sDomain) && bx_get('domain') !== false)
+    		$sDomain = bx_process_input(bx_get('domain'), BX_DATA_TEXT);
+
+    	if(empty($sDomain))
+    		return $sDomain;
+
+        $aInfo = parse_url($sDomain);
+        return !empty($aInfo['host']) ? $aInfo['host'] : '';
+    }
+
+    protected function getBrowseProducts($sType, $aParams = array())
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$aParams['type'] = $sType;
+        $aEntries = BxDolService::call($CNF['MARKET'], 'get_entries_by', array($aParams));
+
+		$this->_prepareForOutputEntries($aEntries, false, $aParams);
+		return $aEntries;
+    }
+
+    protected function getBrowseUpdates($aProducts, $aParams = array(), $bAuthorized = true)
+    {
+		$aResults = array();
+
+		$iClient = isset($aParams['client']) ? (int)$aParams['client'] : 0;
+    	if(empty($aProducts) || !is_array($aProducts) || empty($iClient))
+    		return $aResults;
+
+		foreach($aProducts as $aProduct) {
+			$aInfo = $this->_getUpdate($iClient, $aProduct, $aParams['key'], $bAuthorized);
+			if(empty($aInfo) || !is_array($aInfo))
+				continue;
+
+			$aResults[] = $aInfo;
+		}
+
+		$this->_prepareForOutputEntries($aResults, true);
+		return $aResults;
+    }
+
+    protected function _prepareForOutputEntries(&$aItems, $bForcePurchased = false, $aParams = array())
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$oPermalink = BxDolPermalinks::getInstance();
+
+    	$aCurrency = $this->_oConfig->getCurrency();
+
+    	$iRateMin = $iRateMax = 1;
+    	$aVoteSystems = BxDolVote::getSystems();
+    	if(!empty($aVoteSystems[$CNF['MOBJECT_VOTES']]) && is_array($aVoteSystems[$CNF['MOBJECT_VOTES']])) {
+    		$iRateMin = $aVoteSystems[$CNF['MOBJECT_VOTES']]['min_value'];
+    		$iRateMax = $aVoteSystems[$CNF['MOBJECT_VOTES']]['max_value'];
+    	}
+
+    	$bShort = isset($aParams['short']) && $aParams['short'] === true;
+    	$sKey = !empty($aParams['key']) ? $aParams['key'] : '';
+    	$aInstalled = !empty($aParams['products']) && is_array($aParams['products']) ? $aParams['products'] : array();
+
+    	$aResults = array();
+		foreach($aItems as $aItem) {
+			$oAuthor = BxDolProfile::getInstance($aItem[$CNF['MFIELD_AUTHOR']]);
+
+			$bFree = (float)$aItem[$CNF['MFIELD_PRICE_SINGLE']] == 0 && (float)$aItem[$CNF['MFIELD_PRICE_RECURRING']] == 0;
+			$bPurchased = (isset($aItem['purchased_on']) && (int)$aItem['purchased_on'] != 0) || $bForcePurchased;
+			$bDownloadable = ($bFree || $bPurchased) && (int)$aItem['file_id'] != 0;
+
+			/**
+			 * Associate license with Key if App is installed and user has non-associated license for this App.
+			 */
+			$bPurchasedAndAvailable = $bPurchased && !empty($aItem['license']) && isset($aItem['purchased_for']) && $aItem['purchased_for'] == '';
+			if(array_key_exists($aItem[$CNF['MFIELD_NAME']], $aInstalled) && $bPurchasedAndAvailable)
+				if(BxDolService::call($CNF['MARKET'], 'update_license', array(array('domain' => $sKey), array('license' => $aItem['license']))))
+					$aItem['purchased_for'] = $sKey;
+
+			$aDiscount = array();
+			/*
+			//TODO: Process discount if the feature was added.
+			if(!$bFree && $aItem[$CNF['MFIELD_PRICE_DISCOUNT']] != 0) {
+				$aDiscount = array(
+					'percent' => $aItem[$CNF['MFIELD_PRICE_DISCOUNT']], 
+					'save' => round($aItem[$CNF['MFIELD_PRICE_DISCOUNT']] * $aItem[$CNF['MFIELD_PRICE_SINGLE']] / 100, 2),
+					'price' => round((1 - $CNF['MFIELD_PRICE_DISCOUNT'] / 100) * $aItem[$CNF['MFIELD_PRICE_SINGLE']], 2)
+				);
+			}
+			*/
+
+			if($bShort)
+				$aResult = array(
+					'name' => $aItem[$CNF['MFIELD_NAME']],
+				);
+			else
+				$aResult = array(
+					'id' => $aItem[$CNF['MFIELD_ID']],
+					'name' => $aItem[$CNF['MFIELD_NAME']],
+					'title' => $aItem[$CNF['MFIELD_TITLE']],
+					'description' => strip_tags($aItem[$CNF['MFIELD_TEXT']]),
+					'description_plain' => BxTemplFunctions::getInstance()->getStringWithLimitedLength(strip_tags($aItem[$CNF['MFIELD_TEXT']]), (int)getParam($CNF['PARAM_PLAIN_DESCRIPTION_CHARS'])),
+					'url' => BX_DOL_URL_ROOT . $oPermalink->permalink($CNF['MURL_VIEW_ENTRY'] . $aItem[$CNF['MFIELD_ID']]),
+					'thumbnail' => BxDolService::call($CNF['MARKET'], 'get_thumbnail', array($aItem[$CNF['MFIELD_THUMB']])),
+					//--- Single price related fields
+					'price_single' => $aItem[$CNF['MFIELD_PRICE_SINGLE']],
+					'discount_single' => $aDiscount,
+					//--- Recurring price related fields
+					'price_recurring' => $aItem[$CNF['MFIELD_PRICE_RECURRING']],
+		    		'duration_recurring' => $aItem[$CNF['MFIELD_DURATION_RECURRING']],
+					'views_cnt' => $aItem[$CNF['MFIELD_VIEWS']],
+					'votes_cnt' => $aItem[$CNF['MFIELD_VOTES']],
+					'rate' => $aItem[$CNF['MFIELD_RATE']],
+					'rate_min' => $iRateMin,
+					'rate_max' => $iRateMax,
+					'created_date' => $aItem[$CNF['MFIELD_ADDED']],
+					'author_id' => $aItem[$CNF['MFIELD_AUTHOR']],
+					'author_name' => $oAuthor->getDisplayName(),
+					'author_url' => $oAuthor->getUrl(),
+					'author_currency_code' => $aCurrency['code'],
+					'author_currency_sign' => $aCurrency['sign'],
+					'is_file' => $bDownloadable ? 1 : 0,
+					'file_id' => $bDownloadable ? $aItem['file_id'] : 0,
+					'file_version' => $bDownloadable ? $aItem['file_version'] : '',
+					'file_version_to' => $bDownloadable && isset($aItem['file_version_to']) ? $aItem['file_version_to'] : '',
+					'is_free' => $bFree ? 1 : 0,
+					'is_purchased' => $bPurchased ? 1 : 0,
+					'purchased_for' => $bPurchased && isset($aItem['purchased_for']) ? $aItem['purchased_for'] : '',
+					'purchased_on' => $bPurchased && isset($aItem['purchased_on']) ? $aItem['purchased_on'] : 0,
+					'license' => $bPurchased && isset($aItem['license']) ? $aItem['license'] : ''
+				);
+
+			if($bPurchased && !empty($aItem['purchased_by']))
+				$aResult['hash'] = $this->_getHash((int)$aItem[$CNF['MFIELD_ID']], $aItem['purchased_by'], $sKey);
+
+			$aResults[] = $aResult; 
+		}
+
+		$aItems = $aResults;
+    }
+
+    protected function _getHash($iProductId, $iClientId, $sClientKey)
+    {
+    	if(empty($iClientId))
+    		return '';
+
+		$aInfo = array(
+			'product' => $iProductId,
+			'client_ip' => $_SERVER['REMOTE_ADDR'],
+			'client_key' => $sClientKey,
+			'salt' => $iClientId
+		);
+
+    	return md5(implode('-', $aInfo));
+    }
+
+    protected function _getFile($iId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$aFile = BxDolService::call($CNF['MARKET'], 'get_file', array($iId));
+    	$aEntry = BxDolService::call($CNF['MARKET'], 'get_entry_by', array('id', $aFile['content_id']));
+
+    	if(empty($aFile) || !is_array($aFile) || empty($aEntry) || !is_array($aEntry))
+    		return array();
+
+		return array(
+			'module_name' => $aEntry[$CNF['MFIELD_NAME']],
+			'id' => $aFile['file_id'],
+			'name' => $aFile['file_name'],
+			'size' => $aFile['file_size'],
+			'content' => urlencode(bx_file_get_contents($aFile['file_url']))
+		);    	
+    }
+
+    protected function _getUpdate($iClient, $aEntry, $sKey, $bAuthorized = true)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$aEntryInfo = BxDolService::call($CNF['MARKET'], 'get_entry_by', array('name', $aEntry[$CNF['MFIELD_NAME']]));
+		if(empty($aEntryInfo) || !is_array($aEntryInfo) || version_compare($aEntryInfo['file_version'], $aEntry['version']) == 0)
+			return array();
+
+		$bFree = (float)$aEntryInfo[$CNF['MFIELD_PRICE_SINGLE']] == 0 && (float)$aEntryInfo[$CNF['MFIELD_PRICE_RECURRING']] == 0;
+		$bGranted = in_array($aEntryInfo[$CNF['MFIELD_ID']], $this->_getGranted($sKey));
+		if(!$bAuthorized && !$bFree && !$bGranted && strcmp($aEntry['hash'],  $this->_getHash($aEntryInfo[$CNF['MFIELD_ID']], $iClient, $sKey)) != 0)
+			return array();
+
+		if(!$bFree && !$bGranted && !BxDolService::call($CNF['MARKET'], 'has_license', array($iClient, $aEntryInfo[$CNF['MFIELD_ID']], $sKey)))
+			return array();
+
+		$aUpdate = BxDolService::call($CNF['MARKET'], 'get_updates', array($aEntryInfo[$CNF['MFIELD_ID']], $aEntry['version']));
+		if(empty($aUpdate) || !is_array($aUpdate)) 
+			return array();
+
+		return array_merge($aEntryInfo, array(
+			'file_id' => $aUpdate['file_id'],
+			'file_name' => $aUpdate['file_name'],
+			'file_version' => $aUpdate['version'], 						
+			'file_version_to' => $aUpdate['version_to'],
+		));
+    }
+
+    protected function _getGranted($sKey)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$aOauthClient = $this->_getOauthClientByKey($sKey);
+    	if(empty($aOauthClient) || empty($aOauthClient['parent_id']))
+    		return array();
+
+    	$iParent = (int)$aOauthClient['parent_id'];
+		$oParent = BxDolProfile::getInstance($iParent);
+		if(!$oParent)
+			return array();
+
+		$aLicense = BxDolService::call($CNF['MARKET'], 'get_license', array(array('type' => 'profile_id', 'profile_id' => $iParent)));
+    	if(empty($aLicense) || !is_array($aLicense))
+    		return array();
+    		
+		$aLicenseInfo = $this->_oDb->getLicense(array(
+			'type' => 'license_id_profile_id',
+			'license_id' => $aLicense['id'],
+			'profile_id' => $iParent));
+		if(empty($aLicenseInfo) || !is_array($aLicenseInfo) || $aLicenseInfo[$CNF['FIELD_TYPE']] != BX_MARKET_API_LICENSE_TYPE_NETWORK)
+			return array();
+
+		$aEntries = BxDolService::call($CNF['MARKET'], 'get_entries_by', array(array(
+			'type' => 'vendor',
+			'value' => $this->_oConfig->getMarketMainSellerId()
+		)));
+		if(empty($aEntries) || !is_array($aEntries))
+			return array();
+
+		foreach($aEntries as $aEntry)
+			$aResult[] = $aEntry['id'];
+
+    	return $aResult;
+    }
+
+    protected function _getClientByKey($sKey)
+    {
+		$aOauthClient = $this->_getOauthClientByKey($sKey);
+
+		return !empty($aOauthClient['user_id']) ? (int)$aOauthClient['user_id'] : 0;
+    }
+
+	protected function _getOauthClientByKey($sKey)
+	{
+    	return BxDolService::call($this->_oConfig->CNF['OAUTH'], 'get_clients_by', array(array('type' => 'client_id', 'client_id' => $sKey)));
+    }
+}
+
+/** @} */

+ 23 - 0
modules/boonex/market_api/classes/BxMarketApiPageKands.php

@@ -0,0 +1,23 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+/**
+ * Profile's Keys & Secrets page.
+ */
+class BxMarketApiPageKands extends BxBaseModTextPageAuthor
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 67 - 0
modules/boonex/market_api/classes/BxMarketApiResponse.php

@@ -0,0 +1,67 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxMarketApiResponse extends BxDolAlertsResponse
+{
+    public function __construct()
+    {
+        parent::__construct();
+
+        $this->_oModule = BxDolModule::getInstance('bx_market_api');
+    }
+
+    /**
+     * Overwtire the method of parent class.
+     *
+     * @param BxDolAlerts $oAlert an instance of alert.
+     */
+    public function response($oAlert)
+    {
+    	$sMethod = '_process' . bx_gen_method_name($oAlert->sUnit . '_' . $oAlert->sAction);
+		if(!method_exists($this, $sMethod))
+			return;
+
+		return $this->$sMethod($oAlert);
+    }
+
+    protected function _processAccountLogin($oAlert)
+    {
+    	bx_require_authentication();
+
+    	$oSession = BxDolSession::getInstance();
+    	$sSessionKeysPrefix = $this->_oModule->_oConfig->getSessionKeysPrefix();
+
+    	if($oSession->isValue($sSessionKeysPrefix . 'purchase'))
+    		$this->_oModule->addToCart();
+
+		if($oSession->isValue($sSessionKeysPrefix . 'subscribe'))
+    		$this->_oModule->subscribe();
+    }
+
+	protected function _processSystemBeforeRegisterPayment($oAlert)
+	{
+		if(empty($oAlert->aExtras['pending']) || !is_array($oAlert->aExtras['pending']))
+			return;
+
+		$aPending = $oAlert->aExtras['pending'];
+		if(empty($aPending['type']) || $aPending['type'] != 'recurring')
+			return;
+
+		$this->_oModule->redirectAfterPayment();
+	}
+
+	protected function _processSystemRegisterPayment($oAlert)
+    {
+    	$this->_oModule->redirectAfterPayment();
+    }
+}
+
+/** @} */

+ 34 - 0
modules/boonex/market_api/classes/BxMarketApiTemplate.php

@@ -0,0 +1,34 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+/*
+ * Module representation.
+ */
+class BxMarketApiTemplate extends BxBaseModGeneralTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        $this->MODULE = 'bx_market_api';
+
+        parent::__construct($oConfig, $oDb);
+    }
+
+    public function getJsCode($sType, $aParams = array(), $bWrap = true)
+    {
+    	$aParams = array_merge($aParams, array(
+    		'aHtmlIds' => $this->_oConfig->getHtmlIds()
+    	));
+
+    	return parent::getJsCode($sType, $aParams, $bWrap);
+    }
+}
+
+/** @} */

+ 75 - 0
modules/boonex/market_api/install/config.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_market_api',
+    'title' => 'Market API',
+    'note' => 'Market API module.',
+    'version' => '9.0.0',
+    'vendor' => 'BoonEx',
+	'help_url' => 'http://feed.boonex.com/?section={module_name}',
+
+    'compatible_with' => array(
+        '9.0.x'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/market_api/',
+    'home_uri' => 'market_api',
+
+    'db_prefix' => 'bx_market_api_',
+    'class_prefix' => 'BxMarketApi',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Market API',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+    	'check_dependencies' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+    	'check_dependencies' => 1,
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(
+    	'market' => 'Market'
+    ),
+);
+
+/** @} */

+ 20 - 0
modules/boonex/market_api/install/installer.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    MarketApi MarketApi
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxMarketApiInstaller extends BxBaseModGeneralInstaller
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}
+
+/** @} */

+ 26 - 0
modules/boonex/market_api/install/langs/en.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string name="_bx_market_api"><![CDATA[Market API]]></string>
+	<string name="_bx_market_api_err_wrong_data"><![CDATA[Wrong incoming data.]]></string>
+	<string name="_bx_market_api_err_ks_used"><![CDATA[Key & Secret were already used for another domain.]]></string>
+
+	<string name="_bx_market_api_option_plain_description_chars"><![CDATA[Number of characters in plain product description when product hasn't thumb]]></string>
+
+	<string name="_bx_market_api_page_title_sys_kands_manage"><![CDATA[Keys & Secrets]]></string>
+    <string name="_bx_market_api_page_title_kands_manage"><![CDATA[Keys & Secrets]]></string>
+    <string name="_bx_market_api_page_block_title_kands_manage"><![CDATA[Keys & Secrets]]></string>
+
+	<string name="_bx_market_api_menu_item_title_system_kands_manage"><![CDATA[Keys & Secrets]]></string>
+	<string name="_bx_market_api_menu_item_title_kands_manage"><![CDATA[Keys & Secrets]]></string>
+
+	<string name="_bx_market_api_menu_item_title_kands_manage_myself"><![CDATA[for myself]]></string>
+	<string name="_bx_market_api_menu_item_title_kands_manage_clients"><![CDATA[for client]]></string>
+
+	<string name="_bx_market_api_user_id"><![CDATA[Client]]></string>
+	<string name="_bx_market_api_client_url"><![CDATA[Site URL]]></string>
+	<string name="_bx_market_api_client_id"><![CDATA[Key]]></string>
+	<string name="_bx_market_api_client_secret"><![CDATA[Secret]]></string>
+	<string name="_bx_market_api_redirect_uri"><![CDATA[URL]]></string>
+	<string name="_bx_market_api_add"><![CDATA[Add new key]]></string>
+	<string name="_bx_market_api_pass"><![CDATA[Pass the key]]></string>	
+</resources>

+ 37 - 0
modules/boonex/market_api/install/sql/disable.sql

@@ -0,0 +1,37 @@
+SET @sName = 'bx_market_api';
+
+
+-- SETTINGS
+SET @iTypeId = (SELECT `ID` FROM `sys_options_types` WHERE `name` = @sName LIMIT 1);
+SET @iCategoryId = (SELECT `ID` FROM `sys_options_categories` WHERE `type_id` = @iTypeId LIMIT 1);
+DELETE FROM `sys_options` WHERE `category_id` = @iCategoryId;
+DELETE FROM `sys_options_categories` WHERE `type_id` = @iTypeId;
+DELETE FROM `sys_options_types` WHERE `id` = @iTypeId;
+
+
+-- PAGES
+DELETE FROM `sys_objects_page` WHERE `module` = @sName;
+DELETE FROM `sys_pages_blocks` WHERE `module` = @sName;
+
+
+-- MENU
+DELETE FROM `sys_objects_menu` WHERE `module` = @sName;
+DELETE FROM `sys_menu_sets` WHERE `module` = @sName;
+DELETE FROM `sys_menu_items` WHERE `module` = @sName;
+
+
+-- GRID
+DELETE FROM `sys_objects_grid` WHERE `object` LIKE 'bx_market_api%';
+DELETE FROM `sys_grid_fields` WHERE `object` LIKE 'bx_market_api%';
+DELETE FROM `sys_grid_actions` WHERE `object` LIKE 'bx_market_api%';
+
+
+-- ACL
+DELETE `sys_acl_actions`, `sys_acl_matrix` FROM `sys_acl_actions`, `sys_acl_matrix` WHERE `sys_acl_matrix`.`IDAction` = `sys_acl_actions`.`ID` AND `sys_acl_actions`.`Module` = 'bx_market_api';
+DELETE FROM `sys_acl_actions` WHERE `Module` = 'bx_market_api';
+
+
+-- ALERTS
+SET @iHandlerId = (SELECT `id` FROM `sys_alerts_handlers` WHERE `name` = @sName LIMIT 1);
+DELETE FROM `sys_alerts` WHERE `handler_id` = @iHandlerId;
+DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandlerId LIMIT 1;

+ 91 - 0
modules/boonex/market_api/install/sql/enable.sql

@@ -0,0 +1,91 @@
+SET @sName = 'bx_market_api';
+
+
+-- SETTINGS
+SET @iTypeOrder = (SELECT MAX(`order`) FROM `sys_options_types` WHERE `group` = 'modules');
+INSERT INTO `sys_options_types`(`group`, `name`, `caption`, `icon`, `order`) VALUES 
+('modules', @sName, '_bx_market_api', 'bx_market_api@modules/boonex/market_api/|std-mi.png', IF(ISNULL(@iTypeOrder), 1, @iTypeOrder + 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, @sName, '_bx_market_api', 1);
+SET @iCategoryId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
+('bx_market_api_plain_description_chars', '240', @iCategoryId, '_bx_market_api_option_plain_description_chars', 'digit', '', '', '', 10);
+
+
+-- PAGE: Profile's keys & secrets
+INSERT INTO `sys_objects_page`(`object`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `uri`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_market_api_kands_manage', '_bx_market_api_page_title_sys_kands_manage', '_bx_market_api_page_title_kands_manage', @sName, 5, 2147483647, 1, 'kands-manage', '', '', '', '', 0, 1, 0, 'BxMarketApiPageKands', 'modules/boonex/market_api/classes/BxMarketApiPageKands.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_market_api_kands_manage', 1, @sName, '', '_bx_market_api_page_block_title_kands_manage', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:13:\"bx_market_api\";s:6:\"method\";s:22:\"get_block_kands_manage\";}', 0, 0, 1, 0);
+
+
+-- MENU: Notifications
+SET @iMIOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_account_notifications');
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('sys_account_notifications', @sName, 'kands-manage', '_bx_market_api_menu_item_title_system_kands_manage', '_bx_market_api_menu_item_title_kands_manage', 'page.php?i=kands-manage', '', '', 'key col-green2', '', '', 2147483646, 1, 0, @iMIOrder + 1);
+
+
+-- GRID
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_mode`, `sorting_fields`, `override_class_name`, `override_class_file`) VALUES
+('bx_market_api_kands', 'Array', '', '', 'id', '', '', 10, NULL, 'start', '', 'title,client_id,client_secret,redirect_uri', 'auto', 'title,client_id,client_secret,redirect_uri', 'BxMarketApiGridKands', 'modules/boonex/market_api/classes/BxMarketApiGridKands.php'),
+('bx_market_api_kands_clients', 'Array', '', '', 'id', '', '', 10, NULL, 'start', '', 'title,client_id,client_secret,redirect_uri', 'auto', 'title,client_id,client_secret,redirect_uri', 'BxMarketApiGridKandsClients', 'modules/boonex/market_api/classes/BxMarketApiGridKandsClients.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `params`, `order`) VALUES
+('bx_market_api_kands', 'checkbox', 'Select', '2%', '', 10),
+('bx_market_api_kands', 'title', '_Title', '15%', '', 20),
+('bx_market_api_kands', 'client_id', '_bx_market_api_client_id', '15%', '', 30),
+('bx_market_api_kands', 'client_secret', '_bx_market_api_client_secret', '30%', '', 40),
+('bx_market_api_kands', 'redirect_uri', '_bx_market_api_redirect_uri', '30%', '', 50),
+('bx_market_api_kands', 'actions', '', '8%', '', 60),
+
+('bx_market_api_kands_clients', 'checkbox', 'Select', '2%', '', 10),
+('bx_market_api_kands_clients', 'user_id', '_bx_market_api_user_id', '13%', '', 20),
+('bx_market_api_kands_clients', 'title', '_Title', '15%', '', 30),
+('bx_market_api_kands_clients', 'client_id', '_bx_market_api_client_id', '20%', '', 40),
+('bx_market_api_kands_clients', 'client_secret', '_bx_market_api_client_secret', '25%', '', 50),
+('bx_market_api_kands_clients', 'redirect_uri', '_bx_market_api_redirect_uri', '25%', '', 60);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_market_api_kands', 'single', 'pass', '_bx_market_api_pass', 'share-square-o', 1, 0, 1),
+('bx_market_api_kands', 'bulk', 'delete', '_Delete', '', 0, 1, 1),
+('bx_market_api_kands', 'independent', 'add', '_bx_market_api_add', '', 0, 0, 1),
+
+('bx_market_api_kands_clients', 'bulk', 'delete', '_Delete', '', 0, 1, 1),
+('bx_market_api_kands_clients', 'independent', 'add', '_bx_market_api_add', '', 0, 0, 1);
+
+
+-- ACL
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_market_api', 'pass', NULL, '_bx_market_api_acl_action_pass', '', 1, 3);
+SET @iIdActionPass = LAST_INSERT_ID();
+
+SET @iUnauthenticated = 1;
+SET @iAccount = 2;
+SET @iStandard = 3;
+SET @iUnconfirmed = 4;
+SET @iPending = 5;
+SET @iSuspended = 6;
+SET @iModerator = 7;
+SET @iAdministrator = 8;
+SET @iPremium = 9;
+
+INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
+
+-- pass
+(@iModerator, @iIdActionPass),
+(@iAdministrator, @iIdActionPass);
+
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers`(`name`, `class`, `file`, `service_call`) VALUES 
+(@sName, 'BxMarketApiResponse', 'modules/boonex/market_api/classes/BxMarketApiResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('account', 'login', @iHandler),
+('system', 'before_register_payment', @iHandler),
+('system', 'register_payment', @iHandler);

+ 25 - 0
modules/boonex/market_api/install/sql/install.sql

@@ -0,0 +1,25 @@
+SET @sName = 'bx_market_api';
+
+
+-- TABLE: licenses
+CREATE TABLE IF NOT EXISTS `bx_market_api_licenses` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `license_id` int(11) unsigned NOT NULL default '0',
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `type` varchar(16) collate utf8_unicode_ci NOT NULL default '',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `license_id` (`license_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+
+-- STUDIO: page & widget
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_market_api', '_bx_market_api', 'bx_market_api@modules/boonex/market_api/|std-pi.png');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, @sName, '{url_studio}module.php?name=bx_market_api', '', 'bx_market_api@modules/boonex/market_api/|std-wi.png', '_bx_market_api', '', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_actions";s:6:"params";a:0:{}s:5:"class";s:18:"TemplStudioModules";}');
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), IF(ISNULL(@iParentPageOrder), 1, @iParentPageOrder + 1));

+ 11 - 0
modules/boonex/market_api/install/sql/uninstall.sql

@@ -0,0 +1,11 @@
+SET @sName = 'bx_market_api';
+
+
+-- TABLES
+DROP TABLE IF EXISTS `bx_market_api_licenses`;
+
+
+-- STUDIO: page & widget
+DELETE FROM `tp`, `tw`, `tpw`
+USING `sys_std_pages` AS `tp`, `sys_std_widgets` AS `tw`, `sys_std_pages_widgets` AS `tpw`
+WHERE `tp`.`id` = `tw`.`page_id` AND `tw`.`id` = `tpw`.`widget_id` AND `tp`.`name` = @sName;

+ 21 - 0
modules/boonex/market_api/js/kands.js

@@ -0,0 +1,21 @@
+function BxMarketApiKands(oOptions) {
+	this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxMarketApiKands' : oOptions.sObjName;
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+}
+
+BxMarketApiKands.prototype.paOnShowPopup = function(oSelect) {
+	var $this = this;
+
+	$('#' + this._aHtmlIds['field_user']).autocomplete({
+        source: this._sActionsUrl + 'get_users/',
+        select: function(oEvent, oUi) {
+        	$('#' + $this._aHtmlIds['field_user_id']).val(oUi.item.value);
+            $(this).val(oUi.item.label);
+
+            oEvent.preventDefault();
+        }
+    });
+};

+ 18 - 0
modules/boonex/market_api/request.php

@@ -0,0 +1,18 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Posts Posts
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+check_logged();
+
+BxBaseModTextRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

+ 3 - 0
modules/boonex/market_api/template/css/kands.css

@@ -0,0 +1,3 @@
+.ui-autocomplete.ui-front {
+    z-index: 1001;
+}

BIN
modules/boonex/market_api/template/images/icons/std-mi.png


BIN
modules/boonex/market_api/template/images/icons/std-pi.png


BIN
modules/boonex/market_api/template/images/icons/std-si.png


BIN
modules/boonex/market_api/template/images/icons/std-wi.png


+ 15 - 0
modules/boonex/market_api/template/kands_add_form.html

@@ -0,0 +1,15 @@
+<div class="bx-def-padding bx-def-color-bg-block">__form__</div>
+<script>
+    $(document).ready(function () {
+        $("#__form_id__").ajaxForm({
+            dataType: "json",
+            beforeSubmit: function (formData, jqForm, options) {
+                bx_loading($("#__form_id__"), true);
+            },
+            success: function (data) {
+                $(".bx-popup-active").dolPopupHide();
+                glGrids.__object__.processJson(data, "add");
+            }
+        });
+    });
+</script>

+ 17 - 0
modules/boonex/market_api/template/kands_add_form_clients.html

@@ -0,0 +1,17 @@
+<div class="bx-def-padding bx-def-color-bg-block">__form__</div>
+<script>
+    $(document).ready(function () {
+        $("#__form_id__").ajaxForm({
+            dataType: "json",
+            beforeSubmit: function (formData, jqForm, options) {
+                bx_loading($("#__form_id__"), true);
+            },
+            success: function (data) {
+                $(".bx-popup-active").dolPopupHide();
+                glGrids.__object__.processJson(data, "add");
+            }
+        });
+
+        __js_object__.paOnShowPopup();
+    });
+</script>

+ 17 - 0
modules/boonex/market_api/template/kands_pass_form.html

@@ -0,0 +1,17 @@
+<div class="bx-def-padding bx-def-color-bg-block">__form__</div>
+<script>
+    $(document).ready(function () {
+        $("#__form_id__").ajaxForm({
+            dataType: "json",
+            beforeSubmit: function (formData, jqForm, options) {
+                bx_loading($("#__form_id__"), true);
+            },
+            success: function (data) {
+                $(".bx-popup-active").dolPopupHide();
+                glGrids.__object__.processJson(data, "pass");
+            }
+        });
+
+        __js_object__.paOnShowPopup();
+    });
+</script>

+ 19 - 0
modules/boonex/paypal_payflow/classes/BxPfwCart.php

@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtCart.php');
+
+class BxPfwCart extends BxPmtCart
+{
+    /*
+     * Constructor.
+     */
+    function BxPfwCart(&$oDb, &$oConfig, &$oTemplate)
+    {
+    	parent::BxPmtCart($oDb, $oConfig, $oTemplate);
+    }
+}

+ 215 - 0
modules/boonex/paypal_payflow/classes/BxPfwConfig.php

@@ -0,0 +1,215 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtConfig.php');
+
+class BxPfwConfig extends BxPmtConfig
+{
+	protected $_sParentHomePath;
+	protected $_sParentHomeUrl;
+	protected $_sParentClassPrefix;
+
+	protected $_sProvider;
+	protected $_sMode;
+	protected $_iTimeout;
+	protected $_sCancelUrl;
+	protected $_sResponseUrl;
+
+	protected $_sPfwEndpointCall;
+	protected $_sPfwEndpointHosted;
+	protected $_sPpEndpointHosted;
+
+	protected $_sLogPath;
+	protected $_aLogFiles;
+	protected $_aLogEnabled;
+
+    function BxPfwConfig($aModule)
+    {
+        parent::BxPmtConfig($aModule);
+
+        $sParentDirectory = 'boonex/payment/';
+        $this->_sParentHomePath = BX_DIRECTORY_PATH_MODULES . $sParentDirectory;
+        $this->_sParentHomeUrl = BX_DOL_URL_MODULES . $sParentDirectory;
+        $this->_sParentClassPrefix = 'BxPmt';
+
+        $this->_sProvider = '';
+        $this->_sMode = BX_PFW_MODE_LIVE;
+        $this->_iTimeout = 90;
+		$this->_sReturnUrl = $this->getBaseUri();
+        $this->_sCancelUrl = $this->getBaseUri() . 'cart/';
+        $this->_sResponseUrl = $this->getBaseUri() . 'response/';
+
+        $this->_aPrefixes = array(
+        	'general' => 'bx_pfw_',
+        	'langs' => '_bx_pfw_',
+        	'options' => 'bx_pfw_',
+        );
+        $this->_aJsClasses = array(
+        	'cart' => 'BxPfwCart',
+        	'cart_parent' => 'BxPmtCart',
+        	'orders' => 'BxPfwOrders',
+        	'orders_parent' => 'BxPmtOrders'
+        );
+        $this->_aJsObjects = array(
+        	'cart' => 'oPfwCart',
+        	'orders' => 'oPfwOrders'
+        );
+
+        $this->_sOptionsCategory = 'PayPal PayFlow Pro';
+
+        $this->_sLogPath = $this->getHomePath() . 'log/';
+        $this->_aLogFiles = array(
+        	'info' => 'pp.info.log',
+	        'error' => 'pp.error.log',
+        );
+        $this->_aLogEnabled = array(
+        	'info' => 1,
+	        'error' => 1,
+        );
+    }
+
+	function init(&$oDb)
+    {
+        parent::init($oDb);
+
+        $sOptionPrefix = $this->getOptionsPrefix();
+        //TODO: init necessary settings here.
+    }
+
+	function setProvider($sProvider)
+    {
+    	$this->_sProvider = $sProvider;
+    }
+	function getProvider()
+    {
+    	return $this->_sProvider;
+    }
+    function setMode($sMode)
+    {
+    	$this->_sMode = $sMode;
+    }
+	function getMode()
+    {
+    	return $this->_sMode;
+    }
+	function getParentClassPrefix()
+    {
+        return $this->_sParentClassPrefix;
+    }
+	function getParentHomePath()
+    {
+        return $this->_sParentHomePath;
+    }
+    function getParentHomeUrl()
+    {
+        return $this->_sParentHomeUrl;
+    }
+
+    function getTimeout()
+    {
+    	return $this->_iTimeout;
+    }
+	function getReturnUrl($bSsl = false)
+    {
+    	$sResult = '';
+
+    	switch($this->getProvider()) {
+    		case BX_PFW_PROVIDER_HOSTED:
+    			$sResult = $this->_sReturnUrl . 'finalize_checkout/';
+    	 		break;
+
+    		case BX_PFW_PROVIDER_EXPRESS:
+    	 		$sResult = $this->_sReturnUrl . 'confirm/';
+    	 		break;
+
+    	 	case BX_PFW_PROVIDER_RECURRING:
+    	 		$sResult = $this->_sReturnUrl . 'confirm/';
+    	 		break;
+    	}
+
+    	$sResult = BX_DOL_URL_ROOT . $sResult;
+    	if($bSsl && strpos($sResult, 'https://') === false)
+    		$sResult = 'https://' . bx_ltrim_str($sResult, 'http://');
+
+        return $sResult;
+    }
+	function getCancelUrl($bSsl = false)
+    {
+    	$sResult = BX_DOL_URL_ROOT . $this->_sCancelUrl;
+    	if($bSsl && strpos($sResult, 'https://') === false)
+    		$sResult = 'https://' . bx_ltrim_str($sResult, 'http://');
+
+        return $sResult;
+    }
+	function getResponseUrl($bSsl = false)
+    {
+    	$sResult = BX_DOL_URL_ROOT . $this->_sResponseUrl;
+    	if($bSsl && strpos($sResult, 'https://') === false)
+    		$sResult = 'https://' . bx_ltrim_str($sResult, 'http://');
+
+        return $sResult;
+    }
+    function getPfwEndpoint($sType = BX_PFW_ENDPOINT_TYPE_CALL)
+    {
+    	switch($this->_sMode) {
+    		case BX_PFW_MODE_LIVE:
+    			$sPfwEndpointCall = 'https://payflowpro.paypal.com';
+				$sPfwEndpointHosted = 'https://payflowlink.paypal.com';
+    			break;
+
+    		case BX_PFW_MODE_TEST:
+    			$sPfwEndpointCall = 'https://pilot-payflowpro.paypal.com';
+				$sPfwEndpointHosted = 'https://pilot-payflowlink.paypal.com';
+    			break;
+    	}
+
+    	$sResult = '';
+    	switch($sType) {
+    		case BX_PFW_ENDPOINT_TYPE_CALL;
+    			$sResult = $sPfwEndpointCall;
+    			break;
+
+    		case BX_PFW_ENDPOINT_TYPE_HOSTED:
+    			$sResult = $sPfwEndpointHosted;
+    			break;
+    	}
+
+    	return $sResult;
+    }
+    function getPpEndpoint($sType = BX_PFW_ENDPOINT_TYPE_HOSTED)
+    {
+    	switch($this->_sMode) {
+    		case BX_PFW_MODE_LIVE:
+    			$sPpEndpointHosted = 'https://www.paypal.com/cgi-bin/webscr';
+    			break;
+
+    		case BX_PFW_MODE_TEST:
+    			$sPpEndpointHosted = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
+    			break;
+    	}
+
+    	$sResult = '';
+    	switch($sType) {
+    		case BX_PFW_ENDPOINT_TYPE_HOSTED:
+    			$sResult = $sPpEndpointHosted;
+    			break;
+    	}
+
+    	return $sResult;
+    }
+    function isLog($sType)
+    {
+    	return isset($this->_aLogEnabled[$sType]) && (int)$this->_aLogEnabled[$sType] == 1;
+    }
+	function getLogPath()
+    {
+        return $this->_sLogPath;
+    }
+    function getLogFile($sType)
+    {
+    	return isset($this->_aLogFiles[$sType]) ? $this->_aLogFiles[$sType] : $this->_aLogFiles['error'];
+    }
+}

+ 143 - 0
modules/boonex/paypal_payflow/classes/BxPfwDb.php

@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtDb.php');
+
+class BxPfwDb extends BxPmtDb
+{
+    function BxPfwDb(&$oConfig)
+    {
+        parent::BxPmtDb($oConfig);
+    }
+
+	function getSubscription($aParams)
+    {
+        return $this->getProcessed($aParams);
+    }
+
+	function getProcessed($aParams)
+    {
+        $sDateFormat = $this->_oConfig->getDateFormat('orders');
+
+        $sMethodName = 'getRow';
+        $sWhereClause = "";
+        switch($aParams['type']) {
+            case 'id':
+                $sWhereClause = " AND `tt`.`id`='" . $aParams['id'] . "'";
+                break;
+            case 'order_id':
+                $sWhereClause = " AND `tt`.`order_id`='" . $aParams['order_id'] . "'";
+                break;
+			case 'order_profile':
+                $sWhereClause = " AND `ttp`.`order_profile`='" . $aParams['order_profile'] . "'";
+                break;
+            case 'mixed':
+                $sMethodName = 'getAll';
+                foreach($aParams['conditions'] as $sKey => $sValue)
+                    $sWhereClause .= " AND `tt`.`" . $sKey . "`='" . $sValue . "'";
+                break;
+
+        }
+
+        $sSql = "SELECT
+        		`tt`.`pending_id` AS `pending_id`,
+                `tt`.`order_id` AS `order_id`,
+                `tt`.`client_id` AS `client_id`,
+                `tt`.`seller_id` AS `seller_id`,
+                `tt`.`module_id` AS `module_id`,
+                `tt`.`item_id` AS `item_id`,
+                `tt`.`item_count` AS `item_count`,
+                `tt`.`amount` AS `amount`,
+                `tt`.`date` AS `date`,
+                DATE_FORMAT(FROM_UNIXTIME(`tt`.`date`), '" . $sDateFormat . "') AS `date_uf`,
+                `ttp`.`order` AS `order`,
+                `ttp`.`order_ref` AS `order_ref`,
+                `ttp`.`order_profile` AS `order_profile`,
+                `ttp`.`error_msg` AS `error_msg`,
+                `ttp`.`provider` AS `provider`
+            FROM `" . $this->_sPrefix . "transactions` AS `tt`
+            LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id`
+            WHERE 1" . $sWhereClause;
+
+        return $this->$sMethodName($sSql);
+    }
+	function getSubscriptionOrders($aParams)
+    {
+        $sDateFormat = $this->_oConfig->getDateFormat('orders');
+
+        $sFilterAddon = "";
+        if(!empty($aParams['filter']))
+            $sFilterAddon = " AND (DATE_FORMAT(FROM_UNIXTIME(`tt`.`date`), '" . $sDateFormat . "') LIKE '%" . $aParams['filter'] . "%' OR `tt`.`order_id` LIKE '%" . $aParams['filter'] . "%' OR `tp`.`NickName` LIKE '%" . $aParams['filter'] . "%' OR `ttp`.`order` LIKE '%" . $aParams['filter'] . "%')";
+
+        $sSql = "SELECT
+               `tt`.`id` AS `id`,
+               `ttp`.`order` AS `order`,
+               `tt`.`amount` AS `amount`,
+               `tt`.`order_id` AS `license`,
+               `tt`.`date` AS `date`,
+               DATE_FORMAT(FROM_UNIXTIME(`tt`.`date`), '" . $sDateFormat . "') AS `date_uf`,
+               '1' AS `products`,
+               `tt`.`item_count` AS `items`,
+               `tt`.`client_id` AS `user_id`,
+               `tp`.`NickName` AS `user_name`
+           FROM `" . $this->_sPrefix . "transactions` AS `tt`
+           LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id`
+           LEFT JOIN `Profiles` AS `tp` ON `tt`.`client_id`=`tp`.`ID`
+           WHERE `tt`.`seller_id`='" . $aParams['seller_id'] . "' AND `ttp`.`order_profile`<>'' " . $sFilterAddon . "
+           ORDER BY `tt`.`date` DESC
+           LIMIT " . $aParams['start'] . ", " . $aParams['per_page'];
+
+        return $this->getAll($sSql);
+    }
+    function getSubscriptionOrdersCount($aParams)
+    {
+        $sDateFormat = $this->_oConfig->getDateFormat('orders');
+
+        $sFilterAddon = "";
+        if(!empty($aParams['filter']))
+            $sFilterAddon = " AND (DATE_FORMAT(FROM_UNIXTIME(`tt`.`date`), '" . $sDateFormat . "') LIKE '%" . $aParams['filter'] . "%' OR `tt`.`order_id` LIKE '%" . $aParams['filter'] . "%' OR `tp`.`NickName` LIKE '%" . $aParams['filter'] . "%' OR `ttp`.`order` LIKE '%" . $aParams['filter'] . "%')";
+
+        $sSql = "SELECT
+               COUNT(`tt`.`id`)
+           FROM `" . $this->_sPrefix . "transactions` AS `tt`
+           LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id`
+           LEFT JOIN `Profiles` AS `tp` ON `tt`.`client_id`=`tp`.`ID`
+           WHERE `tt`.`seller_id`='" . $aParams['seller_id'] . "' AND `ttp`.`order_profile`<>'' " . $sFilterAddon . "
+           LIMIT 1";
+
+        return (int)$this->getOne($sSql);
+    }
+	function getHistoryOrders($aParams)
+    {
+        $sDateFormat = $this->_oConfig->getDateFormat('orders');
+
+        $sFilterAddon = $aParams['seller_id'] != BX_PMT_EMPTY_ID ? " AND `tt`.`seller_id`='" . $aParams['seller_id'] . "'" : " AND `tt`.`seller_id`<>'" . BX_PMT_ADMINISTRATOR_ID . "'";
+        if(!empty($aParams['filter']))
+            $sFilterAddon = " AND (DATE_FORMAT(FROM_UNIXTIME(`tt`.`date`), '" . $sDateFormat . "') LIKE '%" . $aParams['filter'] . "%' OR `tt`.`order_id` LIKE '%" . $aParams['filter'] . "%' OR `tp`.`NickName` LIKE '%" . $aParams['filter'] . "%' OR `ttp`.`order` LIKE '%" . $aParams['filter'] . "%')";
+
+        $sSql = "SELECT
+               `tt`.`id` AS `id`,
+               `ttp`.`order` AS `order`,
+               `ttp`.`order_profile` AS `order_profile`,
+               `tt`.`amount` AS `amount`,
+               `tt`.`order_id` AS `license`,
+               `tt`.`date` AS `date`,
+               DATE_FORMAT(FROM_UNIXTIME(`tt`.`date`), '" . $sDateFormat . "') AS `date_uf`,
+               '1' AS `products`,
+               `tt`.`item_count` AS `items`,
+               `tp`.`ID` AS `user_id`,
+               `tp`.`NickName` AS `user_name`
+           FROM `" . $this->_sPrefix . "transactions` AS `tt`
+           LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id`
+           LEFT JOIN `Profiles` AS `tp` ON `tt`.`seller_id`=`tp`.`ID`
+           WHERE `tt`.`client_id`='" . $aParams['user_id'] . "' " . $sFilterAddon . "
+           ORDER BY `tt`.`date` DESC
+           LIMIT " . $aParams['start'] . ", " . $aParams['per_page'];
+
+        return $this->getAll($sSql);
+    }
+}

+ 19 - 0
modules/boonex/paypal_payflow/classes/BxPfwDetails.php

@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtDetails.php');
+
+class BxPfwDetails extends BxPmtDetails
+{
+    /*
+     * Constructor.
+     */
+    function BxPfwDetails(&$oDb, &$oConfig)
+    {
+    	parent::BxPmtDetails($oDb, $oConfig);
+    }
+}

+ 134 - 0
modules/boonex/paypal_payflow/classes/BxPfwExpressCheckout.php

@@ -0,0 +1,134 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once("BxPfwPayPal.php");
+
+class BxPfwExpressCheckout extends BxPfwPayPal
+{
+	function BxPfwExpressCheckout($oDb, $oConfig, $aConfig)
+	{
+		parent::BxPfwPayPal($oDb, $oConfig, $aConfig);
+
+		$this->_aCallParameters['TENDER'] = 'P';
+	}
+
+	function initializeCheckout($iPendingId, $aCartInfo)
+	{
+		$this->_setExpressCheckout($iPendingId, $aCartInfo);
+
+		$aResponse = $this->_executeCall();
+		if($aResponse === false)
+			return false;
+
+		$this->_logInfo(__METHOD__, $aResponse);
+
+		$sToken = process_db_input($aResponse['TOKEN'], BX_TAGS_STRIP);
+
+		$sRedirectUrl = $this->_oConfig->getPpEndpoint(BX_PFW_ENDPOINT_TYPE_HOSTED);
+		$sRedirectUrl = bx_append_url_params($sRedirectUrl, array('cmd' => '_express-checkout', 'token' => $sToken));
+
+		header("Location: " . $sRedirectUrl);
+		exit;
+	}
+
+	function confirmCheckout($sToken, $sPayerId)
+	{
+		$this->_getExpressCheckout($sToken, $sPayerId);
+
+		$aResponse = $this->_executeCall();
+		if($aResponse === false)
+			return false;
+
+		$this->_logInfo(__METHOD__, $aResponse);
+		return $aResponse;
+	}
+
+	function finalizeCheckout(&$aData)
+	{
+		$this->_logInfo(__METHOD__, $aData);
+
+		$iPending = (int)$aData['pendingid'];
+		$aPending = $this->_oDb->getPending(array('type' => 'id', 'id' => $iPending));
+        if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
+            return array('code' => 0, 'message' => _t($this->_sLangsPrefix . 'err_already_processed'));
+
+		$sToken = process_db_input($aData['token'], BX_TAGS_STRIP);
+		$sPayerId = process_db_input($aData['payerid'], BX_TAGS_STRIP);
+		$sPayerName = process_db_input($aData['payername'], BX_TAGS_STRIP);
+		$sPayerEmail = process_db_input($aData['payeremail'], BX_TAGS_STRIP);
+		$sAmt = process_db_input($aData['amt'], BX_TAGS_STRIP);
+		$this->_doExpressCheckout($sToken, $sPayerId, $sAmt);
+
+		$aResponse = $this->_executeCall();
+		if($aResponse === false)
+			return array('code' => 0, 'message' => _t($this->_sLangsPrefix . 'err_unknown'));
+
+		$this->_logInfo(__METHOD__, $aResponse);
+
+		$iResponseCode = (int)$aResponse['RESULT'];
+		$sResponseMessage = process_db_input($aResponse['RESPMSG'], BX_TAGS_STRIP);
+
+		$aResult = array(
+			'code' => $iResponseCode == 0 ? 1 : 0, 
+			'message' => $iResponseCode == 0 ? _t($this->_sLangsPrefix . 'msg_accepted') : $sResponseMessage,
+			'pending_id' => $iPending,
+			'payer_name' => $sPayerName, 
+			'payer_email' => $sPayerEmail,
+		);
+
+        //--- Update pending transaction ---//
+        $this->_oDb->updatePending($iPending, array(
+            'order' => process_db_input($aResponse['PPREF'], BX_TAGS_STRIP),
+        	'order_ref' => process_db_input($aResponse['PNREF'], BX_TAGS_STRIP),
+            'error_code' => $aResult['code'],
+            'error_msg' => $sResponseMessage
+        ));
+
+		return $aResult;
+	}
+
+    protected function _setExpressCheckout($iPendingId, $aCartInfo)
+    {
+    	$this->_aValidationParameters = array('TENDER', 'TRXTYPE', 'ACTION', 'RETURNURL', 'CANCELURL', 'AMT', 'CURRENCY');
+
+		$this->_aCallParameters['ACTION'] = 'S';
+
+		$this->_aCallParameters['RETURNURL'] = $this->_oConfig->getReturnUrl() . $this->_sName . '/' . $aCartInfo['vendor_id'] . '/';
+		$this->_aCallParameters['CANCELURL'] = $this->_oConfig->getCancelUrl();
+
+		$this->_aCallParameters['AMT'] = sprintf( "%.2f", (float)$aCartInfo['items_price']);
+		$this->_aCallParameters['CURRENCY'] = $aCartInfo['vendor_currency_code'];
+		$this->_aCallParameters['CUSTOM'] = $iPendingId;
+
+		foreach($aCartInfo['items'] as $iIndex => $aItem)
+			$this->_aCallParameters = array_merge($this->_aCallParameters, array(
+				'L_NAME' . $iIndex => $aItem['title'],
+				'L_DESC' . $iIndex => $aItem['description'],
+				'L_COST' . $iIndex => $aItem['price'],
+				'L_TAXAMT' . $iIndex => '0.00',
+				'L_QTY' . $iIndex => $aItem['quantity'],
+				'L_AMT' . $iIndex => sprintf( "%.2f", (float)$aItem['quantity'] * $aItem['price']),
+			));
+    }
+
+    protected function _getExpressCheckout($sToken, $sPayerId)
+    {
+    	$this->_aValidationParameters = array('TENDER', 'TRXTYPE', 'ACTION');
+
+    	$this->_aCallParameters['ACTION'] = 'G';
+    	$this->_aCallParameters['TOKEN'] = $sToken;
+    }
+
+	protected function _doExpressCheckout($sToken, $sPayerId, $sAmount)
+    {
+    	$this->_aValidationParameters = array('TENDER', 'TRXTYPE', 'ACTION', 'TOKEN', 'PAYERID', 'AMT');
+
+    	$this->_aCallParameters['ACTION'] = 'D';
+    	$this->_aCallParameters['TOKEN'] = $sToken;
+    	$this->_aCallParameters['PAYERID'] = $sPayerId;
+    	$this->_aCallParameters['AMT'] = $sAmount;
+    }
+}

+ 65 - 0
modules/boonex/paypal_payflow/classes/BxPfwHostedCheckout.php

@@ -0,0 +1,65 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once("BxPfwPayPal.php");
+require_once("BxPfwSecureToken.php");
+
+class BxPfwHostedCheckout extends BxPfwPayPal
+{
+	function BxPfwHostedCheckout($oDb, $oConfig, $aConfig)
+	{
+		parent::BxPfwPayPal($oDb, $oConfig, $aConfig);
+
+		$this->_aCallParameters['TENDER'] = 'C';
+	}
+
+	function initializeCheckout($iPendingId, $aCartInfo)
+	{
+		$oSecureToken = new BxPfwSecureToken($this->_oDb, $this->_oConfig, $this->_aConfig); 
+		$aSecureToken = $oSecureToken->getSecureToken($iPendingId, $aCartInfo);
+		if($aSecureToken === false)
+			return false;
+
+		$sRequestUrl = $this->_oConfig->getPfwEndpoint(BX_PFW_ENDPOINT_TYPE_HOSTED);
+		$sRequestData = array(
+			'MODE' => $this->_oConfig->getMode() == BX_PFW_MODE_LIVE ? 'LIVE' : 'TEST', 
+    		'SECURETOKEN' => $aSecureToken['token'], 
+    		'SECURETOKENID' => $aSecureToken['token_id']
+    	);
+
+		Redirect($sRequestUrl, $sRequestData, 'post');
+		exit;
+	}
+
+	function finalizeCheckout(&$aData)
+	{
+		$this->_logInfo(__METHOD__, $aData);
+
+		$iPending = (int)$aData['INVNUM'];
+		$aPending = $this->_oDb->getPending(array('type' => 'id', 'id' => $iPending));
+        if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
+            return array('code' => 0, 'message' => _t($this->_sLangsPrefix . 'err_already_processed'));
+
+		$iResponseCode = (int)$aData['RESULT'];
+		$sResponseMessage = process_db_input($aData['RESPMSG'], BX_TAGS_STRIP);
+
+		$aResult = array(
+			'code' => $iResponseCode == 0 ? 1 : 0, 
+			'message' => $iResponseCode == 0 ? _t($this->_sLangsPrefix . 'msg_accepted') : $sResponseMessage,
+			'pending_id' => $iPending
+		);
+
+        //--- Update pending transaction ---//
+        $this->_oDb->updatePending($iPending, array(
+            'order' => process_db_input($aData['PPREF'], BX_TAGS_STRIP),
+        	'order_ref' => process_db_input($aData['PNREF'], BX_TAGS_STRIP),
+            'error_code' => $aResult['code'],
+            'error_msg' => $sResponseMessage
+        ));
+
+		return $aResult;
+	}
+}

+ 88 - 0
modules/boonex/paypal_payflow/classes/BxPfwLog.php

@@ -0,0 +1,88 @@
+<?php
+class BxPfwLog
+{
+	protected $sLogDateFormat;
+	protected $sPath;
+
+	protected $bLogInfo;
+	protected $sFileInfo;
+
+	protected $bLogError;
+	protected $sFileError;
+
+	protected function BxPfwLog()
+	{
+		$oMain = BxDolModule::getInstance('BxPfwModule');
+
+		$this->sLogDateFormat = 'm.d.y H:i:s';
+		$this->sPath = $oMain->_oConfig->getLogPath();
+
+		$this->bLogInfo = $oMain->_oConfig->isLog('info');
+		$this->sFileInfo = $this->bLogInfo ? $oMain->_oConfig->getLogFile('info') : '';
+
+		$this->bLogError = $oMain->_oConfig->isLog('error');
+		$this->sFileError = $this->bLogError ? $oMain->_oConfig->getLogFile('error') : '';
+	}
+
+	public static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses']['BxPfwLog']))
+            $GLOBALS['bxDolClasses']['BxPfwLog'] = new BxPfwLog();
+
+        return $GLOBALS['bxDolClasses']['BxPfwLog'];
+    }
+
+	public function logInfo() {
+		if(!$this->bLogInfo)
+			return;
+
+		$sFile = $this->sFileInfo;
+		$sMessage = "--- Info: {date}";
+
+		$this->_log($sFile, $sMessage);
+
+		$aArgs = func_get_args();
+		foreach($aArgs as $mixedArg)
+			$this->_log($sFile, $mixedArg);
+
+		$this->_log($sFile, $sMessage . "\n");
+	}
+
+	public function logError() {
+		if(!$this->bLogError)
+			return;
+
+		$sFile = $this->sFileError;
+		$sMessage = "--- Error Occured: {date}";
+
+		$this->_log($sFile, $sMessage);
+
+		$aArgs = func_get_args();
+		foreach($aArgs as $mixedArg)
+			$this->_log($sFile, $mixedArg);
+
+		$this->_log($sFile, $sMessage . "\n");
+	}
+
+	protected function _log($sFile, $mixedValue)
+	{
+	    $rHandle = fopen($this->sPath . $sFile, 'a');
+	    if(!$rHandle)
+	    	return;
+
+        if(is_array($mixedValue) || is_object($mixedValue)) {
+            ob_start();
+            print_r($mixedValue);
+            $sValue = ob_get_contents();
+            ob_end_clean();
+            fwrite($rHandle, "$sValue\n");
+        }
+        else {
+        	$mixedValue = str_replace('{date}', date($this->sLogDateFormat), $mixedValue);
+
+            fwrite($rHandle, "$mixedValue\n");
+        }
+
+		fclose($rHandle);
+	}
+}

+ 312 - 0
modules/boonex/paypal_payflow/classes/BxPfwModule.php

@@ -0,0 +1,312 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtModule.php');
+
+require_once('BxPfwLog.php');
+
+define('BX_PFW_MODE_LIVE', 1);
+define('BX_PFW_MODE_TEST', 2);
+
+define('BX_PFW_ENDPOINT_TYPE_CALL', 'call');
+define('BX_PFW_ENDPOINT_TYPE_HOSTED', 'hosted');
+
+define('BX_PFW_PROVIDER_EXPRESS', 'express_checkout');
+define('BX_PFW_PROVIDER_HOSTED', 'hosted_checkout');
+define('BX_PFW_PROVIDER_RECURRING', 'recurring_billing');
+
+class BxPfwModule extends BxPmtModule
+{
+    function BxPfwModule($aModule)
+    {
+        parent::BxPmtModule($aModule);
+
+		$this->_oTemplate->init($this);
+    }
+
+    /**
+     * System Methods
+     */
+
+    /**
+     * Action Methods
+     */
+    function actionCart()
+    {
+    	$this->_oTemplate->addJsTranslation(array(
+		    $this->_sLangsPrefix . 'err_nothing_selected'
+		));
+
+    	bx_import('PageCart', $this->_aModule);
+    	$oPage = new BxPfwPageCart($this);
+
+    	$aParams = array(
+            'index' => 1,
+            'title' => array(
+                'page' => _t($this->_sLangsPrefix . 'pcpt_view_cart')
+            ),
+            'content' => array(
+                'page_main_code' => $oPage->getCode()
+            )
+        );
+        $this->_oTemplate->getPageCode($aParams);
+    }
+
+	function actionHistory($sType = '')
+    {
+    	bx_import('PageHistory', $this->_aModule);
+    	$oPage = new BxPfwPageHistory($sType, $this);
+
+    	$aParams = array(
+            'index' => 2,
+            'css' => array('orders.css', '_orders.css'),
+    		'js' => array('orders.js', '_orders.js'),
+            'title' => array(
+                'page' => _t($this->_sLangsPrefix . 'pcpt_cart_history')
+            ),
+            'content' => array(
+                'page_main_code' => $oPage->getCode(),
+            	'more_code' => $this->getMoreWindow(),
+				'js_code' => $this->_oTemplate->getJsCode('orders', true)
+            )
+        );
+        $this->_oTemplate->getPageCode($aParams);
+    }
+
+	function actionOrders($sType = '')
+    {
+    	bx_import('PageOrders', $this->_aModule);
+    	$oPage = new BxPfwPageOrders($sType, $this);
+
+    	$aParams = array(
+            'index' => 3,
+            'css' => array('orders.css', '_orders.css'),
+    		'js' => array('orders.js', '_orders.js'),
+            'title' => array(
+                'page' => _t($this->_sLangsPrefix . 'pcpt_view_orders')
+            ),
+            'content' => array(
+                'page_main_code' => $oPage->getCode(),
+            	'more_code' => $this->getMoreWindow(),
+				'manual_order_code' => $this->getManualOrderWindow(),
+				'js_code' => $this->_oTemplate->getJsCode('orders', true)
+            )
+        );
+        $this->_oTemplate->getPageCode($aParams);
+    }
+
+    function actionDetails()
+    {
+    	bx_import('PageDetails', $this->_aModule);
+    	$oPage = new BxPfwPageDetails($this);
+
+    	$aParams = array(
+            'index' => 4,
+            'title' => array(
+                'page' => _t($this->_sLangsPrefix . 'pcpt_details')
+            ),
+            'content' => array(
+                'page_main_code' => $oPage->getCode()
+            )
+        );
+        $this->_oTemplate->getPageCode($aParams);
+    }
+
+	function actionAdmin($sName = '')
+    {
+        $GLOBALS['iAdminPage'] = 1;
+        require_once(BX_DIRECTORY_PATH_INC . 'admin_design.inc.php');
+
+        $sUri = $this->_oConfig->getUri();
+
+        check_logged();
+        if(!@isAdmin()) {
+            send_headers_page_changed();
+            login_form("", 1);
+            exit;
+        }
+
+        //--- Process actions ---//
+        $mixedResultSettings = '';
+        if(isset($_POST['save']) && isset($_POST['cat'])) {
+            $mixedResultSettings = $this->setSettings($_POST);
+        }
+        //--- Process actions ---//
+
+		$aDetailsBox = $this->getDetailsForm(BX_PMT_ADMINISTRATOR_ID);
+		$aPendingOrdersBox = $this->getOrdersBlock(BX_PMT_ORDERS_TYPE_PENDING, BX_PMT_ADMINISTRATOR_ID);
+		$aProcessedOrdersBox = $this->getOrdersBlock(BX_PMT_ORDERS_TYPE_PROCESSED, BX_PMT_ADMINISTRATOR_ID);
+		$aSubscriptionOrdersBox = $this->getOrdersBlock(BX_PMT_ORDERS_TYPE_SUBSCRIPTION, BX_PMT_ADMINISTRATOR_ID);
+
+		$sContent = ''; 
+		$sContent .= $this->_oTemplate->getJsCode('orders', true);
+        $sContent .= DesignBoxAdmin(_t($this->_sLangsPrefix . 'bcpt_settings'), $GLOBALS['oAdmTemplate']->parseHtmlByName('design_box_content.html', array('content' => $this->getSettingsForm($mixedResultSettings))));
+        $sContent .= DesignBoxAdmin(_t($this->_sLangsPrefix . 'bcpt_details'), $GLOBALS['oAdmTemplate']->parseHtmlByName('design_box_content.html', array('content' => $aDetailsBox[0])));
+        $sContent .= DesignBoxAdmin(_t($this->_sLangsPrefix . 'bcpt_pending_orders'), $aPendingOrdersBox[0]);
+		$sContent .= DesignBoxAdmin(_t($this->_sLangsPrefix . 'bcpt_processed_orders'), $aProcessedOrdersBox[0]);
+		$sContent .= DesignBoxAdmin(_t($this->_sLangsPrefix . 'bcpt_subscription_orders'), $aSubscriptionOrdersBox[0]);
+		$sContent .= $this->getMoreWindow();
+		$sContent .= $this->getManualOrderWindow();
+
+		$this->_oTemplate->addAdminJs(array('orders.js', '_orders.js'));
+		$this->_oTemplate->addAdminCss(array('orders.css', '_orders.css'));
+
+        $aParams = array(
+            'title' => array(
+                'page' => _t($this->_sLangsPrefix . 'pcpt_administration')
+            ),
+            'content' => array(
+                'page_main_code' => $sContent
+            )
+        );
+        $this->_oTemplate->getPageCodeAdmin($aParams);
+    }
+
+	/**
+     * Action Methods: PayFlow Integration
+     * Perform payment confirmation for Express Checkout 
+     */
+	function actionConfirm($sProvider, $iVendorId)
+	{
+		$sToken = bx_get('token');
+		$sPayerId = bx_get('PayerID');
+		if($sToken === false || $sPayerId === false) {
+			$this->_oTemplate->getPageCodeError($this->_sLangsPrefix . 'err_wrong_data');
+			return;
+		}
+
+		$sToken = process_db_input($sToken, BX_TAGS_STRIP);
+		$sPayerId = process_db_input($sPayerId, BX_TAGS_STRIP);
+
+		$oProvider = $this->_getProvider($sProvider, $iVendorId);
+		if(is_string($oProvider)) {
+			$this->_oTemplate->getPageCodeError($oProvider);
+			return;
+		}
+
+		$aOrderInfo = $oProvider->confirmCheckout($sToken, $sPayerId);
+		if($aOrderInfo === false) {
+			$this->_oTemplate->getPageCodeError($this->_sLangsPrefix . 'err_unknown');
+        	return;
+		}
+
+		$iPendingId = (int)$aOrderInfo['CUSTOM'];
+    	$aPending = $this->_oDb->getPending(array('type' => 'id', 'id' => $iPendingId));
+        if(empty($aPending) || !is_array($aPending)) {
+        	$this->getPageCodeError($this->_sLangsPrefix . 'wrong_data');
+        	return;
+        }
+
+        $aCartInfo = $this->_oCart->getInfo((int)$aPending['client_id'], $iVendorId, $aPending['items']);
+		$this->_oTemplate->displayConfirmPage($sProvider, $iVendorId, $aOrderInfo, $aCartInfo);
+	}
+
+	/**
+	 * Currently is used with errors returned from Hosted Checkout Pages.
+	 */
+	function actionResponse($sProvider, $iVendorId)
+	{
+		$oProvider = $this->_getProvider($sProvider, $iVendorId);
+		if(is_string($oProvider)) {
+			$this->_oTemplate->getPageCodeError($oProvider);
+			return;
+		}
+
+		$aResult = $oProvider->processResponse($_REQUEST);
+
+		$sMethod = 'getPageCodeResponse';
+		$sMessage = $this->_sLangsPrefix . 'msg_successfully_done';
+		if((int)$aResult['code'] != 0) {
+			$sMethod = 'getPageCodeError';
+			$sMessage = $this->_sLangsPrefix . 'err_unknown';
+		}
+
+		$this->_oTemplate->$sMethod($sMessage);
+		return;
+	}
+
+	/**
+	 * Subscription related actions
+	 */
+	function actionCancelSubscription()
+	{
+		$iId = (int)bx_get('id');
+		$sType = process_db_input(bx_get('type'), BX_TAGS_STRIP);
+
+		if(empty($iId) || empty($sType) || !in_array($sType, array(BX_PMT_ORDERS_TYPE_SUBSCRIPTION, BX_PMT_ORDERS_TYPE_HISTORY)))
+			return $this->_onResultJson(array('code' => 1, 'message' => '_bx_pfw_err_wrong_data'));
+
+		$aOrder = $this->_oDb->getProcessed(array('type' => 'id', 'id' => $iId));
+		if(empty($aOrder))
+			return $this->_onResultJson(array('code' => 1, 'message' => '_bx_pfw_err_wrong_data'));
+
+		if(!isAdmin() && (int)$aOrder['client_id'] != getLoggedId())
+			return $this->_onResultJson(array('code' => 2, 'message' => '_bx_pfw_err_not_allowed'));
+
+		$oProvider = $this->_getProvider($aOrder['provider'], $aOrder['seller_id']);
+		if(is_string($oProvider))
+			return $this->_onResultJson(array('code' => 3, 'message' => $oProvider));
+
+		if(!$oProvider->cancelSubscription($aOrder))
+			return $this->_onResultJson(array('code' => 4, 'message' => '_bx_pfw_err_unknown'));
+
+		return $this->_onResultJson(array('code' => 0, 'message' => '_bx_pfw_msg_subscription_canceled'));
+	}
+
+    /**
+     * Service Methods
+     */
+	function serviceProlongSubscription($sOrderId)
+	{
+		$aOrder = $this->_oDb->getProcessed(array('type' => 'order_id', 'order_id' => $sOrderId));
+		if(empty($aOrder))
+			return array('code' => 1, 'message' => '_bx_pfw_err_wrong_data');
+
+		$oProvider = $this->_getProvider($aOrder['provider'], $aOrder['seller_id']);
+		if(is_string($oProvider))
+			return array('code' => 2, 'message' => $oProvider);
+
+		if(!method_exists($oProvider, 'prolongSubscription'))
+			return array('code' => 3, 'message' => '_bx_pfw_err_not_available');
+
+		$aResult = $oProvider->prolongSubscription($aOrder);
+		if($aResult['code'] != 0)
+			return $aResult;
+
+		//--- Register payment by pending in associated modules
+		$this->_oCart->updateInfo($aResult['pending_id']);
+
+		return $aResult;
+	}
+
+	/**
+     * Protected Methods
+     */
+	protected function _getProvider($sProvider, $iVendorId)
+	{
+		if($iVendorId == BX_PMT_EMPTY_ID)
+			return $this->_sLangsPrefix . 'err_unknown_vendor';
+
+        $aProvider = $this->_oDb->getVendorInfoProviders($iVendorId, $sProvider);
+        $sClassPath = !empty($aProvider['class_file']) ? BX_DIRECTORY_PATH_ROOT . $aProvider['class_file'] : $this->_oConfig->getClassPath() . $aProvider['class_name'] . '.php';
+        if(empty($aProvider) || !file_exists($sClassPath))
+        	return $this->_sLangsPrefix . 'err_incorrect_provider';
+
+        require_once($sClassPath);
+        return new $aProvider['class_name']($this->_oDb, $this->_oConfig, $aProvider);
+	}
+	
+	protected function _onResultJson($aResult)
+    {
+        $oJson = new Services_JSON();
+
+        if(isset($aResult['message']))
+        	$aResult['message'] = _t($aResult['message']);
+
+		header('Content-Type:text/javascript; charset=utf-8');
+        return $oJson->encode($aResult);
+    }
+}

+ 19 - 0
modules/boonex/paypal_payflow/classes/BxPfwOrders.php

@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtOrders.php');
+
+class BxPfwOrders extends BxPmtOrders
+{
+    /*
+     * Constructor.
+     */
+    function BxPfwOrders($iUserId, &$oDb, &$oConfig, &$oTemplate)
+    {
+    	parent::BxPmtOrders($iUserId, $oDb, $oConfig, $oTemplate);
+    }
+}

+ 32 - 0
modules/boonex/paypal_payflow/classes/BxPfwPageCart.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+bx_import('BxDolPageView');
+
+class BxPfwPageCart extends BxDolPageView
+{
+	protected $_oMain;
+
+    function BxPfwPageCart(&$oMain)
+    {
+        parent::BxDolPageView('bx_pfw_cart');
+
+        $this->_oMain = $oMain;
+
+        $GLOBALS['oTopMenu']->setCurrentProfileID($this->_oMain->getUserId());
+        $GLOBALS['oTopMenu']->setCustomVar('sys_payment_module_uri', $this->_oMain->_oConfig->getUri());
+    }
+
+	function getBlockCode_Featured()
+    {
+        return $this->_oMain->getCartContent(BX_PMT_ADMINISTRATOR_ID);
+    }
+
+    function getBlockCode_Common()
+    {
+        return $this->_oMain->getCartContent(BX_PMT_EMPTY_ID);
+    }
+}

+ 27 - 0
modules/boonex/paypal_payflow/classes/BxPfwPageDetails.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+bx_import('BxDolPageView');
+
+class BxPfwPageDetails extends BxDolPageView
+{
+	protected $_oMain;
+
+    function BxPfwPageDetails(&$oMain)
+    {
+        parent::BxDolPageView('bx_pfw_details');
+
+        $this->_oMain = $oMain;
+
+        $GLOBALS['oTopMenu']->setCurrentProfileID($this->_oMain->getUserId());
+        $GLOBALS['oTopMenu']->setCustomVar('sys_payment_module_uri', $this->_oMain->_oConfig->getUri());
+    }
+
+	function getBlockCode_Details()
+    {
+        return $this->_oMain->getDetailsForm();
+    }
+}

+ 29 - 0
modules/boonex/paypal_payflow/classes/BxPfwPageHistory.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+bx_import('BxDolPageView');
+
+class BxPfwPageHistory extends BxDolPageView
+{
+	protected $_oMain;
+    protected $_iVendorId;
+
+    function BxPfwPageHistory($sType, &$oMain)
+    {
+        parent::BxDolPageView('bx_pfw_history');
+
+        $this->_iVendorId = $sType == 'site' ? BX_PMT_ADMINISTRATOR_ID : BX_PMT_EMPTY_ID;
+        $this->_oMain = $oMain;
+
+        $GLOBALS['oTopMenu']->setCurrentProfileID($this->_oMain->getUserId());
+        $GLOBALS['oTopMenu']->setCustomVar('sys_payment_module_uri', $this->_oMain->_oConfig->getUri());
+    }
+
+	function getBlockCode_History()
+    {
+        return $this->_oMain->getCartHistory($this->_iVendorId);
+    }
+}

+ 32 - 0
modules/boonex/paypal_payflow/classes/BxPfwPageOrders.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+bx_import('BxDolPageView');
+
+class BxPfwPageOrders extends BxDolPageView
+{
+	protected $_oMain;
+    protected $_sType;
+
+    function BxPfwPageOrders($sType, &$oMain)
+    {
+        parent::BxDolPageView('bx_pfw_orders');
+
+        $this->_sType = $sType;
+        $this->_oMain = $oMain;
+
+        $GLOBALS['oTopMenu']->setCurrentProfileID($this->_oMain->getUserId());
+        $GLOBALS['oTopMenu']->setCustomVar('sys_payment_module_uri', $this->_oMain->_oConfig->getUri());
+    }
+
+	function getBlockCode_Orders()
+    {
+        if(empty($this->_sType))
+            $this->_sType = BX_PMT_ORDERS_TYPE_PROCESSED;
+
+        return $this->_oMain->getOrdersBlock($this->_sType);
+    }
+}

+ 169 - 0
modules/boonex/paypal_payflow/classes/BxPfwPayPal.php

@@ -0,0 +1,169 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once("BxPfwProvider.php");
+
+class BxPfwPayPal extends BxPfwProvider
+{
+	protected $_aValidationParameters;
+
+	protected $_aCallParameters;
+	protected $_aCallCredentials;
+
+	protected $_sLangsPrefix;
+
+	function BxPfwPayPal($oDb, $oConfig, $aConfig)
+	{
+		parent::BxPfwProvider($oDb, $oConfig, $aConfig);
+
+		$this->_aCallParameters = array(
+			'TENDER' => '',
+			'TRXTYPE' => 'S',
+			'ACTION' => '',
+			'VERBOSITY' => 'HIGH',
+			'BUTTONSOURCE' => 'Boonex_SP',
+		);
+
+		$this->_aCallCredentials = array(
+			'PARTNER' => $this->getOption('partner'),
+			'VENDOR' => $this->getOption('vendor'),
+			'USER' => $this->getOption('user'),
+			'PWD' => $this->getOption('password'),
+		);
+
+		$this->_oConfig->setProvider($this->_sName);
+		$this->_oConfig->setMode($this->getOption('mode'));
+
+		$this->_sLangsPrefix = $this->_oConfig->getLangsPrefix();
+	}
+
+	function processResponse(&$aData)
+	{
+		if(empty($aData) || !is_array($aData)) {
+			$this->_logError("--- An unknown error occured.");
+
+			return $aResult = array(
+				'code' => -1,
+				'message' => $this->_sLangsPrefix . 'err_unknown'
+			);
+		}
+
+		if((int)$aData['RESULT'] != 0) {
+			$sLog .= "--- An error occured.";
+			$sLog .= "\n--- Response code: " . $aData['RESULT'] . ",";
+			$sLog .= "\n--- Response message: " . $aData['RESPMSG'];
+			$this->_logError($sLog);
+
+			return array(
+				'code' => $aData['RESULT'],
+				'message' => $aData['RESPMSG']
+			);
+		}
+
+		return array(
+			'code' => 0,
+			'message' => $this->_sLangsPrefix . 'msg_successfully_done'
+		);
+	}
+
+	protected function _executeCall()
+	{
+		if(!$this->_validateCallParameters())
+			return false;
+
+		$sRequestUrl = $this->_oConfig->getPfwEndpoint(BX_PFW_ENDPOINT_TYPE_CALL);
+		$sRequestParams = $this->_getRequestParams();
+
+		$rCurl = curl_init ();
+		curl_setopt($rCurl, CURLOPT_URL,  $sRequestUrl);
+		curl_setopt($rCurl, CURLOPT_VERBOSE, 1);
+		curl_setopt($rCurl, CURLOPT_POST, true);
+		curl_setopt($rCurl, CURLOPT_POSTFIELDS, $sRequestParams);
+		curl_setopt($rCurl, CURLOPT_SSL_VERIFYPEER, false);
+		curl_setopt($rCurl, CURLOPT_SSL_VERIFYHOST, false);
+		curl_setopt($rCurl, CURLOPT_RETURNTRANSFER, true);
+		curl_setopt($rCurl, CURLOPT_CONNECTTIMEOUT ,0);
+		curl_setopt($rCurl, CURLOPT_TIMEOUT, $this->_oConfig->getTimeout());
+
+		$sResponse = curl_exec($rCurl);
+		$aResponse = $this->_decodeRequestResponse($sResponse);
+
+		if(empty($aResponse) || !is_array($aResponse)) {
+			$sLog .= "Empty response for API call(" . $this->_aCallParameters['ACTION'] . "). ";
+			$this->_logError(__METHOD__, $sLog);
+
+			return false;
+		}
+
+		if((int)$aResponse['RESULT'] != 0) {
+			$sLog .= "API call(" . $this->_aCallParameters['ACTION'] . ") error.";
+			$sLog .= "\nResponse code: " . $aResponse['RESULT'] . ",";
+			$sLog .= "\nResponse message: " . $aResponse['RESPMSG'];
+			$this->_logError(__METHOD__, $sLog);
+		}
+
+		return $aResponse;
+	}
+
+	protected function _validateCallParameters()
+	{
+		if(!is_array($this->_aValidationParameters) || empty($this->_aValidationParameters))
+			return true;
+
+		foreach($this->_aValidationParameters as $sName)
+			if(!array_key_exists($sName, $this->_aCallParameters)) {
+				$sLog .= "--- " . __METHOD__ . ": " . $sName . " is listed as a required variable and not present in the call variables.'";
+				$this->_logError($sLog);
+
+				return false;
+			}
+
+		return true;
+	}
+
+	protected function _getRequestParams()
+	{
+		$s = '';
+
+		foreach($this->_aCallCredentials as $sKey => $sValue)
+			$s .= $sKey . '['.strlen($sValue).']=' . $sValue . '&';
+
+		foreach($this->_aCallParameters as $sKey => $sValue)
+			$s .= $sKey . '['.strlen($sValue).']=' . $sValue . '&';
+
+		return $s;
+	}
+
+	protected function _decodeRequestResponse($sResponse)
+	{		
+		$aResponse = array();
+
+		$aKeys = explode('&', $sResponse);
+		foreach($aKeys as $sKeyValue) {
+			$aKeyValue = explode('=', $sKeyValue);
+			if(isset($aKeyValue[1]))
+				$aResponse[$aKeyValue[0]] = $aKeyValue[1];
+		}
+
+		return $aResponse;
+	}
+
+	protected function _logInfo()
+	{
+		$aArgs = func_get_args();
+
+		$oLog = BxPfwLog::getInstance();
+		call_user_func_array(array($oLog, 'logInfo'), $aArgs);
+	}
+
+	protected function _logError($mixedValue)
+	{
+		$aArgs = func_get_args();
+
+		$oLog = BxPfwLog::getInstance();
+		call_user_func_array(array($oLog, 'logError'), $aArgs);
+	}
+}

+ 15 - 0
modules/boonex/paypal_payflow/classes/BxPfwProfileFields.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtProfileFields.php');
+
+class BxPfwProfileFields extends BxPmtProfileFields
+{
+    function BxPfwProfileFields($iAreaID)
+    {
+        parent::BxPmtProfileFields($iAreaID);
+    }
+}

+ 22 - 0
modules/boonex/paypal_payflow/classes/BxPfwProvider.php

@@ -0,0 +1,22 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtProvider.php');
+
+class BxPfwProvider extends BxPmtProvider
+{
+	protected $_aConfig;
+
+    /**
+     * Constructor
+     */
+    function BxPfwProvider($oDb, $oConfig, $aConfig)
+    {
+        parent::BxPmtProvider($oDb, $oConfig, $aConfig);
+
+        $this->_aConfig = $aConfig;
+    }
+}

+ 258 - 0
modules/boonex/paypal_payflow/classes/BxPfwRecurringBilling.php

@@ -0,0 +1,258 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once("BxPfwExpressCheckout.php");
+
+class BxPfwRecurringBilling extends BxPfwExpressCheckout
+{
+	function BxPfwRecurringBilling($oDb, $oConfig, $aConfig)
+	{
+		parent::BxPfwExpressCheckout($oDb, $oConfig, $aConfig);
+
+		$this->_aCallParameters['TRXTYPE'] = 'R';
+	}
+
+	function initializeCheckout($iPendingId, $aCartInfo)
+	{
+		if((int)$aCartInfo['items_count'] > 1) {
+			$oMain = bx_instance($this->_oConfig->getClassPrefix() . 'Module');
+			$oMain->_oTemplate->getPageCodeError($this->_sLangsPrefix . 'msg_one_item_only');
+			exit;
+		}
+
+		parent::initializeCheckout($iPendingId, $aCartInfo);
+	}
+
+	function confirmCheckout($sToken, $sPayerId)
+	{
+		$aResponse = parent::confirmCheckout($sToken, $sPayerId);
+		$aResponse['SUBSCRIPTION'] = 1;
+
+		return $aResponse;
+	}
+
+	function finalizeCheckout(&$aData)
+	{
+		$this->_logInfo(__METHOD__, $aData);
+
+		$iPending = (int)$aData['pendingid'];
+		$aPending = $this->_oDb->getPending(array('type' => 'id', 'id' => $iPending));
+        if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
+            return array('code' => 0, 'message' => _t($this->_sLangsPrefix . 'err_already_processed'));
+
+		$sToken = process_db_input($aData['token'], BX_TAGS_STRIP);
+		$sPayerId = process_db_input($aData['payerid'], BX_TAGS_STRIP);
+		$sPayerName = process_db_input($aData['payername'], BX_TAGS_STRIP);
+		$sPayerEmail = process_db_input($aData['payeremail'], BX_TAGS_STRIP);
+		$sAmt = process_db_input($aData['amt'], BX_TAGS_STRIP);
+		$this->_doExpressCheckout($sToken, $sPayerId, $sAmt);
+
+		$aResponsePay = $this->_executeCall();
+		if($aResponsePay === false)
+			return array('code' => 0, 'message' => _t($this->_sLangsPrefix . 'err_unknown'));
+
+		$this->_logInfo(__METHOD__, $aResponsePay);
+
+		$iResponseCode = (int)$aResponsePay['RESULT'];
+		$sResponseMessage = process_db_input($aResponsePay['RESPMSG'], BX_TAGS_STRIP);
+		$bPaymentAccepted = $iResponseCode == 0;
+
+		$aResult = array(
+			'code' => $bPaymentAccepted ? 1 : 0, 
+			'message' => $bPaymentAccepted ? _t($this->_sLangsPrefix . 'msg_accepted') : $sResponseMessage,
+			'pending_id' => $iPending,
+			'payer_name' => $sPayerName,
+			'payer_email' => $sPayerEmail,
+		);
+
+        //--- Update pending transaction ---//
+        $this->_oDb->updatePending($iPending, array(
+            'order' => process_db_input($aResponsePay['PPREF'], BX_TAGS_STRIP),
+        	'order_ref' => process_db_input($aResponsePay['PNREF'], BX_TAGS_STRIP),
+            'error_code' => $aResult['code'],
+            'error_msg' => $sResponseMessage
+        ));
+
+        //--- Establish subscription ---//
+		if($bPaymentAccepted && !empty($aResponsePay['BAID'])) {
+			$sBaid = process_db_input($aResponsePay['BAID'], BX_TAGS_STRIP);
+
+			$oMain = bx_instance($this->_oConfig->getClassPrefix() . 'Module');
+			$aCartInfo = $oMain->_oCart->getInfo((int)$aPending['client_id'], (int)$aPending['seller_id'], $aPending['items']);
+
+			$this->_createRecurringBillingProfile($iPending, $aCartInfo, $sBaid);
+
+			$aResponseSubscribe = $this->_executeCall();
+			if($aResponseSubscribe !== false && (int)$aResponseSubscribe['RESULT'] == 0) {
+				$this->_logInfo(__METHOD__, $aResponseSubscribe);
+
+				$aResult['message'] = _t($this->_sLangsPrefix . 'msg_accepted_subscribed');
+
+				$this->_oDb->updatePending($iPending, array(
+		        	'order_profile' => process_db_input($aResponseSubscribe['PROFILEID'], BX_TAGS_STRIP)
+		        ));
+			}
+		}
+
+		return $aResult;
+	}
+
+	function inquiryRecurringBillingProfileDetails($sRecurringProfileId, $bPaymentsHistory = false)
+	{
+		$this->_inquiryRecurringBillingProfileDetails($sRecurringProfileId, $bPaymentsHistory);
+
+		$aResponse = $this->_executeCall();
+		if($aResponse === false || (isset($aResponse['RESULT']) && $aResponse['RESULT'] != 0))
+			return false;
+
+		if($bPaymentsHistory)
+			$aResponse = $this->_decodePaymentHistory($aResponse);
+
+		$this->_logInfo(__METHOD__, $aResponse);
+
+		return $aResponse;
+	}
+
+	function cancelSubscription($aOrder)
+	{
+		if(empty($aOrder['order_profile']))
+			return false;
+
+		$this->_cancelRecurringBillingProfile($aOrder['order_profile']);
+
+		$aResponse = $this->_executeCall();
+		if($aResponse === false || (isset($aResponse['RESULT']) && $aResponse['RESULT'] != 0))
+			return false;
+
+		$aProcOrders = $this->_oDb->getProcessed(array('type' => 'order_profile', 'order_profile' => $aOrder['order_profile']));
+		foreach($aProcOrders as $aProcOrder)
+			$this->_oDb->updatePending($aProcOrder['pending_id'], array('order_profile' => ''));
+
+		return true;
+	}
+
+	function prolongSubscription($aOrder)
+	{
+		if(empty($aOrder['order_profile']))
+			return array('code' => 4, 'message' => '_bx_pfw_err_subscription_not_found');
+
+		$aPayments = $this->inquiryRecurringBillingProfileDetails($aOrder['order_profile'], true);
+		if(empty($aPayments) || !is_array($aPayments))
+			return array('code' => 5, 'message' => '_bx_pfw_err_subscription_not_prolonged');
+
+		$aNextPayment = array();
+		foreach($aPayments as $aPayment) {
+			if((int)$aPayment['RESULT'] != 0 || (int)$aPayment['TRANSTATE'] != 8)
+				continue;
+
+			if(strtotime($aPayment['TRANSTIME']) > $aOrder['date'] && $aPayment['PNREF'] != $aOrder['order_ref'] && (float)$aPayment['AMT'] >= (float)$aOrder['amount']) {
+				$aNextPayment = $aPayment;
+				break;
+			}
+		}
+
+		if(empty($aNextPayment) || !is_array($aNextPayment))
+			return array('code' => 5, 'message' => '_bx_pfw_err_subscription_not_prolonged');
+
+		//--- Create pending for newly accepted payment
+		$iPendingId = $this->_oDb->insertPending($aOrder['client_id'], $aOrder['provider'], array(
+			'vendor_id' => $aOrder['seller_id'],
+			'items_price' => $aOrder['amount'],
+			'items' => array(
+				array('module_id' => $aOrder['module_id'], 'id' => $aOrder['item_id'], 'quantity' => $aOrder['item_count'])
+			)
+		));
+
+		$this->_oDb->updatePending($iPendingId, array(
+            'order' => $aOrder['order'],
+        	'order_ref' => process_db_input($aPayment['PNREF'], BX_TAGS_STRIP),
+			'order_profile' => $aOrder['order_profile'],
+            'error_code' => 1,
+            'error_msg' => 'Prolonged'
+        ));
+
+        return array('code' => 0, 'message' => '_bx_pfw_msg_accepted', 'pending_id' => $iPendingId);
+	}
+
+	protected function _setExpressCheckout($iPendingId, $aCartInfo)
+	{
+		parent::_setExpressCheckout($iPendingId, $aCartInfo);
+
+		$this->_aCallParameters['TRXTYPE'] = 'S';
+		$this->_aCallParameters['BILLINGTYPE'] = 'RecurringBilling';
+	}
+
+	protected function _getExpressCheckout($sToken, $sPayerId)
+    {
+    	parent::_getExpressCheckout($sToken, $sPayerId);
+
+    	$this->_aCallParameters['TRXTYPE'] = 'S';
+    }
+
+    protected function _doExpressCheckout($sToken, $sPayerId, $sAmount)
+    {
+    	parent::_doExpressCheckout($sToken, $sPayerId, $sAmount);
+
+    	$this->_aCallParameters['TRXTYPE'] = 'S';
+    }
+
+	protected function _createRecurringBillingProfile($iPendingId, $aCartInfo, $sBaid)
+	{
+		$aItem = array_shift($aCartInfo['items']);
+
+		$this->_aValidationParameters = array('TENDER', 'TRXTYPE', 'ACTION', 'BAID', 'PROFILENAME', 'START', 'PAYPERIOD', 'TERM', 'AMT');
+
+		$this->_aCallParameters['TRXTYPE'] = 'R';
+		$this->_aCallParameters['ACTION'] = 'A';
+		$this->_aCallParameters['BAID'] = $sBaid;
+
+		$this->_aCallParameters['PROFILENAME'] = _t($this->_sLangsPrefix . 'txt_subscription_for', $aItem['title']);
+		$this->_aCallParameters['START'] = date('mdY', (time() + 86400 * $aItem['duration']));
+		$this->_aCallParameters['PAYPERIOD'] = 'DAYS';
+		$this->_aCallParameters['FREQUENCY'] = $aItem['duration'];
+		$this->_aCallParameters['TERM'] = 0;
+
+		$this->_aCallParameters['AMT'] = sprintf( "%.2f", (float)$aItem['price']);
+		$this->_aCallParameters['CURRENCY'] = $aCartInfo['vendor_currency_code'];
+	}
+
+	protected function _cancelRecurringBillingProfile($sRecurringProfileId)
+	{
+		$this->_aValidationParameters = array('TENDER', 'TRXTYPE', 'ACTION', 'ORIGPROFILEID');
+
+		$this->_aCallParameters['TRXTYPE'] = 'R';
+		$this->_aCallParameters['ACTION'] = 'C';
+
+		$this->_aCallParameters['ORIGPROFILEID'] = $sRecurringProfileId;
+	}
+
+	protected function _inquiryRecurringBillingProfileDetails($sRecurringProfileId, $bPaymentsHistory = false)
+	{
+		$this->_aValidationParameters = array('TENDER', 'TRXTYPE', 'ACTION', 'ORIGPROFILEID');
+
+		$this->_aCallParameters['TRXTYPE'] = 'R';
+		$this->_aCallParameters['ACTION'] = 'I';
+
+		$this->_aCallParameters['ORIGPROFILEID'] = $sRecurringProfileId;
+
+		if($bPaymentsHistory)
+			$this->_aCallParameters['PAYMENTHISTORY'] = 'Y';
+	}
+
+	protected function _decodePaymentHistory($aResponse)
+	{
+		$aPayments = array();
+
+		foreach($aResponse as $sKey => $sValue) {
+			$aMatches = array();
+			if(preg_match("/P_([A-Z]+)([0-9]+)/", $sKey, $aMatches)) {
+				$aPayments[$aMatches[2]][$aMatches[1]] = $sValue;
+			}
+		}
+
+		return $aPayments;
+	}
+}

+ 53 - 0
modules/boonex/paypal_payflow/classes/BxPfwSecureToken.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once("BxPfwPayPal.php");
+
+class BxPfwSecureToken extends BxPfwPayPal
+{
+	function BxPfwSecureToken($oDb, $oConfig, $aConfig)
+	{
+		parent::BxPfwPayPal($oDb, $oConfig, $aConfig);
+
+		unset($this->_aCallParameters['TENDER']);
+		unset($this->_aCallParameters['ACTION']);
+		unset($this->_aCallParameters['VERBOSITY']);
+	}
+
+	function getSecureToken($iPendingId, $aCartInfo)
+	{
+		$sTokenId = md5(uniqid(rand(), true));
+		$this->_getSecureToken($iPendingId, $aCartInfo, $sTokenId);
+
+		$aResponse = $this->_executeCall();
+		if($aResponse === false || strcmp($sTokenId, $aResponse['SECURETOKENID']) != 0)
+			return false;
+
+		$this->_logInfo(__METHOD__, $aResponse);
+		return array(
+			'token' => $aResponse['SECURETOKEN'], 
+			'token_id' => $aResponse['SECURETOKENID'],
+		);
+	}
+
+	protected function _getSecureToken($iPendingId, $aCartInfo, $sTokenId)
+	{
+		$this->_aValidationParameters = array('CREATESECURETOKEN', 'SECURETOKENID', 'ERRORURL', 'CANCELURL', 'AMT');
+
+		$this->_aCallParameters['CREATESECURETOKEN'] = 'Y';
+		$this->_aCallParameters['SECURETOKENID'] = $sTokenId;
+
+		$sUrlAddon = $this->_sName . '/' . $aCartInfo['vendor_id'] . '/';
+		$this->_aCallParameters['RETURNURL'] = $this->_oConfig->getReturnUrl() . $sUrlAddon;
+		$this->_aCallParameters['CANCELURL'] = $this->_oConfig->getCancelUrl();
+		$this->_aCallParameters['ERRORURL'] = $this->_oConfig->getResponseUrl() . $sUrlAddon;
+
+		$this->_aCallParameters['INVNUM'] = $iPendingId;
+
+		$this->_aCallParameters['AMT'] = sprintf( "%.2f", (float)$aCartInfo['items_price']);
+		$this->_aCallParameters['CURRENCY'] = $aCartInfo['vendor_currency_code'];
+	}
+}

+ 268 - 0
modules/boonex/paypal_payflow/classes/BxPfwTemplate.php

@@ -0,0 +1,268 @@
+<?php
+
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/payment/classes/BxPmtTemplate.php');
+
+class BxPfwTemplate extends BxPmtTemplate
+{
+	var $_oModule;
+
+    function BxPfwTemplate(&$oConfig, &$oDb)
+    {
+        parent::BxPmtTemplate($oConfig, $oDb);
+
+        $this->_removeLocations($this);
+        $this->_addLocations($this);
+
+        global $oAdmTemplate;
+        $this->_removeLocations($oAdmTemplate);
+        $this->_addLocations($oAdmTemplate);
+
+        $this->_oModule = null;
+    }
+
+	function init(&$oModule)
+    {
+        $this->_oModule = $oModule;
+    }
+
+	function addAdminParentJs($mixedFiles, $bDynamic = false)
+    {
+        global $oAdmTemplate;
+        return $oAdmTemplate->addJs($mixedFiles, $bDynamic);
+    }
+
+    function addAdminParentCss($mixedFiles, $bDynamic = false)
+    {
+        global $oAdmTemplate;
+        return $oAdmTemplate->addCss($mixedFiles, $bDynamic);
+    }
+
+	function getJsCode($sType, $bWrap = false)
+    {
+    	$sJsObject = $this->_oConfig->getJsObject($sType);
+        $sJsClass = $this->_oConfig->getJsClass($sType);
+
+        $aOptions = array(
+        	'sActionUrl' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri(),
+        	'sObjName' => $sJsObject,
+        	'sAnimationEffect' => $this->_oConfig->getAnimationEffect(),
+        	'iAnimationSpeed' => $this->_oConfig->getAnimationSpeed()
+        );
+        $sContent .= 'var ' . $sJsObject . ' = new ' . $sJsClass . '(' . json_encode($aOptions) . ');';
+
+        return $bWrap ? $this->_wrapInTagJsCode($sContent) : $sContent;
+    }
+
+    function displayOrder($sType, $iId)
+    {
+    	$sOrder = parent::displayOrder(($sType == BX_PMT_ORDERS_TYPE_SUBSCRIPTION ? BX_PMT_ORDERS_TYPE_PROCESSED : $sType), $iId);
+    	if(!in_array($sType, array(BX_PMT_ORDERS_TYPE_SUBSCRIPTION, BX_PMT_ORDERS_TYPE_HISTORY)))
+    		return $sOrder;
+
+    	$sMethodName = 'get' . ucfirst($sType);
+        $aOrder = $this->_oDb->$sMethodName(array('type' => 'id', 'id' => $iId));
+        if(empty($aOrder['order_profile']) || (!isAdmin() && (int)$aOrder['client_id'] != getLoggedId()))
+        	return $sOrder;
+
+    	return $this->parseHtmlByName('rb_processed_order.html', array(
+    		'js_object'  => $this->_oConfig->getJsObject('orders'),
+    		'type' => $sType,
+    		'id' => $iId,
+    		'order' => $sOrder,
+    		'order_profile' => $aOrder['order_profile'],
+    		'loading' => LoadingBox('pfw-order-loading-' . $iId)
+    	));
+    }
+    
+    function displayCartContent($aCartInfo, $iVendorId = BX_PMT_EMPTY_ID)
+    {
+    	$sResult = parent::displayCartContent($aCartInfo, $iVendorId);
+
+    	$this->addJs(array('_cart.js'));
+    	return $sResult;
+    }
+
+    function displayCartJs($bWrapped = true)
+    {
+    	$sResult = parent::displayCartJs($bWrapped);
+
+    	$this->addJs(array('_cart.js'));
+    	return $sResult;
+    }
+
+    function displayAddToCartJs($iVendorId, $iModuleId, $iItemId, $iItemCount, $bNeedRedirect = false, $bWrapped = true)
+    {
+    	$aResult = parent::displayAddToCartJs($iVendorId, $iModuleId, $iItemId, $iItemCount, $bNeedRedirect, $bWrapped);
+
+    	$this->addJs(array('_cart.js'));
+    	return $aResult;
+    }
+
+    function displayAddToCartLink($iVendorId, $iModuleId, $iItemId, $iItemCount, $bNeedRedirect = false)
+    {
+    	$sResult = parent::displayAddToCartLink($iVendorId, $iModuleId, $iItemId, $iItemCount, $bNeedRedirect);
+
+		$this->addJs(array('_cart.js'));
+    	return $sResult;
+    }
+
+    function displayConfirmPage($sProvider, $iVendorId, &$aOrderInfo, &$aCartInfo)
+    {
+    	$bSubscription = isset($aOrderInfo['SUBSCRIPTION']) && (int)$aOrderInfo['SUBSCRIPTION'] == 1;
+
+    	$aTmplItems = array();
+        foreach($aCartInfo['items'] as $aItem) {
+        	$aTmplItems[] = array(
+	        	'url' => $aItem['url'],
+	        	'title' => $aItem['title'],
+        		'quantity' => $aItem['quantity'],
+        		'price' => $aItem['price'],
+        		'currency' => $aCartInfo['vendor_currency_code'],
+        		'bx_if:show_duration' => array(
+        			'condition' => $bSubscription,
+        			'content' => array(
+        				'duration' => _t($this->_sLangsPrefix . 'txt_subscription_duration_mask', $aItem['duration'])
+        			)
+        		)
+        	);
+        }
+        
+
+    	$sDetails = $this->parseHtmlByName('ec_confirm.html', array(
+    		'buyer_name' => _t($this->_sLangsPrefix . 'txt_buyer_name_mask', $aOrderInfo['FIRSTNAME'], $aOrderInfo['LASTNAME']),
+    		'buyer_email' =>  _t($this->_sLangsPrefix . 'txt_buyer_email_mask', $aOrderInfo['EMAIL'], $aOrderInfo['PAYERSTATUS']),
+    		'txt_items_info' => _t($bSubscription ? '_bx_pfw_txt_subscription_info' : '_bx_pfw_txt_products_info'),
+    		'bx_repeat:items' => $aTmplItems,
+    		'total_count' => $aCartInfo['items_count'],
+    		'total_price' => $aCartInfo['items_price'],
+    		'currency' => $aCartInfo['vendor_currency_code']
+    	));
+
+    	$aForm = array(
+            'form_attrs' => array(
+                'id' => 'pfw_ec_confirm',
+                'name' => 'pfw_ec_confirm',
+                'action' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'finalize_checkout/' . $sProvider . '/' . $iVendorId,
+                'method' => 'post'
+            ),
+            'params' => array(
+                'db' => array(
+                    'table' => '',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'submit'
+                ),
+            ),
+            'inputs' => array (
+            	'token' => array(
+            		'type' => 'hidden',
+            		'name' => 'token',
+            		'value' => $aOrderInfo['TOKEN']
+            	),
+            	'payerid' => array(
+            		'type' => 'hidden',
+            		'name' => 'payerid',
+            		'value' => $aOrderInfo['PAYERID']
+            	),
+            	'payername' => array(
+            		'type' => 'hidden',
+            		'name' => 'payername',
+            		'value' => _t($this->_sLangsPrefix . 'txt_buyer_name_mask', $aOrderInfo['FIRSTNAME'], $aOrderInfo['LASTNAME'])
+            	),
+            	'payeremail' => array(
+            		'type' => 'hidden',
+            		'name' => 'payeremail',
+            		'value' => $aOrderInfo['EMAIL']
+            	),
+            	'amt' => array(
+            		'type' => 'hidden',
+            		'name' => 'amt',
+            		'value' => $aOrderInfo['AMT']
+            	),
+            	'pendingid' => array(
+            		'type' => 'hidden',
+            		'name' => 'pendingid',
+            		'value' => (int)$aOrderInfo['CUSTOM']
+            	),
+            	'details' => array(
+		            'type' => 'custom',
+		            'name' => 'details',
+		            'content' => $sDetails,
+            		'colspan' => 2,
+		        ),
+				'submit' => array(
+		            'type' => 'submit',
+		            'name' => 'submit',
+		            'value' => _t($this->_sLangsPrefix . 'btn_' . ($bSubscription ? 'subscribe' : 'pay')),
+		        	'colspan' => 2,
+		        )
+            )
+        );
+
+        bx_import('BxTemplFormView');
+        $oForm = new BxTemplFormView($aForm);
+
+    	$aParams = array(
+    		'index' => 1,
+    		'css' => array('_cart.css'),
+            'title' => array(
+                'page' => _t($this->_sLangsPrefix . 'pcpt_receipt'),
+            ),
+            'content' => array(
+                'page_main_code' => DesignBoxContent(_t($this->_sLangsPrefix . 'bcpt_receipt'), $oForm->getCode(), 11)
+            )
+        );
+        $this->getPageCode($aParams);
+    }
+
+    function _getJsContentCart()
+    {
+    	$sJsObject = $this->_oConfig->getJsObject('cart');
+    	$sJsContent = $this->getJsCode('cart', true);
+    	
+    	return array('js_object' => $sJsObject, 'js_content' => $sJsContent);
+    }
+
+	function _isSubscription($aOrder)
+    {
+    	return _t('_bx_pfw_txt_' . (!empty($aOrder['order_profile']) ? 'yes' : 'no'));
+    }
+
+    protected function _addLocations($oTemplate)
+    {
+    	//--- Add Parent module locations
+        $sClassPrefix = $this->_oConfig->getParentClassPrefix();
+        $sHomePath = $this->_oConfig->getParentHomePath();
+        $sHomeUrl = $this->_oConfig->getParentHomeUrl();
+
+        $oTemplate->addLocation($sClassPrefix, $sHomePath, $sHomeUrl);
+        $oTemplate->addLocationJs($sClassPrefix, $sHomePath . 'js/', $sHomeUrl . 'js/');
+
+        //--- Add current module locations
+		$sClassPrefix = $this->_oConfig->getClassPrefix();
+        $sHomePath = $this->_oConfig->getHomePath();
+        $sHomeUrl = $this->_oConfig->getHomeUrl();
+
+        $oTemplate->addLocation($sClassPrefix, $sHomePath, $sHomeUrl);
+        $oTemplate->addLocationJs($sClassPrefix, $sHomePath . 'js/', $sHomeUrl . 'js/');
+    }
+
+    protected function _removeLocations($oTemplate)
+    {
+    	//--- Remove Parent module locations
+		$sClassPrefix = $this->_oConfig->getParentClassPrefix();
+		$oTemplate->removeLocation($sClassPrefix);
+        $oTemplate->removeLocationJs($sClassPrefix);
+
+    	//--- Remove current module locations
+    	$sClassPrefix = $this->_oConfig->getClassPrefix();
+        $oTemplate->removeLocation($sClassPrefix);
+        $oTemplate->removeLocationJs($sClassPrefix);
+    }
+}

+ 70 - 0
modules/boonex/paypal_payflow/install/config.php

@@ -0,0 +1,70 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    PayPalPayflow PayPalPayflow
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+	'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_payflow',
+    'title' => 'PayPal Payflow Pro',
+    'note' => 'PayPal Payflow Pro module.',
+    'version' => '8.0.0.DEV',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.boonex.com/?section={module_name}',
+
+    'compatible_with' => array(
+        '8.0.x'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/paypal_payflow/',
+    'home_uri' => 'payflow',
+
+    'db_prefix' => 'bx_payflow_',
+    'class_prefix' => 'BxPayflow',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'PayPal Payflow Pro',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+	'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 25 - 0
modules/boonex/paypal_payflow/install/installer.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once(BX_DIRECTORY_PATH_CLASSES . "BxDolInstaller.php");
+
+class BxPfwInstaller extends BxDolInstaller
+{
+    function BxPfwInstaller($aConfig)
+    {
+        parent::BxDolInstaller($aConfig);
+    }
+
+    function install($aParams)
+    {
+        $aResult = parent::install($aParams);
+
+        if($aResult['result'])
+            BxDolService::call($this->_aConfig['home_uri'], 'update_dependent_modules');
+
+        return $aResult;
+    }
+}

+ 164 - 0
modules/boonex/paypal_payflow/install/langs/en.php

@@ -0,0 +1,164 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+$aLangContent = array(
+	'_sys_module_payflow' => 'PayPal PayFlow Pro',
+	'_bx_pfw_pcpt_join' => 'Join',
+	'_bx_pfw_bcpt_join' => 'Join',
+	'_bx_pfw_chart' => 'Transactions',
+
+	//--- Menus
+	'_bx_pfw_admin_menu_sitem' => 'PayPal PayFlow Pro',
+
+	//--- Administration
+	'_bx_pfw_pcpt_administration' => 'PayPal PayFlow Pro',
+	'_bx_pfw_bcpt_settings' => 'Settings',
+
+	//--- Details
+	'_bx_pfw_pcpt_details' => 'Payment Details',
+	'_bx_pfw_bcpt_details' => 'Details',
+	'_bx_pfw_details_submit' => 'Save',
+	'_bx_pfw_po_active_cpt' => 'Active',
+    '_bx_pfw_po_active_dsc' => 'Activate(Deactivate) provider',
+    '_bx_pfw_po_mode_cpt' => 'Mode',
+    '_bx_pfw_po_mode_dsc' => 'Mode to process payments',
+    '_bx_pfw_po_mode_live' => 'live',
+    '_bx_pfw_po_mode_test' => 'test',
+	'_bx_pfw_po_partner_cpt' => 'Partner',
+	'_bx_pfw_po_partner_dsc' => 'Your partner, usually "PayPal".',
+	'_bx_pfw_po_vendor_cpt' => 'Merchant login',
+	'_bx_pfw_po_vendor_dsc' => 'You merchant login in PayPal Manager',
+	'_bx_pfw_po_user_cpt' => 'User login',
+	'_bx_pfw_po_user_dsc' => 'The user should have Full privileges to manage API calls. "Merchant login" can be used but it is not recomended.',
+	'_bx_pfw_po_password_cpt' => 'User password',
+	'_bx_pfw_po_password_dsc' => 'Password for User account. If "Merchant" is used as "User" then use "Merchant" password here.',
+
+	//--- Orders
+	'_bx_pfw_pcpt_view_orders' => 'Order Administration',
+	'_bx_pfw_bcpt_pending_orders' => 'Pending Orders',
+	'_bx_pfw_bcpt_processed_orders' => 'Processed Orders',
+	'_bx_pfw_bcpt_subscription_orders' => 'Subscriptions',
+	'_bx_pfw_wcpt_manual_order' => 'Manual Order',
+    '_bx_pfw_wcpt_order_info' => 'Order Info',
+    '_bx_pfw_btn_settings' => 'Settings',
+    '_bx_pfw_btn_orders_processed' => 'Processed',
+    '_bx_pfw_btn_orders_pending' => 'Pending',
+	'_bx_pfw_btn_process' => 'Process',
+	'_bx_pfw_btn_cancel' => 'Cancel',
+    '_bx_pfw_btn_report' => 'Report',
+	'_bx_pfw_btn_add' => 'Add',
+    '_bx_pfw_btn_manual_order' => 'Manual Order',
+	'_bx_pfw_btn_unsubscribe' => 'Unsubscribe',
+    '_bx_pfw_fcpt_module' => 'Items from',
+    '_bx_pfw_fcpt_client' => 'Client',
+    '_bx_pfw_fcpt_order' => 'Order',
+	'_bx_pfw_ocpt_select' => 'Select one',
+	'_bx_pfw_txt_date' => 'Date',
+    '_bx_pfw_txt_client' => 'Client',
+    '_bx_pfw_txt_seller' => 'Seller',
+    '_bx_pfw_txt_order' => 'Order ID',
+	'_bx_pfw_txt_order_profile' => 'Subscription ID',
+    '_bx_pfw_txt_amount' => 'Amount',
+    '_bx_pfw_txt_license' => 'License',
+	'_bx_pfw_txt_subscription' => 'Subscription',
+    '_bx_pfw_txt_action' => 'More',
+	'_bx_pfw_txt_more_products' => 'products',
+	'_bx_pfw_txt_more_items' => 'items',
+    '_bx_pfw_txt_action_more' => 'More',
+	'_bx_pfw_txt_processed_with' => 'Processed with',
+    '_bx_pfw_txt_message' => 'Result',
+    '_bx_pfw_txt_products' => 'Products',
+	'_bx_pfw_txt_yes' => 'Yes',
+	'_bx_pfw_txt_no' => 'No',
+
+	//--- Cart
+	'_bx_pfw_pcpt_view_cart' => 'Shopping cart',
+	'_bx_pfw_pcpt_cart_history' => 'Cart History',
+	'_bx_pfw_pcpt_receipt' => 'Receipt',
+	'_bx_pfw_pcpt_credit_card' => 'Credit Card Details',
+	'_bx_pfw_bcpt_cart_featured' => 'Site Cart',
+    '_bx_pfw_bcpt_cart_common' => 'Members\' Carts',
+    '_bx_pfw_bcpt_cart_history' => 'Cart History',
+	'_bx_pfw_bcpt_receipt' => 'Receipt',
+	'_bx_pfw_bcpt_credit_card' => 'Credit Card Details',
+	'_bx_pfw_btn_history' => 'History',
+	'_bx_pfw_btn_delete' => 'Delete',
+    '_bx_pfw_btn_checkout' => 'Checkout',
+    '_bx_pfw_btn_cart' => 'Cart',
+	'_bx_pfw_btn_pay' => 'Pay',
+	'_bx_pfw_btn_subscribe' => 'Subscribe',
+	'_bx_pfw_fcpt_details_title' => 'Title',
+	'_bx_pfw_fcpt_details_quantity' => 'Quantity',
+	'_bx_pfw_fcpt_details_price' => 'Price',
+	'_bx_pfw_fcpt_details_total' => 'Total',
+	'_bx_pfw_txt_add_to_cart' => 'Add to Cart',
+	'_bx_pfw_txt_shopping_cart' => '\'s cart',
+	'_bx_pfw_txt_buyer_info' => 'Buyer',
+	'_bx_pfw_txt_buyer_name' => 'Name',
+	'_bx_pfw_txt_buyer_name_mask' => '{0} {1}',
+	'_bx_pfw_txt_buyer_email' => 'Email',
+	'_bx_pfw_txt_buyer_email_mask' => '{0} ({1})',
+	'_bx_pfw_txt_products_info' => 'Products',
+	'_bx_pfw_txt_subscription_info' => 'Subscription',
+	'_bx_pfw_txt_subscription_duration_mask' => '(auto-renew in {0} days)',
+	'_bx_pfw_txt_free' => 'free',
+
+	//--- Errors, Warnings and Messages
+	'_bx_pfw_pcpt_error' => 'Error occured',
+	'_bx_pfw_pcpt_response' => 'Operation result',
+	'_bx_pfw_pcpt_payment_result' => 'Payment result',
+	'_bx_pfw_bcpt_payment_result' => 'Payment result',
+	'_bx_pfw_msg_no_results' => 'Empty',
+	'_bx_pfw_msg_accepted' => 'Payment was successfully accepted.',
+	'_bx_pfw_msg_accepted_subscribed' => 'Subscription profile was created and the first payment was successfully accepted.',
+	'_bx_pfw_msg_subscription_canceled' => 'Subscription profile was canceled.',
+	'_bx_pfw_msg_successfully_done' => 'Operation was successfully done.',
+	'_bx_pfw_inf_successfully_added' => 'The item was successfully added to your shopping cart',
+	'_bx_pfw_inf_successfully_deleted' => 'The item was successfully deleted from your shopping cart',
+	'_bx_pfw_inf_successfully_reported' => 'The order was successfully reported',
+	'_bx_pfw_inf_successfully_processed_free' => 'Free item was successfully processed',
+	'_bx_pfw_err_nothing_selected' => 'You need to select at least one item',
+	'_bx_pfw_err_access_db' => 'Database access error.',
+	'_bx_pfw_err_wrong_data' => 'Incorrect input data',
+	'_bx_pfw_err_wrong_amount' => 'Incorrect payment amount',
+	'_bx_pfw_err_required_login' => 'You need to login',
+	'_bx_pfw_err_unknown' => 'An error occurred during the operation',
+	'_bx_pfw_err_empty_order' => 'The order is empty',
+	'_bx_pfw_err_empty_orders' => 'Fill in order numbers', 
+	'_bx_pfw_err_unknown_vendor' => 'Unknown vendor',
+	'_bx_pfw_err_incorrect_provider' => 'Incorrect or unavailable payment provider',
+	'_bx_pfw_err_wrong_client' => 'Incorrect client\'s name',
+	'_bx_pfw_err_purchase_from_yourself' => 'You cannot purchase your own item. It\'s just a bad luck thing, you know.',
+	'_bx_pfw_err_wrong_order' => 'Incorrect order',
+	'_bx_pfw_err_wrong_module' => 'Select where item is located',
+	'_bx_pfw_err_empty_items' => 'There are no items selected',
+	'_bx_pfw_err_wrong_quantity' => 'Incorrect quantity specified',
+	'_bx_pfw_err_inactive_vendor' => 'The vendor status is not active',
+	'_bx_pfw_err_not_accept_payments' => 'The vendor does not accept payments',
+	'_bx_pfw_err_not_allowed' => 'Action is not allowed',
+	'_bx_pfw_err_not_available' => 'Action is not available',
+	'_bx_pfw_err_already_processed' => 'This payment has been already processed.',
+	'_bx_pfw_err_not_processed' => 'This payment was not processed.',
+
+	//--- Hosted Checkout Pages
+	'_bx_pfw_cpt_hosted_checkout' => 'PayPal Hosted Checkout',
+	'_bx_pfw_dsc_hosted_checkout' => 'Hosted Checkout with PayPal PayFlow Pro',
+	'_bx_pfw_txt_cart_hosted_checkout' => 'PayPal (credit card)',
+
+	//--- Express Checkout
+	'_bx_pfw_cpt_express_checkout' => 'PayPal Express Checkout',
+	'_bx_pfw_dsc_express_checkout' => 'Express Checkout with PayPal PayFlow Pro',
+	'_bx_pfw_txt_cart_express_checkout' => 'PayPal',
+
+	//--- Recurring Billing
+	'_bx_pfw_cpt_recurring_billing' => 'PayPal Recurring Billing',
+	'_bx_pfw_dsc_recurring_billing' => 'Recurring Billing with PayPal PayFlow Pro',
+	'_bx_pfw_txt_cart_recurring_billing' => 'PayPal (subscription)',
+	'_bx_pfw_txt_subscription_for' => 'Subscription for "{0}"',
+	'_bx_pfw_msg_one_item_only' => 'You may subscribe to only one item at a time. Please, select a single item in your shopping cart which you want to subscribe to.',
+	'_bx_pfw_err_subscription_not_found' => 'Subscription profile was not found.',
+	'_bx_pfw_err_subscription_not_prolonged' => 'Subscription profile was not prolonged.',
+);

+ 164 - 0
modules/boonex/paypal_payflow/install/langs/ru.php

@@ -0,0 +1,164 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+$aLangContent = array(
+	'_sys_module_payflow' => 'PayPal PayFlow Pro',
+	'_bx_pfw_pcpt_join' => 'Регистрация',
+	'_bx_pfw_bcpt_join' => 'Регистрация',
+	'_bx_pfw_chart' => 'Транзакции',
+
+	//--- Menus
+	'_bx_pfw_admin_menu_sitem' => 'PayPal PayFlow Pro',
+
+	//--- Administration
+	'_bx_pfw_pcpt_administration' => 'PayPal PayFlow Pro',
+	'_bx_pfw_bcpt_settings' => 'Настройки',
+
+	//--- Details
+	'_bx_pfw_pcpt_details' => 'Детали платежей',
+	'_bx_pfw_bcpt_details' => 'Детали',
+	'_bx_pfw_details_submit' => 'Сохранить',
+	'_bx_pfw_po_active_cpt' => 'Активировать',
+    '_bx_pfw_po_active_dsc' => 'Активировать провайдера',
+    '_bx_pfw_po_mode_cpt' => 'Режим',
+    '_bx_pfw_po_mode_dsc' => 'Режим обработки платежей',
+    '_bx_pfw_po_mode_live' => 'рабочий',
+    '_bx_pfw_po_mode_test' => 'тестовый',
+	'_bx_pfw_po_partner_cpt' => 'Партнер',
+	'_bx_pfw_po_partner_dsc' => 'Ваш партнер, обычно "PayPal".',
+	'_bx_pfw_po_vendor_cpt' => 'Торговый логин',
+	'_bx_pfw_po_vendor_dsc' => 'Ваш торговый логин в PayPal Manager',
+	'_bx_pfw_po_user_cpt' => 'Логин пользователя',
+	'_bx_pfw_po_user_dsc' => 'Пользователь должен иметь все права на работу с API запросами. Можут быть использован "Торговый логин" (не рекомендуется).',
+	'_bx_pfw_po_password_cpt' => 'Пароль пользователя',
+	'_bx_pfw_po_password_dsc' => 'Пароль от аккаунта пользователя. Используйте пароль от торгового аккаунта, если торговый логин используется как логин пользователя.',
+
+	//--- Orders
+	'_bx_pfw_pcpt_view_orders' => 'Администрация заказов',
+	'_bx_pfw_bcpt_pending_orders' => 'Обрабатываемые заказы',
+	'_bx_pfw_bcpt_processed_orders' => 'Обработанные заказы',
+	'_bx_pfw_bcpt_subscription_orders' => 'Подписки',
+	'_bx_pfw_wcpt_manual_order' => 'Добавить вручную',
+    '_bx_pfw_wcpt_order_info' => 'Информация о заказе',
+    '_bx_pfw_btn_settings' => 'Настройки',
+    '_bx_pfw_btn_orders_processed' => 'Обработано',
+    '_bx_pfw_btn_orders_pending' => 'Ожидающие',
+	'_bx_pfw_btn_process' => 'Обработать',
+	'_bx_pfw_btn_cancel' => 'Отменить',
+    '_bx_pfw_btn_report' => 'Пожаловаться',
+	'_bx_pfw_btn_add' => 'Добавить',
+    '_bx_pfw_btn_manual_order' => 'Добавить вручную',
+	'_bx_pfw_btn_unsubscribe' => 'Отписаться',
+    '_bx_pfw_fcpt_module' => 'Товары из',
+    '_bx_pfw_fcpt_client' => 'Клиент',
+    '_bx_pfw_fcpt_order' => 'Заказ',
+	'_bx_pfw_ocpt_select' => 'Выберите',
+	'_bx_pfw_txt_date' => 'Дата',
+    '_bx_pfw_txt_client' => 'Клиент',
+    '_bx_pfw_txt_seller' => 'Продавец',
+    '_bx_pfw_txt_order' => 'Номер заказа',
+	'_bx_pfw_txt_order_profile' => 'Номер подписки',
+    '_bx_pfw_txt_amount' => 'Цена',
+    '_bx_pfw_txt_license' => 'Лицензия',
+	'_bx_pfw_txt_subscription' => 'Подписка',
+    '_bx_pfw_txt_action' => 'Детали',
+	'_bx_pfw_txt_more_products' => 'товар(а)',
+	'_bx_pfw_txt_more_items' => 'шт',
+    '_bx_pfw_txt_action_more' => 'Еще',
+	'_bx_pfw_txt_processed_with' => 'Обработан',
+    '_bx_pfw_txt_message' => 'Результат',
+    '_bx_pfw_txt_products' => 'Товары',
+	'_bx_pfw_txt_yes' => 'Да',
+	'_bx_pfw_txt_no' => 'Нет',
+
+	//--- Cart
+	'_bx_pfw_pcpt_view_cart' => 'Корзина',
+	'_bx_pfw_pcpt_cart_history' => 'История корзины',
+	'_bx_pfw_pcpt_receipt' => 'Квитанция',
+	'_bx_pfw_pcpt_credit_card' => 'Детали кредитной карты',
+	'_bx_pfw_bcpt_cart_featured' => 'Корзина сайта',
+    '_bx_pfw_bcpt_cart_common' => 'Корзины пользователей',
+    '_bx_pfw_bcpt_cart_history' => 'История корзины',
+	'_bx_pfw_bcpt_receipt' => 'Квитанция',
+	'_bx_pfw_bcpt_credit_card' => 'Детали кредитной карты',
+	'_bx_pfw_btn_history' => 'История',
+	'_bx_pfw_btn_delete' => 'Удалить',
+    '_bx_pfw_btn_checkout' => 'Оплатить',
+    '_bx_pfw_btn_cart' => 'Корзина',
+	'_bx_pfw_btn_pay' => 'Оплатить',
+	'_bx_pfw_btn_subscribe' => 'Подписаться',
+	'_bx_pfw_fcpt_details_title' => 'Наименование',
+	'_bx_pfw_fcpt_details_quantity' => 'Количество',
+	'_bx_pfw_fcpt_details_price' => 'Цена',
+	'_bx_pfw_fcpt_details_total' => 'Итого',
+	'_bx_pfw_txt_add_to_cart' => 'В корзину',
+	'_bx_pfw_txt_shopping_cart' => ' корзина',
+	'_bx_pfw_txt_buyer_info' => 'Покупатель',
+	'_bx_pfw_txt_buyer_name' => 'Имя',
+	'_bx_pfw_txt_buyer_name_mask' => '{0} {1}',
+	'_bx_pfw_txt_buyer_email' => 'E-mail',
+	'_bx_pfw_txt_buyer_email_mask' => '{0} ({1})',
+	'_bx_pfw_txt_products_info' => 'Товары',
+	'_bx_pfw_txt_subscription_info' => 'Подписка',
+	'_bx_pfw_txt_subscription_duration_mask' => '(автообновление через {0} дней)',
+	'_bx_pfw_txt_free' => 'бесплатно',
+
+	//--- Errors, Warnings and Messages
+	'_bx_pfw_pcpt_error' => 'Ошибка',
+	'_bx_pfw_pcpt_response' => 'Результаты',
+	'_bx_pfw_pcpt_payment_result' => 'Результаты',
+	'_bx_pfw_bcpt_payment_result' => 'Результаты',
+	'_bx_pfw_msg_no_results' => 'Пусто',
+	'_bx_pfw_msg_accepted' => 'Платеж был успешно принят.',
+	'_bx_pfw_msg_accepted_subscribed' => 'Профайл подписки был создан. Первый платеж был успешно принят.',
+	'_bx_pfw_msg_subscription_canceled' => 'Профайл подписки был аннулирован.',
+	'_bx_pfw_msg_successfully_done' => 'Операция была успешно выполнена.',
+	'_bx_pfw_inf_successfully_added' => 'Товар был успешно добавлен в Вашу корзину.',
+	'_bx_pfw_inf_successfully_deleted' => 'Товар был успешно удален из Вашей корзины.',
+	'_bx_pfw_inf_successfully_reported' => 'Жалоба была принята.',
+	'_bx_pfw_inf_successfully_processed_free' => 'Бесплатный товар был обработан.',
+	'_bx_pfw_err_nothing_selected' => 'Вы должны выбрать как минимум один товар.',
+	'_bx_pfw_err_access_db' => 'Ошибка доступа к базе даных.',
+	'_bx_pfw_err_wrong_data' => 'Неверные входные данные.',
+	'_bx_pfw_err_wrong_amount' => 'Неверная сумма платежа.',
+	'_bx_pfw_err_required_login' => 'Вам нужно войти.',
+	'_bx_pfw_err_unknown' => 'Произошла ошибка во время операции.',
+	'_bx_pfw_err_empty_order' => 'Заказ пустой.',
+	'_bx_pfw_err_empty_orders' => 'Заполните номера заказов.', 
+	'_bx_pfw_err_unknown_vendor' => 'Неизвестный продавец.',
+	'_bx_pfw_err_incorrect_provider' => 'Неправильная или недоступная платежная система.',
+	'_bx_pfw_err_wrong_client' => 'Неправильное имя клиента.',
+	'_bx_pfw_err_purchase_from_yourself' => 'Вы не можете купить свой собственный товар. Вам просто не повезло, знаете ли.',
+	'_bx_pfw_err_wrong_order' => 'Неправильный номер заказа.',
+	'_bx_pfw_err_wrong_module' => 'Выберите, где расположен товар.',
+	'_bx_pfw_err_empty_items' => 'Нет выбранных товаров.',
+	'_bx_pfw_err_wrong_quantity' => 'Указано неправильное количество.',
+	'_bx_pfw_err_inactive_vendor' => 'У продавца неактивный статус.',
+	'_bx_pfw_err_not_accept_payments' => 'Продавец не принимает платежи.',
+	'_bx_pfw_err_not_allowed' => 'Выполнение действия не разрешено.',
+	'_bx_pfw_err_not_available' => 'Действие не доступно.',
+	'_bx_pfw_err_already_processed' => 'Платеж был уже обработан.',
+	'_bx_pfw_err_not_processed' => 'Платеж не был обработан.',
+
+	//--- Hosted Checkout Pages
+	'_bx_pfw_cpt_hosted_checkout' => 'PayPal Hosted Checkout',
+	'_bx_pfw_dsc_hosted_checkout' => 'Hosted Checkout в PayPal PayFlow Pro',
+	'_bx_pfw_txt_cart_hosted_checkout' => 'PayPal (Кредитная карта)',
+
+	//--- Express Checkout
+	'_bx_pfw_cpt_express_checkout' => 'PayPal Express Checkout',
+	'_bx_pfw_dsc_express_checkout' => 'Express Checkout в PayPal PayFlow Pro',
+	'_bx_pfw_txt_cart_express_checkout' => 'PayPal',
+
+	//--- Recurring Billing
+	'_bx_pfw_cpt_recurring_billing' => 'PayPal Recurring Billing',
+	'_bx_pfw_dsc_recurring_billing' => 'Recurring Billing в PayPal PayFlow Pro',
+	'_bx_pfw_txt_cart_recurring_billing' => 'PayPal (Подписка)',
+	'_bx_pfw_txt_subscription_for' => 'Подписка на "{0}"',
+	'_bx_pfw_msg_one_item_only' => 'Единовременно можно оформить только одну подписку. Пожалуйста, выберите продукт, на который Вы хотели бы подписаться.',
+	'_bx_pfw_err_subscription_not_found' => 'Профайл подписки не найден.',
+	'_bx_pfw_err_subscription_not_prolonged' => 'Профайл подписки не был продлён.',
+);

+ 214 - 0
modules/boonex/paypal_payflow/install/sql/install.sql

@@ -0,0 +1,214 @@
+SET @sModuleName = 'bx_payflow';
+
+--
+-- Table structure for table `[db_prefix]providers`
+--
+CREATE TABLE IF NOT EXISTS `[db_prefix]providers` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(64) NOT NULL default '',
+  `caption` varchar(128) NOT NULL default '',
+  `description` text NOT NULL default '',
+  `option_prefix` varchar(32) NOT NULL default '',
+  `for_visitor` tinyint(4) NOT NULL default '0',
+  `class_name` varchar(128) NOT NULL default '',
+  `class_file` varchar(255) NOT NULL  default '',
+  PRIMARY KEY(`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `[db_prefix]providers_options`
+--
+CREATE TABLE IF NOT EXISTS `[db_prefix]providers_options` (
+  `id` int(11) NOT NULL auto_increment,
+  `provider_id` varchar(64) NOT NULL default '',
+  `name` varchar(64) NOT NULL default '',
+  `type` varchar(64) NOT NULL default 'text',
+  `caption` varchar(255) NOT NULL default '',
+  `description` text NOT NULL default '',
+  `extra` varchar(255) NOT NULL default '',
+  `check_type` varchar(64) NOT NULL default '',
+  `check_params` varchar(128) NOT NULL default '',
+  `check_error` varchar(128) NOT NULL default '',
+  `order` tinyint(4) NOT NULL default '0',
+  PRIMARY KEY(`id`),
+  UNIQUE KEY `name`(`name`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `[db_prefix]user_values`
+--
+CREATE TABLE IF NOT EXISTS `[db_prefix]user_values` (
+  `user_id` int(11) NOT NULL default '0',
+  `option_id` int(11) NOT NULL default '0',  
+  `value` varchar(255) NOT NULL default '',
+  UNIQUE KEY `value`(`user_id`, `option_id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `[db_prefix]cart`
+--
+CREATE TABLE IF NOT EXISTS `[db_prefix]cart` (
+  `client_id` int(11) NOT NULL default '0',
+  `items` text NOT NULL default '',
+  PRIMARY KEY(`client_id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `[db_prefix]transactions`
+--
+CREATE TABLE IF NOT EXISTS `[db_prefix]transactions` (
+  `id` int(11) NOT NULL auto_increment,
+  `pending_id` int(11) NOT NULL default '0',
+  `order_id` varchar(16) NOT NULL default '',
+  `client_id` int(11) NOT NULL default '0',
+  `seller_id` int(11) NOT NULL default '0',
+  `module_id` int(11) NOT NULL default '0',  
+  `item_id` int(11) NOT NULL default '0',
+  `item_count` int(11) NOT NULL default '0',
+  `amount` float NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  `reported` tinyint(4) NOT NULL default '0',
+  PRIMARY KEY(`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `[db_prefix]transactions_pending`
+--
+CREATE TABLE IF NOT EXISTS `[db_prefix]transactions_pending` (
+  `id` int(10) unsigned NOT NULL auto_increment,
+  `client_id` int(11) NOT NULL default '0',
+  `seller_id` int(11) NOT NULL default '0',
+  `items` text NOT NULL default '',
+  `amount` float NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `order_ref` varchar(32) NOT NULL default '',
+  `order_profile` varchar(32) NOT NULL default '',
+  `error_code` varchar(16) NOT NULL default '',
+  `error_msg` varchar(255) NOT NULL default '',
+  `provider` varchar(32) NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  `processed` tinyint(4) NOT NULL default '0',
+  `reported` tinyint(4) NOT NULL default '0',
+  PRIMARY KEY(`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+--
+-- Table structure for table `[db_prefix]modules`
+--
+CREATE TABLE IF NOT EXISTS `[db_prefix]modules` (
+  `id` int(11) NOT NULL auto_increment,
+  `uri` varchar(64) NOT NULL default '',  
+  PRIMARY KEY(`id`),
+  UNIQUE KEY `uri`(`uri`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+
+-- options
+SET @iCategoryOrder = (SELECT MAX(`menu_order`) FROM `sys_options_cats`) + 1;
+INSERT INTO `sys_options_cats` (`name` , `menu_order` ) VALUES ('PayPal PayFlow Pro', @iCategoryOrder);
+SET @iCategoryId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`Name`, `VALUE`, `kateg`, `desc`, `Type`, `check`, `err_text`, `order_in_kateg`, `AvailableValues`) VALUES
+('permalinks_module_payflow', 'on', 26, 'Enable friendly PayPal PayFlow Pro permalink', 'checkbox', '', '', 0, ''),
+('bx_pfw_default_currency_code', 'USD', @iCategoryId, 'Currency code', 'select', 'return strlen($arg0) > 0;', 'Cannot be empty.', 1, 'AUD,CAD,EUR,GBP,USD,YEN'),
+('bx_pfw_default_currency_sign', '&#36;', @iCategoryId, 'Currency sign', 'digit', 'return strlen($arg0) > 0;', 'Cannot be empty.', 2, ''),
+('bx_pfw_site_admin', '', @iCategoryId, 'Site administrator', 'select', '', '', 3, 'PHP:return BxDolService::call(''payment'', ''get_admins'');');
+
+
+-- permalinks
+INSERT INTO `sys_permalinks`(`standard`, `permalink`, `check`) VALUES
+('modules/?r=payflow/', 'm/payflow/', 'permalinks_module_payflow');
+
+
+-- pages and blocks
+SET @iPCPOrder = (SELECT MAX(`Order`) FROM `sys_page_compose_pages`);
+INSERT INTO `sys_page_compose_pages`(`Name`, `Title`, `Order`) VALUES 
+('bx_pfw_cart', 'Payflow Shopping Cart', @iPCPOrder+1),
+('bx_pfw_history', 'Payflow Cart History', @iPCPOrder+2),
+('bx_pfw_orders', 'Payflow Order Administration', @iPCPOrder+3),
+('bx_pfw_details', 'Payflow Payment Settings', @iPCPOrder+4);
+
+INSERT INTO `sys_page_compose` (`Page`, `PageWidth`, `Desc`, `Caption`, `Column`, `Order`, `Func`, `Content`, `DesignBox`, `ColWidth`, `Visible`, `MinWidth`) VALUES
+('bx_pfw_cart', '1140px', 'Site Cart', '_bx_pfw_bcpt_cart_featured', 2, 0, 'Featured', '', 1, 50, 'memb', 0),
+('bx_pfw_cart', '1140px', 'Members Cart', '_bx_pfw_bcpt_cart_common', 3, 0, 'Common', '', 1, 50, 'memb', 0),
+('bx_pfw_history', '1140px', 'History', '_bx_pfw_bcpt_cart_history', 2, 0, 'History', '', 1, 100, 'memb', 0),
+('bx_pfw_orders', '1140px', 'Orders', '_bx_pfw_bcpt_processed_orders', 2, 0, 'Orders', '', 1, 100, 'memb', 0),
+('bx_pfw_details', '1140px', 'Payment Details', '_bx_pfw_bcpt_details', 2, 0, 'Details', '', 1, 100, 'memb', 0);
+
+
+-- menus
+SET @iOrder = (SELECT MAX(`order`) FROM `sys_menu_admin` WHERE `parent_id`='2');
+INSERT INTO `sys_menu_admin`(`parent_id`, `name`, `title`, `url`, `description`, `icon`, `icon_large`, `check`, `order`) VALUES
+(2, @sModuleName, '_bx_pfw_admin_menu_sitem', '{siteUrl}modules/?r=payflow/admin/', 'For managing PayPal PayFlow Pro', 'credit-card', '', '', @iOrder+1);
+
+
+-- alert
+INSERT INTO `sys_alerts_handlers`(`name`, `class`, `file`, `eval`) VALUES 
+(@sModuleName, '', '', 'BxDolService::call(\'payflow\', \'response\', array($this));');
+SET @iHandlerId = LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('profile', 'join', @iHandlerId),
+('profile', 'delete', @iHandlerId);
+
+
+-- email templates
+INSERT INTO `sys_email_templates`(`Name`, `Subject`, `Body`, `Desc`, `LangID`) VALUES
+('bx_pfw_paid_need_join', 'Payment was accepted', '<bx_include_auto:_email_header.html />\r\n\r\n<p><b>Dear <RealName></b>,</p>\r\n\r\n<p>\r\nYour payment was accepted. If you did not fill in the join form yet, then you may do it using the following link. \r\n</p>\r\n\r\n<p>\r\n<a href="<JoinLink>">Join Now</a>\r\n</p>\r\n\r\n<bx_include_auto:_email_footer.html />', 'PayPal PayFlow Pro: Paid and need to join', 0);
+
+
+-- chart
+SET @iMaxOrderCharts = (SELECT MAX(`order`)+1 FROM `sys_objects_charts`);
+INSERT INTO `sys_objects_charts` (`object`, `title`, `table`, `field_date_ts`, `field_date_dt`, `query`, `active`, `order`) VALUES
+(@sModuleName, '_bx_pfw_chart', 'bx_pfw_transactions', 'date', '', '', 1, @iMaxOrderCharts);
+
+
+-- payments
+INSERT INTO `sys_objects_payments` (`object`, `title`, `uri`) VALUES
+(@sModuleName, '_sys_module_payflow', 'payflow');
+
+
+--
+-- Hosted Checkout Pages with PayPal PayFlow Pro
+--
+INSERT INTO `[db_prefix]providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `class_name`) VALUES
+('hosted_checkout', '_bx_pfw_cpt_hosted_checkout', '_bx_pfw_dsc_hosted_checkout', 'hc_', 0, 'BxPfwHostedCheckout');
+SET @iProviderId = LAST_INSERT_ID();
+
+INSERT INTO `[db_prefix]providers_options`(`provider_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
+(@iProviderId, 'hc_active', 'checkbox', '_bx_pfw_po_active_cpt', '_bx_pfw_po_active_dsc', '', '', '', '', 1),
+(@iProviderId, 'hc_mode', 'select', '_bx_pfw_po_mode_cpt', '_bx_pfw_po_mode_dsc', '1|_bx_pfw_po_mode_live,2|_bx_pfw_po_mode_test', '', '', '', 2),
+(@iProviderId, 'hc_partner', 'text', '_bx_pfw_po_partner_cpt', '_bx_pfw_po_partner_dsc', '', '', '', '', 3),
+(@iProviderId, 'hc_vendor', 'text', '_bx_pfw_po_vendor_cpt', '_bx_pfw_po_vendor_dsc', '', '', '', '', 4),
+(@iProviderId, 'hc_user', 'text', '_bx_pfw_po_user_cpt', '_bx_pfw_po_user_dsc', '', '', '', '', 5),
+(@iProviderId, 'hc_password', 'text', '_bx_pfw_po_password_cpt', '_bx_pfw_po_password_dsc', '', '', '', '', 6);
+
+--
+-- Express Checkout with PayPal PayFlow Pro
+--
+INSERT INTO `[db_prefix]providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `class_name`) VALUES
+('express_checkout', '_bx_pfw_cpt_express_checkout', '_bx_pfw_dsc_express_checkout', 'ec_', 1, 'BxPfwExpressCheckout');
+SET @iProviderId = LAST_INSERT_ID();
+
+INSERT INTO `[db_prefix]providers_options`(`provider_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
+(@iProviderId, 'ec_active', 'checkbox', '_bx_pfw_po_active_cpt', '_bx_pfw_po_active_dsc', '', '', '', '', 1),
+(@iProviderId, 'ec_mode', 'select', '_bx_pfw_po_mode_cpt', '_bx_pfw_po_mode_dsc', '1|_bx_pfw_po_mode_live,2|_bx_pfw_po_mode_test', '', '', '', 2),
+(@iProviderId, 'ec_partner', 'text', '_bx_pfw_po_partner_cpt', '_bx_pfw_po_partner_dsc', '', '', '', '', 3),
+(@iProviderId, 'ec_vendor', 'text', '_bx_pfw_po_vendor_cpt', '_bx_pfw_po_vendor_dsc', '', '', '', '', 4),
+(@iProviderId, 'ec_user', 'text', '_bx_pfw_po_user_cpt', '_bx_pfw_po_user_dsc', '', '', '', '', 5),
+(@iProviderId, 'ec_password', 'text', '_bx_pfw_po_password_cpt', '_bx_pfw_po_password_dsc', '', '', '', '', 6);
+
+--
+-- Recurring Billing with PayPal PayFlow Pro
+--
+INSERT INTO `[db_prefix]providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `class_name`) VALUES
+('recurring_billing', '_bx_pfw_cpt_recurring_billing', '_bx_pfw_dsc_recurring_billing', 'rb_', 1, 'BxPfwRecurringBilling');
+SET @iProviderId = LAST_INSERT_ID();
+
+INSERT INTO `[db_prefix]providers_options`(`provider_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
+(@iProviderId, 'rb_active', 'checkbox', '_bx_pfw_po_active_cpt', '_bx_pfw_po_active_dsc', '', '', '', '', 1),
+(@iProviderId, 'rb_mode', 'select', '_bx_pfw_po_mode_cpt', '_bx_pfw_po_mode_dsc', '1|_bx_pfw_po_mode_live,2|_bx_pfw_po_mode_test', '', '', '', 2),
+(@iProviderId, 'rb_partner', 'text', '_bx_pfw_po_partner_cpt', '_bx_pfw_po_partner_dsc', '', '', '', '', 3),
+(@iProviderId, 'rb_vendor', 'text', '_bx_pfw_po_vendor_cpt', '_bx_pfw_po_vendor_dsc', '', '', '', '', 4),
+(@iProviderId, 'rb_user', 'text', '_bx_pfw_po_user_cpt', '_bx_pfw_po_user_dsc', '', '', '', '', 5),
+(@iProviderId, 'rb_password', 'text', '_bx_pfw_po_password_cpt', '_bx_pfw_po_password_dsc', '', '', '', '', 6);

+ 47 - 0
modules/boonex/paypal_payflow/install/sql/uninstall.sql

@@ -0,0 +1,47 @@
+SET @sModuleName = 'bx_payflow';
+
+
+DROP TABLE IF EXISTS `[db_prefix]providers`;
+DROP TABLE IF EXISTS `[db_prefix]providers_options`;
+DROP TABLE IF EXISTS `[db_prefix]user_values`;
+DROP TABLE IF EXISTS `[db_prefix]cart`;
+DROP TABLE IF EXISTS `[db_prefix]transactions`;
+DROP TABLE IF EXISTS `[db_prefix]transactions_pending`;
+DROP TABLE IF EXISTS `[db_prefix]modules`;
+
+
+-- options
+SET @iCategoryId = (SELECT `ID` FROM `sys_options_cats` WHERE `name`='PayPal PayFlow Pro' LIMIT 1);
+DELETE FROM `sys_options_cats` WHERE `name`='PayPal PayFlow Pro' LIMIT 1;
+DELETE FROM `sys_options` WHERE `kateg`=@iCategoryId OR `Name` IN ('permalinks_module_payflow');
+
+
+-- permalinks
+DELETE FROM `sys_permalinks` WHERE `check`='permalinks_module_payflow';
+
+
+-- pages and blocks
+DELETE FROM `sys_page_compose_pages` WHERE `Name` IN ('bx_pfw_cart', 'bx_pfw_history', 'bx_pfw_orders', 'bx_pfw_details');
+DELETE FROM `sys_page_compose` WHERE `Page` IN ('bx_pfw_cart', 'bx_pfw_history', 'bx_pfw_orders', 'bx_pfw_details');
+
+
+-- menus
+DELETE FROM `sys_menu_admin` WHERE `name`=@sModuleName;
+
+
+-- alert
+SET @iHandlerId = (SELECT `id` FROM `sys_alerts_handlers` WHERE `name`=@sModuleName LIMIT 1);
+DELETE FROM `sys_alerts_handlers` WHERE `id`=@iHandlerId LIMIT 1;
+DELETE FROM `sys_alerts` WHERE `handler_id`=@iHandlerId;
+
+
+-- email templates
+DELETE FROM `sys_email_templates` WHERE `Name` IN ('bx_pfw_paid_need_join');
+
+
+-- chart
+DELETE FROM `sys_objects_charts` WHERE `object` = @sModuleName;
+
+
+-- payments
+DELETE FROM `sys_objects_payments` WHERE `object` = @sModuleName;

+ 7 - 0
modules/boonex/paypal_payflow/js/_cart.js

@@ -0,0 +1,7 @@
+function BxPfwCart(oOptions) {
+	this.init(oOptions);
+
+	this._sErrNothingSelected = '_bx_pfw_err_nothing_selected';	
+};
+
+BxPfwCart.prototype = new BxPmtCart({});

+ 32 - 0
modules/boonex/paypal_payflow/js/_orders.js

@@ -0,0 +1,32 @@
+function BxPfwOrders(oOptions) {
+	this.init(oOptions);
+};
+
+BxPfwOrders.prototype = new BxPmtOrders({});
+
+BxPfwOrders.prototype.unsubscribe = function(sType, iId) {
+    var $this = this;
+
+    this._getOrderLoading(iId);
+
+    $.post(
+        this._sActionsUrl + 'act_cancel_subscription/',
+        {
+            type: sType,
+            id: iId
+        },
+        function(oData) {
+        	$this._getOrderLoading(iId);
+
+        	if(oData.message)
+        		alert(oData.message);
+
+            $('#pmt-orders-more').dolPopupHide({});
+        },
+        'json'
+    );
+};
+
+BxPfwOrders.prototype._getOrderLoading = function(iId) {
+	$('#pfw-order-loading-' + iId).bx_loading();
+};

+ 4 - 0
modules/boonex/paypal_payflow/log/.gitignore

@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore

+ 12 - 0
modules/boonex/paypal_payflow/request.php

@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+require_once( BX_DIRECTORY_PATH_CLASSES . 'BxDolRequest.php' );
+if(isset($aRequest[0]) && substr($aRequest[0], 0, 4) == 'act_') {
+    $aRequest[0] = str_replace('act_', '', $aRequest[0]);
+    echo BxDolRequest::processAsAction($aModule, $aRequest);
+} else
+    BxDolRequest::processAsAction($aModule, $aRequest);

+ 27 - 0
modules/boonex/paypal_payflow/template/css/_cart.css

@@ -0,0 +1,27 @@
+/*--- Express Checkout ---*/
+div.bx-pfw-ec-confirm-wrp {
+	width: 50%;
+}
+div.bx-pfw-ec-confirm {
+	left: -50%;
+}
+div.bx-pfw-ecc-buyer,
+div.bx-pfw-ecc-products {
+	font-weight: bold;
+}
+table.bx-pfw-ecc-buyer-info th,
+table.bx-pfw-ecc-buyer-info td,
+table.bx-pfw-ecc-products-info th, 
+table.bx-pfw-ecc-products-info td {
+	padding: 5px;
+	border: 0px;
+}
+td.bx-pfw-ecc-pi-quantity {
+	text-align: center;
+}
+td.bx-pfw-ecc-bi-caption,
+td.bx-pfw-ecc-pi-caption {
+	font-weight: bold;
+}
+
+/*--- Hosted Checout Pages ---*/

+ 11 - 0
modules/boonex/paypal_payflow/template/css/_orders.css

@@ -0,0 +1,11 @@
+div.pmt-vo-row-actions div.pmt-vo-caption, 
+div.pmt-vo-row-actions div.pmt-vo-content {
+	height: 36px;
+}
+div.pmt-vo-row-actions .bx-btn {
+	margin: 5px 0px;
+}
+div.pmt-orders-field.pmt-of-subscription {
+	width: 90px;
+    text-align: center;
+}

+ 35 - 0
modules/boonex/paypal_payflow/template/ec_confirm.html

@@ -0,0 +1,35 @@
+<div class="bx-pfw-ec-confirm bx-def-padding-sec">
+	<div class="bx-pfw-ecc-buyer bx-def-font-large"><bx_text:_bx_pfw_txt_buyer_info />:</div>
+	<div class="bx-pfw-ecc-buyer-info bx-def-padding-left">
+		<table class="bx-pfw-ecc-buyer-info">
+			<tr>
+				<td class="bx-pfw-ecc-bi-caption"><bx_text:_bx_pfw_txt_buyer_name />:</td><td>__buyer_name__</td>
+			</tr>
+			<tr>
+				<td class="bx-pfw-ecc-bi-caption"><bx_text:_bx_pfw_txt_buyer_email />:</td><td>__buyer_email__</td>
+			</tr>
+		</table>
+	</div>
+	<div class="bx-pfw-ecc-products bx-def-margin-top bx-def-font-large">__txt_items_info__:</div>
+	<div class="bx-pfw-ecc-products-info bx-def-padding-left">
+		<table class="bx-pfw-ecc-products-info">
+			<tr>
+				<th><bx_text:_bx_pfw_fcpt_details_title /></th>
+				<th><bx_text:_bx_pfw_fcpt_details_quantity /></th>
+				<th><bx_text:_bx_pfw_fcpt_details_price /></th>
+			</tr>
+			<bx_repeat:items>
+				<tr>
+					<td><a href="__link__">__title__</a></td>
+					<td class="bx-pfw-ecc-pi-quantity">__quantity__</td>
+					<td>__price__ __currency__<bx_if:show_duration> __duration__</bx_if:show_duration></td>
+				</tr>
+			</bx_repeat:items>
+			<tr>
+				<td class="bx-pfw-ecc-pi-caption"><bx_text:_bx_pfw_fcpt_details_total /></td>
+				<td class="bx-pfw-ecc-pi-quantity">__total_count__</td>
+				<td>__total_price__ __currency__</td>
+			</tr>
+		</table>
+	</div>
+</div>

+ 1 - 0
modules/boonex/paypal_payflow/template/hc_credit_card.html

@@ -0,0 +1 @@
+<iframe src='__request_url__' class="bx-pfw-hc-ccd" width='490' height='565' border='0' frameborder='0' scrolling='no' allowtransparency='true'></iframe>

+ 20 - 0
modules/boonex/paypal_payflow/template/history_orders.html

@@ -0,0 +1,20 @@
+<bx_repeat:orders>
+    <div class="pmt-orders-item">
+        <div class="pmt-orders-date">__date_uf__</div>
+        <div class="pmt-orders-client">
+        	<bx_if:show_link>
+            	<a href="__user_url__" target="_blank">__user_name__</a>
+            </bx_if:show_link>
+            <bx_if:show_text>__user_name__</bx_if:show_text>
+        </div>
+        <div class="pmt-orders-order">__order__</div>
+        <div class="pmt-orders-amount">__amount__</div>
+        <div class="pmt-orders-license">__license__</div>        
+        <div class="pmt-orders-field pmt-of-subscription">__subscription__</div>
+        <div class="pmt-orders-action">
+            __products__ __txt_more_products__ __items__ __txt_more_items__ <a href="javascript:void(0)" onclick="javascript:__js_object__.more('history', __id__)">__txt_action_more__</a>
+        </div>
+        <div class="pmt-cb">&nbsp;</div>
+    </div>
+</bx_repeat:orders>
+__paginate_panel__

+ 14 - 0
modules/boonex/paypal_payflow/template/history_orders_block.html

@@ -0,0 +1,14 @@
+__filter_panel__
+<div id="pmt-orders-history" class="bx-def-bc-margin">
+    <div class="pmt-orders-header">
+        <div class="pmt-orders-date">__txt_date__</div>
+        <div class="pmt-orders-client">__txt_seller__</div>
+        <div class="pmt-orders-order">__txt_order__</div>
+        <div class="pmt-orders-amount">__txt_amount__</div>
+        <div class="pmt-orders-license">__txt_license__</div>
+        <div class="pmt-orders-field pmt-of-subscription"><bx_text:_bx_pfw_txt_subscription /></div>
+        <div class="pmt-orders-action">__txt_action__</div>
+    </div>
+    <div class="pmt-orders-content">__orders__</div>
+</div>
+__loading__

BIN
modules/boonex/paypal_payflow/template/images/icons/std-mi.png


BIN
modules/boonex/paypal_payflow/template/images/icons/std-pi.png


BIN
modules/boonex/paypal_payflow/template/images/icons/std-si.png


BIN
modules/boonex/paypal_payflow/template/images/icons/std-wi.png


+ 21 - 0
modules/boonex/paypal_payflow/template/rb_processed_order.html

@@ -0,0 +1,21 @@
+__order__
+<div class="pmt-view-order bx-def-margin-top">
+    <div class="pmt-vo-row">
+        <div class="pmt-vo-caption">
+        	<bx_text:_bx_pfw_txt_order_profile />
+        </div>
+        <div class="pmt-vo-content">__order_profile__</div>
+        <div class="pmt-cb">&nbsp;</div>
+    </div>
+    <div class="pmt-vo-row pmt-vo-row-actions">
+    	<div class="pmt-vo-caption">&nbsp;</div>
+    	<div class="pmt-vo-content">
+	        <button class="bx-btn bx-btn-small" onclick="javascript:__js_object__.unsubscribe('__type__', __id__);">
+	        	<bx_text:_bx_pfw_btn_unsubscribe />
+	        </button>
+	        <div class="pmt-cb">&nbsp;</div>
+		</div>
+        <div class="pmt-cb">&nbsp;</div>
+    </div>
+</div>
+__loading__

+ 6 - 2
modules/boonex/russian/data/langs/system/ru.xml

@@ -585,7 +585,7 @@
 	<string name="_adm_dsg_err_remove_old_icon"><![CDATA[Невозможно удалить текущую иконку.]]></string>
 	<string name="_adm_dsg_err_remove_old_logo"><![CDATA[Невозможно удалить текущую лого-картинку.]]></string>
 	<string name="_adm_dsg_err_save"><![CDATA[Невозможно сохранить: ]]></string>
-	<string name="_adm_dsg_err_save_file"><![CDATA[Не удается сохранить изменения в файле.]]></string>
+	<string name="_adm_dsg_err_save_changes"><![CDATA[Невозможно сохранить изменения.]]></string>
 	<string name="_adm_dsg_scs_save"><![CDATA[Изменения удачно сохранены]]></string>
 	<string name="_adm_dsg_txt_alt_text"><![CDATA[Текст лого]]></string>
 	<string name="_adm_dsg_txt_cover_common"><![CDATA[Стандартная обложка страницы]]></string>
@@ -599,6 +599,10 @@
 	<string name="_adm_dsg_txt_upload_icon"><![CDATA[Загрузить новую иконку сайта]]></string>
 	<string name="_adm_dsg_txt_upload_image"><![CDATA[Загрузить новую лого-картинку]]></string>
 	<string name="_adm_dsg_txt_upload_image_preview"><![CDATA[Текущая лого-картинка]]></string>
+	<string name="_adm_dsg_txt_inj_head"><![CDATA[&lt;HEAD&gt; инъекция]]></string>
+	<string name="_adm_dsg_txt_inj_head_inf"><![CDATA[Код для вставки перед закрывающим HEAD тагом]]></string>
+	<string name="_adm_dsg_txt_inj_body"><![CDATA[&lt;BODY&gt; инъекция]]></string>
+	<string name="_adm_dsg_txt_inj_body_inf"><![CDATA[Код для вставки перед закрывающим BODY тагом]]></string>
 	<string name="_adm_err_cannot_process_action"><![CDATA[Невозможно выполнить действие. Отсутствует связанный обработчик.]]></string>
 	<string name="_adm_err_form_view_iu_delete"><![CDATA[Невозможно удалить текущую картинку.]]></string>
 	<string name="_adm_err_form_view_iu_save"><![CDATA[Невозможно сохранить: ]]></string>
@@ -2282,7 +2286,7 @@
 	<string name="_sys_uploader_close"><![CDATA[Закрыть]]></string>
 	<string name="_sys_uploader_confirm_close_popup"><![CDATA[О нет! Вы уверены? Это может прервать загрузку файлов.]]></string>
     <string name="_sys_uploader_confirm_leaving_page"><![CDATA[Загрузка в процессе! Не закрывайте эту страницу, или загрузка будет прервана на полпути.]]></string>
-    <string name="_sys_uploader_crop_button_name"><![CDATA[Обрезка картинки]]></string>
+    <string name="_sys_uploader_crop_button_name"><![CDATA[Обрезка Картинки]]></string>
     <string name="_sys_uploader_crop_rotate_left"><![CDATA[Повернуть налево]]></string>
 	<string name="_sys_uploader_crop_rotate_right"><![CDATA[Повернуть направо]]></string>
 	<string name="_sys_uploader_crop_submit_button"><![CDATA[Загрузить]]></string>    

BIN
phing.phar


BIN
plugins/ffmpeg/ffmpeg_win.exe


+ 132 - 0
pricing.html

@@ -0,0 +1,132 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>Insert title here</title>
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/common.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/cache_public/lessphp_668b9bea7611d892ef23351548502d820fccb3b5.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/general.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/icons.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/colors.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/forms.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/media-desktop.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/media-tablet.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/media-phone.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/media-print.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/plugins_public/marka/marka.min.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/page_layouts.css" />
+<link type="text/css" rel="stylesheet" href="http://trident.me/template/css/menu.css" />
+</head>
+<body>
+	<div style="width:1000px; margin: 0px auto;">
+
+
+<style>
+.una-pricing-boxes {
+	text-align: center;
+}
+.una-pricing-box {
+display:inline-block;
+width:18.4rem;
+text-align:center;
+vertical-align:top;
+}
+.una-pricing-box p {
+margin: 0.3125rem 0;
+}
+.una-pricing-box .bx-btn {
+float:none;
+display:inline-block;
+}
+.una-pricing-name {
+float:left;
+text-transform: uppercase;
+line-height:50px;
+font-weight:bold;
+}
+.una-pricing-price {
+float:right;
+text-align: right;
+line-height:25px;
+font-weight:bold;
+color:#4faf7f;
+}
+.una-pricing-desc {
+min-height:17.25rem;
+}
+</style>
+
+<h1 style="text-align:center;">Software and Support</h1>
+<p  style="text-align:center;" class="bx-def-font-large">No contracts. Unlimited users. Deployed on your own dedicated servers or cloud-hosting.</p>
+
+<div class="una-pricing-boxes bx-def-margin-top bx-def-padding-top">
+
+<!-- ############# PREMIUM ############ -->
+<div class="una-pricing-box bx-def-box bx-def-round-corners bx-def-color-bg-box bx-def-border bx-def-margin-bottom bx-def-margin-sec-leftright">
+<div class="una-pricing-title bx-def-padding bx-clearfix">
+<div class="una-pricing-name bx-def-font-large">Premium</div>
+<div class="una-pricing-price">$399<br />monthly</div>
+</div>
+<hr class="bx-def-hr "/>
+<div class="una-pricing-desc bx-def-padding">
+<p>Core Apps</p>
+<p>Premium Apps</p>
+<p>Forum Support</p>
+<p>Email Support (48h SLA)</p>
+<p>Sandbox Hosting</p>
+<p>Onboarding</p>
+</div>
+<a id="una-btn-pro" class="bx-btn bx-def-margin-bottom" href="https://Unaio.chargebee.com/hosted_pages/plans/premium">Purchase Plan</a>
+</div>
+
+<!-- ############# BUSINESS ############ -->
+<div class="una-pricing-box bx-def-box bx-def-round-corners bx-def-color-bg-box bx-def-border bx-def-margin-bottom bx-def-margin-sec-leftright">
+<div class="una-pricing-title bx-def-padding bx-clearfix">
+<div class="una-pricing-name bx-def-font-large">Business</div>
+<div class="una-pricing-price">$799<br />monthly</div>
+</div>
+<hr class="bx-def-hr "/>
+<div class="una-pricing-desc bx-def-padding">
+<p>Core Apps</p>
+<p>Premium Apps</p>
+<p>Forum Support</p>
+<p>Email Support (24h SLA)</p>
+<p>Chat Support (Mon-Fri)</p>
+<p>Sandbox Hosting</p>
+<p>Onboarding</p>
+<p>Implementation</p>
+</div>
+<a id="una-btn-business" class="bx-btn bx-def-margin-bottom" href="https://Unaio.chargebee.com/hosted_pages/plans/business">Purchase Plan</a>
+</div>
+
+<!-- ############# ENTERPRISE ############ -->
+<div class="una-pricing-box bx-def-box bx-def-round-corners bx-def-color-bg-box bx-def-border bx-def-margin-bottom bx-def-margin-sec-leftright">
+<div class="una-pricing-title bx-def-padding bx-clearfix">
+<div class="una-pricing-name bx-def-font-large">Enterprise</div>
+<div class="una-pricing-price">$1599<br />monthly</div>
+</div>
+<hr class="bx-def-hr "/>
+<div class="una-pricing-desc bx-def-padding">
+<p>Core Apps</p>
+<p>Premium Apps</p>
+<p>Forum Support</p>
+<p>Email Support (12h SLA)</p>
+<p>Chat Support (Mon-Sun)</p>
+<p>Phone Support (Mon-Sun)</p>
+<p>Sandbox Hosting</p>
+<p>Onboarding</p>
+<p>Implementation</p>
+<p>Customisation</p>
+</div>
+<a id="una-btn-enterprise" class="bx-btn bx-def-margin-bottom" href="https://Unaio.chargebee.com/hosted_pages/plans/enterprise">Purchase Plan</a>
+</div>
+
+</div> <!-- .una-pricing-boxes -->
+
+<div style="text-align:center;">
+Any questions? <a href="https://una.io/page/contact">Contact us</a>
+</div>
+
+	</div>
+</body>
+</html>

+ 35 - 0
print.php

@@ -0,0 +1,35 @@
+<?php
+
+$a = array('sys_simple', 'sys_html5');
+ 
+var_dump(serialize($a));
+exit;
+
+require_once('./inc/header.inc.php');
+require_once(BX_DIRECTORY_PATH_INC . "utils.inc.php");
+
+$sDomain = BX_DOL_URL_ROOT;
+$aDomain = parse_url($sDomain);
+
+$sReferrer = 'http://dolphin/studio/store.php?page=goodies';
+$aReferrer = parse_url($sReferrer);
+
+var_dump($aDomain['host'], $aReferrer['host']);
+exit;
+
+$sPathFrom = BX_DIRECTORY_PATH_ROOT . 'delete/less.php';
+$sPathTo = BX_DIRECTORY_PATH_ROOT . 'delete/coppied/less.php';
+
+bx_import('BxDolFile');
+$oFile = BxDolFile::getInstance();
+$bResult = $oFile->copy($sPathFrom, $sPathTo);
+
+var_dump($bResult);
+exit;
+
+/*
+if ($handle = fopen('tmp/print.txt', 'a')) {
+	fwrite($handle, print_r($_POST, true));
+	fclose($handle);
+} 
+*/   

+ 8 - 11
studio/classes/BxDolStudioDesigner.php

@@ -224,19 +224,16 @@ class BxDolStudioDesigner extends BxTemplStudioPage
 
 	function submitInjections(&$oForm)
     {
-		$sPath = BxDolTemplate::getInstance()->getTemplatePath('_header.html', BX_DOL_TEMPLATE_CHECK_IN_BASE);
+		$bResult = $this->oDb->updateInjection('sys_head', $oForm->getCleanValue('sys_head'));
+        $bResult |= $this->oDb->updateInjection('sys_body', $oForm->getCleanValue('sys_body'));
+        if($bResult) {
+        	if(getParam('sys_db_cache_enable'))
+            	$this->oDb->cleanCache('sys_injections.inc');
 
-		$rHandle = fopen($sPath, 'w');
-		if(!$rHandle)
-			return $this->getJsResult('_adm_dsg_err_save_file');
-
-		$bResult = fwrite($rHandle, $oForm->getCleanValue('code'));
-		fclose($rHandle);
-
-		if(!$bResult)
-			return $this->getJsResult('_adm_dsg_err_save_file');
+            return $this->getJsResult('_adm_dsg_scs_save', true, true, BX_DOL_URL_STUDIO . 'designer.php?page=' . BX_DOL_STUDIO_DSG_TYPE_INJECTIONS);
+        }
 
-        return $this->getJsResult('_adm_dsg_scs_save', true, true, BX_DOL_URL_STUDIO . 'designer.php?page=' . BX_DOL_STUDIO_DSG_TYPE_INJECTIONS);
+        return $this->getJsResult('_adm_dsg_err_save_changes');
     }
 }
 

+ 9 - 0
studio/classes/BxDolStudioDesignerQuery.php

@@ -13,6 +13,15 @@ class BxDolStudioDesignerQuery extends BxDolStudioPageQuery
     {
         parent::__construct();
     }
+
+    public function updateInjection($sName, $sValue)
+    {
+		return (int)$this->query("UPDATE `sys_injections` SET `data`=:data WHERE `name`=:name", array(
+			'name' => $sName,
+			'data' => $sValue
+		)) > 0;
+    }
+    
 }
 
 /** @} */

+ 9 - 6
studio/js/designer.js

@@ -20,12 +20,15 @@ function BxDolStudioDesigner(oOptions) {
 }
 
 BxDolStudioDesigner.prototype.initCodeMirror = function(sSelector) {
-    var e = CodeMirror.fromTextArea($(sSelector).get(0), {
-        lineNumbers: true,
-        mode: "htmlmixed",
-        htmlMode: true,
-        matchBrackets: true
-    });
+	var oSelector = $(sSelector);
+	for(var i = 0; i < oSelector.length; i++) {
+	    var e = CodeMirror.fromTextArea(oSelector.get(i), {
+	        lineNumbers: true,
+	        mode: "htmlmixed",
+	        htmlMode: true,
+	        matchBrackets: true
+	    });
+	}
 };
 
 BxDolStudioDesigner.prototype.makeDefault = function(sUri) {

+ 15 - 6
studio/template/scripts/BxBaseStudioDesigner.php

@@ -553,12 +553,21 @@ class BxBaseStudioDesigner extends BxDolStudioDesigner
 					'name' => 'page',
 					'value' => $this->sPage
 				),
-				'code' => array(
+				'sys_head' => array(
                     'type' => 'textarea',
                     'code' => true,
-                    'name' => 'code',
-					'caption' => '',
-					'value' => BxDolTemplate::getInstance()->getHtml('_header.html', BX_DOL_TEMPLATE_CHECK_IN_BASE),
+                    'name' => 'sys_head',
+					'caption' => _t('_adm_dsg_txt_inj_head'),
+					'info' => _t('_adm_dsg_txt_inj_head_inf'),
+					'value' => $this->oDb->getOne("SELECT `data` FROM `sys_injections` WHERE `name`='sys_head'"),
+				),
+				'sys_body' => array(
+                    'type' => 'textarea',
+                    'code' => true,
+                    'name' => 'sys_body',
+					'caption' => _t('_adm_dsg_txt_inj_body'),
+					'info' => _t('_adm_dsg_txt_inj_body_inf'),
+					'value' => $this->oDb->getOne("SELECT `data` FROM `sys_injections` WHERE `name`='sys_body'"),
 				),
 				'save' => array(
 					'type' => 'submit',
@@ -582,13 +591,13 @@ class BxBaseStudioDesigner extends BxDolStudioDesigner
 		return $oTemplate->parseHtmlByName('designer.html', array(
 			'content' => $this->getBlockCode(array(
 				'items' => $oTemplate->parseHtmlByName('dsr_injections.html', array(
-					'warning' => MsgBox(_t('_adm_dsg_dsc_splash_warning')),
+					'warning' => '',
 					'splash_iframe_id' => $this->sInjectionsIframeId, 
 					'form' => $oForm->getCode()
 				)),
 			)),
 			'js_content' => $this->getPageJsCode(array(
-				'sCodeMirror' => 'textarea[name=code]'
+				'sCodeMirror' => 'textarea[name=sys_head],textarea[name=sys_body]'
 			))
 		));
     }

+ 40 - 0
template/splash.bkp.html

@@ -0,0 +1,40 @@
+<div class="bx-splash bx-def-color-bg-page">
+	<div class="bx-spl-image">
+		<img src="<bx_image_url:splash.png />" />
+	</div>
+	<div class="bx-spl-slogan bx-def-font-h1">
+		<bx_text:_sys_splash_slogan />
+	</div>
+	<div class="bx-spl-form">
+		__form__
+		<script type="text/javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                    	var oForm = $("#__form_id__");
+                    	oForm.find('.bx-form-warn').hide();
+
+                        bx_loading(oForm, true);
+                    },
+                    success: function (oData) {
+                    	bx_loading($("#__form_id__"), false);
+
+                    	if (oData && oData.msg != undefined) {
+                            alert(oData.msg);
+                        }
+
+                    	if (oData && oData.err != undefined) {
+                            for(var sKey in oData.err)
+                            	$('#bx-form-element-' + sKey + ' .bx-form-warn').html(oData.err[sKey]).show();
+                        }
+
+                    	if (oData && oData.eval != undefined) {
+                            eval(oData.eval);
+                        }
+                    }
+                });
+            });
+        </script>
+	</div>
+</div>

+ 117 - 0
test.php

@@ -0,0 +1,117 @@
+<?php
+$aParams = array('per_page' => 'bx_market_per_page_profile');
+var_dump(serialize($aParams)); exit;
+
+define('BX_DOL', 1);
+
+define('BX_DOL_URL_ROOT', 'http://trident.me/'); ///< site url
+define('BX_DIRECTORY_PATH_ROOT', 'Z:/home/trident.me/www/'); ///< site path
+
+define('BX_DATABASE_HOST', 'localhost'); ///< db host
+define('BX_DATABASE_SOCK', ''); ///< db socket
+define('BX_DATABASE_PORT', ''); ///< db port
+define('BX_DATABASE_USER', 'root'); ///< db user
+define('BX_DATABASE_PASS', ''); ///< db password
+define('BX_DATABASE_NAME', 'boonex_trident'); ///< db name
+
+define('BX_DB_FULL_VISUAL_PROCESSING', true); ///< upon db error - show error message
+define('BX_DB_FULL_DEBUG_MODE', true); ///< upon db error - show detailed report (turn off in production mode)
+define('BX_DB_DO_EMAIL_ERROR_REPORT', true); ///< upon db error - send email with detailed report
+
+require_once (BX_DIRECTORY_PATH_ROOT . 'inc/utils.inc.php');
+require_once (BX_DIRECTORY_PATH_ROOT . 'inc/classes/BxDol.php');
+require_once (BX_DIRECTORY_PATH_ROOT . 'inc/classes/BxDolDb.php');
+
+$oDb = BxDolDb::getInstance();
+
+/*
+$oStmt = $oDb->prepare("SELECT `title` FROM `sys_modules` WHERE `name`=?", "system");
+$mixedResult = $oDb->getOne($oStmt);
+*/
+
+/*
+$oStmt = $oDb->prepare("SELECT * FROM `sys_modules` WHERE `name`=?", "system");
+$mixedResult = $oDb->getRow($oStmt);
+*/
+
+/*
+$oStmt = $oDb->prepare("SELECT * FROM `sys_modules` WHERE `enabled`=?", 1);
+$mixedResult = $oDb->getColumn($oStmt, 3);
+*/
+
+/*
+$oStmt = $oDb->prepare("SELECT * FROM `sys_modules` WHERE `type`=? AND `enabled`=?", "module", 1);
+
+$mixedResult[] = $oDb->getFirstRow($oStmt);
+while($aRow = $oDb->getNextRow($oStmt)) {
+	$mixedResult[] = $aRow;
+}
+*/
+
+/*
+$oStmt = "SELECT * FROM `sys_modules` WHERE `type`=:type AND `enabled`=:enabled";
+
+$mixedResult[] = $oDb->getFirstRow($oStmt, array('type' => 'module', 'enabled' => 1));
+while($aRow = $oDb->getNextRow($oStmt)) {
+	$mixedResult[] = $aRow;
+}
+*/
+
+/*
+$oStmt = $oDb->prepare("SELECT * FROM `sys_modules` WHERE `type`=? AND `enabled`=?", "module", 1);
+$mixedResult = $oDb->getRow($oStmt);
+$mixedResult = $oDb->getAffectedRows($oStmt);
+*/
+
+/*
+$oStmt = $oDb->prepare("UPDATE `sys_modules` SET `pending_uninstall`='0' WHERE `pending_uninstall`='0'");
+$mixedResult = $oDb->query($oStmt);
+*/
+
+/*
+$oStmt = $oDb->prepare("SELECT * FROM `sys_modules` WHERE `type`=? AND `enabled`=?", "language", 1);
+$mixedResult = $oDb->getAll($oStmt);
+*/
+
+/*
+$oStmt = $oDb->pdoQuery("SELECT * FROM `sys_modules` WHERE `type`='template' AND `enabled`=1");
+$mixedResult = $oDb->fillArray($oStmt);
+*/
+
+/*
+$oStmt = $oDb->prepare("SELECT * FROM `sys_modules` WHERE `type`=? AND `enabled`=?", "language", 1);
+$mixedResult = $oDb->getAllWithKey($oStmt, 'name');
+*/
+
+/*
+$oStmt = $oDb->prepare("SELECT * FROM `sys_modules` WHERE `type`=? AND `enabled`=?", "language", 1);
+$mixedResult = $oDb->getPairs($oStmt, 'name', 'title');
+*/
+
+/*
+$oDb->pdoExec("INSERT INTO `sys_modules` SET `name`='ttt', `path`='ttt', `uri`='ttt', `class_prefix`='ttt', `db_prefix`='ttt'");
+$mixedResult = $oDb->lastId();
+*/
+
+/*
+$mixedResult = $oDb->listTables();
+*/
+
+/*
+$mixedResult = $oDb->getFields('sys_modules');
+$mixedResult = $oDb->isFieldExists('sys_modules', 'dependencies');
+*/
+
+/*
+$mixedResult = $oDb->getEncoding();
+*/
+
+/*
+$mixedResult = $oDb->escape("It's test!!!");
+$mixedResult = $oDb->implode_escape(array("It's test!", "Test", "I'm testing!"));
+$mixedResult = $oDb->arrayToSQL(array("title" => "Test", "message" => "It's test!", "content" => "I'm testing!"));
+*/
+
+$mixedResult = $oDb->setTimezone('UTC');
+
+var_dump($mixedResult);