Kaynağa Gözat

Modules updates.

AntonLV 4 ay önce
ebeveyn
işleme
38f2a9731c
100 değiştirilmiş dosya ile 20148 ekleme ve 0 silme
  1. 51 0
      modules/boonex/acl/updates/15.0.1_15.0.2/install/config.php
  2. 13 0
      modules/boonex/acl/updates/15.0.1_15.0.2/install/installer.php
  3. 6 0
      modules/boonex/acl/updates/15.0.1_15.0.2/install/langs/en.xml
  4. 11 0
      modules/boonex/acl/updates/15.0.1_15.0.2/install/sql/install.sql
  5. 174 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclConfig.php
  6. 274 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclDb.php
  7. 150 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclFormPrice.php
  8. 228 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclGridAdministration.php
  9. 253 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclGridView.php
  10. 64 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclResponse.php
  11. 70 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/install/config.php
  12. 142 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/install/langs/en.xml
  13. 159 0
      modules/boonex/acl/updates/15.0.1_15.0.2/source/install/sql/install.sql
  14. 51 0
      modules/boonex/ads/updates/15.0.2_15.0.3/install/config.php
  15. 13 0
      modules/boonex/ads/updates/15.0.2_15.0.3/install/installer.php
  16. 7 0
      modules/boonex/ads/updates/15.0.2_15.0.3/install/langs/en.xml
  17. 8 0
      modules/boonex/ads/updates/15.0.2_15.0.3/install/sql/install.sql
  18. 450 0
      modules/boonex/ads/updates/15.0.2_15.0.3/source/classes/BxAdsFormEntry.php
  19. 2780 0
      modules/boonex/ads/updates/15.0.2_15.0.3/source/classes/BxAdsModule.php
  20. 151 0
      modules/boonex/ads/updates/15.0.2_15.0.3/source/install/config.php
  21. 697 0
      modules/boonex/ads/updates/15.0.2_15.0.3/source/install/langs/en.xml
  22. 1141 0
      modules/boonex/ads/updates/15.0.2_15.0.3/source/install/sql/install.sql
  23. 51 0
      modules/boonex/albums/updates/15.0.1_15.0.2/install/config.php
  24. 13 0
      modules/boonex/albums/updates/15.0.1_15.0.2/install/installer.php
  25. 4 0
      modules/boonex/albums/updates/15.0.1_15.0.2/install/langs/en.xml
  26. 2 0
      modules/boonex/albums/updates/15.0.1_15.0.2/install/sql/install.sql
  27. 171 0
      modules/boonex/albums/updates/15.0.1_15.0.2/source/classes/BxAlbumsDb.php
  28. 288 0
      modules/boonex/albums/updates/15.0.1_15.0.2/source/classes/BxAlbumsSearchResultMedia.php
  29. 143 0
      modules/boonex/albums/updates/15.0.1_15.0.2/source/install/config.php
  30. 311 0
      modules/boonex/albums/updates/15.0.1_15.0.2/source/install/langs/en.xml
  31. 609 0
      modules/boonex/albums/updates/15.0.1_15.0.2/source/install/sql/install.sql
  32. 51 0
      modules/boonex/analytics/updates/15.0.0_15.0.1/install/config.php
  33. 13 0
      modules/boonex/analytics/updates/15.0.0_15.0.1/install/installer.php
  34. 576 0
      modules/boonex/analytics/updates/15.0.0_15.0.1/source/classes/BxAnalyticsModule.php
  35. 77 0
      modules/boonex/analytics/updates/15.0.0_15.0.1/source/install/config.php
  36. 51 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/install/config.php
  37. 13 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/install/installer.php
  38. 4 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/install/langs/en.xml
  39. 4 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/install/sql/install.sql
  40. 54 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/source/classes/BxAntispamAlertsResponse.php
  41. 206 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/source/classes/BxAntispamDNSBlacklists.php
  42. 634 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/source/classes/BxAntispamModule.php
  43. 68 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/source/install/config.php
  44. 233 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/source/install/langs/en.xml
  45. 73 0
      modules/boonex/antispam/updates/14.0.2_15.0.0/source/install/sql/install.sql
  46. 51 0
      modules/boonex/channels/updates/15.0.0_15.0.1/install/config.php
  47. 13 0
      modules/boonex/channels/updates/15.0.0_15.0.1/install/installer.php
  48. 12 0
      modules/boonex/channels/updates/15.0.0_15.0.1/install/sql/enable.sql
  49. 203 0
      modules/boonex/channels/updates/15.0.0_15.0.1/source/classes/BxCnlConfig.php
  50. 664 0
      modules/boonex/channels/updates/15.0.0_15.0.1/source/classes/BxCnlModule.php
  51. 153 0
      modules/boonex/channels/updates/15.0.0_15.0.1/source/install/config.php
  52. 266 0
      modules/boonex/channels/updates/15.0.0_15.0.1/source/install/langs/en.xml
  53. 444 0
      modules/boonex/channels/updates/15.0.0_15.0.1/source/install/sql/enable.sql
  54. 58 0
      modules/boonex/channels/updates/15.0.0_15.0.1/source/install/sql/uninstall.sql
  55. 51 0
      modules/boonex/convos/updates/15.0.1_15.0.2/install/config.php
  56. 13 0
      modules/boonex/convos/updates/15.0.1_15.0.2/install/installer.php
  57. 274 0
      modules/boonex/convos/updates/15.0.1_15.0.2/source/classes/BxCnvFormEntry.php
  58. 104 0
      modules/boonex/convos/updates/15.0.1_15.0.2/source/install/config.php
  59. 51 0
      modules/boonex/credits/updates/15.0.0_15.0.1/install/config.php
  60. 13 0
      modules/boonex/credits/updates/15.0.0_15.0.1/install/installer.php
  61. 6 0
      modules/boonex/credits/updates/15.0.0_15.0.1/install/sql/enable.sql
  62. 338 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsConfig.php
  63. 124 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGrid.php
  64. 105 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridHistoryAdministration.php
  65. 39 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridHistoryCommon.php
  66. 68 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridOrdersAdministration.php
  67. 109 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridProfilesAdministration.php
  68. 106 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridWithdrawalsAdministration.php
  69. 84 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridWithdrawalsCommon.php
  70. 1528 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsModule.php
  71. 204 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsTemplate.php
  72. 77 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/install/config.php
  73. 293 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/install/sql/enable.sql
  74. 55 0
      modules/boonex/credits/updates/15.0.0_15.0.1/source/js/withdraw.js
  75. 51 0
      modules/boonex/developer/updates/15.0.1_15.0.2/install/config.php
  76. 13 0
      modules/boonex/developer/updates/15.0.1_15.0.2/install/installer.php
  77. 52 0
      modules/boonex/developer/updates/15.0.1_15.0.2/install/langs/en.xml
  78. 19 0
      modules/boonex/developer/updates/15.0.1_15.0.2/install/sql/install.sql
  79. 541 0
      modules/boonex/developer/updates/15.0.1_15.0.2/source/classes/BxDevBuilderPage.php
  80. 145 0
      modules/boonex/developer/updates/15.0.1_15.0.2/source/classes/BxDevFunctions.php
  81. 517 0
      modules/boonex/developer/updates/15.0.1_15.0.2/source/classes/BxDevModule.php
  82. 69 0
      modules/boonex/developer/updates/15.0.1_15.0.2/source/install/config.php
  83. 565 0
      modules/boonex/developer/updates/15.0.1_15.0.2/source/install/langs/en.xml
  84. 754 0
      modules/boonex/developer/updates/15.0.1_15.0.2/source/install/sql/install.sql
  85. 51 0
      modules/boonex/donations/updates/15.0.0_15.0.1/install/config.php
  86. 13 0
      modules/boonex/donations/updates/15.0.0_15.0.1/install/installer.php
  87. 4 0
      modules/boonex/donations/updates/15.0.0_15.0.1/install/sql/enable.sql
  88. 154 0
      modules/boonex/donations/updates/15.0.0_15.0.1/source/classes/BxDonationsConfig.php
  89. 318 0
      modules/boonex/donations/updates/15.0.0_15.0.1/source/classes/BxDonationsModule.php
  90. 192 0
      modules/boonex/donations/updates/15.0.0_15.0.1/source/classes/BxDonationsTemplate.php
  91. 71 0
      modules/boonex/donations/updates/15.0.0_15.0.1/source/install/config.php
  92. 114 0
      modules/boonex/donations/updates/15.0.0_15.0.1/source/install/sql/enable.sql
  93. 51 0
      modules/boonex/elasticsearch/updates/15.0.1_15.0.2/install/config.php
  94. 13 0
      modules/boonex/elasticsearch/updates/15.0.1_15.0.2/install/installer.php
  95. 249 0
      modules/boonex/elasticsearch/updates/15.0.1_15.0.2/source/classes/BxElsApi.php
  96. 71 0
      modules/boonex/elasticsearch/updates/15.0.1_15.0.2/source/install/config.php
  97. 51 0
      modules/boonex/events/updates/15.0.1_15.0.2/install/config.php
  98. 13 0
      modules/boonex/events/updates/15.0.1_15.0.2/install/installer.php
  99. 4 0
      modules/boonex/events/updates/15.0.1_15.0.2/install/langs/en.xml
  100. 5 0
      modules/boonex/events/updates/15.0.1_15.0.2/install/sql/install.sql

+ 51 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Paid Levels',
+    'version_from' => '15.0.1',
+    'version_to' => '15.0.2',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/acl/updates/update_15.0.1_15.0.2/',
+    'home_uri' => 'acl_update_1501_1502',
+
+    'module_dir' => 'boonex/acl/',
+    'module_uri' => 'acl',
+
+    'db_prefix' => 'bx_acl_',
+    'class_prefix' => 'BxAcl',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Paid Levels',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxAclUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 6 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/install/langs/en.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string_add name="_bx_acl_txt_level_caption_standard"><![CDATA[Free]]></string_add>
+    <string_add name="_bx_acl_txt_level_description_standard"><![CDATA[Try basic features for free.]]></string_add>
+    <string_add name="_bx_acl_txt_level_details_standard"><![CDATA[More info may be here.]]></string_add>
+</resources>

+ 11 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/install/sql/install.sql

@@ -0,0 +1,11 @@
+SET @sName = 'bx_acl';
+
+
+-- TABLES
+DELETE FROM `bx_acl_level_prices` WHERE `name`='standard';
+INSERT INTO `bx_acl_level_prices` (`level_id`, `name`, `caption`, `description`, `details`, `period`, `period_unit`, `trial`, `price`, `immediate`, `added`, `active`, `order`) VALUES
+(3, 'standard', '_bx_acl_txt_level_caption_standard', '_bx_acl_txt_level_description_standard', '_bx_acl_txt_level_details_standard', 0, '', 0, 0, 1, UNIX_TIMESTAMP(), 0, 1);
+
+
+-- GRIDS
+UPDATE `sys_objects_grid` SET `source`='SELECT `tlp`.*, `tl`.`Name` AS `level_name`, `tl`.`Icon` AS `level_icon` FROM `bx_acl_level_prices` AS `tlp` LEFT JOIN `sys_acl_levels` AS `tl` ON `tlp`.`level_id`=`tl`.`ID` WHERE `tlp`.`active`<>''0'' && `tl`.`Active`=''yes'' ' WHERE `object`='bx_acl_view';

+ 174 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclConfig.php

@@ -0,0 +1,174 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAclConfig extends BxBaseModGeneralConfig
+{
+    protected $_oDb;
+    protected $_aHtmlIds;
+    protected $_aCurrency;
+    protected $_iOwner;
+
+    protected $_bShowToUnauthenticated;
+
+    protected $_iExpireNotificationDays;
+    protected $_bExpireNotifyOnce;
+    protected $_iRemoveExpiredFor;
+
+    /**
+     * Constructor
+     */
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+            // database tables
+            'TABLE_PRICES' => $aModule['db_prefix'] . 'level_prices',
+            'TABLE_LICENSES' => $aModule['db_prefix'] . 'licenses',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_LEVEL_ID' => 'level_id',
+            'FIELD_NAME' => 'name',
+            'FIELD_PRICE' => 'price',
+            'FIELD_TRIAL' => 'trial',
+            'FIELD_IMMEDIATE' => 'immediate',
+
+            // page URIs
+            'URL_ADMINISTRATION' => 'page.php?i=acl-administration',
+            'URL_VIEW' => 'page.php?i=acl-view',
+
+            // some params
+            'PARAM_DATE_FORMAT' => 'd.m.Y',
+            'PARAM_RECURRING_RESERVE' => 'bx_acl_recurring_reserve',
+            'PARAM_RECURRING_PRIORITIZE' => 'bx_acl_recurring_prioritize',
+            'PARAM_SHOW_TO_UNAUTHENTICATED' => 'bx_acl_show_to_unauthenticated',
+            'PARAM_ACTIONS_LIMIT_BLOCK' => 20,
+            'PARAM_ACTIONS_LIMIT_POPUP' => 10,
+
+            // objects 
+            'OBJECT_GRID_ADMINISTRATION' => 'bx_acl_administration',
+            'OBJECT_GRID_VIEW' => 'bx_acl_view',
+            'OBJECT_FORM_PRICE' => 'bx_acl_price',
+            'OBJECT_FORM_PRICE_DISPLAY_ADD' => 'bx_acl_price_add',
+            'OBJECT_FORM_PRICE_DISPLAY_EDIT' => 'bx_acl_price_edit',
+            'OBJECT_FORM_PRELISTS_PERIOD_UNITS' => 'bx_acl_period_units',
+
+            // email templates
+            'ETEMPLATE_SBS_CANCEL_REQUIRED' => 'bx_acl_subscription_cancel_required',
+        );
+
+        $this->_aGridObjects = array(
+            'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION'],
+            'view' => $this->CNF['OBJECT_GRID_VIEW'],
+        );
+
+        $this->_aPrefixes = array(
+            'style' => 'bx-acl',
+            'option' => 'bx_acl_',
+        );
+
+        $this->_aJsClasses = array(
+            'main' => 'BxAclMain',
+            'form' => 'BxAclForm',
+            'administration' => 'BxAclAdministration',
+        );
+        $this->_aJsObjects = array(
+            'main' => 'oAclMain',
+            'form' => 'oAclForm',
+            'administration' => 'oAclAdministration',
+        );
+
+        $sHtmlPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = [
+            'actions_popup' => $sHtmlPrefix . '-actions-popup-',
+            'actions_list' => $sHtmlPrefix . '-actions-list-',
+            'popup_price' => $sHtmlPrefix . '-popup-price'
+        ];
+
+        $this->_iOwner = 0;
+        $this->_aCurrency = [];
+
+        $this->_bExpireNotifyOnce = true;
+        $this->_iExpireNotificationDays = 1;
+        $this->_iRemoveExpiredFor = 30;
+    }
+
+    public function init(&$oDb)
+    {
+        $this->_oDb = &$oDb;
+
+        $sOptionPrefix = $this->getPrefix('option');
+        
+        $this->_bShowToUnauthenticated = getParam($this->CNF['PARAM_SHOW_TO_UNAUTHENTICATED']) == 'on';
+
+        //TODO: Looks like these 3 settings aren't used or work.
+        $this->_iExpireNotificationDays = (int)getParam($sOptionPrefix . 'expire_notification_days');
+        $this->_bExpireNotifyOnce = getParam($sOptionPrefix . 'expire_notify_once') == 'on';
+        $this->_iRemoveExpiredFor = (int)getParam($sOptionPrefix . 'remove_expired_for');
+    }
+
+    public function isShowToUnauthenticated()
+    {
+        return $this->_bShowToUnauthenticated;
+    }
+
+    public function getExpireNotificationDays()
+    {
+        return $this->_iExpireNotificationDays;
+    }
+
+    public function isExpireNotifyOnce()
+    {
+        return $this->_bExpireNotifyOnce;
+    }
+
+    public function getRemoveExpiredFor()
+    {
+        return $this->_iRemoveExpiredFor;
+    }
+
+    public function getHtmlIds($sKey = '')
+    {
+        if(empty($sKey))
+            return $this->_aHtmlIds;
+
+        return isset($this->_aHtmlIds[$sKey]) ? $this->_aHtmlIds[$sKey] : '';
+    }
+
+    public function getOwner()
+    {
+        if(empty($this->_iOwner))
+            $this->_iOwner = (int)BxDolPayments::getInstance()->getOption('site_admin');
+
+    	return $this->_iOwner;
+    }
+
+    public function getCurrency()
+    {
+        if(empty($this->_aCurrency) || !is_array($this->_aCurrency))
+            $this->_aCurrency = BxDolPayments::getInstance()->getCurrencyInfo($this->getOwner());
+
+    	return $this->_aCurrency;
+    }
+
+    public function getPriceName($sName)
+    {
+        return uriGenerate($sName, $this->CNF['TABLE_PRICES'], $this->CNF['FIELD_NAME'], ['lowercase' => false]);
+    }
+
+    public function formatDate($iTs)
+    {
+        return gmdate($this->CNF['PARAM_DATE_FORMAT'], $iTs);
+    }
+}
+
+/** @} */

+ 274 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclDb.php

@@ -0,0 +1,274 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAclDb extends BxDolModuleDb
+{
+    protected $_oConfig;
+
+    public function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+
+        $this->_oConfig = $oConfig;
+    }
+
+    public function getProductsNames ($iVendor = 0, $iLimit = 1000)
+    {
+        return $this->getColumn("SELECT `name` FROM `bx_acl_level_prices` WHERE 1 LIMIT :limit", ['limit' => $iLimit]);
+    }
+
+    public function getLevels($aParams, $bReturnCount = false)
+    {
+        $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
+        $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        $sSelectClause = "
+            `tal`.`ID` AS `id`,
+            `tal`.`Name` AS `name`,
+            `tal`.`Icon` AS `icon`,
+            `tal`.`Description` AS `description`,
+            `tal`.`Active` AS `active`,
+            `tal`.`Purchasable` AS `purchasable`,
+            `tal`.`Removable` AS `removable`,
+            `tal`.`QuotaSize` AS `quota_size`,
+            `tal`.`QuotaNumber` AS `quota_number`,
+            `tal`.`QuotaMaxFileSize` AS `quota_max_file_size`,
+            `tal`.`Order` AS `order`";
+
+        if(!isset($aParams['order']) || empty($aParams['order']))
+            $sOrderClause = "ORDER BY `tal`.`Order` ASC";
+
+        switch($aParams['type']) {
+            case 'by_id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = array(
+                    'id' => $aParams['value']
+                );
+
+                $sWhereClause .= "AND `tal`.`id`=:id";
+                break;
+
+            case 'for_selector':
+                $aMethod['name'] = 'getPairs';
+                $aMethod['params'][1] = 'id';
+                $aMethod['params'][2] = 'name';
+                $sWhereClause .= "AND `tal`.`Active`='yes' AND (`tal`.`ID`='3' OR `tal`.`Purchasable`='yes' OR `tal`.`Removable`='yes')";
+                break;
+        }
+
+        $sSql = "SELECT {select} FROM `sys_acl_levels` AS `tal` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " {order} {limit}";
+
+        $aMethod['params'][0] = str_replace(array('{select}', '{order}', '{limit}'), array($sSelectClause, $sOrderClause, $sLimitClause), $sSql);
+        $aItems = call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+
+        if(!$bReturnCount)
+            return $aItems;
+
+        $aMethod['name'] = 'getOne';
+        $aMethod['params'][0] = str_replace(array('{select}', '{order}', '{limit}'), array("COUNT(*)", "", ""), $sSql);
+
+        return array(
+            'items' => $aItems, 
+            'count' => (int)call_user_func_array(array($this, $aMethod['name']), $aMethod['params'])
+        );
+    }
+
+    public function updateLevels($aSet, $aWhere)
+    {
+        $sSql = "UPDATE `sys_acl_levels` SET " . $this->arrayToSQL($aSet, " AND ") . " WHERE " . $this->arrayToSQL($aWhere, " AND ");
+        return (int)$this->query($sSql) > 0;
+    }
+
+    public function getPrices($aParams, $bReturnCount = false)
+    {
+        $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
+        $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        $sSelectClause = "
+            `tap`.`id` AS `id`,
+            `tap`.`level_id` AS `level_id`,
+            `tap`.`name` AS `name`,
+            `tap`.`caption` AS `caption`,
+            `tap`.`description` AS `description`,
+            `tap`.`details` AS `details`,
+            `tap`.`period` AS `period`,
+            `tap`.`period_unit` AS `period_unit`,
+            `tap`.`price` AS `price`,
+            `tap`.`trial` AS `trial`,
+            `tap`.`immediate` AS `immediate`,
+            `tap`.`added` AS `added`,
+            `tap`.`order` AS `order`";
+
+        if(!isset($aParams['order']) || empty($aParams['order']))
+            $sOrderClause = "ORDER BY `tap`.`Order` ASC";
+
+        switch($aParams['type']) {
+            case 'by_id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = array(
+                    'id' => $aParams['value']
+                );
+
+                $sWhereClause .= "AND `tap`.`id`=:id";
+                break;
+
+            case 'by_id_full':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = array(
+                    'id' => $aParams['value']
+                );
+
+                $sSelectClause .= ", `tal`.`Name` AS `level_name`, `tal`.`Description` AS `level_description`";
+                $sJoinClause .= "LEFT JOIN `sys_acl_levels` AS `tal` ON `tap`.`level_id`=`tal`.`ID`";
+                $sWhereClause .= "AND `tap`.`id`=:id";
+                break;
+
+            case 'by_name_full':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = array(
+                    'name' => $aParams['value']
+                );
+
+                $sSelectClause .= ", `tal`.`Name` AS `level_name`, `tal`.`Description` AS `level_description`";
+                $sJoinClause .= "LEFT JOIN `sys_acl_levels` AS `tal` ON `tap`.`level_id`=`tal`.`ID`";
+                $sWhereClause .= "AND `tap`.`name`=:name";
+                break;
+
+            case 'by_level_id':
+                $aMethod['params'][1] = array(
+                    'level_id' => $aParams['value']
+                );
+
+                $sWhereClause .= "AND `tap`.`level_id`=:level_id";
+                break;
+
+            case 'by_level_id_duration':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = array(
+                    'level_id' => $aParams['level_id'],
+                    'period' => $aParams['period'],
+                    'period_unit' => $aParams['period_unit'],
+                );
+
+                $sWhereClause .= "AND `tap`.`level_id`=:level_id AND `tap`.`period`=:period AND `tap`.`period_unit`=:period_unit";
+                break;
+
+            case 'all_full':
+                $sSelectClause .= ", `tal`.`Name` AS `level_name`, `tal`.`Description` AS `level_description`";
+                $sJoinClause .= "LEFT JOIN `sys_acl_levels` AS `tal` ON `tap`.`level_id`=`tal`.`ID`";
+                break;
+        }
+
+        $sSql = "SELECT {select} FROM `" . $this->_oConfig->CNF['TABLE_PRICES'] . "` AS `tap` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " {order} {limit}";
+
+        $aMethod['params'][0] = str_replace(array('{select}', '{order}', '{limit}'), array($sSelectClause, $sOrderClause, $sLimitClause), $sSql);
+        $aItems = call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+
+        if(!$bReturnCount)
+            return $aItems;
+
+        $aMethod['name'] = 'getOne';
+        $aMethod['params'][0] = str_replace(array('{select}', '{order}', '{limit}'), array("COUNT(*)", "", ""), $sSql);
+
+        return array(
+            'items' => $aItems, 
+            'count' => (int)call_user_func_array(array($this, $aMethod['name']), $aMethod['params'])
+        );
+    }
+
+    public function getPriceOrderMax($iLevelId)
+    {
+        $sSql = $this->prepare("SELECT MAX(`order`) FROM `" . $this->_oConfig->CNF['TABLE_PRICES'] . "` WHERE `level_id`=?", $iLevelId);
+        return (int)$this->getOne($sSql);
+    }
+
+    public function deletePrices($aWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sWhereClause = $this->arrayToSQL($aWhere, " AND ");
+
+        $aPrices = $this->getAll("SELECT * FROM `" . $CNF['TABLE_PRICES'] . "` WHERE " . $sWhereClause);
+        if(!empty($aPrices) && is_array($aPrices)) {
+            $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+
+            foreach($aPrices as $aPrice)
+                $oLanguage->deleteLanguageString($aPrice['caption']);
+        }
+
+        return (int)$this->query("DELETE FROM `" . $CNF['TABLE_PRICES'] . "` WHERE " . $sWhereClause) > 0;
+    }
+    
+    public function getLicenses($aParams, $bReturnCount = false)
+    {
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+
+        $sSelectClause = "`tl`.*";
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        switch($aParams['type']) {
+            case 'by_order':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'order' => $aParams['order']
+                ];
+
+                $sWhereClause .= "AND `tl`.`order`=:order";
+                break;
+
+            case 'by_license':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'license' => $aParams['license']
+                ];
+
+                $sWhereClause .= "AND `tl`.`license`=:license";
+                break;
+        }
+
+        $sSql = "SELECT {select} FROM `" . $this->_oConfig->CNF['TABLE_LICENSES'] . "` AS `tl` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " {order} {limit}";
+
+        $aMethod['params'][0] = str_replace(['{select}', '{order}', '{limit}'], [$sSelectClause, $sOrderClause, $sLimitClause], $sSql);
+        $aItems = call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
+
+        if(!$bReturnCount)
+            return $aItems;
+
+        $aMethod['name'] = 'getOne';
+        $aMethod['params'][0] = str_replace(['{select}', '{order}', '{limit}'], ['COUNT(*)', '', ''], $sSql);
+
+        return [
+            'items' => $aItems, 
+            'count' => (int)call_user_func_array([$this, $aMethod['name']], $aMethod['params'])
+        ];
+    }
+
+    public function insertLicense($aSet)
+    {
+        $sSetClause = $this->arrayToSQL($aSet);
+        return (int)$this->query("INSERT INTO `" . $this->_oConfig->CNF['TABLE_LICENSES'] . "` SET " . $sSetClause) > 0;
+    }
+
+    public function updateLicenses($aSet, $aWhere)
+    {
+        $sSetClause = $this->arrayToSQL($aSet);
+        $sWhereClause = $this->arrayToSQL($aWhere, " AND ");
+        return (int)$this->query("UPDATE `" . $this->_oConfig->CNF['TABLE_LICENSES'] . "` SET " . $sSetClause . " WHERE " . $sWhereClause) > 0;
+    }
+
+    public function deleteLicenses($aWhere)
+    {
+        $sWhereClause = $this->arrayToSQL($aWhere, " AND ");
+        return (int)$this->query("DELETE FROM `" . $this->_oConfig->CNF['TABLE_LICENSES'] . "` WHERE " . $sWhereClause) > 0;
+    }
+}
+
+/** @} */

+ 150 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclFormPrice.php

@@ -0,0 +1,150 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxAclFormPrice extends BxTemplStudioFormView
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_bDisplayEdit;
+
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_acl';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct($aInfo, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_bDisplayEdit = $this->aParams['display'] == $CNF['OBJECT_FORM_PRICE_DISPLAY_EDIT'];
+
+        if(isset($this->aInputs[$CNF['FIELD_LEVEL_ID']])) {
+            $aLevels = $this->_oModule->_oDb->getLevels(['type' => 'for_selector']);
+
+            $this->aInputs[$CNF['FIELD_LEVEL_ID']]['values'][] = ['key' => 0, 'value' => _t('_sys_please_select')];
+            foreach($aLevels as $iId => $sTitle)
+                if($iId != MEMBERSHIP_ID_STANDARD)
+                    $this->aInputs[$CNF['FIELD_LEVEL_ID']]['values'][] = [
+                        'key' => $iId, 
+                        'value' => _t($sTitle)
+                    ];
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_NAME']])) {
+            $sJsObject = $this->_oModule->_oConfig->getJsObject('form');
+
+            $iId = $this->getId();
+            $aMask = array('mask' => "javascript:%s.checkName('%s');", $sJsObject, $CNF['FIELD_NAME']);
+            if(!empty($iId) && $this->_bDisplayEdit) {
+                $aMask['mask'] = "javascript:%s.checkName('%s', %d);";
+                $aMask[] = $iId;
+            }
+
+            $sOnBlur = call_user_func_array('sprintf', array_values($aMask)); 
+            $this->aInputs[$CNF['FIELD_NAME']]['attrs']['onblur'] = $sOnBlur;
+        }
+    }
+
+    public function setAction($sAction)
+    {
+        $this->aFormAttrs['action'] = $sAction;
+    }
+
+    public function setLevelId($iLevelId)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->aInputs[$CNF['FIELD_LEVEL_ID']]['value'] = $iLevelId;
+        if(!empty($iLevelId) && empty($this->aInputs[$CNF['FIELD_NAME']]['value'])) {
+            $aLevel = $this->_oModule->_oDb->getLevels(array('type' => 'by_id', 'value' => $iLevelId));
+            $this->aInputs[$CNF['FIELD_NAME']]['value'] = $this->_oModule->_oConfig->getPriceName(_t($aLevel['name']));
+        }
+    }
+
+    public function getCode($bDynamicMode = false)
+    {
+    	$sJs = $this->_oModule->_oTemplate->addJs(array('form.js'), $bDynamicMode);
+
+        $sCode = '';
+        if($bDynamicMode)
+            $sCode .= $sJs;
+
+        $sCode .= $this->_oModule->_oTemplate->getJsCode('form');
+        $sCode .= parent::getCode($bDynamicMode);
+
+        return $sCode;
+    }
+
+    public function initChecker($aValues = [], $aSpecificValues = [])
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        parent::initChecker($aValues, $aSpecificValues);
+
+        if($this->_bDisplayEdit) {
+            if(($aValues['level_id'] ?? 0) == MEMBERSHIP_ID_STANDARD) {
+                foreach([$CNF['FIELD_LEVEL_ID'], $CNF['FIELD_NAME'], $CNF['FIELD_PRICE'], $CNF['FIELD_TRIAL'], $CNF['FIELD_IMMEDIATE']] as $sField)
+                    if(isset($this->aInputs[$sField]))
+                        $this->aInputs[$sField]['type'] = 'hidden';
+            }
+        }
+    }
+
+    public function insert ($aValsToAdd = array(), $isIgnore = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(isset($this->aInputs[$CNF['FIELD_NAME']])) {
+            $sName = BxDolForm::getSubmittedValue($CNF['FIELD_NAME'], $this->aFormAttrs['method'], $this->_aSpecificValues);
+            $sName = $this->_oModule->_oConfig->getPriceName($sName);
+            BxDolForm::setSubmittedValue($CNF['FIELD_NAME'], $sName, $this->aFormAttrs['method'], $this->_aSpecificValues);
+        }
+
+        return parent::insert ($aValsToAdd, $isIgnore);
+    }
+
+    public function update ($iContentId, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sNameKey = $CNF['FIELD_NAME'];
+        if(isset($this->aInputs[$sNameKey])) {
+            $sName = $this->getCleanValue($sNameKey);
+            $aContentInfo = $this->_oModule->_oDb->getPrices(array('type' => 'by_id', 'value' => $iContentId));
+
+            if($aContentInfo[$sNameKey] != $sName) {
+                $sName = $this->_oModule->_oConfig->getPriceName($sName);
+                BxDolForm::setSubmittedValue($sNameKey, $sName, $this->aFormAttrs['method'], $this->_aSpecificValues);
+            }
+        }
+
+        return parent::update ($iContentId, $aValsToAdd, $aTrackTextFieldsChanges);
+    }
+
+    public function getId()
+    {
+        $iResult = 0;
+
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $sId = bx_get('id');
+            if(!empty($sId))
+                $iResult = $sId;
+        }
+        else
+            $iResult = array_shift($aIds);
+
+        return (int)$iResult;
+    }
+}
+
+/** @} */

+ 228 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclGridAdministration.php

@@ -0,0 +1,228 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxAclGridLevels.php');
+
+class BxAclGridAdministration extends BxAclGridLevels
+{
+    protected $_sParamsDivider = '#-#';
+    protected $_aLevels = [];
+    protected $_iLevelId = 0;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_aLevels = $this->_oModule->_oDb->getLevels(['type' => 'for_selector']);
+
+        if(($iLevelId = bx_get('level')) !== false) {
+            $this->_iLevelId = (int)$iLevelId;
+            $this->_aQueryAppend['level'] = $this->_iLevelId;
+        }
+    }
+
+    public function performActionAdd()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$sAction = 'add';
+
+        $sFilter = bx_get('filter');
+        if(strpos($sFilter, $this->_sParamsDivider) !== false)
+            list($this->_iLevelId, $sFilter) = explode($this->_sParamsDivider, $sFilter);
+
+    	$oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_PRICE'], $CNF['OBJECT_FORM_PRICE_DISPLAY_ADD']);
+
+        $aActionParams = ['o' => $this->_sObject, 'a' => $sAction];
+        if($this->_iLevelId) {
+            $aActionParams['level'] = $this->_iLevelId;
+
+            $oForm->setLevelId($this->_iLevelId);
+        }
+
+        $oForm->setAction(bx_append_url_params(BX_DOL_URL_ROOT . 'grid.php', $aActionParams));
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+            $iLevelId = $oForm->getCleanValue('level_id');
+            $iPeriod = $oForm->getCleanValue('period');
+            $sPeriodUnit = $oForm->getCleanValue('period_unit');
+
+            if(!empty($iPeriod) && empty($sPeriodUnit)) 
+                return echoJson(['msg' => _t('_bx_acl_form_price_input_err_period_unit')]);
+
+            $aPriceSimilar = $this->_oModule->_oDb->getPrices([
+                'type' => 'by_level_id_duration', 
+                'level_id' => $iLevelId, 
+                'period' => $iPeriod, 
+                'period_unit' => $sPeriodUnit
+            ]);
+
+            if(($iId = (int)$oForm->insert(['added' => time(), 'order' => $this->_oModule->_oDb->getPriceOrderMax($iLevelId) + 1])) != 0) {
+            	//TODO: May be we don't need to have this 'Purchasable' flag at all or at least we shouldn't update it from here.
+                $this->_oModule->_oDb->updateLevels(['Purchasable' => 'yes'], ['ID' => $iLevelId]);
+
+                $aRes = ['grid' => $this->getCode(false), 'blink' => $iId];
+                if(!empty($aPriceSimilar) && is_array($aPriceSimilar))
+                    $aRes['msg'] = _t('_bx_acl_err_price_duplicate');
+            } 
+            else
+                $aRes = ['msg' => _t('_bx_acl_err_cannot_perform')];
+
+            echoJson($aRes);
+            return;
+        }
+
+        bx_import('BxTemplStudioFunctions');
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('popup_price'), _t('_bx_acl_popup_title_price_add'), $this->_oModule->_oTemplate->parseHtmlByName('popup_price.html', [
+            'form_id' => $oForm->aFormAttrs['id'],
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false, 'removeOnClose' => true]]]);
+    }
+
+    public function performActionEdit()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sAction = 'edit';
+
+        $aIds = $this->_getIds();
+        if($aIds === false)
+            return echoJson([]);
+
+        $iId = $aIds[0];
+
+        $aItem = $this->_oModule->_oDb->getPrices(['type' => 'by_id', 'value' => $iId]);
+        if(!is_array($aItem) || empty($aItem)) {
+            echoJson([]);
+            exit;
+        }
+
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_PRICE'], $CNF['OBJECT_FORM_PRICE_DISPLAY_EDIT']);
+        $oForm->setAction(BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction . '&level=' . $this->_iLevelId);
+
+        $oForm->initChecker($aItem);
+        if($oForm->isSubmittedAndValid()) {
+            if($oForm->update($aItem['id']) !== false)
+                $aRes = ['grid' => $this->getCode(false), 'blink' => $aItem['id']];
+            else
+                $aRes = ['msg' => _t('_bx_acl_err_cannot_perform')];
+
+            echoJson($aRes);
+            return;
+        }
+
+        bx_import('BxTemplStudioFunctions');
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('popup_price'), _t('_bx_acl_popup_title_price_edit'), $this->_oModule->_oTemplate->parseHtmlByName('popup_price.html', [
+            'form_id' => $oForm->aFormAttrs['id'],
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false, 'removeOnClose' => true]]]);
+    }
+
+    public function performActionDelete()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            echoJson([]);
+            return;
+        }
+
+        $iAffected = 0;
+        $aIdsAffected = [];
+        foreach($aIds as $iId)
+            if($this->_oModule->_oDb->deletePrices(['id' => $iId])) {
+                $aIdsAffected[] = $iId;
+                $iAffected++;
+            }
+
+        echoJson($iAffected ? ['grid' => $this->getCode(false), 'blink' => $aIdsAffected] : ['msg' => _t('_bx_acl_err_cannot_perform')]);
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oModule->_oTemplate->addStudioJs(array('jquery.form.min.js', 'administration.js'));
+        $this->_oModule->_oTemplate->addStudioCss(array('administration.css'));
+
+        $oForm = new BxTemplFormView(array());
+        $oForm->addCssJs();
+    }
+
+    protected function _getCellLevelId($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = _t($this->_aLevels[$mixedValue] ?? '_undefined');
+
+    	return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getFilterControls()
+    {
+        parent::_getFilterControls();
+
+        $sContent = '';
+        $oForm = new BxTemplFormView([]);
+
+        $aInputLevels = [
+            'type' => 'select',
+            'name' => 'level',
+            'attrs' => [
+                'id' => 'bx-grid-level-' . $this->_sObject,
+                'onChange' => 'javascript:' . $this->_oModule->_oConfig->getJsObject('administration') . '.onChangeLevel()'
+            ],
+            'value' => $this->_iLevelId,
+            'values' => array(0 => _t('_bx_acl_txt_all_level'))
+        ];
+
+        foreach($this->_aLevels as $iId => $sTitle)
+            $aInputLevels['values'][$iId] = _t($sTitle);
+
+        $sContent .=  $oForm->genRow($aInputLevels);
+
+        $aInputSearch = [
+            'type' => 'text',
+            'name' => 'keyword',
+            'attrs' => [
+                'id' => 'bx-grid-search-' . $this->_sObject,
+            ]
+        ];
+        $sContent .= $oForm->genRow($aInputSearch);
+
+        return $sContent;
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(empty($this->_iLevelId) && strpos($sFilter, $this->_sParamsDivider) !== false)
+            list($this->_iLevelId, $sFilter) = explode($this->_sParamsDivider, $sFilter);
+
+        if(!empty($this->_iLevelId))
+            $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString("AND `level_id`=? ", $this->_iLevelId);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);;
+    }
+
+    protected function _isVisibleGrid ($a)
+    {
+        return isAdmin();
+    }
+}
+
+/** @} */

+ 253 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclGridView.php

@@ -0,0 +1,253 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxAclGridLevels.php');
+
+class BxAclGridView extends BxAclGridLevels
+{
+    /**
+     * Array with check_sum => JS_code pairs of all JS codes 
+     * which should be added to the page.
+     */
+    protected $_aJsCodes;
+
+    protected $_iLogged;
+    protected $_iOwner;
+    protected $_oPayment;
+    protected $_bTypeSingle;
+    protected $_bTypeRecurring;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_aJsCodes = array();
+
+        $this->_iLogged = bx_get_logged_profile_id();
+        $this->_iOwner = $this->_oModule->_oConfig->getOwner();
+        $this->_oPayment = BxDolPayments::getInstance();
+        $this->_bTypeSingle = $this->_oPayment->isAcceptingPayments($this->_iOwner, BX_PAYMENT_TYPE_SINGLE);
+        $this->_bTypeRecurring = !$this->_oPayment->isCreditsOnly() && $this->_oPayment->isAcceptingPayments($this->_iOwner, BX_PAYMENT_TYPE_RECURRING);
+    }
+
+    public function performActionChoose()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!$this->_iLogged)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $aIds = $this->_getIds();
+        if($aIds === false)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $aItem = $this->_oModule->_oDb->getPrices(['type' => 'by_id', 'value' => $aIds[0]]);
+        if(!is_array($aItem) || empty($aItem) || (float)$aItem['price'] != 0)
+            return $this->_bIsApi ? [] : echoJson(array());
+
+        $aResult = [];
+        $iUserId = bx_get_logged_profile_id();
+        if(BxDolAcl::getInstance()->setMembership($iUserId, $aItem['level_id'], ['period' => $aItem['period'], 'period_unit' => $aItem['period_unit']], true))
+            $aResult = ['grid' => $this->getCode(false), 'blink' => $aItem['id'], 'msg' => _t('_bx_acl_msg_performed')];
+        else
+            $aResult = ['msg' => _t('_bx_acl_err_cannot_perform')];
+
+        return $this->_bIsApi ? [] : echoJson($aResult);
+    }
+
+    public function performActionBuy()
+    {
+        if(!$this->_iLogged)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        if(!$this->_bIsApi)
+            return echoJson([]);
+        
+        $aIds = $this->_getIds();
+        if($aIds === false || !is_array($aIds))
+            return [];
+
+        if(($sDescriptor = array_shift($aIds))) {
+            $aDescriptor = $this->_oPayment->getActiveObject()->_oConfig->descriptorS2A($sDescriptor);
+            if($aDescriptor && is_array($aDescriptor) && (int)$aDescriptor[0] == $this->_iOwner) {
+                $aResult = $this->_oPayment->addToCart($this->_iOwner, $this->MODULE, (int)$aDescriptor[2], 1, true);
+                if(isset($aResult['code']) && (int)$aResult['code'] != 0)
+                    return [bx_api_get_msg($aResult['message'])];
+            }
+        }
+
+        return [];
+    }
+
+    public function performActionSubscribe()
+    {
+        if(!$this->_iLogged)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        if(!$this->_bIsApi)
+            return echoJson([]);
+        
+        $aIds = $this->_getIds();
+        if($aIds === false)
+            return [];
+
+        //TODO: Payment Provider selector should be realized.
+        $aResult = $this->_oPayment->subscribeWithAddons($this->_iOwner, 'stripe_v3', $this->MODULE, array_shift($aIds), 1, true);
+        if(isset($aResult['code']) && (int)$aResult['code'] != 0)
+            return [bx_api_get_msg($aResult['message'])];
+
+        return [];
+    }
+
+    public function getCode($isDisplayHeader = true)
+    {
+    	return parent::getCode($isDisplayHeader) . $this->getJsCode();
+    }
+
+    public function getJsCode()
+    {
+        if(empty($this->_aJsCodes) || !is_array($this->_aJsCodes))
+            return '';
+
+        return implode('', $this->_aJsCodes);
+    }
+
+    protected function _getCellLevelIcon($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = $this->_oModule->_oTemplate->displayLevelIcon($mixedValue);
+    	return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellLevelName($mixedValue, $sKey, $aField, $aRow)
+    {
+        if(!$this->_bIsApi)
+            $mixedValue = $this->_oModule->_oTemplate->parseLink('javascript:void(0)', $mixedValue, [
+                'onclick' => $this->_oModule->_oConfig->getJsObject('main') . '.viewActions(this, ' . $aRow['level_id'] . ')'
+            ]);
+
+    	return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getActionChoose ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if((float)$aRow['price'] != 0 || BxDolAcl::getInstance()->isMemberLevelInSet(array($aRow['level_id'])))
+            return $this->_bIsApi ? [] : '';
+
+        if($this->_bIsApi)
+            return array_merge($a, ['name' => $sKey, 'type' => 'callback', 'on_callback' => 'hide']);
+
+        if(!$this->_iLogged)
+            $a['attr'] = array_merge($a['attr'], [
+                'onclick' => "window.open('" . $this->_getLoginUrl() . "','_self');"
+            ]);
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionBuy ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if((float)$aRow['price'] == 0 || !$this->_bTypeSingle || ($this->_bTypeRecurring && getParam($CNF['PARAM_RECURRING_PRIORITIZE']) == 'on' && !$this->_isLifetime($aRow)))
+            return $this->_bIsApi ? [] : '';
+
+        if($this->_bIsApi)
+            return array_merge($a, [
+                'name' => $sKey, 
+                'type' => 'callback', 
+                'on_callback' => 'redirect',
+                'redirect_url' => bx_api_get_relative_url($this->_oPayment->getCartUrl($this->_iOwner)),
+                'payment_type' => BX_PAYMENT_TYPE_SINGLE,
+                'seller_id' => $this->_iOwner,
+                'items' => [$this->_oPayment->getCartItemDescriptor($this->_iOwner, $this->_oModule->_oConfig->getId(), $aRow['id'], 1)],
+            ]);
+        
+        if($this->_iLogged) {
+            $aJs = $this->_oPayment->getAddToCartJs($this->_iOwner, $this->MODULE, $aRow['id'], 1, true);
+            if(!empty($aJs) && is_array($aJs)) {
+                list($sJsCode, $sJsMethod) = $aJs;
+
+                $sJsCodeCheckSum = md5($sJsCode);
+                if(!isset($this->_aJsCodes[$sJsCodeCheckSum]))
+                    $this->_aJsCodes[$sJsCodeCheckSum] = $sJsCode;
+
+                $a['attr'] = array(
+                    'title' => bx_html_attribute(_t('_bx_acl_grid_action_buy_title')),
+                    'onclick' => $sJsMethod
+                );
+            }
+        }
+        else
+            $a['attr'] = array_merge($a['attr'], [
+                'onclick' => "window.open('" . $this->_getLoginUrl() . "','_self');"
+            ]);
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionSubscribe ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if((float)$aRow['price'] == 0 || !$this->_bTypeRecurring || ($this->_bTypeSingle && $this->_isLifetime($aRow)))
+            return $this->_bIsApi ? [] : '';
+
+        if($this->_bIsApi)
+            return array_merge($a, [
+                'name' => $sKey, 
+                'type' => 'object', 
+                'object_name' => 'stripe_v3',
+                'payment_type' => BX_PAYMENT_TYPE_RECURRING,
+                'seller_id' => $this->_iOwner,
+                'items' => [$this->_oPayment->getCartItemDescriptor($this->_iOwner, $this->_oModule->_oConfig->getId(), $aRow['id'], 1)],
+            ]);
+
+        if($this->_iLogged) {
+            $aJs = $this->_oPayment->getSubscribeJs($this->_iOwner, '', $this->MODULE, $aRow['id'], 1);
+            if(!empty($aJs) && is_array($aJs)) {
+                list($sJsCode, $sJsMethod) = $aJs;
+
+                $sJsCodeCheckSum = md5($sJsCode);
+                if(!isset($this->_aJsCodes[$sJsCodeCheckSum]))
+                    $this->_aJsCodes[$sJsCodeCheckSum] = $sJsCode;
+
+                $a['attr'] = array(
+                    'title' => bx_html_attribute(_t('_bx_acl_grid_action_subscribe_title')),
+                    'onclick' => $sJsMethod
+                );
+            }
+        }
+        else
+            $a['attr'] = array_merge($a['attr'], [
+                'onclick' => "window.open('" . $this->_getLoginUrl() . "','_self');"
+            ]);
+
+    	return parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if($this->_iLogged && ($oLogged = BxDolProfile::getInstance($this->_iLogged)) !== false)
+            $this->_aOptions['source'] .= "AND `tl`.`UnavailableTo` NOT LIKE " . $this->_oModule->_oDb->escape('%|' . $oLogged->getModule() . '|%') . " ";
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);;
+    }
+
+    protected function _isLifetime($aRow)
+    {
+        return empty($aRow['period']) && empty($aRow['period_unit']);
+    }
+
+    protected function _getLoginUrl()
+    {
+        return bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=login'));
+    }
+}
+
+/** @} */

+ 64 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/classes/BxAclResponse.php

@@ -0,0 +1,64 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAclResponse extends BxDolAlertsResponse
+{
+    protected $MODULE;
+    protected $_oModule;
+
+    public function __construct()
+    {
+    	$this->MODULE = 'bx_acl';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+        parent::__construct();
+    }
+
+    /**
+     * 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;
+
+        $this->$sMethod($oAlert);
+    }
+
+    protected function _processSystemPageOutputBlockAclLevel($oAlert)
+    {
+        if((int)$oAlert->aExtras['block_owner'] != $this->_oModule->getUserId())
+            return;
+
+        $oAlert->aExtras['block_tmpl_vars']['bx_if:show_actions']['content']['bx_repeat:actions'][] = [
+            'href' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($this->_oModule->_oConfig->CNF['URL_VIEW']),
+            'bx_if:show_onclick' => [
+                'condition' => false,
+                'content' => [
+                    'onclick' => ''
+                ]
+            ],
+            'content' => _t('_bx_acl_txt_upgrade')
+        ];
+
+        $oAlert->aExtras['block_code'] = BxDolTemplate::getInstance()->parseHtmlByName($oAlert->aExtras['block_tmpl_name'], $oAlert->aExtras['block_tmpl_vars']);
+    }
+
+    protected function _processAclDeleted($oAlert)
+    {
+        $this->_oModule->_oDb->deletePrices(array('level_id' => $oAlert->iObject));
+    }
+}
+
+/** @} */

+ 70 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/install/config.php

@@ -0,0 +1,70 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_acl',
+    'title' => 'Paid Levels',
+    'note' => 'Paid Levels module.',
+    'version' => '15.0.2',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/acl/',
+    'home_uri' => 'acl',
+
+    'db_prefix' => 'bx_acl_',
+    'class_prefix' => 'BxAcl',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Paid Levels',
+
+    /**
+     * 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(),
+);
+
+/** @} */

+ 142 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/install/langs/en.xml

@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_acl"><![CDATA[Paid Levels]]></string>
+
+
+    <!-- Option -->
+    <string name="_bx_acl_option_recurring_reserve"><![CDATA[Reserve (in days) to accept and process recurring payment notification]]></string>
+    <string name="_bx_acl_option_recurring_prioritize"><![CDATA[Prioritize recurring payments]]></string>
+    <string name="_bx_acl_option_show_to_unauthenticated"><![CDATA[Show available levels list to unauthenticated visitors]]></string>
+
+
+    <!-- Pages -->
+    <string name="_bx_acl_page_title_sys_view"><![CDATA[Membership]]></string>
+    <string name="_bx_acl_page_title_view"><![CDATA[Membership]]></string>
+    <string name="_bx_acl_page_block_title_system_view"><![CDATA[Membership]]></string>
+    <string name="_bx_acl_page_block_title_view"><![CDATA[Membership]]></string>
+
+
+    <!-- Menus -->
+    <string name="_bx_acl_lmi_cpt_manage"><![CDATA[Manage]]></string>
+
+
+    <!-- Forms -->
+    <string name="_bx_acl_form_price"><![CDATA[Price]]></string>
+    <string name="_bx_acl_form_price_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_acl_form_price_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_acl_form_price_input_sys_id"><![CDATA[ID]]></string>
+    <string name="_bx_acl_form_price_input_sys_level_id"><![CDATA[Level]]></string>
+    <string name="_bx_acl_form_price_input_level_id"><![CDATA[Level]]></string>
+    <string name="_bx_acl_form_price_input_err_level_id"><![CDATA[You need to select a level here.]]></string>
+    <string name="_bx_acl_form_price_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_acl_form_price_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_acl_form_price_input_inf_name"><![CDATA[Unique value which will be used in back end and API calls]]></string>
+    <string name="_bx_acl_form_price_input_err_name"><![CDATA[Field's value should be unique and is needed for internal usage.]]></string>
+    <string name="_bx_acl_form_price_input_sys_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_acl_form_price_input_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_acl_form_price_input_inf_caption"><![CDATA[The caption will be used in user end.]]></string>
+    <string name="_bx_acl_form_price_input_err_caption"><![CDATA[You need to enter a value here.]]></string>
+    <string name="_bx_acl_form_price_input_sys_description"><![CDATA[Description]]></string>
+    <string name="_bx_acl_form_price_input_description"><![CDATA[Description]]></string>
+    <string name="_bx_acl_form_price_input_sys_details"><![CDATA[Details]]></string>
+    <string name="_bx_acl_form_price_input_details"><![CDATA[Details]]></string>
+    <string name="_bx_acl_form_price_input_sys_period"><![CDATA[Period]]></string>
+    <string name="_bx_acl_form_price_input_period"><![CDATA[Period]]></string>
+    <string name="_bx_acl_form_price_input_inf_period"><![CDATA[Use 0 for lifetime]]></string>
+    <string name="_bx_acl_form_price_input_sys_period_unit"><![CDATA[Period unit]]></string>
+    <string name="_bx_acl_form_price_input_period_unit"><![CDATA[Period unit]]></string>
+    <string name="_bx_acl_form_price_input_inf_period_unit"><![CDATA[Leave empty for lifetime level.]]></string>
+    <string name="_bx_acl_form_price_input_err_period_unit"><![CDATA[You need to select a period unit here.]]></string>
+    <string name="_bx_acl_form_price_input_sys_price"><![CDATA[Price]]></string>
+    <string name="_bx_acl_form_price_input_price"><![CDATA[Price]]></string>
+    <string name="_bx_acl_form_price_input_inf_price"><![CDATA[Use 0 or leave empty to create free level.]]></string>
+    <string name="_bx_acl_form_price_input_sys_trial"><![CDATA[Trial]]></string>
+    <string name="_bx_acl_form_price_input_trial"><![CDATA[Trial]]></string>
+    <string name="_bx_acl_form_price_input_inf_trial"><![CDATA[The number of days new customers should receive a free trial. Leave empty to disable trial period.]]></string>
+    <string name="_bx_acl_form_price_input_sys_immediate"><![CDATA[Activate immediately]]></string>
+    <string name="_bx_acl_form_price_input_immediate"><![CDATA[Activate immediately after purchase]]></string>
+    <string name="_bx_acl_form_price_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_acl_form_price_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_acl_form_price_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_acl_form_price_input_do_cancel"><![CDATA[Cancel]]></string>
+
+
+    <!-- Pre Lists -->
+    <string name="_bx_acl_pre_lists_period_units"><![CDATA[Billing Interval]]></string>
+    <string name="_bx_acl_pre_values_day"><![CDATA[day]]></string>
+    <string name="_bx_acl_pre_values_week"><![CDATA[week]]></string>
+    <string name="_bx_acl_pre_values_month"><![CDATA[month]]></string>
+    <string name="_bx_acl_pre_values_year"><![CDATA[year]]></string>
+
+
+    <!-- Grids -->
+    <string name="_bx_acl_grid_column_level_icon"><![CDATA[]]></string>
+    <string name="_bx_acl_grid_column_level_name"><![CDATA[Level]]></string>
+    <string name="_bx_acl_grid_column_level_id"><![CDATA[Level]]></string>
+    <string name="_bx_acl_grid_column_name"><![CDATA[Name]]></string>
+    <string name="_bx_acl_grid_column_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_acl_grid_column_description"><![CDATA[Description]]></string>
+    <string name="_bx_acl_grid_column_details"><![CDATA[Details]]></string>
+    <string name="_bx_acl_grid_column_period"><![CDATA[Period]]></string>
+    <string name="_bx_acl_grid_column_price"><![CDATA[Price]]></string>
+    <string name="_bx_acl_grid_column_price_value"><![CDATA[{0}{1}]]></string>
+    <string name="_bx_acl_grid_column_trial"><![CDATA[Trial]]></string>
+
+    <string name="_bx_acl_grid_action_add"><![CDATA[Add]]></string>
+    <string name="_bx_acl_grid_action_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_acl_grid_action_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_acl_grid_action_buy"><![CDATA[Buy]]></string>
+    <string name="_bx_acl_grid_action_buy_title"><![CDATA[One-time payment]]></string>
+    <string name="_bx_acl_grid_action_subscribe"><![CDATA[Subscribe]]></string>
+    <string name="_bx_acl_grid_action_subscribe_title"><![CDATA[Create a subscription with recurring billing]]></string>
+    <string name="_bx_acl_grid_action_choose"><![CDATA[Choose]]></string>
+    <string name="_bx_acl_grid_action_choose_title"><![CDATA[Switch to the level for free]]></string>
+
+    <string name="_bx_acl_popup_title_price_add"><![CDATA[Add Pricing Plan]]></string>
+    <string name="_bx_acl_popup_title_price_edit"><![CDATA[Edit Pricing Plan]]></string>
+
+
+    <!-- Menus -->
+    <string name="_bx_acl_menu_item_title_system_acl_view"><![CDATA[Membership]]></string>
+    <string name="_bx_acl_menu_item_title_acl_view"><![CDATA[Membership]]></string>
+
+
+    <!-- Email Templates -->
+    <string name="_bx_acl_et_txt_name_subscription_cancel_required"><![CDATA[Subscription cancel required.]]></string>
+    <string name="_bx_acl_et_txt_subject_subscription_cancel_required"><![CDATA[Subscription cancel required.]]></string>
+    <string name="_bx_acl_et_txt_body_subscription_cancel_required"><![CDATA[{email_header}
+<p>Hello,</p>
+<p>You've paid for a new membership level but your old level's subscription is still active. You need to cancel it manually.</p>
+<p>Old subscription info:<br />Membership level: {level}<br />Order: {order}<br />Date Starts: {date_starts}<br />Date Expires: {date_expires}</p>
+{email_footer}]]></string>
+
+
+    <!-- Common Texts -->
+    <string name="_bx_acl_msg_empty_owner"><![CDATA['Main seller' is empty. You need to mark some profile as 'Main seller' in Settings of currently active Payment app.]]></string>
+    <string name="_bx_acl_msg_performed"><![CDATA[The action was successfully performed.]]></string>
+    <string name="_bx_acl_err_price_duplicate"><![CDATA[Note! Similar pricing plan is already exists.]]></string>
+    <string name="_bx_acl_err_cannot_perform"><![CDATA[An error appeared. Please report.]]></string>
+
+    <string name="_bx_acl_txt_all_level"><![CDATA[All levels]]></string>
+    <string name="_bx_acl_txt_level_caption_standard"><![CDATA[Free]]></string>
+    <string name="_bx_acl_txt_level_description_standard"><![CDATA[Try basic features for free.]]></string>
+    <string name="_bx_acl_txt_level_details_standard"><![CDATA[More info may be here.]]></string>
+    <string name="_bx_acl_txt_cart_item_title"><![CDATA[{0} ({1} {2})]]></string>
+    <string name="_bx_acl_txt_cart_item_title_lifetime"><![CDATA[{0} (lifetime)]]></string>
+    <string name="_bx_acl_txt_cart_item_description"><![CDATA[Membership level on {0} site.]]></string>
+    <string name="_bx_acl_txt_upgrade"><![CDATA[Upgrade]]></string>
+    <string name="_bx_acl_txt_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_acl_txt_n_unit"><![CDATA[{0} ({1})]]></string>
+    <string name="_bx_acl_txt_none"><![CDATA[none]]></string>
+    <string name="_bx_acl_txt_free"><![CDATA[free]]></string>
+    <string name="_bx_acl_txt_lifetime"><![CDATA[lifetime]]></string>
+    <string name="_bx_acl_txt_x_actions"><![CDATA['{0}' Membership Level Actions]]></string>
+    <string name="_bx_acl_txt_action_title"><![CDATA[Action]]></string>
+    <string name="_bx_acl_txt_action_limit_count"><![CDATA[Limited in quantity]]></string>
+    <string name="_bx_acl_txt_action_limit_time"><![CDATA[Limited in time]]></string>
+    <string name="_bx_acl_txt_n_times"><![CDATA[{0} times]]></string>
+    <string name="_bx_acl_txt_n_times_m_hours"><![CDATA[{0} times resetting every {1} hours]]></string>
+    <string name="_bx_acl_txt_after_x"><![CDATA[allowed after {0}]]></string>
+    <string name="_bx_acl_txt_until_x"><![CDATA[allowed until {0}]]></string>
+    <string name="_bx_acl_txt_after_x_until_y"><![CDATA[allowed between {0} and {1}]]></string>
+</resources>

+ 159 - 0
modules/boonex/acl/updates/15.0.1_15.0.2/source/install/sql/install.sql

@@ -0,0 +1,159 @@
+SET @sName = 'bx_acl';
+
+
+-- TABLES
+CREATE TABLE `bx_acl_level_prices` (
+  `id` int(11) NOT NULL auto_increment,
+  `level_id` int(11) unsigned NOT NULL default '0',
+  `name` varchar(128) NOT NULL default '',
+  `caption` varchar(128) NOT NULL default '',
+  `description` text NOT NULL,
+  `details` text NOT NULL,
+  `period` int(11) unsigned NOT NULL default '1',
+  `period_unit` varchar(32) NOT NULL default '',
+  `trial` int(11) unsigned NOT NULL default '0',
+  `price` float unsigned NOT NULL default '1',
+  `immediate` tinyint(4) NOT NULL default '1',
+  `added` int(11) NOT NULL default '0',
+  `active` tinyint(4) NOT NULL default '1',
+  `order` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`)
+);
+
+INSERT INTO `bx_acl_level_prices` (`level_id`, `name`, `caption`, `description`, `details`, `period`, `period_unit`, `trial`, `price`, `immediate`, `added`, `active`, `order`) VALUES
+(3, 'standard', '_bx_acl_txt_level_caption_standard', '_bx_acl_txt_level_description_standard', '_bx_acl_txt_level_details_standard', 0, '', 0, 0, 1, UNIX_TIMESTAMP(), 0, 1);
+
+-- TABLE: licenses
+CREATE TABLE IF NOT EXISTS `bx_acl_licenses` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `price_id` int(11) unsigned NOT NULL default '0',
+  `type` varchar(16) NOT NULL default 'single',
+  `order` varchar(32) NOT NULL default '',
+  `license` varchar(32) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `price_id` (`price_id`, `profile_id`),
+  KEY `license` (`license`)
+);
+
+
+-- FORMS
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_acl_price', @sName, '_bx_acl_form_price', '', '', 'do_submit', 'bx_acl_level_prices', 'id', '', '', '', 0, 1, 'BxAclFormPrice', 'modules/boonex/acl/classes/BxAclFormPrice.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_acl_price_add', @sName, 'bx_acl_price', '_bx_acl_form_price_display_add', 0),
+('bx_acl_price_edit', @sName, 'bx_acl_price', '_bx_acl_form_price_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_acl_price', @sName, 'id', '', '', 0, 'hidden', '_bx_acl_form_price_input_sys_id', '', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_acl_price', @sName, 'level_id', '', '', 0, 'select', '_bx_acl_form_price_input_sys_level_id', '_bx_acl_form_price_input_level_id', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_acl_form_price_input_err_level_id', 'Int', '', 1, 0),
+('bx_acl_price', @sName, 'name', '', '', 0, 'text', '_bx_acl_form_price_input_sys_name', '_bx_acl_form_price_input_name', '_bx_acl_form_price_input_inf_name', 1, 0, 0, '', '', '', 'Avail', '', '_bx_acl_form_price_input_err_name', 'Xss', '', 1, 0),
+('bx_acl_price', @sName, 'caption', '0', '', 0, 'text_translatable', '_bx_acl_form_price_input_sys_caption', '_bx_acl_form_price_input_caption', '_bx_acl_form_price_input_inf_caption', 1, 0, 0, '', '', '', 'AvailTranslatable', 'a:1:{i:0;s:7:"caption";}', '_bx_acl_form_price_input_err_caption', 'Xss', '', 0, 0),
+('bx_acl_price', @sName, 'description', '0', '', 0, 'textarea_translatable', '_bx_acl_form_price_input_sys_description', '_bx_acl_form_price_input_description', '', 0, 0, 3, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_acl_price', @sName, 'details', '0', '', 0, 'textarea_translatable', '_bx_acl_form_price_input_sys_details', '_bx_acl_form_price_input_details', '', 0, 0, 3, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_acl_price', @sName, 'period', '', '', 0, 'text', '_bx_acl_form_price_input_sys_period', '_bx_acl_form_price_input_period', '_bx_acl_form_price_input_inf_period', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_acl_price', @sName, 'period_unit', '', '#!bx_acl_period_units', 0, 'select', '_bx_acl_form_price_input_sys_period_unit', '_bx_acl_form_price_input_period_unit', '_bx_acl_form_price_input_inf_period_unit', 1, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_acl_price', @sName, 'trial', '', '', 0, 'text', '_bx_acl_form_price_input_sys_trial', '_bx_acl_form_price_input_trial', '_bx_acl_form_price_input_inf_trial', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_acl_price', @sName, 'price', '', '', 0, 'price', '_bx_acl_form_price_input_sys_price', '_bx_acl_form_price_input_price', '_bx_acl_form_price_input_inf_price', 1, 0, 0, '', '', '', '', '', '', 'Float', '', 1, 0),
+('bx_acl_price', @sName, 'immediate', 1, '', 1, 'switcher', '_bx_acl_form_price_input_sys_immediate', '_bx_acl_form_price_input_immediate', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_acl_price', @sName, 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_acl_price', @sName, 'do_submit', '_bx_acl_form_price_input_do_submit', '', 0, 'submit', '_bx_acl_form_price_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_acl_price', @sName, 'do_cancel', '_bx_acl_form_price_input_do_cancel', '', 0, 'button', '_bx_acl_form_price_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_acl_price_add', 'id', 2147483647, 0, 1),
+('bx_acl_price_add', 'level_id', 2147483647, 1, 2),
+('bx_acl_price_add', 'name', 2147483647, 1, 3),
+('bx_acl_price_add', 'caption', 2147483647, 1, 4),
+('bx_acl_price_add', 'description', 2147483647, 1, 5),
+('bx_acl_price_add', 'details', 2147483647, 1, 6),
+('bx_acl_price_add', 'price', 2147483647, 1, 7),
+('bx_acl_price_add', 'period', 2147483647, 1, 8),
+('bx_acl_price_add', 'period_unit', 2147483647, 1, 9),
+('bx_acl_price_add', 'trial', 2147483647, 1, 10),
+('bx_acl_price_add', 'immediate', 2147483647, 1, 11),
+('bx_acl_price_add', 'controls', 2147483647, 1, 12),
+('bx_acl_price_add', 'do_submit', 2147483647, 1, 13),
+('bx_acl_price_add', 'do_cancel', 2147483647, 1, 14),
+
+('bx_acl_price_edit', 'id', 2147483647, 1, 1),
+('bx_acl_price_edit', 'level_id', 2147483647, 1, 2),
+('bx_acl_price_edit', 'name', 2147483647, 1, 3),
+('bx_acl_price_edit', 'caption', 2147483647, 1, 4),
+('bx_acl_price_edit', 'description', 2147483647, 1, 5),
+('bx_acl_price_edit', 'details', 2147483647, 1, 6),
+('bx_acl_price_edit', 'price', 2147483647, 1, 7),
+('bx_acl_price_edit', 'period', 2147483647, 1, 8),
+('bx_acl_price_edit', 'period_unit', 2147483647, 1, 9),
+('bx_acl_price_edit', 'trial', 2147483647, 1, 10),
+('bx_acl_price_edit', 'immediate', 2147483647, 1, 11),
+('bx_acl_price_edit', 'controls', 2147483647, 1, 12),
+('bx_acl_price_edit', 'do_submit', 2147483647, 1, 13),
+('bx_acl_price_edit', 'do_cancel', 2147483647, 1, 14);
+
+
+-- PRE-VALUES
+INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`) VALUES
+('bx_acl_period_units', '_bx_acl_pre_lists_period_units', 'bx_acl', '0');
+
+INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALUES
+('bx_acl_period_units', '', 0, '_sys_please_select', ''),
+('bx_acl_period_units', 'day', 1, '_bx_acl_pre_values_day', ''),
+('bx_acl_period_units', 'week', 2, '_bx_acl_pre_values_week', ''),
+('bx_acl_period_units', 'month', 3, '_bx_acl_pre_values_month', ''),
+('bx_acl_period_units', 'year', 4, '_bx_acl_pre_values_year', '');
+
+
+-- GRIDS
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_acl_administration', 'Sql', 'SELECT * FROM `bx_acl_level_prices` WHERE 1 ', 'bx_acl_level_prices', 'id', 'order', 'active', '', 100, NULL, 'start', '', 'period,period_unit,price', 'caption', 'like', '', '', 192, 'BxAclGridAdministration', 'modules/boonex/acl/classes/BxAclGridAdministration.php'),
+('bx_acl_view', 'Sql', 'SELECT `tlp`.*, `tl`.`Name` AS `level_name`, `tl`.`Icon` AS `level_icon` FROM `bx_acl_level_prices` AS `tlp` LEFT JOIN `sys_acl_levels` AS `tl` ON `tlp`.`level_id`=`tl`.`ID` WHERE `tlp`.`active`<>''0'' && `tl`.`Active`=''yes'' ', 'bx_acl_level_prices', 'id', 'order', '', '', 100, NULL, 'start', '', 'period,period_unit,price', 'tl`.`Name', 'like', '', '', 2147483647, 'BxAclGridView', 'modules/boonex/acl/classes/BxAclGridView.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_acl_administration', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_acl_administration', 'order', '', '2%', 0, '', '', 2),
+('bx_acl_administration', 'switcher', '', '6%', 0, '', '', 3),
+('bx_acl_administration', 'level_id', '_bx_acl_grid_column_level_id', '10%', 0, 16, '', 4),
+('bx_acl_administration', 'name', '_bx_acl_grid_column_name', '10%', 0, 16, '', 5),
+('bx_acl_administration', 'caption', '_bx_acl_grid_column_caption', '10%', 1, 16, '', 6),
+('bx_acl_administration', 'description', '_bx_acl_grid_column_description', '8%', 1, 8, '', 7),
+('bx_acl_administration', 'details', '_bx_acl_grid_column_details', '8%', 1, 8, '', 8),
+('bx_acl_administration', 'price', '_bx_acl_grid_column_price', '8%', 0, 16, '', 9),
+('bx_acl_administration', 'period', '_bx_acl_grid_column_period', '8%', 0, 16, '', 10),
+('bx_acl_administration', 'trial', '_bx_acl_grid_column_trial', '8%', 0, 16, '', 11),
+('bx_acl_administration', 'actions', '', '20%', 0, '', '', 12),
+
+('bx_acl_view', 'level_icon', '_bx_acl_grid_column_level_icon', '5%', 0, 0, '', 1),
+('bx_acl_view', 'level_name', '_bx_acl_grid_column_level_name', '25%', 1, 32, '', 2),
+('bx_acl_view', 'description', '_bx_acl_grid_column_description', '10%', 1, 8, '', 3),
+('bx_acl_view', 'details', '_bx_acl_grid_column_details', '10%', 1, 8, '', 4),
+('bx_acl_view', 'price', '_bx_acl_grid_column_price', '10%', 0, 16, '', 5),
+('bx_acl_view', 'period', '_bx_acl_grid_column_period', '10%', 0, 16, '', 6),
+('bx_acl_view', 'trial', '_bx_acl_grid_column_trial', '10%', 0, 16, '', 7),
+('bx_acl_view', 'actions', '', '20%', 0, '', '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_acl_administration', 'independent', 'add', '_bx_acl_grid_action_add', '', 0, 0, 1),
+('bx_acl_administration', 'single', 'edit', '_bx_acl_grid_action_edit', 'pencil-alt', 1, 0, 1),
+('bx_acl_administration', 'single', 'delete', '_bx_acl_grid_action_delete', 'remove', 1, 1, 2),
+('bx_acl_administration', 'bulk', 'delete', '_bx_acl_grid_action_delete', '', 0, 1, 1),
+
+('bx_acl_view', 'single', 'buy', '_bx_acl_grid_action_buy', 'cart-plus', 0, 0, 1),
+('bx_acl_view', 'single', 'subscribe', '_bx_acl_grid_action_subscribe', 'credit-card', 0, 0, 2),
+('bx_acl_view', 'single', 'choose', '_bx_acl_grid_action_choose', 'far check-square', 0, 0, 3);
+
+
+-- STUDIO PAGE & WIDGET
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_acl', '_bx_acl', 'bx_acl@modules/boonex/acl/|std-icon.svg');
+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`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, @sName, 'users', '{url_studio}module.php?name=bx_acl', '', 'bx_acl@modules/boonex/acl/|std-icon.svg', '_bx_acl', '', '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));

+ 51 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Ads',
+    'version_from' => '15.0.2',
+    'version_to' => '15.0.3',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/ads/updates/update_15.0.2_15.0.3/',
+    'home_uri' => 'ads_update_1502_1503',
+
+    'module_dir' => 'boonex/ads/',
+    'module_uri' => 'ads',
+
+    'db_prefix' => 'bx_ads_',
+    'class_prefix' => 'BxAds',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Ads',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxAdsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 7 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/install/langs/en.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_add name="_bx_ads_form_entry_input_sys_do_select"><![CDATA[Select]]></string_add>
+    <string_add name="_bx_ads_form_entry_input_do_select"><![CDATA[Select]]></string_add>
+
+	<string_upd name="_bx_ads_form_entry_input_title_err"><![CDATA[Title is essential min 3 max 160.]]></string_upd>
+</resources>

+ 8 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/install/sql/install.sql

@@ -0,0 +1,8 @@
+-- FORMS
+DELETE FROM `sys_form_inputs` WHERE `object`='bx_ads' AND `name`='do_select';
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_ads', 'bx_ads', 'do_select', '_bx_ads_form_entry_input_do_select', '', 0, 'button', '_bx_ads_form_entry_input_sys_do_select', '', '', 0, 0, 0, 'a:1:{s:7:"onclick";s:32:"{js_object}.selectCategory(this)";}', '', '', '', '', '', '', '', 1, 0);
+
+UPDATE `sys_form_inputs` SET `checker_func`='Length', `checker_params`='a:2:{s:3:"min";i:3;s:3:"max";i:160;}' WHERE `object`='bx_ads' AND `name`='title';
+
+UPDATE `sys_form_display_inputs` SET `input_name`='do_select' WHERE `display_name`='bx_ads_entry_add' AND `input_name`='do_submit';

+ 450 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/source/classes/BxAdsFormEntry.php

@@ -0,0 +1,450 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Ads Ads
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Create/Edit entry form
+ */
+class BxAdsFormEntry extends BxBaseModTextFormEntry
+{
+    protected $_aContentInfo;
+
+    protected $_bSources;
+    protected $_iCategory;
+    protected $_sGhostTemplateCover = 'form_ghost_template_cover.html';
+    protected $_bDisplayEditBudget;
+	
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_ads';
+        parent::__construct($aInfo, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('form');
+
+        $this->_bSources = $this->_oModule->_oConfig->isSources();
+        $this->_bDisplayEditBudget = $this->aParams['display'] == $CNF['OBJECT_FORM_ENTRY_DISPLAY_EDIT_BUDGET'];
+
+        $this->_initCategoryFields((int)BxDolSession::getInstance()->getValue($this->aParams['display'] . '_category'));
+
+        if(!$this->_bSources) {
+            if(isset($this->aInputs[$CNF['FIELD_SOURCE_TYPE']]))
+                unset($this->aInputs[$CNF['FIELD_SOURCE_TYPE']]);
+
+            if(isset($this->aInputs[$CNF['FIELD_SOURCE']]))
+                unset($this->aInputs[$CNF['FIELD_SOURCE']]);
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_SOURCE_TYPE']], $this->aInputs[$CNF['FIELD_SOURCE']])) {
+            $aMask = ["javascript:%s.loadFromSource(this, '%s', '%s');", $sJsObject, $CNF['FIELD_SOURCE_TYPE'], $CNF['FIELD_SOURCE']];
+            $sOnBlur = call_user_func_array('sprintf', $aMask);
+
+            $this->aInputs[$CNF['FIELD_SOURCE']]['attrs']['onblur'] = $sOnBlur;
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_TITLE']], $this->aInputs[$CNF['FIELD_NAME']])) {
+            $aMask = array('mask' => "javascript:%s.checkName(this, '%s', '%s');", $sJsObject, $CNF['FIELD_TITLE'], $CNF['FIELD_NAME']);
+            if($this->aParams['display'] == $CNF['OBJECT_FORM_ENTRY_DISPLAY_EDIT'] && bx_get('id') !== false) {
+                $aMask['mask'] = "javascript:%s.checkName(this, '%s', '%s', %d);";
+                $aMask[] = (int)bx_get('id');
+            }
+
+            $sOnBlur = call_user_func_array('sprintf', array_values($aMask)); 
+        	$this->aInputs[$CNF['FIELD_TITLE']]['attrs']['onblur'] = $sOnBlur;
+        	$this->aInputs[$CNF['FIELD_NAME']]['attrs']['onblur'] = $sOnBlur;
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_AUCTION']]) && !$this->_oModule->_oConfig->isAuction())
+            unset($this->aInputs[$CNF['FIELD_AUCTION']]);
+
+        if(!$this->_oModule->_oConfig->isPromotion()) {
+            if(isset($this->aInputs[$CNF['FIELD_BUDGET_TOTAL']]))
+                unset($this->aInputs[$CNF['FIELD_BUDGET_TOTAL']]);
+
+            if(isset($this->aInputs[$CNF['FIELD_BUDGET_DAILY']]))
+                unset($this->aInputs[$CNF['FIELD_BUDGET_DAILY']]);
+        }
+
+        //--- Segmentation
+        if(isset($this->aInputs[$CNF['FIELD_SEG_AGE']])) {
+            if(!isset($this->aInputs[$CNF['FIELD_SEG_AGE']]['attrs']))
+                $this->aInputs[$CNF['FIELD_SEG_AGE']]['attrs'] = [];
+
+            $this->aInputs[$CNF['FIELD_SEG_AGE']]['attrs'] = array_merge($this->aInputs[$CNF['FIELD_SEG_AGE']]['attrs'], [
+                'min' => 0, 
+                'max' => 100
+            ]);
+            $this->aInputs[$CNF['FIELD_SEG_AGE']]['value'] = '18-99';
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_SEG_COUNTRY']]))
+            $this->aInputs[$CNF['FIELD_SEG_COUNTRY']]['values'] = array_merge(['' => _t('_bx_ads_form_entry_input_seg_country_all')], $this->aInputs[$CNF['FIELD_SEG_COUNTRY']]['values']);
+
+        $this->addMarkers([
+            'js_object' => $sJsObject
+        ]);
+    }
+
+    function getCode($bDynamicMode = false)
+    {
+        $sJs = $this->_oModule->_oTemplate->addJs(array('form.js'), $bDynamicMode);
+
+        $sCode = '';
+        if($bDynamicMode)
+            $sCode .= $sJs;
+
+        $sCode .= $this->_oModule->_oTemplate->getJsCode('form', [
+            'sObjNameForm' => $this->getJsObjectName()
+        ]);
+        $sCode .= parent::getCode($bDynamicMode);
+
+        return $sCode;
+    }
+
+    function initChecker ($aValues = [], $aSpecificValues = [])
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if($aValues && !empty($aValues['id']))
+            $this->_aContentInfo = $this->_oModule->_oDb->getContentInfoById($aValues['id']);
+        $bContentInfo = !empty($this->_aContentInfo) && is_array($this->_aContentInfo);
+
+        $iContentAuthorId = bx_get_logged_profile_id();
+        if($bContentInfo) {
+            $iContentAuthorId = (int)$this->_aContentInfo[$CNF['FIELD_AUTHOR']];
+
+            $this->_initCategoryFields($this->_aContentInfo[$CNF['FIELD_CATEGORY']]);
+        }
+
+        if($this->_bSources) {
+            if(isset($this->aInputs[$CNF['FIELD_SOURCE_TYPE']]) && !empty($iContentAuthorId)) {
+                $aSource = $this->_oModule->serviceGetSource($iContentAuthorId);
+                if(!empty($aSource) && is_array($aSource))
+                    $this->aInputs[$CNF['FIELD_SOURCE_TYPE']]['value'] = $aSource['name'];
+            }
+
+            if(isset($this->aInputs[$CNF['FIELD_SOURCE']]) && $bContentInfo) {
+                if(!isset($this->aInputs[$CNF['FIELD_SOURCE']]['attrs']))
+                    $this->aInputs[$CNF['FIELD_SOURCE']]['attrs'] = [];
+
+                $this->aInputs[$CNF['FIELD_SOURCE']]['attrs']['disabled'] = 'disabled';
+            }
+        }
+
+        if(isset($CNF['FIELD_COVER']) && isset($this->aInputs[$CNF['FIELD_COVER']])) {
+            if($bContentInfo)
+                $this->aInputs[$CNF['FIELD_COVER']]['content_id'] = $this->_aContentInfo[$CNF['FIELD_ID']];
+
+            $this->aInputs[$CNF['FIELD_COVER']]['ghost_template'] = $this->_oModule->_oTemplate->parseHtmlByName($this->_sGhostTemplateCover, $this->_getCoverGhostTmplVars($this->_aContentInfo));
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_SEG_AGE']]) && $bContentInfo)
+            $this->aInputs[$CNF['FIELD_SEG_AGE']]['value'] = implode('-', [$this->_aContentInfo[$CNF['FIELD_SEG_AGE_MIN']], $this->_aContentInfo[$CNF['FIELD_SEG_AGE_MAX']]]);
+
+        parent::initChecker ($aValues, $aSpecificValues);
+
+        $sKeyBt = $CNF['FIELD_BUDGET_TOTAL'];
+        if($this->isSubmitted() && $this->_bDisplayEditBudget && isset($this->aInputs[$sKeyBt], $aValues[$sKeyBt])) {
+            if((float)$this->getCleanValue($sKeyBt) < (float)$aValues[$sKeyBt]) {
+                $this->aInputs[$CNF['FIELD_BUDGET_TOTAL']]['error'] = _t('_bx_ads_form_entry_input_budget_total_err_decreased');
+                $this->_isValid = false;
+            }
+        }
+    }
+
+    public function insert ($aValsToAdd = [], $isIgnore = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        $bPromotion = $this->_oModule->_oConfig->isPromotion();
+
+        $aValsToAdd[$CNF['FIELD_SINGLE']] = $this->_getSingleField();
+
+        if($bPromotion && isset($this->aInputs[$CNF['FIELD_BUDGET_TOTAL']]) && $this->getCleanValue($CNF['FIELD_BUDGET_TOTAL']) > 0)
+            $aValsToAdd[$CNF['FIELD_STATUS_ADMIN']] = BX_ADS_STATUS_ADMIN_UNPAID;
+
+        if(isset($this->aInputs[$CNF['FIELD_SEG_AGE']]) && ($sSegAge = $this->getCleanValue($CNF['FIELD_SEG_AGE'])) != '') {
+            $aSegAge = explode('-', $sSegAge);
+            if(is_array($aSegAge) && count($aSegAge) == 2)
+                list($aValsToAdd[$CNF['FIELD_SEG_AGE_MIN']], $aValsToAdd[$CNF['FIELD_SEG_AGE_MAX']]) = $aSegAge;
+        }
+
+        $iContentId = parent::insert($aValsToAdd, $isIgnore);
+        if(!empty($iContentId)) {
+            $this->processFiles($CNF['FIELD_COVER'], $iContentId, true);
+            
+            $aContentInfo = $this->_oModule->_oDb->getContentInfoById($iContentId);
+            if(!empty($aContentInfo[$CNF['FIELD_PRICE']]))
+                $this->_oModule->_oDb->insertCommodity($iContentId, BX_ADS_COMMODITY_TYPE_PRODUCT, $aContentInfo[$CNF['FIELD_PRICE']]);
+
+            if($bPromotion && (float)$aContentInfo[$CNF['FIELD_BUDGET_TOTAL']] > 0)
+                $this->_oModule->_oDb->insertCommodity($iContentId, BX_ADS_COMMODITY_TYPE_PROMOTION, $aContentInfo[$CNF['FIELD_BUDGET_TOTAL']]);
+        }
+
+        return $iContentId;
+    }
+
+    public function update ($iContentId, $aValsToAdd = [], &$aTrackTextFieldsChanges = null)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $bPromotion = $this->_oModule->_oConfig->isPromotion();
+
+        if(!$this->_bDisplayEditBudget)
+            $aValsToAdd[$CNF['FIELD_SINGLE']] = $this->_getSingleField();
+
+        if(isset($this->aInputs[$CNF['FIELD_SEG_AGE']]) && ($sSegAge = $this->getCleanValue($CNF['FIELD_SEG_AGE'])) != '') {
+            $aSegAge = explode('-', $sSegAge);
+            if(is_array($aSegAge) && count($aSegAge) == 2)
+                list($aValsToAdd[$CNF['FIELD_SEG_AGE_MIN']], $aValsToAdd[$CNF['FIELD_SEG_AGE_MAX']]) = $aSegAge;
+        }
+
+        $aTrackTextFieldsChanges = [];
+        $iResult = parent::update($iContentId, $aValsToAdd, $aTrackTextFieldsChanges);
+        if($bPromotion && in_array($CNF['FIELD_BUDGET_TOTAL'], $aTrackTextFieldsChanges['changed_fields'])) {
+            $fBudgetTotalDiff = (float)$this->getCleanValue($CNF['FIELD_BUDGET_TOTAL']) - (float)$aTrackTextFieldsChanges['data'][$CNF['FIELD_BUDGET_TOTAL']];
+            if($fBudgetTotalDiff > 0) {
+                $this->_oModule->_oDb->insertCommodity($iContentId, BX_ADS_COMMODITY_TYPE_PROMOTION, $fBudgetTotalDiff);
+
+                $this->_oModule->_oDb->updateEntriesBy([$CNF['FIELD_STATUS_ADMIN'] => BX_ADS_STATUS_ADMIN_UNPAID], [$CNF['FIELD_ID'] => $iContentId]);
+            }
+        }
+
+        $this->processFiles($CNF['FIELD_COVER'], $iContentId, false);
+
+        return $iResult;
+    }
+
+    public function delete ($iContentId, $aContentInfo = [])
+    {
+        $this->_oModule->_oDb->deleteOffer(['content_id' => $iContentId]);
+        $this->_oModule->_oDb->deleteCommodity(['entry_id' => $iContentId]);
+        $this->_oModule->_oDb->deleteLicense(['entry_id' => $iContentId]);
+
+        return parent::delete($iContentId, $aContentInfo);
+    }
+
+    protected function genCustomInputSource (&$aInput)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sUrl = BX_DOL_URL_ROOT . 'modules/?r=' . $this->_oModule->_oConfig->getUri() . '/get_source';
+        if(isset($this->aInputs[$CNF['FIELD_SOURCE_TYPE']]) && !empty($this->aInputs[$CNF['FIELD_SOURCE_TYPE']]['value']))
+            $sUrl = bx_append_url_params($sUrl, ['source_type' => $this->aInputs[$CNF['FIELD_SOURCE_TYPE']]['value']]);
+
+        $aInput = array_merge($aInput, [
+            'ajax_get_suggestions' => $sUrl, 
+            'placeholder' => _t('_bx_ads_form_entry_input_source_paceholder'),
+            'custom' => ['only_once' => true]
+        ]);
+        
+         if (bx_is_api()){
+             $aInput['ajax_get_suggestions'] = 'bx_ads/get_source_data&params[]=';
+             $aInput['type'] = 'suggestion';
+             $aInput['custom']['callback'] = 'bx_ads/load_entry_from_source&params[]=shopify_admin&params[]=';
+             return $aInput;
+        }
+
+        return $this->genCustomInputUsernamesSuggestions($aInput);
+    }
+    
+    protected function genCustomInputSourceValue($aInput)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $aValue = is_array($aInput['value']) ? $aInput['value'] : [$aInput['value']];
+
+        $iAuthorId = $this->_getAuthorId();
+        $sSourceType = $this->_getSourceType();
+
+        $oSource = $this->_oModule->getObjectSource($sSourceType, $iAuthorId);
+        if(!$oSource)
+            return [];
+
+        $aResults = [];
+        foreach($aValue as $sValue) {
+            $aEntry = $oSource->getEntry($sValue);
+            if(empty($aEntry) || !is_array($aEntry))
+                continue;
+
+            $aResults[] = [
+                'item_unit' => '',
+                'item_name' => $aEntry[$CNF['FIELD_TITLE']],
+                'name' => $aInput['name'] . (isset($aInput['custom']['only_once']) && $aInput['custom']['only_once'] == 1 ? '' : '[]'),
+                'value' => $sValue
+            ];
+        }
+
+        return $aResults;
+    }
+
+    public function genInputPrice(&$aInput)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!isset($aInput['value_currency'])) {
+            $iAuthorId = $this->_getAuthorId();
+            $aInput['value_currency'] = BxDolPayments::getInstance()->getCurrencyCode($iAuthorId);
+        }
+
+        return parent::genInputPrice($aInput);
+    }
+
+    protected function genCustomInputCategoryView(&$aInput)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!empty($this->_iCategory)) {
+            $aCategory = $this->_oModule->_oDb->getCategories(array('type' => 'id', 'id' => $this->_iCategory));
+            if(!empty($aCategory) && is_array($aCategory))
+                $aInput['value'] = bx_process_output(_t($aCategory['title']));
+        }
+
+        return $this->genInputStandard($aInput);
+    }
+ 
+    protected function genCustomViewRowValueCategoryView(&$aInput)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(empty($aInput['value']))
+            return '';
+
+        $aCategory = $this->_oModule->_oDb->getCategories(array('type' => 'id', 'id' => $aInput['value']));
+        if(empty($aCategory) || !is_array($aCategory))
+            return '';
+
+        $sLink = bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_CATEGORIES'], array($CNF['GET_PARAM_CATEGORY'] => $aCategory['id'])));
+        return $this->_oModule->_oTemplate->parseLink($sLink, bx_process_output(_t($aCategory['title'])));
+    }
+
+    protected function genCustomViewRowValueQuantity(&$aInput)
+    {
+        return (int)$aInput['value'] > 0 ? (int)$aInput['value'] : 0;
+    }
+
+    protected function _getCoverGhostTmplVars($aContentInfo = array())
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	return array (
+            'name' => $this->aInputs[$CNF['FIELD_COVER']]['name'],
+            'content_id' => $this->aInputs[$CNF['FIELD_COVER']]['content_id'],
+            'editor_id' => isset($CNF['FIELD_TEXT_ID']) ? $CNF['FIELD_TEXT_ID'] : '',
+            'thumb_id' => isset($CNF['FIELD_THUMB']) && isset($aContentInfo[$CNF['FIELD_THUMB']]) ? $aContentInfo[$CNF['FIELD_THUMB']] : 0,
+            'name_thumb' => isset($CNF['FIELD_THUMB']) ? $CNF['FIELD_THUMB'] : ''
+        );
+    }
+
+    protected function _getPhotoGhostTmplVars($aContentInfo = [])
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	return [
+            'name' => $this->aInputs[$CNF['FIELD_PHOTO']]['name'],
+            'content_id' => (int)$this->aInputs[$CNF['FIELD_PHOTO']]['content_id'],
+            'editor_id' => isset($CNF['FIELD_TEXT_ID']) ? $CNF['FIELD_TEXT_ID'] : '',
+            'bx_if:set_thumb' => [
+                'condition' => false,
+                'content' => []
+            ],
+    	];
+    }
+
+    protected function _getSingleField()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!isset($this->aInputs[$CNF['FIELD_QUANTITY']])) 
+            return;
+
+        $iSingle = 1;
+        $iQuantity = (int)$this->getCleanValue($CNF['FIELD_QUANTITY']);
+        if($iQuantity > 1)
+            $iSingle = 0;
+
+        return $iSingle;
+    }
+
+    protected function _initCategoryFields($iCategory = 0)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_iCategory = (int)$iCategory;
+        if(empty($this->_iCategory) && bx_get('category') !== false)
+            $this->_iCategory = (int)bx_get('category');
+
+        if(isset($CNF['FIELD_CATEGORY']) && isset($this->aInputs[$CNF['FIELD_CATEGORY']]) && $this->_iCategory != 0)
+            $this->aInputs[$CNF['FIELD_CATEGORY']]['value'] = $this->_iCategory;
+
+        if(isset($CNF['FIELD_CATEGORY_VIEW']) && isset($this->aInputs[$CNF['FIELD_CATEGORY_VIEW']]) && $this->_iCategory != 0)
+            $this->aInputs[$CNF['FIELD_CATEGORY_VIEW']]['value'] = $this->_iCategory;
+
+        if(isset($CNF['FIELD_CATEGORY_SELECT']) && isset($this->aInputs[$CNF['FIELD_CATEGORY_SELECT']]))
+            $this->aInputs[$CNF['FIELD_CATEGORY_SELECT']]['values'] = $this->_oModule->serviceGetCategoryOptions(0, true);
+    }
+
+    protected function _isChangeUserForAdmins($sDisplay)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!$this->_bAllowChangeUserForAdmins)
+            return false;
+
+        if(strpos($sDisplay, '_add') !== false)
+            return $sDisplay != $CNF['OBJECT_FORM_ENTRY_DISPLAY_ADD'];
+
+        if(strpos($sDisplay, '_edit') !== false)
+            return true;
+
+        return false;
+    }
+    
+    protected function _getAuthorId()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!empty($this->_aContentInfo) && is_array($this->_aContentInfo))
+            return (int)$this->_aContentInfo[$CNF['FIELD_AUTHOR']];
+
+        if(!empty($this->_iContentId)) {
+            $this->_aContentInfo = $this->_oModule->_oDb->getContentInfoById($this->_iContentId);
+            if(!empty($this->_aContentInfo) && is_array($this->_aContentInfo))
+                return (int)$this->_aContentInfo[$CNF['FIELD_AUTHOR']];
+        }
+
+        return bx_get_logged_profile_id();
+    }
+
+    protected function _getSourceType()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!empty($this->_aContentInfo) && is_array($this->_aContentInfo))
+            return $this->_aContentInfo[$CNF['FIELD_SOURCE_TYPE']];
+        
+        if(!empty($this->_iContentId)) {
+            $this->_aContentInfo = $this->_oModule->_oDb->getContentInfoById($this->_iContentId);
+            if(!empty($this->_aContentInfo) && is_array($this->_aContentInfo))
+                return $this->_aContentInfo[$CNF['FIELD_SOURCE_TYPE']];
+        }
+        
+        $iAuthorId = $this->_getAuthorId();
+        return $this->_oModule->serviceGetSource($iAuthorId);
+    }
+
+    protected function _genInputButtonAttrs(&$aInput)
+    {
+        return $this->_replaceMarkers(parent::_genInputButtonAttrs($aInput));
+    }
+}
+
+/** @} */

+ 2780 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/source/classes/BxAdsModule.php

@@ -0,0 +1,2780 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Ads Ads
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define('BX_ADS_STATUS_OFFER', 'offer');
+define('BX_ADS_STATUS_SOLD', 'sold');
+
+define('BX_ADS_STATUS_ADMIN_UNPAID', 'unpaid');
+
+define('BX_ADS_COMMODITY_TYPE_PRODUCT', 'product');
+define('BX_ADS_COMMODITY_TYPE_PROMOTION', 'promotion');
+
+define('BX_ADS_OFFER_STATUS_ACCEPTED', 'accepted');
+define('BX_ADS_OFFER_STATUS_AWAITING', 'awaiting');
+define('BX_ADS_OFFER_STATUS_DECLINED', 'declined');
+define('BX_ADS_OFFER_STATUS_CANCELED', 'canceled');
+define('BX_ADS_OFFER_STATUS_PAID', 'paid');
+
+/**
+ * Ads module
+ */
+class BxAdsModule extends BxBaseModTextModule
+{
+    protected $_aOfferStatuses;
+
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_oConfig->init($this->_oDb);
+
+        $CNF = &$this->_oConfig->CNF;
+
+        $this->_aSearchableNamesExcept = array_merge($this->_aSearchableNamesExcept, array(
+            $CNF['FIELD_CATEGORY_VIEW'],
+            $CNF['FIELD_CATEGORY_SELECT']
+        ));
+
+        $this->_aOfferStatuses = array(
+            BX_ADS_OFFER_STATUS_ACCEPTED,
+            BX_ADS_OFFER_STATUS_AWAITING,
+            BX_ADS_OFFER_STATUS_DECLINED
+        );
+    }
+    
+    public function decodeDataApi ($aData, $bExtended = false)
+    {
+        $CNF = $this->_oConfig->CNF;
+        
+        $aResult = array_merge(parent::decodeDataApi($aData, $bExtended), [
+            $CNF['FIELD_PRICE'] => $aData[$CNF['FIELD_PRICE']],
+            $CNF['FIELD_QUANTITY'] => $aData[$CNF['FIELD_QUANTITY']],
+            $CNF['FIELD_NOTES_PURCHASED'] => $aData[$CNF['FIELD_NOTES_PURCHASED']]
+        ]);
+
+        if($this->_oConfig->isSources() && !empty($aData[$CNF['FIELD_URL']]))
+            $aResult[$CNF['FIELD_URL']] = $aData[$CNF['FIELD_URL']];
+
+        return $aResult;
+    }
+
+    public function actionGetSource()
+    {
+        $aEntries = $this->serviceGetSourceData();
+
+        header('Content-Type:text/javascript; charset=utf-8');
+        echo(json_encode($aEntries));
+    }
+    
+    public function serviceGetSourceData()
+    {
+        $sSourceType = bx_get('source_type');
+        if(empty($sSourceType)) {
+            $aSource = $this->serviceGetSource($this->_iProfileId);
+            if(!empty($aSource) && is_array($aSource))
+                $sSourceType = $aSource['name'];
+        }
+
+        $sTerm = bx_get('term');
+
+        $aEntries = $this->serviceLoadEntriesFromSourceByTerm($sSourceType, $sTerm);
+
+        return $aEntries;
+    }
+
+    public function actionLoadEntryFromSource()
+    {
+        $sSourceType = bx_process_url_param(bx_process_input(bx_get('source_type')));
+        $sSource = bx_process_url_param(bx_process_input(bx_get('source')));
+        if(empty($sSourceType) || empty($sSource))
+            return echoJson(['code' => 1]);
+
+        $aEntry = $this->serviceLoadEntryFromSource($sSourceType, $sSource);
+        if(empty($aEntry) || !is_array($aEntry))
+            return echoJson(['code' => 2]);
+
+        return echoJson([
+            'code' => 0,
+            'fields' => $aEntry
+        ]);
+    }
+
+    public function actionGetCategoryForm()
+    {
+        if(($iCategory = bx_get('category')) === false || (int)$iCategory == 0)
+            return echoJson(['msg' => _t('_bx_ads_form_entry_input_category_select_err')]);
+
+        return echoJson([
+            'eval' => $this->_oConfig->getJsObject('form') . '.onSelectCategory(oData)',
+            'content' => $this->serviceGetCreatePostForm([
+                'absolute_action_url' => true,
+                'dynamic_mode' => true,
+                'context_id' => ($iContextId = bx_get('context_id')) !== false ? (int)$iContextId : false
+            ])
+        ]);
+    }
+
+    public function actionCheckName()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iId = (int)bx_get($CNF['FIELD_ID']);
+    	$sTitle = bx_process_input(bx_get($CNF['FIELD_TITLE']));
+
+    	echoJson($this->serviceCheckName($sTitle, $iId));
+    }
+
+    public function actionInterested()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return echoJson(array());
+
+        $iViewer = bx_get_logged_profile_id();        
+        $oViewer = BxDolProfile::getInstance($iViewer);
+        if(!$oViewer)
+            return echoJson(array());
+
+        $iContentAuthor = (int)$aContentInfo[$CNF['FIELD_AUTHOR']];
+        if($iContentAuthor == $iViewer)
+            return echoJson(array('msg' => _t('_bx_ads_txt_err_your_own')));
+
+        if($this->_oDb->isInterested($iContentId, $iViewer))
+            return echoJson(array('msg' => _t('_bx_ads_txt_err_duplicate')));
+
+        $iInterestId = $this->_oDb->insertInterested(array('entry_id' => $iContentId, 'profile_id' => $iViewer));
+        if(!$iInterestId)
+            return echoJson(array('msg' => _t('_bx_ads_txt_err_cannot_perform_action')));
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-doInterest 'bx_ads', 'doInterest' - hook after 'interested' action was performed
+         * - $unit_name - equals `bx_ads`
+         * - $action - equals `doInterest`
+         * - $object_id - content id
+         * - $sender_id - profile id who performed the action
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `subobject_id` - [int] id of a database table record, created during the action
+         *      - `subobject_author_id` - [int] profile id who performed the action
+         *      - `object_author_id` - [int] content author profile id
+         * @hook @ref hook-bx_ads-doInterest
+         */
+        bx_alert($this->getName(), 'doInterest', $iContentId, $iViewer, [
+            'subobject_id' => $iInterestId, 
+            'subobject_author_id' => $iViewer, 
+
+            'object_author_id' => $iContentAuthor
+        ]);
+
+        if(getParam($CNF['PARAM_USE_IIN']) == 'on')
+            sendMailTemplate($CNF['ETEMPLATE_INTERESTED'], 0, $iContentAuthor, array(
+                'viewer_name' => $oViewer->getDisplayName(),
+                'viewer_url' => $oViewer->getUrl(),
+                'ad_name' => $aContentInfo[$CNF['FIELD_TITLE']],
+                'ad_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', array(
+                    'i' => $CNF['URI_VIEW_ENTRY'], 
+                    $CNF['FIELD_ID'] => $iContentId
+                )))
+            ));
+
+        return echoJson(array('msg' => _t('_bx_ads_txt_msg_author_notified')));
+    }
+
+    public function actionShow()
+    {
+        return echoJson($this->_actionChangeStatus(BX_BASE_MOD_TEXT_STATUS_ACTIVE));
+    }
+
+    public function actionHide()
+    {
+        return echoJson($this->_actionChangeStatus(BX_BASE_MOD_TEXT_STATUS_HIDDEN));
+    }
+
+    public function actionMakeOffer()
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sJsObject = $this->_oConfig->getJsObject('entry');
+
+        $iAuthorId = bx_get_logged_profile_id();
+        $iContentId = bx_process_input(bx_get('content_id'), BX_DATA_INT);
+
+        if(empty($iAuthorId))
+            return echoJson(['code' => 1, 'eval' => 'window.open("' . BxDolPermalinks::getInstance()->permalink('page.php?i=login') . '", "_self");']);
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return echoJson([]);
+
+        if(($mixedCheck = $this->checkAllowedMakeOffer($aContentInfo)) !== CHECK_ACTION_RESULT_ALLOWED)
+            return echoJson(['code' => 2, 'msg' => $mixedCheck]);
+
+        $aOffer = $this->_oDb->getOffersBy([
+            'type' => 'content_and_author_ids', 
+            'content_id' => $iContentId, 
+            'author_id' => $iAuthorId, 
+            'status' => BX_ADS_OFFER_STATUS_AWAITING
+        ]);
+
+        if(!empty($aOffer) && is_array($aOffer))
+            return echoJson(['code' => 3, 'msg' => _t('_bx_ads_txt_err_duplicate')]);
+
+        $aOffer = $this->_oDb->getOffersBy([
+            'type' => 'content_and_author_ids', 
+            'content_id' => $iContentId, 
+            'author_id' => $iAuthorId, 
+            'status' => BX_ADS_OFFER_STATUS_ACCEPTED
+        ]);
+
+        if(!empty($aOffer) && is_array($aOffer))
+            return echoJson(['code' => 4, 'msg' => _t('_bx_ads_txt_err_offer_accepted')]);
+
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_OFFER'], $CNF['OBJECT_FORM_OFFER_DISPLAY_ADD']);
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . bx_append_url_params($this->_oConfig->getBaseUri() . 'make_offer', ['content_id' => $iContentId]);
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+            $iQuantity = (int)$oForm->getCleanValue($CNF['FIELD_OFR_QUANTITY']);
+            $iQuantityAvailable = $this->getAvailableQuantity($aContentInfo);
+            if($iQuantity > $iQuantityAvailable)
+                return echoJson(['code' => 5, 'msg' => _t('_bx_ads_txt_err_offer_wrong_quantity', $iQuantityAvailable)]);
+
+            $aValToAdd = ['content_id' => $iContentId, 'author_id' => $iAuthorId];
+
+            if(($iId = (int)$oForm->insert($aValToAdd)) != 0) {
+                $this->checkAllowedMakeOffer($aContentInfo, true);
+
+                $this->onOfferAdded($iId, $aResult);
+
+                $aResult = ['code' => 0, 'msg' => _t('_bx_ads_txt_msg_offer_added'), 'eval' => $sJsObject . '.onMakeOffer(oData);', 'id' => $iId];
+            }
+            else
+                $aResult = ['code' => 6, 'msg' => _t('_bx_ads_txt_err_cannot_perform_action')];
+
+            return echoJson($aResult);
+        }
+
+        $sContent = BxTemplFunctions::getInstance()->transBox($this->_oConfig->getHtmlIds('offer_popup'), $this->_oTemplate->parseHtmlByName('offer_popup.html', array(
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode(true)
+        )));
+
+        return echoJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false, 'removeOnClose' => true))));
+    }
+
+    public function actionViewOffer()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iAuthorId = bx_get_logged_profile_id();
+        $iOfferId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        $aOffer = $this->_oDb->getOffersBy(array('type' => 'id', 'id' => $iOfferId));
+        if(empty($aOffer) || !is_array($aOffer))
+            return echoJson(array());
+        
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_OFFER'], $CNF['OBJECT_FORM_OFFER_DISPLAY_VIEW']);
+        $oForm->initChecker($aOffer);
+
+        $sContent = BxTemplFunctions::getInstance()->transBox($this->_oConfig->getHtmlIds('offer_popup'), $this->_oTemplate->parseHtmlByName('offer_popup.html', array(
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode(true)
+        )));
+
+        return echoJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => true, 'removeOnClose' => true))));
+    }
+
+    public function actionAcceptOffer()
+    {
+        $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(($mixedResult = $this->offerAccept($iId)) !== true)
+            return echoJson(['msg' => $mixedResult !== false ? $mixedResult : _t('_bx_ads_txt_err_cannot_perform_action')]);
+
+        return echoJson(['msg' => _t('_bx_ads_txt_msg_offer_accepted'), 'reload' => 1]);
+    }
+
+    public function actionDeclineOffer()
+    {
+        $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(!$this->offerDecline($iId))
+            return echoJson(array('msg' => _t('_bx_ads_txt_err_cannot_perform_action')));
+
+        return echoJson(array('msg' => _t('_bx_ads_txt_msg_offer_declined'), 'reload' => 1));
+    }
+
+    public function actionCancelOffer()
+    {
+        $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(!$this->offerCancel($iId))
+            return echoJson(['msg' => _t('_bx_ads_txt_err_cannot_perform_action')]);
+
+        return echoJson(['msg' => _t('_bx_ads_txt_msg_offer_canceled'), 'reload' => 1]);
+    }
+
+    public function actionPayOffer()
+    {
+        $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(($mixedResult = $this->offerPay($iId)) !== false)
+            return echoJson($mixedResult);
+
+        return echoJson(array('msg' => _t('_bx_ads_txt_err_cannot_perform_action')));     
+    }
+
+    public function actionShipped()
+    {
+        return echoJson($this->_actionMarkAs('shipped'));
+    }
+
+    public function actionReceived()
+    {
+        return echoJson($this->_actionMarkAs('received'));
+    }
+
+    public function actionRegisterImpression($iId)
+    {
+        echoJson($this->serviceRegisterImpression($iId));
+    }
+
+    public function actionRegisterClick($iId)
+    {
+        echoJson($this->serviceRegisterClick($iId));
+    }
+
+    public function serviceGetSafeServices()
+    {
+        return array_merge(parent::serviceGetSafeServices(), [
+            'IsSourcesAvaliable' => '',
+            'LoadEntryFromSource' => '',
+            'LoadEntriesFromSourceByTerm' => '',
+            'EntityReviews' => '',
+            'EntityReviewsRating' => '',
+            'CategoriesList' => '',
+            'BrowseCategory' => '',
+            'RegisterImpression' => '',
+            'RegisterClick' => '',
+            'BlockSourcesDetails' => '',
+            'GetSourceData' => '',
+        ]);
+    }
+
+    public function serviceRegisterImpression($iId)
+    {
+        return ['code' => $this->_oDb->updatePromotionTracker($iId, 'impressions') ? 0 : 1];
+    }
+    
+    public function serviceRegisterClick($iId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oDb->updatePromotionTracker($iId, 'clicks'))
+            return ['code' => 1];
+
+        $sUrl = '';
+        if($this->_oConfig->isSources())  {
+            $aEntry = $this->_oDb->getContentInfoById($iId);
+            if(!empty($aEntry) && is_array($aEntry) && !empty($aEntry[$CNF['FIELD_URL']]))
+                $sUrl = $aEntry[$CNF['FIELD_URL']];
+        }
+
+        return [
+            'code' => 0,
+            'redirect' => !empty($sUrl) ? $sUrl : $this->_oConfig->getViewEntryUrl($iId)
+        ];
+    }
+
+    public function serviceCheckName($sTitle, $iId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	if(empty($sTitle))
+            return array();
+
+        $sName = '';
+        if(!empty($iId)) {
+            $aEntry = $this->_oDb->getContentInfoById($iId); 
+            if(strcmp($sTitle, $aEntry[$CNF['FIELD_NAME']]) == 0) 
+                $sName = $sTitle;
+        }
+
+        return array(
+            'title' => $sTitle,
+            'name' => !empty($sName) ? $sName : $this->_oConfig->getEntryName($sTitle, $iId)
+    	);
+    }
+
+    public function serviceUpdateCategoriesStats($mixedContentInfo = false)
+    {
+        $aContentInfo = [];
+        if(!empty($mixedContentInfo))
+            $aContentInfo = !is_array($mixedContentInfo) ? $this->_oDb->getContentInfoById((int)$mixedContentInfo) : $mixedContentInfo;
+
+        $iCategoryId = 0;
+        if($aContentInfo && !empty($aContentInfo['category']))
+            $iCategoryId = (int)$aContentInfo['category'];
+
+        return $this->serviceUpdateCategoriesStatsByCategory($iCategoryId);
+    }
+
+    public function serviceUpdateCategoriesStatsByCategory($iCategoryId = 0)
+    {
+        $aParams = ['type' => 'collect_stats'];
+        if($iCategoryId)
+            $aParams['category_id'] = (int)$iCategoryId;
+
+        $aStats = $this->_oDb->getCategories($aParams);
+        if(empty($aStats) || !is_array($aStats))
+            return true;
+
+        $iUpdated = 0;
+        foreach($aStats as $aStat)
+            if($this->_oDb->updateCategory(array('items' => $aStat['count']), array('id' => $aStat['id'])))
+                $iUpdated++;
+
+        return count($aStats) == $iUpdated;
+    }
+
+    public function serviceGetCategoryOptions($iParentId, $bPleaseSelect = false)
+    {
+        $aValues = array();
+        if($bPleaseSelect)
+            $aValues[] = array('key' => '', 'value' => _t('_sys_please_select'));
+
+        $this->_getCategoryOptions($iParentId, $aValues);
+
+        return $aValues;
+    }
+
+    public function serviceGetSearchableFields($aInputsAdd = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResult = parent::serviceGetSearchableFields(array_merge($aInputsAdd, $this->_getSearchableFields()));
+        unset($aResult[$CNF['FIELD_NOTES_PURCHASED']]);
+        unset($aResult[$CNF['FIELD_CATEGORY_VIEW']], $aResult[$CNF['FIELD_CATEGORY_SELECT']]);
+        unset($aResult[$CNF['FIELD_PRICE']], $aResult[$CNF['FIELD_YEAR']]);
+
+        return $aResult;
+    }
+
+    public function serviceGetSearchableFieldsExtended($aInputsAdd = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aInputsAdd = array_merge($aInputsAdd, $this->_getSearchableFields());
+
+        if(isset($aInputsAdd[$CNF['FIELD_CATEGORY']])) {
+            $aInputsAdd[$CNF['FIELD_CATEGORY']]['type'] = 'select';
+            $aInputsAdd[$CNF['FIELD_CATEGORY']]['values_src'] = BxDolService::getSerializedService($this->_oConfig->getName(), 'get_category_options', array(0));
+        }
+
+        if(isset($aInputsAdd[$CNF['FIELD_PRICE']])) {
+            $aInputsAdd[$CNF['FIELD_PRICE']]['search_type'] = 'text_range';
+            $aInputsAdd[$CNF['FIELD_PRICE']]['search_operator'] = 'between';
+        }
+
+        if(isset($aInputsAdd[$CNF['FIELD_YEAR']])) {
+            $aInputsAdd[$CNF['FIELD_YEAR']]['search_type'] = 'text_range';
+            $aInputsAdd[$CNF['FIELD_YEAR']]['search_operator'] = 'between';
+        }
+
+        return parent::serviceGetSearchableFieldsExtended($aInputsAdd);
+    }
+
+    /**
+     * Mark an ad as shipped/received by seller/bauer accordingly.
+     * @param type $sAction - shipped/received action.
+     * @param type $iContentId - an ad the action will be performed with.
+     * @param integer $iProfileSrc - profile, who performed the action.
+     * @param integer $iProfileDst - profile, the action is pointed on.
+     * @return boolean - true or a string value with error message.
+     */
+    public function serviceMarkAs($sAction, $iContentId, $iProfileSrc = 0, $iProfileDst = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$iProfileSrc)
+            $iProfileSrc = bx_get_logged_profile_id();
+
+        $sTxtError = '_bx_ads_txt_err_cannot_perform_action';
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+
+        $sMethodCheck = 'checkAllowedMark' . bx_gen_method_name($sAction) . 'ForProfile';
+        if(!method_exists($this, $sMethodCheck))
+            return _t($sTxtError);
+
+        if(($mixedCheckResult = $this->$sMethodCheck($aContentInfo, false, $iProfileSrc)) !== CHECK_ACTION_RESULT_ALLOWED)
+            return $mixedCheckResult;
+
+        if((int)$this->_oDb->updateEntriesBy(array($CNF['FIELD_' . strtoupper($sAction)] => time()), array($CNF['FIELD_ID'] => $iContentId)) == 0)
+            return _t($sTxtError);
+
+        $this->$sMethodCheck($aContentInfo, true, $iProfileSrc);
+
+        $sMethodOnResult = 'on' . bx_gen_method_name($sAction);
+        if(method_exists($this, $sMethodOnResult))
+            $this->$sMethodOnResult($aContentInfo, $iProfileSrc, $iProfileDst);        
+
+        return true;
+    }
+
+    public function serviceEntityCreate ($sParams = false)
+    {
+        $iCategory = is_numeric($sParams) ? (int)$sParams : 0;
+
+        if($this->_bIsApi && ($iCategory = bx_get('category_select')) !== false)
+            $iCategory = (int)$iCategory;
+
+        if(($sDisplay = $this->getCategoryDisplay('add', $iCategory)) !== false) {
+            if(empty($sParams) || !is_array($sParams))
+                $sParams = [];
+
+            $sParams['display'] = $sDisplay;
+        }
+
+        if($iCategory)
+            BxDolSession::getInstance()->setValue($sDisplay . '_category', $iCategory);
+
+        return parent::serviceEntityCreate($sParams);
+    }
+
+    public function serviceEntityEditBudget ($iContentId = 0, $sDisplay = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return parent::serviceEntityEdit($iContentId, $CNF['OBJECT_FORM_ENTRY_DISPLAY_EDIT_BUDGET']);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads 
+     * @subsection bx_ads-page_blocks Page Blocks
+     * @subsubsection bx_ads-entity_reviews entity_reviews
+     * 
+     * @code bx_srv('bx_ads', 'entity_reviews', [...]); @endcode
+     * 
+     * Get reviews for particular content
+     * @param $iContentId content ID
+     * 
+     * @see BxAdsModule::serviceEntityReviews
+     */
+    /** 
+     * @ref bx_ads-entity_reviews "entity_reviews"
+     */
+    public function serviceEntityReviews($iContentId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        if(empty($CNF['OBJECT_REVIEWS']))
+            return false;
+
+        return $this->_entityComments($CNF['OBJECT_REVIEWS'], $iContentId);
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads 
+     * @subsection bx_ads-page_blocks Page Blocks
+     * @subsubsection bx_ads-entity_reviews_rating entity_reviews_rating
+     * 
+     * @code bx_srv('bx_ads', 'entity_reviews_rating', [...]); @endcode
+     * 
+     * Get reviews rating for particular content
+     * @param $iContentId content ID
+     * 
+     * @see BxAdsModule::serviceEntityReviewsRating
+     */
+    /** 
+     * @ref bx_ads-entity_reviews_rating "entity_reviews_rating"
+     */
+    public function serviceEntityReviewsRating($iContentId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        if(empty($CNF['OBJECT_REVIEWS']))
+            return false;
+
+        if(!$iContentId)
+            $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(!$iContentId)
+            return false;
+
+        $oCmts = BxDolCmts::getObjectInstance($CNF['OBJECT_REVIEWS'], $iContentId);
+        if (!$oCmts || !$oCmts->isEnabled())
+            return false;
+
+        return $oCmts->getRatingBlock(array('in_designbox' => false));
+    }
+
+    public function serviceEntityPromotionGrowth($iContentId = 0)
+    {
+        return $this->_serviceTemplateFunc('entryPromotionGrowth', $iContentId);
+    }
+
+    public function serviceEntityPromotionSummary($iContentId = 0)
+    {
+        return $this->_serviceTemplateFunc('entryPromotionSummary', $iContentId);
+    }
+    
+    public function serviceEntityPromotionRoi($iContentId = 0)
+    {
+        return $this->_serviceTemplateFunc('entryPromotionRoi', $iContentId);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads 
+     * @subsection bx_ads-page_blocks Page Blocks
+     * @subsubsection bx_ads-categories_list categories_list
+     * 
+     * @code bx_srv('bx_ads', 'categories_list', [...]); @endcode
+     * 
+     * Get reviews rating for particular content
+     * @param $aParams additional params array, such as 'show_empty'
+     * 
+     * @see BxAdsModule::serviceCategoriesList
+     */
+    /** 
+     * @ref bx_ads-categories_list "categories_list"
+     */
+    public function serviceCategoriesList($aParams = array())
+    {
+        if(!isset($aParams['show_empty']))
+            $aParams['show_empty'] = true;
+
+        return $this->_oTemplate->categoriesList($aParams);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads 
+     * @subsection bx_ads-browse Browse
+     * @subsubsection bx_ads-browse_category browse_category
+     * 
+     * @code bx_srv('bx_ads', 'browse_category', [...]); @endcode
+     * 
+     * Get reviews rating for particular content
+     * @param $iCategoryId category ID
+     * @param $aParams additional params array, such as empty_message, ajax_paginate, etc
+     * 
+     * @see BxAdsModule::serviceBrowseCategory
+     */
+    /**
+     * @ref bx_ads-browse_category "browse_category"
+     */
+    public function serviceBrowseCategory($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $aParams = [])
+    {
+        $mixedResult = parent::serviceBrowseCategory($sUnitView, $bEmptyMessage, $bAjaxPaginate, $aParams);
+        if(empty($mixedResult['content']))
+            return $mixedResult;
+
+        $aCategory = $this->_oDb->getCategories([
+            'type' => 'id', 
+            'id' => !empty($aParams['category']) ? (int)$aParams['category'] : bx_process_input(bx_get('category'), BX_DATA_INT)
+        ]);
+
+        if(!empty($aCategory['title']))
+            $mixedResult['title'] = _t('_bx_ads_page_block_title_entries_by_category_mask', _t($aCategory['title']));
+
+        return $mixedResult;
+    }
+
+    public function serviceGetTimelinePost($aEvent, $aBrowseParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $mixedResult = parent::serviceGetTimelinePost($aEvent, $aBrowseParams);
+        if(!$mixedResult)
+            return $mixedResult;
+        
+        $aContentInfo = $this->_oDb->getContentInfoById($aEvent['object_id']);
+
+        if($this->_oConfig->isSources() && !empty($aContentInfo[$CNF['FIELD_URL']]))
+            $mixedResult['url'] = $aContentInfo[$CNF['FIELD_URL']];
+
+        return $mixedResult;
+    }
+
+    public function serviceGetNotificationsData()
+    {
+        $sModule = $this->_aModule['name'];
+
+        $sEventPrivacy = $sModule . '_allow_view_event_to';
+        if(BxDolPrivacy::getObjectInstance($sEventPrivacy) === false)
+            $sEventPrivacy = '';
+
+        $aResult = parent::serviceGetNotificationsData();
+        $aResult['handlers'] = array_merge($aResult['handlers'], array(
+            array('group' => $sModule . '_interest', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'doInterest', 'module_name' => $sModule, 'module_method' => 'get_notifications_interest', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy),
+            array('group' => $sModule . '_paid', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'license_register', 'module_name' => $sModule, 'module_method' => 'get_notifications_license_register', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy),
+            //---> To Buyer
+            array('group' => $sModule . '_shipped', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'shipped', 'module_name' => $sModule, 'module_method' => 'get_notifications_shipped', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy),
+            //<---
+            array('group' => $sModule . '_received', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'received', 'module_name' => $sModule, 'module_method' => 'get_notifications_received', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy),
+
+            array('group' => $sModule . '_offer_added', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'offer_added', 'module_name' => $sModule, 'module_method' => 'get_notifications_offer_added', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy),
+            //---> To Offerer
+            array('group' => $sModule . '_offer_accepted', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'offer_accepted', 'module_name' => $sModule, 'module_method' => 'get_notifications_offer_accepted', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy),
+            array('group' => $sModule . '_offer_declined', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'offer_declined', 'module_name' => $sModule, 'module_method' => 'get_notifications_offer_declined', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy),
+            //<---
+            array('group' => $sModule . '_offer_canceled', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'offer_canceled', 'module_name' => $sModule, 'module_method' => 'get_notifications_offer_canceled', 'module_class' => 'Module', 'module_event_privacy' => $sEventPrivacy)
+        ));
+
+        $aResult['settings'] = array_merge($aResult['settings'], array(
+            array('group' => 'interest', 'unit' => $sModule, 'action' => 'doInterest', 'types' => array('personal')),
+
+            array('group' => 'usage', 'unit' => $sModule, 'action' => 'license_register', 'types' => array('personal')),
+            array('group' => 'usage', 'unit' => $sModule, 'action' => 'shipped', 'types' => array('personal')),
+            array('group' => 'usage', 'unit' => $sModule, 'action' => 'received', 'types' => array('personal')),
+            array('group' => 'usage', 'unit' => $sModule, 'action' => 'offer_added', 'types' => array('personal')),
+            array('group' => 'usage', 'unit' => $sModule, 'action' => 'offer_accepted', 'types' => array('personal')),
+            array('group' => 'usage', 'unit' => $sModule, 'action' => 'offer_declined', 'types' => array('personal')),
+            array('group' => 'usage', 'unit' => $sModule, 'action' => 'offer_canceled', 'types' => array('personal')),
+        ));
+
+        $aResult['alerts'] = array_merge($aResult['alerts'], array(
+            array('unit' => $sModule, 'action' => 'doInterest'),
+            array('unit' => $sModule, 'action' => 'license_register'),
+            array('unit' => $sModule, 'action' => 'shipped'),
+            array('unit' => $sModule, 'action' => 'received'),
+            array('unit' => $sModule, 'action' => 'offer_added'),
+            array('unit' => $sModule, 'action' => 'offer_accepted'),
+            array('unit' => $sModule, 'action' => 'offer_declined'),
+            array('unit' => $sModule, 'action' => 'offer_canceled'),
+        ));
+
+        return $aResult; 
+    }
+
+    public function serviceGetNotificationsInsertData($oAlert, $aHandler, $aDataItems)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResult = [];
+        switch($oAlert->sAction) {
+            case 'license_register':
+                $aContentInfo = $this->_oDb->getContentInfoById($oAlert->aExtras['product_id']);
+                if(empty($aContentInfo) || !is_array($aContentInfo)) {
+                    $aResult = $aDataItems;
+                    break;
+                }
+
+                foreach($aDataItems as $aDataItem) {
+                    $aDataItem = array_merge($aDataItem, [
+                        'owner_id' => $oAlert->aExtras['profile_id'],
+                        'object_id' => $aContentInfo[$CNF['FIELD_ID']],
+                        'object_owner_id' => $aContentInfo[$CNF['FIELD_AUTHOR']],
+                        'object_privacy_view' => BX_DOL_PG_ALL
+                    ]);
+
+                    $aResult[] = $aDataItem;
+                }
+                break;
+
+            case 'shipped':
+                foreach($aDataItems as $aDataItem) {
+                    $aLicense = $this->_oDb->getLicense(['type' => 'entry_id', 'entry_id' => $aDataItem['object_id'], 'newest' => true]);
+                    if(!empty($aLicense) && is_array($aLicense))
+                        $aDataItem['object_owner_id'] = $aLicense['profile_id'];
+
+                    $aResult[] = $aDataItem;
+                }
+                break;
+
+            case 'offer_added':
+            case 'offer_canceled':
+                foreach($aDataItems as $aDataItem) {
+                    $aDataItem['object_owner_id'] = $oAlert->aExtras['object_author_id'];
+
+                    $aResult[] = $aDataItem;
+                }
+                break;
+
+            case 'offer_accepted':
+            case 'offer_declined':
+                foreach($aDataItems as $aDataItem) {
+                    $aDataItem['object_owner_id'] = $oAlert->aExtras['offer_author_id'];
+
+                    $aResult[] = $aDataItem;
+                }
+                break;
+
+            default:
+                $aResult = $aDataItems;
+        }
+
+        return $aResult;
+    }
+
+    public function serviceGetNotificationsInterest($aEvent)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$iContentId = (int)$aEvent['object_id'];
+    	$aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return [];
+
+        $iInterestedId = (int)$aEvent['subobject_id'];
+        $aInterestedInfo = $this->_oDb->getInterested(['type' => 'id', 'id' => $iInterestedId]);
+        if(empty($aInterestedInfo) || !is_array($aInterestedInfo))
+            return [];
+
+        if(($oInterestedProfile = BxDolProfile::getInstance($aInterestedInfo['profile_id'])) !== false)
+            $sEntryUrl = $oInterestedProfile->getUrl();
+        else
+            $sEntryUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iContentId), '{bx_url_root}');
+
+        return [
+            'entry_sample' => $CNF['T']['txt_sample_single'],
+            'entry_url' => $sEntryUrl,
+            'entry_caption' => $aContentInfo[$CNF['FIELD_TITLE']],
+            'entry_author' => $aContentInfo[$CNF['FIELD_AUTHOR']],
+            'subentry_sample' => $CNF['T']['txt_sample_interest_single'],
+            'subentry_url' => '',
+            'lang_key' => '_bx_ads_txt_ntfs_subobject_interested', //may be empty or not specified. In this case the default one from Notification module will be used.
+        ];
+    }
+
+    public function serviceGetNotificationsLicenseRegister($aEvent)
+    {
+        return $this->_serviceGetNotificationsByEntryAndAction($aEvent, 'license_register');
+    }
+
+    public function serviceGetNotificationsShipped($aEvent)
+    {
+        $aResult = $this->_serviceGetNotificationsByEntryAndAction($aEvent, 'shipped');
+        $aResult['entry_author'] = $aEvent['object_owner_id'];
+
+        return $aResult;
+    }
+
+    public function serviceGetNotificationsReceived($aEvent)
+    {
+        return $this->_serviceGetNotificationsByEntryAndAction($aEvent, 'received');
+    }
+
+    public function serviceGetNotificationsOfferAdded($aEvent)
+    {
+        return $this->_serviceGetNotificationsByOfferAndAction($aEvent, 'offer_added');
+    }
+
+    public function serviceGetNotificationsOfferAccepted($aEvent)
+    {
+        $aResult = $this->_serviceGetNotificationsByOfferAndAction($aEvent, 'offer_accepted');
+        $aResult['entry_author'] = $aEvent['object_owner_id'];
+
+        return $aResult;
+    }
+
+    public function serviceGetNotificationsOfferDeclined($aEvent)
+    {
+        $aResult = $this->_serviceGetNotificationsByOfferAndAction($aEvent, 'offer_declined');
+        $aResult['entry_author'] = $aEvent['object_owner_id'];
+
+        return $aResult;
+    }
+
+    public function serviceGetNotificationsOfferCanceled($aEvent)
+    {
+        return $this->_serviceGetNotificationsByOfferAndAction($aEvent, 'offer_canceled');
+    }
+
+    protected function _serviceGetNotificationsByEntryAndAction($aEvent, $sAction)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$iContentId = (int)$aEvent['object_id'];
+    	$aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return array();
+
+        $sEntryUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iContentId), '{bx_url_root}');
+
+        return array(
+            'entry_sample' => $CNF['T']['txt_sample_single'],
+            'entry_url' => $sEntryUrl,
+            'entry_caption' => $aContentInfo[$CNF['FIELD_TITLE']],
+            'entry_author' => $aContentInfo[$CNF['FIELD_AUTHOR']],
+            'subentry_sample' => '',
+            'subentry_url' => '',
+            'lang_key' => '_bx_ads_txt_ntfs_object_' . $sAction, //may be empty or not specified. In this case the default one from Notification module will be used.
+        );
+    }
+
+    protected function _serviceGetNotificationsByOfferAndAction($aEvent, $sAction)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$iOfferId = (int)$aEvent['object_id'];
+    	$aOfferInfo = $this->_oDb->getOffersBy(['type' => 'id', 'id' => $iOfferId]);
+        if(empty($aOfferInfo) || !is_array($aOfferInfo))
+            return array();
+
+        $iContentId = $aOfferInfo[$CNF['FIELD_OFR_CONTENT']];
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return array();
+
+        $sEntryUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iContentId), '{bx_url_root}');
+
+        return array(
+            'object_id' => $iContentId,
+            'entry_sample' => $CNF['T']['txt_sample_single'],
+            'entry_url' => $sEntryUrl,
+            'entry_caption' => $aContentInfo[$CNF['FIELD_TITLE']],
+            'entry_author' => $aContentInfo[$CNF['FIELD_AUTHOR']],
+            'subentry_sample' => '',
+            'subentry_url' => '',
+            'lang_key' => '_bx_ads_txt_ntfs_' . $sAction, //may be empty or not specified. In this case the default one from Notification module will be used.
+        );
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-page_blocks Page Blocks
+     * @subsubsection bx_ads-block_licenses block_licenses
+     * 
+     * @code bx_srv('bx_ads', 'block_licenses', [...]); @endcode
+     * 
+     * Get page block with a list of licenses purchased by currently logged member.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxAdsModule::serviceBlockLicenses
+     */
+    /** 
+     * @ref bx_ads-block_licenses "block_licenses"
+     */
+    public function serviceBlockLicenses() 
+    {
+        return $this->_getBlockLicenses();
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-page_blocks Page Blocks
+     * @subsubsection bx_ads-block_licenses_administration block_licenses_administration
+     * 
+     * @code bx_srv('bx_ads', 'block_licenses_administration', [...]); @endcode
+     * 
+     * Get page block with a list of all licenses. It's needed for moderators/administrators.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxAdsModule::serviceBlockLicensesAdministration
+     */
+    /** 
+     * @ref bx_ads-block_licenses "block_licenses"
+     */
+    public function serviceBlockLicensesAdministration() 
+    {
+        return $this->_getBlockLicenses('administration');
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-page_blocks Page Blocks
+     * @subsubsection bx_ads-block_licenses_note block_licenses_note
+     * 
+     * @code bx_srv('bx_ads', 'block_licenses_note', [...]); @endcode
+     * 
+     * Get page block with a notice for licenses usage.
+     *
+     * @return HTML string with block content to display on the site.
+     * 
+     * @see BxAdsModule::serviceBlockLicensesNote
+     */
+    /** 
+     * @ref bx_ads-block_licenses_note "block_licenses_note"
+     */
+    public function serviceBlockLicensesNote()
+    {
+        return MsgBox(_t('_bx_ads_page_block_content_licenses_note'));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-get_payment_data get_payment_data
+     * 
+     * @code bx_srv('bx_ads', 'get_payment_data', [...]); @endcode
+     * 
+     * Get an array with module's description. Is needed for payments processing module.
+     * 
+     * @return an array with module's description.
+     * 
+     * @see BxAdsModule::serviceGetPaymentData
+     */
+    /** 
+     * @ref bx_ads-get_payment_data "get_payment_data"
+     */
+    public function serviceGetPaymentData()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oPermalink = BxDolPermalinks::getInstance();
+        return array_merge($this->_aModule, [
+            'url_browse_order_common' => bx_absolute_url(bx_append_url_params($oPermalink->permalink($CNF['URL_LICENSES_COMMON']), ['filter' => '{order}'], false)),
+            'url_browse_order_administration' => bx_absolute_url(bx_append_url_params($oPermalink->permalink($CNF['URL_LICENSES_ADMINISTRATION']), ['filter' => '{order}'], false))
+        ]);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-get_cart_item get_cart_item
+     * 
+     * @code bx_srv('bx_ads', 'get_cart_item', [...]); @endcode
+     * 
+     * Get an array with prodict's description. Is used in Shopping Cart in payments processing module.
+     * 
+     * @param $mixedItemId product's ID or Unique Name.
+     * @return an array with prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxAdsModule::serviceGetCartItem
+     */
+    /** 
+     * @ref bx_ads-get_cart_item "get_cart_item"
+     */
+    public function serviceGetCartItem($mixedItemId, $iClientId = 0)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        if(!$mixedItemId || !is_numeric($mixedItemId))
+            return [];
+
+        $aCommodity = $this->_oDb->getCommodity(['sample' => 'id_with_entry', 'id' => (int)$mixedItemId]);
+        if(empty($aCommodity) || !is_array($aCommodity))
+            return [];
+
+        $iItemId = (int)$aCommodity['id'];
+        $sItemType = $aCommodity['type'];
+
+        $iItemAuthor = 0;
+        $fItemPrice = 0;
+        switch($sItemType) {
+            case BX_ADS_COMMODITY_TYPE_PRODUCT:
+                $iItemAuthor = $aCommodity['entry_' . $CNF['FIELD_AUTHOR']];
+                $fItemPrice = (float)$aCommodity['entry_' . $CNF['FIELD_PRICE']];
+                if($this->_oConfig->isAuction() && (int)$aCommodity['entry_' . $CNF['FIELD_AUCTION']] != 0) {
+                    $aOffer = $this->_oDb->getOffersBy([
+                        'type' => 'content_and_author_ids', 
+                        'content_id' => $iItemId, 
+                        'author_id' => $iClientId,
+                        'status' => BX_ADS_OFFER_STATUS_ACCEPTED
+                    ]);
+
+                    if(!empty($aOffer) && is_array($aOffer))
+                        $fItemPrice = (float)$aOffer[$CNF['FIELD_OFR_AMOUNT']];
+                }
+                break;
+
+            case BX_ADS_COMMODITY_TYPE_PROMOTION:
+                $iItemAuthor = BxDolPayments::getInstance()->getOption('site_admin');
+                $fItemPrice = (float)$aCommodity['amount'];
+                break;
+        }
+
+        if(!$iClientId)
+            $iClientId = bx_get_logged_profile_id();
+
+        return [
+            'id' => $iItemId,
+            'author_id' => $iItemAuthor,
+            'name' => $aCommodity['entry_' . $CNF['FIELD_NAME']] . '-' . $sItemType,
+            'title' => $aCommodity['entry_' . $CNF['FIELD_TITLE']],
+            'description' => $sItemDescription = _t($CNF['T']['txt_cd_ct_' . $sItemType]),
+            'url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'], ['id' => $aCommodity['entry_' . $CNF['FIELD_ID']]])),
+            'price_single' => $fItemPrice,
+            'price_recurring' => '',
+            'period_recurring' => 0,
+            'period_unit_recurring' => '',
+            'trial_recurring' => ''
+        ];
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-get_cart_items get_cart_items
+     * 
+     * @code bx_srv('bx_ads', 'get_cart_items', [...]); @endcode
+     * 
+     * Get an array with prodicts' descriptions by seller. Is used in Manual Order Processing in payments processing module.
+     * 
+     * @param $iSellerId seller ID.
+     * @return an array with prodicts' descriptions. Empty array is returned if something is wrong or seller doesn't have any products.
+     * 
+     * @see BxAdsModule::serviceGetCartItems
+     */
+    /** 
+     * @ref bx_ads-get_cart_items "get_cart_items"
+     */
+    public function serviceGetCartItems($iSellerId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        $iSellerId = (int)$iSellerId;
+        if(empty($iSellerId))
+            return [];
+
+        $aItems = $this->_oDb->getCommodity(['sample' => 'entry_author', 'author' => $iSellerId, 'type' => BX_ADS_COMMODITY_TYPE_PRODUCT]);
+
+        $aResult = [];
+        foreach($aItems as $aItem) {
+            $aItemInfo = $this->_oDb->getContentInfoById((int)$aItem['entry_id']);
+            if(empty($aItemInfo) || !is_array($aItemInfo) || $aItemInfo[$CNF['FIELD_STATUS']] != BX_BASE_MOD_TEXT_STATUS_ACTIVE || empty($aItemInfo[$CNF['FIELD_QUANTITY']]))
+                continue;
+
+            $aResult[] = $this->serviceGetCartItem($aItem['id']);
+        }
+
+        return $aResult;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-authorize_cart_item authorize_cart_item
+     * 
+     * @code bx_srv('bx_ads', 'authorize_cart_item', [...]); @endcode
+     * 
+     * Authorize the order to process a single time payment inside the Ads module in future. Is called with payment processing module after the order was authorized there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with authorize prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxAdsModule::serviceAuthorizeCartItem
+     */
+    /** 
+     * @ref bx_ads-authorize_cart_item "authorize_cart_item"
+     */
+    public function serviceAuthorizeCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aItem = $this->serviceGetCartItem($iItemId);
+        if(empty($aItem) || !is_array($aItem))
+            return [];
+
+        $aCommodity = $this->_oDb->getCommodity(['sample' => 'id_with_entry', 'id' => (int)$iItemId]);
+        if(empty($aCommodity) || !is_array($aCommodity))
+            return [];
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-order_authorize 'bx_ads', 'order_authorize' - hook after the order was authorized with payment processing module
+         * - $unit_name - equals `bx_ads`
+         * - $action - equals `order_authorize`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `id` - [int] commodity id 
+         *      - `type` - [string] commodity type, 'product' or 'promotion'
+         *      - `product_id` - [int] ad (entry) id the commodity is associated with
+         *      - `order` - [string] order number provided with payment processing module
+         *      - `count` - [int] number of items in order
+         * @hook @ref hook-bx_ads-order_authorize
+         */
+        bx_alert($this->getName(), 'order_authorize', 0, false, [
+            'id' => $aCommodity['id'],
+            'type' => $aCommodity['type'],
+            'product_id' => $aCommodity['entry_id'],
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'count' => $iItemCount
+        ]);
+
+        return $aItem;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-register_cart_item register_cart_item
+     * 
+     * @code bx_srv('bx_ads', 'register_cart_item', [...]); @endcode
+     * 
+     * Register a processed single time payment inside the Ads module. Is called with payment processing module after the payment was registered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxAdsModule::serviceRegisterCartItem
+     */
+    /** 
+     * @ref bx_ads-register_cart_item "register_cart_item"
+     */
+    public function serviceRegisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        $aCommodity = $this->_oDb->getCommodity(['sample' => 'id', 'id' => (int)$iItemId]);
+        if(empty($aCommodity) || !is_array($aCommodity))
+            return [];
+        
+        $sMethod = '_registerCartItem' . bx_gen_method_name($aCommodity['type']);
+        if(!method_exists($this, $sMethod))
+            return [];
+
+        return $this->$sMethod($iClientId, $iSellerId, $aCommodity, $iItemCount, $sOrder, $sLicense);
+    }
+
+    protected function _registerCartItemProduct($iClientId, $iSellerId, $aCommodity, $iItemCount, $sOrder, $sLicense)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sModule = $this->getName();
+
+    	$aItem = $this->serviceGetCartItem($aCommodity['id']);
+        if(empty($aItem) || !is_array($aItem))
+            return [];
+
+        $aEntry = $this->_oDb->getContentInfoById($aCommodity['entry_id']);
+        if(empty($aEntry) || !is_array($aEntry))
+            return [];
+
+        $iEntryId = (int)$aCommodity['entry_id'];
+        $iEntryQnt = (int)$aEntry[$CNF['FIELD_QUANTITY']];
+        if(($iEntryQnt - $iItemCount) < 0 || !$this->_oDb->registerLicense($iClientId, $iEntryId, $iItemCount, $sOrder, $sLicense))
+            return [];
+
+        $aOffer = $this->_oDb->getOffersBy([
+            'type' => 'content_and_author_ids', 
+            'content_id' => $iEntryId, 
+            'author_id' => $iClientId,
+            'status' => BX_ADS_OFFER_STATUS_ACCEPTED
+        ]);
+
+        if(!empty($aOffer) && is_array($aOffer))
+            $this->_oDb->updateOffer([$CNF['FIELD_OFR_STATUS'] => BX_ADS_OFFER_STATUS_PAID], [$CNF['FIELD_OFR_ID'] => $aOffer[$CNF['FIELD_OFR_ID']]]);
+
+        $iEntryQnt -= $iItemCount;
+        $bEntrySold = $iEntryQnt == 0;
+
+        $aUpdate = [
+            $CNF['FIELD_QUANTITY'] => $iEntryQnt
+        ];
+        if($bEntrySold)
+            $aUpdate = array_merge($aUpdate, [
+                $CNF['FIELD_STATUS'] =>  BX_ADS_STATUS_SOLD,
+                $CNF['FIELD_SOLD'] => time()
+            ]);
+
+        $this->_oDb->updateEntriesBy($aUpdate, [$CNF['FIELD_ID'] => $iEntryId]);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-license_register 'bx_ads', 'license_register' - hook after the payment for an ad was processed with payment processing module
+         * - $unit_name - equals `bx_ads`
+         * - $action - equals `license_register`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `id` - [int] commodity id 
+         *      - `type` - [string] commodity type, 'product' or 'promotion'
+         *      - `product_id` - [int] ad (entry) id the commodity is associated with
+         *      - `profile_id` - [int] client (buyer) profile id
+         *      - `order` - [string] order number provided with payment processing module
+         *      - `license` - [string] license number generated for the order
+         *      - `count` - [int] number of items in order
+         * @hook @ref hook-bx_ads-license_register
+         */
+        bx_alert($sModule, 'license_register', 0, false, [
+            'id' => $aCommodity['id'],
+            'type' => $aCommodity['type'],
+            'product_id' => $iEntryId,
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'license' => $sLicense,
+            'count' => $iItemCount
+        ]);
+
+        if($bEntrySold) {
+            $aParams = $this->_alertParams($aEntry);
+            
+            /**
+             * @hooks
+             * @hookdef hook-bx_ads-sold 'bx_ads', 'sold' - hook after the payment was processed with payment processing module and there is no the ad (entry) for selling anymore
+             * - $unit_name - equals `bx_ads`
+             * - $action - equals `sold`
+             * - $object_id - ad (entry) id
+             * - $sender_id - not used
+             * - $extra_params - array of additional params with the following array keys:
+             *      - `status` - [string] content status
+             *      - `status_admin` - [string] content admin status
+             *      - `privacy_view` - [int] or [string] privacy for view content action, @see BxDolPrivacy
+             *      - `cf` - [int] content's audience filter value
+             * @hook @ref hook-bx_ads-sold
+             */
+            bx_alert($sModule, 'sold', $iEntryId, false, $aParams);
+        }
+
+        $oAuthor = BxDolProfile::getInstanceMagic($aEntry[$CNF['FIELD_AUTHOR']]);
+        $oClient = BxDolProfile::getInstanceMagic($iClientId);
+        $oSeller = BxDolProfile::getInstanceMagic($iSellerId);
+        $sSellerUrl = $oSeller->getUrl();
+        $sSellerName = $oSeller->getDisplayName();
+
+        $sNote = $aEntry[$CNF['FIELD_NOTES_PURCHASED']];
+        if(empty($sNote))
+            $sNote = _t('_bx_ads_txt_purchased_note', $sSellerUrl, $sSellerName);
+
+        $sEmailTemplate = $CNF['ETEMPLATE_PURCHASED'];
+        $aEmailParams = [
+            'client_name' => $oClient->getDisplayName(),
+            'entry_name' => $aEntry[$CNF['FIELD_TITLE']],
+            'entry_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'], ['id' => $iEntryId])),
+            'author_url' => $oAuthor->getUrl(),
+            'author_name' => $oAuthor->getDisplayName(),
+            'vendor_url' => $sSellerUrl,
+            'vendor_name' => $sSellerName,
+            'count' => (int)$iItemCount,
+            'license' => $sLicense,
+            'notes' => $sNote,
+        ];
+
+        $bCancel = false;
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-license_register_notif 'bx_ads', 'license_register_notif' - hook before sending a notification about new license. Allows to override a notification message or cancel the notification
+         * - $unit_name - equals `bx_ads`
+         * - $action - equals `license_register_notif`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `entry_id` - [int] ad (entry) id
+         *      - `order` - [string] order number
+         *      - `recipient_id` - [int] by ref, client (buyer) profile id, can be overridden in hook processing
+         *      - `email_template` - [string] by ref, email template name, can be overridden in hook processing
+         *      - `email_params` - [array] by ref, email params array as key&value pairs, which will be parsed in email subject/body, can be overridden in hook processing
+         *      - `cancel` - [boolean] by ref, if email sending should be canceled or not, can be overridden in hook processing
+         * @hook @ref hook-bx_ads-license_register_notif
+         */
+        bx_alert($sModule, 'license_register_notif', 0, false, [
+            'entry_id' => $iEntryId,
+            'order' => $sOrder,
+
+            'recipient_id' => &$iClientId,
+            'email_template' => &$sEmailTemplate,
+            'email_params' => &$aEmailParams,
+            'cancel' => &$bCancel,
+
+            'recipient_id_ref' => &$iClientId,
+            'email_template_ref' => &$sEmailTemplate,
+            'email_params_ref' => &$aEmailParams,
+            'cancel_ref' => &$bCancel
+        ]);
+
+        if(!$bCancel)
+            sendMailTemplate($sEmailTemplate, 0, $iClientId, $aEmailParams);
+
+        return $aItem;
+    }
+    
+    protected function _registerCartItemPromotion($iClientId, $iSellerId, $aCommodity, $iItemCount, $sOrder, $sLicense)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aItem = $this->serviceGetCartItem($aCommodity['id']);
+        if(empty($aItem) || !is_array($aItem))
+            return [];
+
+        $aEntry = $this->_oDb->getContentInfoById($aCommodity['entry_id']);
+        if(empty($aEntry) || !is_array($aEntry))
+            return [];
+
+        $iEntryId = (int)$aCommodity['entry_id'];
+        if(!$this->_oDb->registerPromotion($aEntry[$CNF['FIELD_AUTHOR']], $aCommodity['id'], $iEntryId, $aCommodity['amount'], $sOrder, $sLicense))
+            return [];
+
+        if($aEntry[$CNF['FIELD_STATUS_ADMIN']] == BX_ADS_STATUS_ADMIN_UNPAID) {
+            $aUpdate[$CNF['FIELD_STATUS_ADMIN']] = BX_BASE_MOD_GENERAL_STATUS_ACTIVE;
+            if(!$this->_isModerator() && !$this->_oConfig->isAutoApproveEnabled())
+                $aUpdate[$CNF['FIELD_STATUS_ADMIN']] = BX_BASE_MOD_GENERAL_STATUS_PENDING;
+            
+            $this->_oDb->updateEntriesBy($aUpdate, [$CNF['FIELD_ID'] => $iEntryId]);
+        }
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-promotion_register 'bx_ads', 'promotion_register' - hook after the payment for promotion was processed with payment processing module
+         * - $unit_name - equals `bx_ads`
+         * - $action - equals `promotion_register`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `id` - [int] commodity id 
+         *      - `type` - [string] commodity type, 'product' or 'promotion'
+         *      - `product_id` - [int] ad (entry) id the commodity is associated with
+         *      - `profile_id` - [int] client (buyer) profile id
+         *      - `amount` - [float] amount paid for the promotion
+         *      - `order` - [string] order number provided with payment processing module
+         *      - `license` - [string] license number generated for the order
+         * @hook @ref hook-bx_ads-promotion_register
+         */
+        bx_alert($this->getName(), 'promotion_register', 0, false, [
+            'id' => $aCommodity['id'],
+            'type' => $aCommodity['type'],
+            'product_id' => $iEntryId,
+            'profile_id' => $iClientId,
+            'amount' => $aCommodity['amount'],
+            'order' => $sOrder,
+            'license' => $sLicense,
+        ]);
+
+        return $aItem;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-authorize_subscription_item authorize_subscription_item
+     * 
+     * @code bx_srv('bx_ads', 'authorize_subscription_item', [...]); @endcode
+     * 
+     * Authorize the order to process a subscription (recurring payment) inside the Ads module in future. Is called with payment processing module after the order for subscription was authorized there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with authorize prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxAdsModule::serviceAuthorizeSubscriptionItem
+     */
+    /** 
+     * @ref bx_ads-authorize_subscription_item "authorize_subscription_item"
+     */
+    public function serviceAuthorizeSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder)
+    {
+        return [];
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-register_subscription_item register_subscription_item
+     * 
+     * @code bx_srv('bx_ads', 'register_subscription_item', [...]); @endcode
+     * 
+     * Register a processed subscription (recurring payment) inside the Ads module. Is called with payment processing module after the subscription was registered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxAdsModule::serviceRegisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_ads-register_subscription_item "register_subscription_item"
+     */
+    public function serviceRegisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return [];
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-reregister_cart_item reregister_cart_item
+     * 
+     * @code bx_srv('bx_ads', 'reregister_cart_item', [...]); @endcode
+     * 
+     * Reregister a single time payment inside the Ads module. Is called with payment processing module after the payment was reregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemIdOld old item ID.
+     * @param $iItemIdNew new item ID.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxAdsModule::serviceReregisterCartItem
+     */
+    /** 
+     * @ref bx_ads-reregister_cart_item "reregister_cart_item"
+     */
+    public function serviceReregisterCartItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+        return [];
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-reregister_subscription_item reregister_subscription_item
+     * 
+     * @code bx_srv('bx_ads', 'reregister_subscription_item', [...]); @endcode
+     * 
+     * Reregister a subscription (recurring payment) inside the Ads module. Is called with payment processing module after the subscription was reregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemIdOld old item ID.
+     * @param $iItemIdNew new item ID.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxAdsModule::serviceReregisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_ads-reregister_subscription_item "reregister_subscription_item"
+     */
+    public function serviceReregisterSubscriptionItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+        return [];
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-unregister_cart_item unregister_cart_item
+     * 
+     * @code bx_srv('bx_ads', 'unregister_cart_item', [...]); @endcode
+     * 
+     * Unregister an earlier processed single time payment inside the Ads module. Is called with payment processing module after the payment was unregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return boolean value determining where the payment was unregistered or not.
+     * 
+     * @see BxAdsModule::serviceUnregisterCartItem
+     */
+    /** 
+     * @ref bx_ads-unregister_cart_item "unregister_cart_item"
+     */
+    public function serviceUnregisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        $aCommodity = $this->_oDb->getCommodity(['sample' => 'id', 'id' => (int)$iItemId]);
+        if(empty($aCommodity) || !is_array($aCommodity))
+            return [];
+        
+        $sMethod = '_unregisterCartItem' . bx_gen_method_name($aCommodity['type']);
+        if(!method_exists($this, $sMethod))
+            return [];
+
+        return $this->$sMethod($iClientId, $iSellerId, $aCommodity, $iItemCount, $sOrder, $sLicense);
+    }
+    
+    protected function _unregisterCartItemProduct($iClientId, $iSellerId, $aCommodity, $iItemCount, $sOrder, $sLicense)
+    {
+        $iEntryId = (int)$aCommodity['entry_id'];
+
+        if(!$this->_oDb->unregisterLicense($iClientId, $iEntryId, $sOrder, $sLicense))
+            return false;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-license_unregister 'bx_ads', 'license_unregister' - hook after the payment for ad was refunded with payment processing module
+         * It's equivalent to @ref hook-bx_ads-license_register
+         * @hook @ref hook-bx_ads-license_unregister
+         */
+        bx_alert($this->getName(), 'license_unregister', 0, false, [
+            'id' => $aCommodity['id'],
+            'type' => $aCommodity['type'],
+            'product_id' => $iEntryId,
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'license' => $sLicense,
+            'count' => $iItemCount
+        ]);
+
+    	return true;
+    }
+
+    protected function _unregisterCartItemPromotion($iClientId, $iSellerId, $aCommodity, $iItemCount, $sOrder, $sLicense)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aEntry = $this->_oDb->getContentInfoById($aCommodity['entry_id']);
+        if(empty($aEntry) || !is_array($aEntry))
+            return false;
+
+        $iEntryId = (int)$aCommodity['entry_id'];
+
+        if(!$this->_oDb->unregisterPromotion($aEntry[$CNF['FIELD_AUTHOR']], $aCommodity['id'], $iEntryId, $sOrder, $sLicense))
+            return false;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-promotion_unregister 'bx_ads', 'promotion_unregister' - hook after the payment for promotion was refunded with payment processing module
+         * It's equivalent to @ref hook-bx_ads-promotion_register
+         * @hook @ref hook-bx_ads-promotion_unregister
+         */
+        bx_alert($this->getName(), 'promotion_unregister', 0, false, [
+            'id' => $aCommodity['id'],
+            'type' => $aCommodity['type'],
+            'product_id' => $iEntryId,
+            'profile_id' => $iClientId,
+            'amount' => $aCommodity['amount'],
+            'order' => $sOrder,
+            'license' => $sLicense
+        ]);
+
+    	return true;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-unregister_subscription_item unregister_subscription_item
+     * 
+     * @code bx_srv('bx_ads', 'unregister_subscription_item', [...]); @endcode
+     * 
+     * Unregister an earlier processed subscription (recurring payment) inside the Ads module. Is called with payment processing module after the subscription was unregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return boolean value determining where the subscription was unregistered or not.
+     * 
+     * @see BxAdsModule::serviceUnregisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_ads-unregister_subscription_item "unregister_subscription_item"
+     */
+    public function serviceUnregisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+    	return true; 
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_ads Ads
+     * @subsection bx_ads-payments Payments
+     * @subsubsection bx_ads-cancel_subscription_item cancel_subscription_item
+     * 
+     * @code bx_srv('bx_ads', 'cancel_subscription_item', [...]); @endcode
+     * 
+     * Cancel an earlier processed subscription (recurring payment) inside the Ads module. Is called with payment processing module after the subscription was canceled there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return boolean value determining where the subscription was canceled or not.
+     * 
+     * @see BxAdsModule::serviceCancelSubscriptionItem
+     */
+    /** 
+     * @ref bx_ads-cancel_subscription_item "cancel_subscription_item"
+     */
+    public function serviceCancelSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder)
+    {
+    	return true;
+    }
+
+    public function serviceGetOffersCount($sStatus = '')
+    {
+        if(!$this->_oConfig->isAuction())
+            return 0;
+
+        if(empty($sStatus) || !in_array($sStatus, $this->_aOfferStatuses))
+            $sStatus = BX_ADS_OFFER_STATUS_AWAITING;
+
+        $iUserId = bx_get_logged_profile_id();
+        return $this->_oDb->getOffersBy(array('type' => 'content_author_id', 'author_id' => $iUserId, 'status' => $sStatus, 'count' => true));
+    }
+
+    public function serviceGetLiveUpdates($sStatus, $aMenuItemParent, $aMenuItemChild, $iCount = 0)
+    {
+        if(!in_array($sStatus, $this->_aOfferStatuses))
+            return false;
+
+        $iUserId = bx_get_logged_profile_id();
+        $iCountNew = $this->_oDb->getOffersBy(array('type' => 'content_author_id', 'author_id' => $iUserId, 'status' => $sStatus, 'count' => true));
+        if($iCountNew == $iCount)
+            return false;
+
+        return array(
+            'count' => $iCountNew, // required
+            'method' => 'bx_menu_show_live_update(oData)', // required
+            'data' => array(
+                'code' => BxDolTemplate::getInstance()->parseHtmlByTemplateName('menu_item_addon', array(
+                    'content' => '{count}'
+                )),
+            'mi_parent' => $aMenuItemParent,
+            'mi_child' => $aMenuItemChild
+            ),  // optional, may have some additional data to be passed in JS method provided using 'method' param above.
+    	);
+    }
+
+    public function serviceOffers()       
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oConfig->isAuction())
+            return '';
+
+        $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_OFFERS_ALL']);
+        if(!$oGrid)
+            return '';
+
+        return $oGrid->getCode();
+    }
+
+    public function serviceEntityOffers($iContentId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oConfig->isAuction())
+            return '';
+
+        $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_OFFERS']);
+        if(!$oGrid)
+            return '';
+
+        if(empty($iContentId) && ($_iContentId = bx_get('id')) !== false)
+            $iContentId = (int)$_iContentId;
+
+        $oGrid->setContentId($iContentId);
+        return $oGrid->getCode();
+    }
+
+    public function serviceEntityOfferAccepted($iContentId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oConfig->isAuction())
+            return '';
+
+        $iUserId = bx_get_logged_profile_id();
+
+        if(empty($iContentId) && ($_iContentId = bx_get('id')) !== false)
+            $iContentId = (int)$_iContentId;
+
+        $aContent = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContent) || !is_array($aContent) || $aContent[$CNF['FIELD_QUANTITY']] <= 0)
+            return '';
+
+        $aOffer = $this->_oDb->getOffersBy(array(
+            'type' => 'content_and_author_ids', 
+            'content_id' => $iContentId, 
+            'author_id' => $iUserId,
+            'status' => BX_ADS_OFFER_STATUS_ACCEPTED
+        ));
+
+        if(empty($aOffer) || !is_array($aOffer))
+            return '';
+
+        return $this->_oTemplate->entryOfferAccepted($iUserId, $aContent, $aOffer);
+    }
+
+    public function serviceIsSourcesAvaliable()
+    {
+        return $this->_oConfig->isSources();
+    }
+    
+    public function serviceGetSources($iAuthorId)
+    {
+        $aSources = $this->_oDb->getSources(['sample' => 'all']);
+        $aOptions = $this->_oDb->getSourcesOptions($iAuthorId);
+
+        $aResult = [];
+        foreach($aSources as $sSource => $aSource) {
+            if(!isset($aOptions[$aSource['option_prefix'] . 'active']) || (int)$aOptions[$aSource['option_prefix'] . 'active']['value'] != 1) 
+                continue;
+
+            foreach($aOptions as $sName => $aOption)
+                if(strpos($sName, $aSource['option_prefix']) !== false)
+                    $aSource['options'][$sName] = $aOption;
+            $aResult[$sSource] = $aSource;
+        }
+
+        return $aResult; 
+    }
+
+    public function serviceGetSource($iAuthorId, $sAuthorSource = '')
+    {
+    	$aSourcesOptions = $this->serviceGetSources($iAuthorId);
+        if(empty($aSourcesOptions) || !is_array($aSourcesOptions))
+            return false;
+
+        $mixedResult = false;
+        if(!empty($sAuthorSource)) {
+            if(!empty($aSourcesOptions[$sAuthorSource]) && is_array($aSourcesOptions[$sAuthorSource]))
+                $mixedResult = $aSourcesOptions[$sAuthorSource];
+        }
+        else 
+            $mixedResult = array_shift($aSourcesOptions);
+
+    	return $mixedResult;
+    }
+
+    public function serviceLoadEntriesFromSourceByTerm($sSourceType, $sTerm)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oSource = $this->getObjectSource($sSourceType, $this->_iProfileId);
+        if(!$oSource)
+            return [];
+
+        $aEntries = $oSource->getEntries(['sample' => 'all_id_title_pairs']);
+        if(empty($aEntries) || !is_array($aEntries))
+            return [];
+
+        $aResults = []; 
+        foreach($aEntries as $aEntry) {
+            if(stripos($aEntry[$CNF['FIELD_TITLE']], $sTerm) === false)
+                continue;
+
+            $aResults[] = [
+                'label' => $aEntry[$CNF['FIELD_TITLE']], 
+                'value' => $aEntry[$CNF['FIELD_SOURCE']], 
+            ];
+        }
+
+        return $aResults; 
+    }
+
+    public function serviceLoadEntryFromSource($sSourceType, $sSource)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResult = [];
+        $oSource = $this->getObjectSource($sSourceType, $this->_iProfileId);
+        if(!$oSource)
+            return $aResult;
+
+        $aEntry = $oSource->getEntry($sSource);
+        if(empty($aEntry) || !is_array($aEntry))
+            return $aResult;
+
+        $aName = $this->serviceCheckName($aEntry[$CNF['FIELD_TITLE']]);
+        if(!empty($aName) && is_array($aName))
+            $aResult[$CNF['FIELD_NAME']] = ['type' => 'text', 'value' => $aName['name']];
+
+        $aKey2Type = [
+            $CNF['FIELD_TITLE'] => 'text',
+            $CNF['FIELD_URL'] => 'hidden',
+            $CNF['FIELD_TEXT'] => 'text_html',
+            $CNF['FIELD_TAGS'] => 'text',
+            $CNF['FIELD_PRICE'] => 'text', 
+            $CNF['FIELD_QUANTITY'] => 'text'
+        ];
+        foreach($aEntry as $sKey => $sValue)
+            if(isset($aKey2Type[$sKey]))
+                $aResult[$sKey] = ['type' => $aKey2Type[$sKey], 'value' => $sValue];
+
+        return $aResult;
+    }
+
+    public function serviceBlockSourcesDetails($iProfileId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!isLogged())
+             return bx_is_api() ? [bx_api_get_msg(_t('_Access denied'))] : ['content' => MsgBox(_t('_Access denied'))];
+
+        $iProfileId = !empty($iProfileId) ? $iProfileId : bx_get_logged_profile_id();
+
+        $oForm = BxTemplFormView::getObjectInstance($CNF['OBJECT_FORM_SOURCES_DETAILS'], $CNF['OBJECT_FORM_SOURCES_DETAILS_DISPLAY_EDIT']);
+        $oForm->setProfileId($iProfileId);
+        $oForm->initChecker();
+
+        if($oForm->isSubmitted()) {
+            if($oForm->isValid()) {
+                $aOptions = $this->_oDb->getSourcesOptions();
+                foreach($aOptions as $aOption) {
+                    $sValue = bx_get($aOption['name']) !== false ? bx_get($aOption['name']) : '';
+                    $this->_oDb->updateSourceOption($iProfileId, $aOption['id'], bx_process_input($sValue));
+                }
+                if (bx_is_api()){
+                    return [
+                        bx_api_get_block('form', $oForm->getCodeAPI(), ['ext' => ['name' => $this->getName(), 'request' => ['url' => '/api.php?r=' . $this->getName() . '/block_sources_details', 'immutable' => true]]]),
+                        bx_api_get_block('redirect', ['uri' => '/' . BxDolPermalinks::getInstance()->permalink($CNF['URL_SOURCES']), 'timeout' => 100])
+                        ];
+                }
+                else{
+                    header('Location: ' . bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_SOURCES'])));
+                    return;
+                }
+            }
+            else
+                foreach($oForm->aInputs as $aInput)
+                    if(!empty($aInput['error']) && !empty($aInput['attrs']['bx-data-source'])) {
+                        $sSourceBlock = 'source_' . (int)$aInput['attrs']['bx-data-source'] . '_begin';
+                        if(!empty($oForm->aInputs[$sSourceBlock]))
+                            $oForm->aInputs[$sSourceBlock]['collapsed'] = false;
+                    }
+        }
+
+        return bx_is_api() ? [bx_api_get_block('form', $oForm->getCodeAPI(), ['ext' => ['name' => $this->getName(), 'request' => ['url' => '/api.php?r=' . $this->getName() . '/block_sources_details', 'immutable' => true]]])] : ['content' => $oForm->getCode()];
+        
+    }
+
+    public function isEntryActive($aContentInfo)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iViewer = bx_get_logged_profile_id();
+        $bModerator = $this->_isModerator();
+
+        $mixedResult = null;
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-is_entry_active 'bx_ads', 'is_entry_active' - hook to override whether an ad (entry) active or not
+         * It's equivalent to @ref hook-bx_base_general-is_entry_active
+         * @hook @ref hook-bx_ads-is_entry_active
+         */
+        bx_alert($this->getName(), 'is_entry_active', 0, 0, [
+            'viewer_id' => $iViewer, 
+            'is_moderator' => $bModerator, 
+            'content_info' => $aContentInfo, 
+            'override_result' => &$mixedResult
+        ]);
+        if($mixedResult !== null)
+            return $mixedResult;
+
+        if($aContentInfo[$CNF['FIELD_AUTHOR']] == $iViewer || $bModerator)
+            return true;
+
+        if(isset($CNF['FIELD_STATUS']) && !in_array($aContentInfo[$CNF['FIELD_STATUS']], array(BX_BASE_MOD_TEXT_STATUS_ACTIVE, BX_ADS_STATUS_OFFER, BX_ADS_STATUS_SOLD)))
+            return false;
+
+        if(isset($CNF['FIELD_STATUS_ADMIN']) && $aContentInfo[$CNF['FIELD_STATUS_ADMIN']] != 'active')
+            return false;
+
+        return true;        
+    }
+
+    public function isAuction($aContentInfo)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->_oConfig->isAuction() && (int)$aContentInfo[$CNF['FIELD_AUCTION']] != 0;
+    }
+
+    public function isAllowedMakeOffer($mixedContent, $isPerformAction = false)
+    {
+        return $this->checkAllowedMakeOffer($mixedContent, $isPerformAction) === CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkAllowedMakeOffer($mixedContent, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sTxtError = '_sys_txt_access_denied';
+
+        $iProfileId = bx_get_logged_profile_id();
+
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+
+        if(empty($mixedContent) || !is_array($mixedContent))
+            return _t($sTxtError);
+
+        if(!$this->isAuction($mixedContent) || (int)$mixedContent[$CNF['FIELD_QUANTITY']] <= 0)
+            return _t($sTxtError);
+
+        if($mixedContent[$CNF['FIELD_AUTHOR']] == $iProfileId)
+            return _t($sTxtError);
+
+        $aCheck = checkActionModule($iProfileId, 'make offer', $this->getName(), $isPerformAction);
+        if($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function isAllowedViewOffers($mixedContent, $isPerformAction = false)
+    {
+        return $this->checkAllowedViewOffers($mixedContent, $isPerformAction) === CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkAllowedViewOffers($mixedContent, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sTxtError = '_sys_txt_access_denied';
+
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+
+        if(empty($mixedContent) || !is_array($mixedContent))
+            return _t($sTxtError);
+
+        if(!$this->isAuction($mixedContent))
+            return _t($sTxtError);
+
+        if($this->_isModerator())
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        if($mixedContent[$CNF['FIELD_AUTHOR']] == bx_get_logged_profile_id())
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        return _t($sTxtError);
+    }
+
+    public function isAllowedMarkShipped($mixedContent, $isPerformAction = false)
+    {
+        return $this->checkAllowedMarkShipped($mixedContent, $isPerformAction) === CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkAllowedMarkShipped($mixedContent, $isPerformAction = false)
+    {
+        return $this->checkAllowedMarkShippedForProfile($mixedContent, $isPerformAction);
+    }
+
+    public function checkAllowedMarkShippedForProfile($mixedContent, $isPerformAction = false, $iProfileId = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sTxtError = '_sys_txt_access_denied';
+
+        if(!$iProfileId)
+            $iProfileId = bx_get_logged_profile_id();
+                
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+
+        if(empty($mixedContent) || !is_array($mixedContent) || !$this->isSingle($mixedContent))
+            return _t($sTxtError);
+
+        if((int)$mixedContent[$CNF['FIELD_SHIPPED']] != 0 || $mixedContent[$CNF['FIELD_STATUS']] != BX_ADS_STATUS_SOLD || (!$this->isEntryAuthor($mixedContent, $iProfileId) && !$this->_isModerator() && !$this->_isAdministrator()))
+            return _t($sTxtError);
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function isAllowedMarkReceived($mixedContent, $isPerformAction = false)
+    {
+        return $this->checkAllowedMarkReceived($mixedContent, $isPerformAction) === CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkAllowedMarkReceived($mixedContent, $isPerformAction = false)
+    {
+        return $this->checkAllowedMarkReceivedForProfile($mixedContent, $isPerformAction);
+    }
+
+    public function checkAllowedMarkReceivedForProfile($mixedContent, $isPerformAction = false, $iProfileId = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sTxtError = '_sys_txt_access_denied';
+
+        if(!$iProfileId)
+            $iProfileId = bx_get_logged_profile_id();
+
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+
+        if(empty($mixedContent) || !is_array($mixedContent) || !$this->isSingle($mixedContent))
+            return _t($sTxtError);
+
+        if((int)$mixedContent[$CNF['FIELD_RECEIVED']] != 0 || !$this->_oDb->hasLicense($iProfileId, $mixedContent[$CNF['FIELD_ID']]))
+            return _t($sTxtError);
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function onPublished($iContentId)
+    {
+        $this->serviceUpdateCategoriesStats($iContentId);
+
+        parent::onPublished($iContentId);
+    }
+
+    public function onOfferAdded($iOfferId, &$aResult)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aOfferInfo = $this->_oDb->getOffersBy(array('type' => 'id', 'id' => $iOfferId));
+        if(empty($aOfferInfo) || !is_array($aOfferInfo))
+            return;
+
+        $iContentId = (int)$aOfferInfo[$CNF['FIELD_OFR_CONTENT']];
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return;
+
+        $iViewer = bx_get_logged_profile_id();        
+        $oViewer = BxDolProfile::getInstanceMagic($iViewer);
+
+        if(getParam($CNF['PARAM_USE_IIN']) == 'on')
+            sendMailTemplate($CNF['ETEMPLATE_OFFER_ADDED'], 0, $aContentInfo[$CNF['FIELD_AUTHOR']], array(
+                'viewer_name' => $oViewer->getDisplayName(),
+                'viewer_url' => $oViewer->getUrl(),
+                'entry_name' => $aContentInfo[$CNF['FIELD_TITLE']],
+                'entry_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', array(
+                    'i' => $CNF['URI_VIEW_ENTRY'], 
+                    $CNF['FIELD_ID'] => $iContentId
+                )))
+            ));
+
+        $aParams = $this->_alertParamsOffer($aContentInfo, $aOfferInfo);
+        $aParams['override_result'] = &$aResult;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-offer_added 'bx_ads', 'offer_added' - hook after an offer was added. Allows to override action results.
+         * - $unit_name - equals `bx_ads`
+         * - $action - equals `offer_added`
+         * - $object_id - offer id
+         * - $sender_id - offer author profile id
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `object_id` - [int] content id
+         *      - `object_author_id` - [int] content author profile id
+         *      - `offer_id` - [int] offer id
+         *      - `offer_author_id` - [int] offer author profile id
+         *      - `privacy_view` - [int] privacy for view action, equals to BX_DOL_PG_ALL, @see BxDolPrivacy
+         *      - `override_result` - [array] by ref, results array as key&value pairs, can be overridden in hook processing
+         * @hook @ref hook-bx_ads-offer_added
+         */
+        bx_alert($this->getName(), 'offer_added', $iOfferId, $aOfferInfo[$CNF['FIELD_OFR_AUTHOR']], $aParams);
+    }
+
+    public function onOfferAccepted($iOfferId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aOfferInfo = $this->_oDb->getOffersBy(array('type' => 'id', 'id' => $iOfferId));
+        if(empty($aOfferInfo) || !is_array($aOfferInfo))
+            return;
+
+        $iContentId = (int)$aOfferInfo[$CNF['FIELD_OFR_CONTENT']];
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return;
+
+        if($aContentInfo[$CNF['FIELD_STATUS']] != BX_ADS_STATUS_OFFER && ($this->isSingle($aContentInfo) || $this->getAvailableQuantity($aContentInfo) <= 0))
+            if(!$this->_oDb->updateEntriesBy([$CNF['FIELD_STATUS'] => BX_ADS_STATUS_OFFER], [$CNF['FIELD_ID'] => $iContentId]))
+                return;
+
+        $this->serviceUpdateCategoriesStats($iContentId);
+
+        $iOfferer = (int)$aOfferInfo[$CNF['FIELD_OFR_AUTHOR']];
+        $oOfferer = BxDolProfile::getInstanceMagic($iOfferer);
+
+        if(getParam($CNF['PARAM_USE_IIN']) == 'on')
+            sendMailTemplate($CNF['ETEMPLATE_OFFER_ACCEPTED'], 0, $iOfferer, array(
+                'offerer_name' => $oOfferer->getDisplayName(),
+                'offerer_url' => $oOfferer->getUrl(),
+                'entry_name' => $aContentInfo[$CNF['FIELD_TITLE']],
+                'entry_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', array(
+                    'i' => $CNF['URI_VIEW_ENTRY'], 
+                    $CNF['FIELD_ID'] => $iContentId
+                )))
+            ));
+
+        $aParams = $this->_alertParamsOffer($aContentInfo, $aOfferInfo);
+        
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-offer_accepted 'bx_ads', 'offer_accepted' - hook after an offer was accepted.
+         * It's equivalent to @ref hook-bx_ads-offer_added
+         * except content author profile id is passed to $sender_id and
+         * `override_result` parameter in $extra_params is missing
+         * @hook @ref hook-bx_ads-offer_accepted
+         */
+        bx_alert($this->getName(), 'offer_accepted', $iOfferId, $aContentInfo[$CNF['FIELD_AUTHOR']], $aParams);
+    }
+
+    public function onOfferDeclined($iOfferId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aOfferInfo = $this->_oDb->getOffersBy(array('type' => 'id', 'id' => $iOfferId));
+        if(empty($aOfferInfo) || !is_array($aOfferInfo))
+            return;
+
+        $iContentId = (int)$aOfferInfo[$CNF['FIELD_OFR_CONTENT']];
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return;
+
+        $iOfferer = (int)$aOfferInfo[$CNF['FIELD_OFR_AUTHOR']];
+        $oOfferer = BxDolProfile::getInstanceMagic($iOfferer);
+
+        if(getParam($CNF['PARAM_USE_IIN']) == 'on')
+            sendMailTemplate($CNF['ETEMPLATE_OFFER_DECLINED'], 0, $iOfferer, array(
+                'offerer_name' => $oOfferer->getDisplayName(),
+                'offerer_url' => $oOfferer->getUrl(),
+                'entry_name' => $aContentInfo[$CNF['FIELD_TITLE']],
+                'entry_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', array(
+                    'i' => $CNF['URI_VIEW_ENTRY'], 
+                    $CNF['FIELD_ID'] => $iContentId
+                )))
+            ));
+
+        $aParams = $this->_alertParamsOffer($aContentInfo, $aOfferInfo);
+        
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-offer_declined 'bx_ads', 'offer_declined' - hook after an offer was declined.
+         * It's equivalent to @ref hook-bx_ads-offer_accepted
+         * @hook @ref hook-bx_ads-offer_declined
+         */
+        bx_alert($this->getName(), 'offer_declined', $iOfferId, $aContentInfo[$CNF['FIELD_AUTHOR']], $aParams);
+    }
+
+    public function onOfferCanceled($iOfferId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aOfferInfo = $this->_oDb->getOffersBy(['type' => 'id', 'id' => $iOfferId]);
+        if(empty($aOfferInfo) || !is_array($aOfferInfo))
+            return;
+
+        $iContentId = (int)$aOfferInfo[$CNF['FIELD_OFR_CONTENT']];
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return;
+
+        if(!$this->_oDb->updateEntriesBy([$CNF['FIELD_STATUS'] => BX_BASE_MOD_TEXT_STATUS_ACTIVE], [$CNF['FIELD_ID'] => $iContentId]))
+            return;
+
+        $this->serviceUpdateCategoriesStats($iContentId);
+
+        $iOfferer = (int)$aOfferInfo[$CNF['FIELD_OFR_AUTHOR']];
+        $oOfferer = BxDolProfile::getInstanceMagic($iOfferer);
+
+        if(getParam($CNF['PARAM_USE_IIN']) == 'on')
+            sendMailTemplate($CNF['ETEMPLATE_OFFER_CANCELED'], 0, (int)$aContentInfo[$CNF['FIELD_AUTHOR']], [
+                'offerer_name' => $oOfferer->getDisplayName(),
+                'offerer_url' => $oOfferer->getUrl(),
+                'entry_name' => $aContentInfo[$CNF['FIELD_TITLE']],
+                'entry_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', [
+                    'i' => $CNF['URI_VIEW_ENTRY'], 
+                    $CNF['FIELD_ID'] => $iContentId
+                ]))
+            ]);
+
+        $aParams = $this->_alertParamsOffer($aContentInfo, $aOfferInfo);
+        
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-offer_canceled 'bx_ads', 'offer_canceled' - hook after an offer was declined.
+         * It's equivalent to @ref hook-bx_ads-offer_added
+         * except `override_result` parameter in $extra_params is missing
+         * @hook @ref hook-bx_ads-offer_canceled
+         */
+        bx_alert($this->getName(), 'offer_canceled', $iOfferId, $aOfferInfo[$CNF['FIELD_OFR_AUTHOR']], $aParams);
+    }
+
+    /**
+     * Common methods.
+     */
+    public function onShipped($mixedContent, $iProfileSrc = 0, $iProfileDst = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+        $iContentId = (int)$mixedContent[$CNF['FIELD_ID']];
+
+        if(empty($iProfileSrc))
+            $iProfileSrc = (int)$mixedContent[$CNF['FIELD_AUTHOR']];
+
+        $oProfileSrc = BxDolProfile::getInstanceMagic($iProfileSrc);
+
+        $aOffer = $this->_oDb->getOffersBy(array('type' => 'accepted', 'content_id' => $iContentId));
+        $bOffer = !empty($aOffer) && is_array($aOffer);
+
+        if(empty($iProfileDst) && $bOffer)
+            $iProfileDst = (int)$aOffer[$CNF['FIELD_OFR_AUTHOR']];        
+
+        if(!empty($oProfileSrc) && !empty($iProfileDst) && getParam($CNF['PARAM_USE_IIN']) == 'on')
+            sendMailTemplate($CNF['ETEMPLATE_SHIPPED'], 0, $iProfileDst, array(
+                'vendor_name' => $oProfileSrc->getDisplayName(),
+                'vendor_url' => $oProfileSrc->getUrl(),
+                'entry_name' => $mixedContent[$CNF['FIELD_TITLE']],
+                'entry_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', array(
+                    'i' => $CNF['URI_VIEW_ENTRY'], 
+                    $CNF['FIELD_ID'] => $iContentId
+                )))
+            ));
+
+        $aParams = $this->_alertParams($mixedContent);
+        $aParams = array_merge($aParams, [
+            'profile_src' => $iProfileSrc,
+            'profile_dst' => $iProfileDst,
+            'offer_id' => $bOffer ? (int)$aOffer[$CNF['FIELD_OFR_ID']] : 0,
+        ]);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-shipped 'bx_ads', 'shipped' - hook after the ad (entry) was marked as shipped
+         * - $unit_name - equals `bx_ads`
+         * - $action - equals `shipped`
+         * - $object_id - ad (entry) id
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `status` - [string] content status
+         *      - `status_admin` - [string] content admin status
+         *      - `privacy_view` - [int] or [string] privacy for view content action, @see BxDolPrivacy
+         *      - `cf` - [int] content's audience filter value
+         *      - `profile_src` - [int] sender profile id
+         *      - `profile_dst` - [int] recipient profile id
+         *      - `offer_id` - [int] offer id if the ad was sold by offer
+         * @hook @ref hook-bx_ads-shipped
+         */
+        bx_alert($this->getName(), 'shipped', $iContentId, false, $aParams);
+    }
+
+    public function onReceived($mixedContent, $iProfileSrc = 0, $iProfileDst = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+        $iContentId = (int)$mixedContent[$CNF['FIELD_ID']];
+
+        if(empty($iProfileDst))
+            $iProfileDst = (int)$mixedContent[$CNF['FIELD_AUTHOR']];
+
+        $aOffer = $this->_oDb->getOffersBy(array('type' => 'accepted', 'content_id' => $iContentId));
+        $bOffer = !empty($aOffer) && is_array($aOffer);
+
+        if(empty($iProfileSrc) && $bOffer)
+            $iProfileSrc = (int)$aOffer[$CNF['FIELD_OFR_AUTHOR']];
+        $oProfileSrc = BxDolProfile::getInstanceMagic($iProfileSrc);
+
+        if(!empty($iProfileSrc) && !empty($iProfileDst) && getParam($CNF['PARAM_USE_IIN']) == 'on')
+            sendMailTemplate($CNF['ETEMPLATE_RECEIVED'], 0, $iProfileDst, array(
+                'client_name' => $oProfileSrc->getDisplayName(),
+                'client_url' => $oProfileSrc->getUrl(),
+                'entry_name' => $mixedContent[$CNF['FIELD_TITLE']],
+                'entry_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', array(
+                    'i' => $CNF['URI_VIEW_ENTRY'], 
+                    $CNF['FIELD_ID'] => $iContentId
+                )))
+            ));
+
+        $aParams = $this->_alertParams($mixedContent);
+        $aParams = array_merge($aParams, [
+            'profile_src' => $iProfileSrc,
+            'profile_dst' => $iProfileDst,
+            'offer_id' => $bOffer ? (int)$aOffer[$CNF['FIELD_OFR_ID']] : 0,
+        ]);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_ads-received 'bx_ads', 'received' - hook after the ad (entry) was marked as received
+         * It's equivalent to @ref hook-bx_ads-shipped
+         * @hook @ref hook-bx_ads-received
+         */
+        bx_alert($this->getName(), 'received', $iContentId, false, $aParams);
+    }
+
+    public function getObjectSource($sSource, $iProfileId = 0)
+    {
+        $aSource = $this->_oDb->getSources(array('sample' => 'by_name', 'name' => $sSource));
+        if(empty($aSource) || !is_array($aSource) || empty($aSource['class_name']))
+            return false;
+
+        if(!empty($iProfileId)) {
+            $aSource['author'] = (int)$iProfileId;
+            $aSource['options'] = $this->_oDb->getSourcesOptions((int)$iProfileId, $aSource['id']);
+        }
+
+        $sClassPath = !empty($aSource['class_file']) ? BX_DIRECTORY_PATH_ROOT . $aSource['class_file'] : $this->_oConfig->getClassPath() . $aSource['class_name'] . '.php';
+        if(!file_exists($sClassPath))
+            return false;
+
+        require_once($sClassPath);
+        return new $aSource['class_name']($this->_iProfileId, $aSource, $this);
+    }
+
+    public function offerAccept($iId) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aOfferInfo = $this->_oDb->getOffersBy(['type' => 'id', 'id' => $iId]);
+        if(empty($aOfferInfo) || !is_array($aOfferInfo))
+            return false;
+
+        $iContentId = (int)$aOfferInfo[$CNF['FIELD_OFR_CONTENT']];
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return false;
+
+        $iQuantity = (int)$aOfferInfo[$CNF['FIELD_OFR_QUANTITY']];
+        $iQuantityAvailable = $this->getAvailableQuantity($aContentInfo);
+        if($iQuantity > $iQuantityAvailable)
+            return _t('_bx_ads_txt_err_offer_wrong_quantity', $iQuantityAvailable);
+
+        if(!$this->_oDb->updateOffer([$CNF['FIELD_OFR_STATUS'] => BX_ADS_OFFER_STATUS_ACCEPTED], [$CNF['FIELD_OFR_ID'] => $iId]))
+            return false;
+
+        $this->onOfferAccepted($iId);
+        return true;
+    }
+
+    public function offerDecline($iId) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oDb->updateOffer([$CNF['FIELD_OFR_STATUS'] => BX_ADS_OFFER_STATUS_DECLINED], [$CNF['FIELD_OFR_ID'] => $iId]))
+            return false;
+
+        $this->onOfferDeclined($iId);
+        return true;        
+    }
+
+    public function offerCancel($iId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oDb->updateOffer([$CNF['FIELD_OFR_STATUS'] => BX_ADS_OFFER_STATUS_CANCELED], [$CNF['FIELD_OFR_ID'] => $iId]))
+            return false;
+
+        $this->onOfferCanceled($iId);
+        return true;
+    }
+    
+    public function offerPay($iId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aOffer = $this->_oDb->getOffersBy(array('type' => 'id', 'id' => $iId));
+        if(empty($aOffer) || !is_array($aOffer))
+            return false;
+
+        $iContent = (int)$aOffer[$CNF['FIELD_OFR_CONTENT']];
+        $aContent = $this->_oDb->getContentInfoById($iContent);
+        $iContentAuthor = (int)$aContent[$CNF['FIELD_AUTHOR']];
+
+        $aReturn = array(
+            'redirect' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php', array(
+                'i' => $CNF['URI_VIEW_ENTRY'], 
+                $CNF['FIELD_ID'] => $aOffer[$CNF['FIELD_OFR_CONTENT']]
+            )))
+        );
+
+        $oPayments = BxDolPayments::getInstance();
+        $aResult = $oPayments->addToCart($iContentAuthor, $this->getName(), $iContent, $aOffer[$CNF['FIELD_OFR_QUANTITY']]);
+        if(!empty($aResult) && is_array($aResult)) {
+            if(!empty($aResult['message']))
+                $aReturn['msg'] = $aResult['message'];
+
+            if(isset($aResult['code']) && (int)$aResult['code'] == 0)
+                $aReturn['redirect'] = $oPayments->getCartUrl($iContentAuthor);
+        }
+
+        return $aReturn;
+    }
+
+    public function processMetasAdd($iContentId)
+    {
+        if(!parent::processMetasAdd($iContentId))
+            return false;
+
+        $CNF = &$this->_oConfig->CNF;
+
+        $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+
+        $aCategoryTypes = $this->_oDb->getCategoryTypes(array('type' => 'all'));
+        foreach($aCategoryTypes as $aCategoryType)
+            $oMetatags->metaAddAuto($iContentId, $aContentInfo, $CNF, $aCategoryType['display_add']);
+
+        return true;
+    }
+
+    public function processMetasEdit($iContentId, $oForm)
+    {
+        if(!parent::processMetasEdit($iContentId, $oForm))
+            return false;
+        
+        $CNF = &$this->_oConfig->CNF;
+
+        $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        
+        $aCategoryTypes = $this->_oDb->getCategoryTypes(array('type' => 'all'));
+        foreach($aCategoryTypes as $aCategoryType)
+            $oMetatags->metaAddAuto($iContentId, $aContentInfo, $CNF, $aCategoryType['display_edit']);
+
+        return true;
+    }
+
+    public function getCategoryDisplay($sDisplayType, $iCategory = 0)
+    {
+        if(empty($iCategory) && bx_get('category') !== false)
+            $iCategory = (int)bx_get('category');
+
+        if(empty($iCategory))
+            return false;
+
+        $aCategory = $this->_oDb->getCategories(array('type' => 'id_full', 'id' => $iCategory));
+
+        $sKey = 'type_display_' . $sDisplayType;
+        if(empty($aCategory[$sKey]))
+            return false;
+
+        return $aCategory[$sKey];
+    }
+
+    public function isSingle($mixedContent)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+
+        return (int)$mixedContent[$CNF['FIELD_SINGLE']] == 1;
+    }
+
+    public function getAvailableQuantity($mixedContent)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!is_array($mixedContent))
+            $mixedContent = $this->_oDb->getContentInfoById((int)$mixedContent);
+
+        if(!$this->_oConfig->isAuction() || (int)$mixedContent[$CNF['FIELD_AUCTION']] == 0)
+            return (int)$mixedContent[$CNF['FIELD_QUANTITY']];
+
+        $iQuantity = (int)$this->_oDb->getOffersBy([
+            'type' => 'quantity_reserved', 
+            'content_id' => $mixedContent[$CNF['FIELD_ID']], 
+            'status' => BX_ADS_OFFER_STATUS_ACCEPTED
+        ]);
+
+        return (int)$mixedContent[$CNF['FIELD_QUANTITY']] - $iQuantity;
+    }
+
+
+    /**
+     * Internal methods.
+     */
+    protected function _actionMarkAs($sAction)
+    {
+        $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(($mixedResult = $this->serviceMarkAs($sAction, $iContentId)) !== true)
+            return array('msg' => $mixedResult); 
+
+        return array(
+            'reload' => 1
+        );
+    }
+
+    protected function _actionChangeStatus($sStatus)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+
+        if(($mixedResult = $this->checkAllowedEdit($aContentInfo)) !== CHECK_ACTION_RESULT_ALLOWED)
+            return array('msg' => $mixedResult);
+
+        if(!$this->_oDb->updateEntriesBy(array($CNF['FIELD_STATUS'] => $sStatus), array($CNF['FIELD_ID'] => $iContentId)))
+            return array('msg' => _t('_bx_ads_txt_err_cannot_perform_action'));
+
+        $this->serviceUpdateCategoriesStats($aContentInfo);
+
+        return array(
+            'reload' => 1
+        );
+    }
+
+    protected function _serviceEntityForm ($sFormMethod, $iContentId = 0, $sDisplay = false, $sCheckFunction = false, $bErrorMsg = true)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $mixedContent = $this->_getContent($iContentId, true);
+        if($mixedContent === false)
+            return false;
+
+        list($iContentId, $aContentInfo) = $mixedContent;
+
+        $sDisplayType = false;
+        switch($sFormMethod) {
+            case 'editDataForm':
+                if($sDisplay === false)
+                    $sDisplayType = 'edit';
+                break;
+            case 'viewDataForm':
+            case 'viewDataEntry':
+                $sDisplayType = 'view';
+                break;
+        }
+
+        if($sDisplayType !== false && ($sDisplayNew = $this->getCategoryDisplay($sDisplayType, $aContentInfo[$CNF['FIELD_CATEGORY']])) !== false)
+            $sDisplay = $sDisplayNew;
+
+        return parent::_serviceEntityForm ($sFormMethod, $iContentId, $sDisplay, $sCheckFunction, $bErrorMsg);
+    }
+
+    protected function _getCategoryOptions($iParentId, &$aValues)
+    {
+        $aCategories = $this->_oDb->getCategories(array('type' => 'parent_id', 'parent_id' => $iParentId, 'active' => true));
+        foreach($aCategories as $aCategory) {
+            $aValues[] = array('key' => $aCategory['id'], 'value' => str_repeat('--', (int)$aCategory['level']) . ' ' . _t($aCategory['title']));
+
+            $this->_getCategoryOptions($aCategory['id'], $aValues);
+        }
+    }
+
+    protected function _getSearchableFields($mixedDisplayType = '')
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($mixedDisplayType))
+            $mixedDisplayType = array('add', 'edit');
+
+        $aResult = array();
+        $aDisplays = $this->_oDb->getDisplays($this->_oConfig->getName() . '_entry', $mixedDisplayType);
+        foreach($aDisplays as $aDisplay) {
+            if($aDisplay['display_name'] == $CNF['OBJECT_FORM_ENTRY_DISPLAY_ADD'])
+                continue;
+
+            $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_ENTRY'], $aDisplay['display_name'], $this->_oTemplate);
+            if(!$oForm)
+                continue;
+
+            $aResult = array_merge($aResult, $oForm->aInputs);
+        }
+
+        return $aResult;
+    }
+    
+    protected function _getContentForTimelinePost($aEvent, $aContentInfo, $aBrowseParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $bDynamic = isset($aBrowseParams['dynamic_mode']) && (bool)$aBrowseParams['dynamic_mode'] === true;
+
+        $sCategory = '';
+        if(!empty($CNF['FIELD_CATEGORY']) && !empty($aContentInfo[$CNF['FIELD_CATEGORY']])) {
+            $iCategory = (int)$aContentInfo[$CNF['FIELD_CATEGORY']];
+            $aCategory = $this->_oDb->getCategories(array('type' => 'id', 'id' => $iCategory));
+            $sCategory = _t($aCategory['title']);
+            $sCategoryLink = bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_CATEGORIES'], array($CNF['GET_PARAM_CATEGORY'] => $iCategory)));
+        }
+
+        $sPrice = _t('_bx_ads_txt_free');
+        if(!empty($CNF['FIELD_PRICE']) && !empty($aContentInfo[$CNF['FIELD_PRICE']]))
+            $sPrice = _t_format_currency_ext((float)$aContentInfo[$CNF['FIELD_PRICE']], [
+                'sign' => BxDolPayments::getInstance()->getCurrencySign((int)$aContentInfo[$CNF['FIELD_AUTHOR']])
+            ]);
+
+        $aResult = parent::_getContentForTimelinePost($aEvent, $aContentInfo, $aBrowseParams);
+
+        if(!$this->_bIsApi) {
+            if(empty($aResult['raw']))
+                $aResult['raw'] = '';
+
+            $aResult['text'] = $this->_oTemplate->parseHtmlByName('timeline_post_text.html', [
+                'category_link' => $sCategoryLink,
+                'category_title' => $sCategory,
+                'category_title_attr' => bx_html_attribute($sCategory),
+                'price' => $sPrice,
+                'text' => $aResult['text']
+            ]);
+        }
+        else
+            $aResult = array_merge($aResult, [
+                'price' => $sPrice,
+                'category_title' => $sCategory,
+                'register_click' => $this->_oConfig->isPromotion() ? 'bx_ads/register_click&params[]=' . $aContentInfo[$CNF['FIELD_ID']] : false,
+                'register_impression' => $this->_oConfig->isPromotion() ? 'bx_ads/register_impression&params[]=' . $aContentInfo[$CNF['FIELD_ID']] : false,
+            ]);
+
+        if($this->_oConfig->isSources() && !empty($aContentInfo[$CNF['FIELD_URL']]))
+            $aResult['url'] = $aContentInfo[$CNF['FIELD_URL']];
+
+        if($this->_bIsApi)
+            return $aResult;
+
+        if($this->_oConfig->isPromotion()) {
+            $sJsObject = $this->_oConfig->getJsObject('main');
+            $sHtmlId = $this->_oConfig->getHtmlIds('unit') . $aContentInfo[$CNF['FIELD_ID']] . '-' . time() . '-' . mt_rand(0, 100);
+
+            $aResult['onclick'] = 'return ' . $sJsObject . '.registerClick(this, ' . $aContentInfo[$CNF['FIELD_ID']] . ')';
+            $aResult['raw'] .= $this->_oTemplate->parseHtmlByName('timeline_post_promotion.html', [
+                'html_id' => $sHtmlId,
+                'js_code' => $this->_oTemplate->_wrapInTagJsCode($sJsObject . '.registerTrakerForTimeline(' . $aContentInfo[$CNF['FIELD_ID']] . ', \'' . $sHtmlId . '\');')
+            ]);
+        }
+
+        $sInclude = $this->_oTemplate->addCss(['timeline.css'], $bDynamic);
+        if($bDynamic)
+            $aResult['raw'] .= $sInclude;
+
+        return $aResult;
+    }
+
+    protected function _getBlockLicenses($sType = '') 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sGrid = $CNF['OBJECT_GRID_LICENSES' . (!empty($sType) ? '_' . strtoupper($sType) : '')];
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return '';
+
+        $this->_oDb->updateLicense(array('new' => 0), array('profile_id' => bx_get_logged_profile_id(), 'new' => 1));
+
+        $this->_oTemplate->addJs(array('licenses.js'));
+        return array(
+            'content' => $this->_oTemplate->getJsCode('licenses', array('sObjNameGrid' => $sGrid)) . $oGrid->getCode(),
+            'menu' => $CNF['OBJECT_MENU_LICENSES']
+        );
+    }
+
+    protected function _alertParamsOffer($aContentInfo, $aOfferInfo)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aParams = array(
+            'object_id' => (int)$aContentInfo[$CNF['FIELD_ID']],
+            'object_author_id' => (int)$aContentInfo[$CNF['FIELD_AUTHOR']],
+
+            'offer_id' => (int)$aOfferInfo[$CNF['FIELD_OFR_ID']],
+            'offer_author_id' => (int)$aOfferInfo[$CNF['FIELD_OFR_AUTHOR']],
+        );
+        if(isset($aContentInfo[$CNF['FIELD_ALLOW_VIEW_TO']]))
+            $aParams['privacy_view'] = $aContentInfo[$CNF['FIELD_ALLOW_VIEW_TO']];
+
+        return $aParams;
+    }
+}
+
+/** @} */

+ 151 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/source/install/config.php

@@ -0,0 +1,151 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Ads Ads
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_ads',
+    'title' => 'Ads',
+    'note' => 'Ads module.',
+    'version' => '15.0.3',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/ads/',
+    'home_uri' => 'ads',
+
+    'db_prefix' => 'bx_ads_',
+    'class_prefix' => 'BxAds',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Ads',
+
+    /**
+     * List of page triggers.
+     */
+    'page_triggers' => array (
+    	'trigger_page_profile_view_entry',
+    ),
+    
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+        'trigger_profile_view_submenu',
+        'trigger_group_view_submenu',
+    ),
+
+    /**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+        'bx_ads_covers',
+    	'bx_ads_photos',
+        'bx_ads_videos',
+        'bx_ads_files'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+        'bx_ads_preview',
+        'bx_ads_gallery',
+        'bx_ads_cover',
+
+        'bx_ads_preview_photos',
+        'bx_ads_gallery_photos',
+        'bx_ads_view_photos',
+
+        'bx_ads_videos_poster',
+        'bx_ads_videos_poster_preview',
+        'bx_ads_videos_mp4',
+        'bx_ads_videos_mp4_hd',
+
+        'bx_ads_preview_files',
+        'bx_ads_gallery_files'
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_ads',
+    	'bx_ads_cmts'
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+        'process_menu_triggers' => 1,
+        'process_page_triggers' => 1,
+    	'process_esearches' => 1,
+        'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'update_relations' => 1,
+        'unregister_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+        'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Relations Section
+     */
+    'relations' => array(
+    	'bx_timeline',
+    	'bx_notifications'
+    ),
+
+);
+
+/** @} */

+ 697 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/source/install/langs/en.xml

@@ -0,0 +1,697 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_ads"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_cmts"><![CDATA[Ads' Comments]]></string>
+    <string name="_bx_ads_reactions"><![CDATA[Ads' Reactions]]></string>
+
+    <string name="_bx_ads_option_enable_auto_approve"><![CDATA[Auto approve an ad after creation]]></string>
+    <string name="_bx_ads_option_summary_chars"><![CDATA[Number of characters in auto-cropped ad summary]]></string>
+    <string name="_bx_ads_option_plain_summary_chars"><![CDATA[Number of characters in plain ad description when ad hasn't thumb]]></string>
+    <string name="_bx_ads_option_per_page_browse"><![CDATA[Number of items per page]]></string>
+    <string name="_bx_ads_option_per_page_profile"><![CDATA[Number of items on profile/group page]]></string>
+    <string name="_bx_ads_option_rss_num"><![CDATA[Number of items in RSS feed]]></string>
+    <string name="_bx_ads_option_searchable_fields"><![CDATA[Searchable fields for keyword search]]></string>
+    <string name="_bx_ads_option_per_page_for_favorites_lists"><![CDATA[Number of favorite lists per page]]></string>
+    <string name="_bx_ads_option_lifetime"><![CDATA[Deactivate automatically after (in days)]]></string>
+    <string name="_bx_ads_option_offer_lifetime"><![CDATA[Decline offers automatically after (in hours)]]></string>
+    <string name="_bx_ads_option_internal_interested_notification"><![CDATA[Use internal notification about Interested action]]></string>
+    <string name="_bx_ads_option_enable_auction"><![CDATA[Enable Auction mode]]></string>
+    <string name="_bx_ads_option_enable_sources"><![CDATA[Enable external sources]]></string>
+    <string name="_bx_ads_option_enable_promotion"><![CDATA[Enable paid promotion]]></string>
+    <string name="_bx_ads_option_promotion_cpm"><![CDATA[Promotion price (CPM)]]></string>
+
+    <string name="_bx_ads_src_cpt_shopify_admin"><![CDATA[Shopify (Admin API)]]></string>
+    <string name="_bx_ads_src_dsc_shopify_admin"><![CDATA[For a custom app created directly in the Shopify admin.]]></string>
+    <string name="_bx_ads_src_opt_cpt_active"><![CDATA[Active]]></string>
+    <string name="_bx_ads_src_opt_dsc_active"><![CDATA[Activate(Deactivate) source]]></string>
+    <string name="_bx_ads_src_opt_cpt_shop_domain"><![CDATA[Shop domain]]></string>
+    <string name="_bx_ads_src_opt_dsc_shop_domain"><![CDATA[]]></string>
+    <string name="_bx_ads_src_opt_cpt_access_token"><![CDATA[Access token]]></string>
+    <string name="_bx_ads_src_opt_dsc_access_token"><![CDATA[]]></string>
+
+
+    <string name="_bx_ads_form_sources_details"><![CDATA[Sources Details]]></string>
+    <string name="_bx_ads_form_sources_details_display_edit"><![CDATA[Edit Sources Details]]></string>
+    <string name="_bx_ads_form_sources_details_input_do_submit"><![CDATA[Submit]]></string>
+
+    <string name="_bx_ads_form_category"><![CDATA[Category]]></string>
+    <string name="_bx_ads_form_category_display_add"><![CDATA[Add Category]]></string>
+    <string name="_bx_ads_form_category_display_edit"><![CDATA[Edit Category]]></string>
+    <string name="_bx_ads_form_category_display_delete"><![CDATA[Delete Category]]></string>
+    <string name="_bx_ads_form_category_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_ads_form_category_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_ads_form_category_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_ads_form_category_input_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_ads_form_category_input_sys_parent_id"><![CDATA[Parent]]></string>
+    <string name="_bx_ads_form_category_input_parent_id"><![CDATA[Parent category]]></string>
+    <string name="_bx_ads_form_category_input_parent_id_empty"><![CDATA[No parent]]></string>
+    <string name="_bx_ads_form_category_input_sys_title"><![CDATA[Title]]></string>
+    <string name="_bx_ads_form_category_input_title"><![CDATA[Title]]></string>
+    <string name="_bx_ads_form_category_input_title_err"><![CDATA[Title is essential. Please, fill in this field.]]></string>
+    <string name="_bx_ads_form_category_input_sys_text"><![CDATA[Text]]></string>
+    <string name="_bx_ads_form_category_input_text"><![CDATA[Text]]></string>
+    <string name="_bx_ads_form_category_input_sys_type"><![CDATA[Type]]></string>
+    <string name="_bx_ads_form_category_input_type"><![CDATA[Type]]></string>
+    <string name="_bx_ads_form_category_input_type_err"><![CDATA[Type is essential. Please, select a value for this field.]]></string>
+    <string name="_bx_ads_form_category_input_type_info"><![CDATA[Type determines a set of fields for entries in this category.]]></string>
+    <string name="_bx_ads_form_category_input_sys_type_clone"><![CDATA[Clone type]]></string>
+    <string name="_bx_ads_form_category_input_type_clone"><![CDATA[Clone type]]></string>
+    <string name="_bx_ads_form_category_input_sys_type_title"><![CDATA[Type title]]></string>
+    <string name="_bx_ads_form_category_input_type_title"><![CDATA[Type title]]></string>
+    <string name="_bx_ads_form_category_input_sys_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_ads_form_category_input_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_ads_form_category_input_sys_delete_confirm"><![CDATA[Delete confirmation]]></string>
+    <string name="_bx_ads_form_category_input_delete_confirm"><![CDATA[I am sure I want to delete this category]]></string>
+    <string name="_bx_ads_form_category_input_delete_confirm_info"><![CDATA[This action can not be undone]]></string>
+    <string name="_bx_ads_form_category_input_delete_confirm_error"><![CDATA[Please check this checkbox if you really want to delete the category]]></string>
+
+    <string name="_bx_ads_form_entry"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_form_entry_display_add"><![CDATA[Add Ad]]></string>
+    <string name="_bx_ads_form_entry_display_edit_budget"><![CDATA[Edit Ad's Budget]]></string>
+    <string name="_bx_ads_form_entry_display_delete"><![CDATA[Delete Ad]]></string>
+
+    <string name="_bx_ads_form_entry_price_display_add"><![CDATA[With Price: Add]]></string>
+    <string name="_bx_ads_form_entry_price_display_edit"><![CDATA[With Price: Edit]]></string>
+    <string name="_bx_ads_form_entry_price_display_view"><![CDATA[With Price: View]]></string>
+
+    <string name="_bx_ads_form_entry_price_year_display_add"><![CDATA[With Price and Year: Add]]></string>
+    <string name="_bx_ads_form_entry_price_year_display_edit"><![CDATA[With Price and Year: Edit]]></string>
+    <string name="_bx_ads_form_entry_price_year_display_view"><![CDATA[With Price and Year: View]]></string>
+
+    <string name="_bx_ads_form_entry_input_author"><![CDATA[Author]]></string>
+    <string name="_bx_ads_form_entry_input_sys_do_select"><![CDATA[Select]]></string>
+    <string name="_bx_ads_form_entry_input_do_select"><![CDATA[Select]]></string>
+    <string name="_bx_ads_form_entry_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_ads_form_entry_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_ads_form_entry_input_sys_title"><![CDATA[Title]]></string>
+    <string name="_bx_ads_form_entry_input_title"><![CDATA[Title]]></string>
+    <string name="_bx_ads_form_entry_input_title_err"><![CDATA[Title is essential min 3 max 160.]]></string>
+    <string name="_bx_ads_form_entry_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_ads_form_entry_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_ads_form_entry_input_name_err"><![CDATA[Name should be unique and is needed for internal usage. Please, fill in this field]]></string>
+    <string name="_bx_ads_form_entry_input_sys_price"><![CDATA[Price]]></string>
+    <string name="_bx_ads_form_entry_input_price"><![CDATA[Price]]></string>
+    <string name="_bx_ads_form_entry_input_sys_auction"><![CDATA[Auction]]></string>
+    <string name="_bx_ads_form_entry_input_auction"><![CDATA[Use auction]]></string>
+    <string name="_bx_ads_form_entry_input_auction_info"><![CDATA[Allow buyers not only to pay a defined price but to make offers too.]]></string>
+    <string name="_bx_ads_form_entry_input_sys_quantity"><![CDATA[Quantity]]></string>
+    <string name="_bx_ads_form_entry_input_quantity"><![CDATA[Quantity]]></string>
+    <string name="_bx_ads_form_entry_input_quantity_err"><![CDATA[Quantity is essential. Please, fill in this field]]></string>
+    <string name="_bx_ads_form_entry_input_sys_year"><![CDATA[Year]]></string>
+    <string name="_bx_ads_form_entry_input_year"><![CDATA[Year]]></string>
+    <string name="_bx_ads_form_entry_input_sys_category"><![CDATA[Category]]></string>
+    <string name="_bx_ads_form_entry_input_sys_category_view"><![CDATA[Category]]></string>
+    <string name="_bx_ads_form_entry_input_category_view"><![CDATA[Category]]></string>
+    <string name="_bx_ads_form_entry_input_sys_category_select"><![CDATA[Category]]></string>
+    <string name="_bx_ads_form_entry_input_category_select"><![CDATA[Category]]></string>
+    <string name="_bx_ads_form_entry_input_category_select_err"><![CDATA[Category field is essential. Please, fill it in.]]></string>
+    <string name="_bx_ads_form_entry_input_sys_text"><![CDATA[Text]]></string>
+    <string name="_bx_ads_form_entry_input_text"><![CDATA[Text]]></string>
+    <string name="_bx_ads_form_entry_input_text_err"><![CDATA[Text field can't be empty]]></string>
+    <string name="_bx_ads_form_entry_input_sys_notes_purchased"><![CDATA[After purchase notes]]></string>
+    <string name="_bx_ads_form_entry_input_notes_purchased"><![CDATA[After purchase notes]]></string>
+    <string name="_bx_ads_form_entry_input_notes_purchased_inf"><![CDATA[Will be attached to email message which is sent to buyer after purchase.]]></string>
+    <string name="_bx_ads_form_entry_input_sys_delete_confirm"><![CDATA[Delete confirmation]]></string>
+    <string name="_bx_ads_form_entry_input_delete_confirm"><![CDATA[I am sure I want to delete this ad]]></string>
+    <string name="_bx_ads_form_entry_input_delete_confirm_info"><![CDATA[This action can not be undone]]></string>
+    <string name="_bx_ads_form_entry_input_delete_confirm_error"><![CDATA[Please check this checkbox if you really want to delete the ad]]></string>
+    <string name="_bx_ads_form_entry_input_sys_covers"><![CDATA[Header Image]]></string>
+    <string name="_bx_ads_form_entry_input_covers"><![CDATA[Header Image]]></string>
+    <string name="_bx_ads_form_entry_input_covers_uploader_simple_title"><![CDATA[Upload]]></string>
+    <string name="_bx_ads_form_entry_input_covers_uploader_html5_title"><![CDATA[Upload]]></string>
+    <string name="_bx_ads_form_entry_input_sys_pictures"><![CDATA[Images]]></string>
+    <string name="_bx_ads_form_entry_input_pictures"><![CDATA[]]></string>
+    <string name="_bx_ads_form_entry_input_sys_videos"><![CDATA[Videos]]></string>
+    <string name="_bx_ads_form_entry_input_videos"><![CDATA[]]></string>
+    <string name="_bx_ads_form_entry_input_sys_files"><![CDATA[Files]]></string>
+    <string name="_bx_ads_form_entry_input_files"><![CDATA[]]></string>
+    <string name="_bx_ads_form_entry_input_sys_polls"><![CDATA[Polls]]></string>
+    <string name="_bx_ads_form_entry_input_sys_link"><![CDATA[Links]]></string>
+    <string name="_bx_ads_form_entry_input_link_err_delete"><![CDATA[Cannot remove the link. Please report.]]></string>
+    <string name="_bx_ads_form_entry_input_sys_allow_view_to"><![CDATA[Visibility]]></string>
+    <string name="_bx_ads_form_entry_input_allow_view_to"><![CDATA[Visibility]]></string>
+    <string name="_bx_ads_form_entry_input_allow_view_favorite_list"><![CDATA[Visibility]]></string>
+    <string name="_bx_ads_form_entry_input_sys_date_added"><![CDATA[Created]]></string>
+    <string name="_bx_ads_form_entry_input_date_added"><![CDATA[Created]]></string>
+    <string name="_bx_ads_form_entry_input_sys_date_changed"><![CDATA[Updated]]></string>
+    <string name="_bx_ads_form_entry_input_date_changed"><![CDATA[Updated]]></string>
+    <string name="_bx_ads_form_entry_input_sys_attachments"><![CDATA[Attachments]]></string>
+    <string name="_bx_ads_form_entry_input_sys_source_type"><![CDATA[Source type]]></string>
+    <string name="_bx_ads_form_entry_input_source_type"><![CDATA[Source type]]></string>
+    <string name="_bx_ads_form_entry_input_sys_source"><![CDATA[Source]]></string>
+    <string name="_bx_ads_form_entry_input_source"><![CDATA[Source]]></string>
+    <string name="_bx_ads_form_entry_input_source_paceholder"><![CDATA[Type product title here...]]></string>
+    <string name="_bx_ads_form_entry_input_sys_url"><![CDATA[Link]]></string>
+    <string name="_bx_ads_form_entry_input_sys_budget_total"><![CDATA[Campaign budget]]></string>
+    <string name="_bx_ads_form_entry_input_budget_total"><![CDATA[Campaign budget]]></string>
+    <string name="_bx_ads_form_entry_input_budget_total_err_decreased"><![CDATA[Campaign budget cannot be decreased.]]></string>
+    <string name="_bx_ads_form_entry_input_sys_budget_daily"><![CDATA[Max daily budget]]></string>
+    <string name="_bx_ads_form_entry_input_budget_daily"><![CDATA[Max daily budget]]></string>
+    <string name="_bx_ads_form_entry_input_sys_header_beg_seg"><![CDATA[Segmentation: begin]]></string>
+    <string name="_bx_ads_form_entry_input_header_beg_seg"><![CDATA[Segmentation]]></string>
+    <string name="_bx_ads_form_entry_input_sys_header_end_seg"><![CDATA[Segmentation: end]]></string>
+    <string name="_bx_ads_form_entry_input_sys_seg"><![CDATA[Enable]]></string>
+    <string name="_bx_ads_form_entry_input_seg"><![CDATA[Enable segmentation]]></string>
+    <string name="_bx_ads_form_entry_input_sys_seg_gender"><![CDATA[Gender]]></string>
+    <string name="_bx_ads_form_entry_input_seg_gender"><![CDATA[Gender]]></string>
+    <string name="_bx_ads_form_entry_input_sys_seg_age"><![CDATA[Age]]></string>
+    <string name="_bx_ads_form_entry_input_seg_age"><![CDATA[Age]]></string>
+    <string name="_bx_ads_form_entry_input_sys_seg_country"><![CDATA[Country]]></string>
+    <string name="_bx_ads_form_entry_input_seg_country"><![CDATA[Country]]></string>
+    <string name="_bx_ads_form_entry_input_seg_country_all"><![CDATA[All countries]]></string>
+
+    <string name="_bx_ads_form_poll"><![CDATA[Polls]]></string>
+    <string name="_bx_ads_form_poll_display_add"><![CDATA[Add Poll]]></string>
+    <string name="_bx_ads_form_poll_input_sys_text"><![CDATA[Question]]></string>
+    <string name="_bx_ads_form_poll_input_text"><![CDATA[Question]]></string>
+    <string name="_bx_ads_form_poll_input_text_err"><![CDATA[Question is essential. Please, fill in this field]]></string>
+    <string name="_bx_ads_form_poll_input_sys_answers"><![CDATA[Answers]]></string>
+    <string name="_bx_ads_form_poll_input_answers"><![CDATA[Answers]]></string>
+    <string name="_bx_ads_form_poll_input_answers_err"><![CDATA[Poll should have at least 2 non-empty answers.]]></string>
+    <string name="_bx_ads_form_poll_input_answers_add"><![CDATA[Add More]]></string>
+    <string name="_bx_ads_form_poll_input_sys_do_submit"><![CDATA[Add]]></string>
+    <string name="_bx_ads_form_poll_input_do_submit"><![CDATA[Add]]></string>
+    <string name="_bx_ads_form_poll_input_do_cancel"><![CDATA[Close]]></string>
+
+    <string name="_bx_ads_form_attach_link"><![CDATA[Attach Link]]></string>
+    <string name="_bx_ads_form_attach_link_display_add"><![CDATA[Attach Link]]></string>
+    <string name="_bx_ads_form_attach_link_input_sys_content_id"><![CDATA[Content Id]]></string>
+    <string name="_bx_ads_form_attach_link_input_sys_url"><![CDATA[Add Link]]></string>
+    <string name="_bx_ads_form_attach_link_input_url_err"><![CDATA[Correct Link is essential. Please, fill in this field.]]></string>
+    <string name="_bx_ads_form_attach_link_input_url"><![CDATA[Add Link]]></string>
+    <string name="_bx_ads_form_attach_link_input_do_submit"><![CDATA[Add]]></string>
+    <string name="_bx_ads_form_attach_link_input_sys_do_submit"><![CDATA[Add]]></string>
+    <string name="_bx_ads_form_attach_link_input_do_cancel"><![CDATA[Close]]></string>
+    
+    <string name="_bx_ads_form_offer"><![CDATA[Offer]]></string>
+    <string name="_bx_ads_form_offer_display_add"><![CDATA[Add Offer]]></string>
+    <string name="_bx_ads_form_offer_display_view"><![CDATA[View Offer]]></string>
+    <string name="_bx_ads_form_offer_input_sys_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_ads_form_offer_input_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_ads_form_offer_input_amount_err"><![CDATA[Amount is essential. Please, fill in this field.]]></string>
+    <string name="_bx_ads_form_offer_input_sys_quantity"><![CDATA[Quantity]]></string>
+    <string name="_bx_ads_form_offer_input_quantity"><![CDATA[Quantity]]></string>
+    <string name="_bx_ads_form_offer_input_quantity_err"><![CDATA[Quantity is essential. Please, fill in this field.]]></string>
+    <string name="_bx_ads_form_offer_input_sys_total"><![CDATA[Total]]></string>
+    <string name="_bx_ads_form_offer_input_total"><![CDATA[Total]]></string>
+    <string name="_bx_ads_form_offer_input_sys_message"><![CDATA[Message]]></string>
+    <string name="_bx_ads_form_offer_input_message"><![CDATA[Message]]></string>
+    <string name="_bx_ads_form_offer_input_sys_do_submit"><![CDATA[Add]]></string>
+    <string name="_bx_ads_form_offer_input_do_submit"><![CDATA[Add]]></string>
+    <string name="_bx_ads_form_offer_input_sys_do_cancel"><![CDATA[Add]]></string>
+    <string name="_bx_ads_form_offer_input_do_cancel"><![CDATA[Close]]></string>
+
+    <string name="_bx_ads_acl_action_create_entry"><![CDATA[Create Ad]]></string>
+    <string name="_bx_ads_acl_action_delete_entry"><![CDATA[Delete Ad]]></string>
+    <string name="_bx_ads_acl_action_view_entry"><![CDATA[View Ad]]></string>
+    <string name="_bx_ads_acl_action_set_thumb"><![CDATA[Set Ad Header Image]]></string>
+    <string name="_bx_ads_acl_action_edit_any_entry"><![CDATA[Edit Any Ad]]></string>
+    <string name="_bx_ads_acl_action_delete_any_entry"><![CDATA[Delete Any Ad]]></string>
+    <string name="_bx_ads_acl_action_make_offer"><![CDATA[Make Offer]]></string>
+
+    <string name="_bx_ads_page_title_sys_create_entry"><![CDATA[New Ad]]></string>
+    <string name="_bx_ads_page_title_create_entry"><![CDATA[New Ad]]></string>
+    <string name="_bx_ads_page_block_title_create_entry"><![CDATA[New Ad]]></string>
+    <string name="_bx_ads_page_title_sys_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_ads_page_title_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_ads_page_block_title_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_ads_page_title_sys_edit_entry_budget"><![CDATA[Edit Budget]]></string>
+    <string name="_bx_ads_page_title_edit_entry_budget"><![CDATA[Edit Budget]]></string>
+    <string name="_bx_ads_page_block_title_edit_entry_budget"><![CDATA[Edit Budget]]></string>
+    <string name="_bx_ads_page_title_sys_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_ads_page_title_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_ads_page_block_title_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_ads_page_title_sys_view_entry"><![CDATA[View Ad]]></string>
+    <string name="_bx_ads_page_title_view_entry"><![CDATA[{title}]]></string>
+    <string name="_bx_ads_page_block_title_entry_breadcrumb"><![CDATA[Breadcrumb]]></string>
+    <string name="_bx_ads_page_block_title_entry_text"><![CDATA[Ad Text]]></string>
+    <string name="_bx_ads_page_title_browse"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_page_title_search_results"><![CDATA[Ads matching "{0}"]]></string>
+    <string name="_bx_ads_page_title_browse_by_author"><![CDATA[Ads of {display_name}]]></string>
+    <string name="_bx_ads_page_title_browse_by_context"><![CDATA[Ads in {display_name}]]></string>
+    <string name="_bx_ads_page_title_browse_featured"><![CDATA[Featured Ads]]></string>
+    <string name="_bx_ads_page_title_browse_recent"><![CDATA[Latest Ads]]></string>
+    <string name="_bx_ads_page_title_browse_popular"><![CDATA[Popular Ads]]></string>
+    <string name="_bx_ads_page_title_browse_updated"><![CDATA[Updated Ads]]></string>
+    <string name="_bx_ads_page_title_browse_category"><![CDATA[Ads in {display_name}]]]]></string>
+    <string name="_bx_ads_page_title_sys_home"><![CDATA[Ads Home]]></string>
+    <string name="_bx_ads_page_title_home"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_page_block_title_featured_entries_view_extended"><![CDATA[Featured Ads]]></string>
+    <string name="_bx_ads_page_block_title_recent_entries"><![CDATA[Latest Ads (Gallery View)]]></string>
+    <string name="_bx_ads_page_block_title_recent_entries_view_extended"><![CDATA[Latest Ads]]></string>
+    <string name="_bx_ads_page_block_title_recent_entries_view_full"><![CDATA[Latest Ads (Full View)]]></string>
+    <string name="_bx_ads_page_block_title_entry_social_sharing"><![CDATA[Share]]></string>
+    <string name="_bx_ads_page_block_title_entry_author"><![CDATA[Author]]></string>
+    <string name="_bx_ads_page_block_title_entry_actions"><![CDATA[Actions]]></string>
+    <string name="_bx_ads_page_block_title_entry_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_ads_page_block_title_entry_reports"><![CDATA[Reports]]></string>
+    <string name="_bx_ads_page_block_title_entry_comments_link"><![CDATA[Comments to <a href="{entry_link}">{title}</a>]]></string>
+    <string name="_bx_ads_page_block_title_sys_entry_promotion_growth"><![CDATA[Promotion (Growth)]]></string>
+    <string name="_bx_ads_page_block_title_entry_promotion_growth"><![CDATA[Promotion for <a href="{entry_link}">{title}</a>]]></string>
+    <string name="_bx_ads_page_block_title_sys_entry_promotion_summary"><![CDATA[Promotion (Summary)]]></string>
+    <string name="_bx_ads_page_block_title_entry_promotion_summary"><![CDATA[Summary]]></string>
+    <string name="_bx_ads_page_block_title_sys_entry_promotion_roi"><![CDATA[ROI]]></string>
+    <string name="_bx_ads_page_block_title_entry_promotion_roi"><![CDATA[ROI]]></string>
+    <string name="_bx_ads_page_block_title_entry_reviews"><![CDATA[Reviews]]></string>
+    <string name="_bx_ads_page_block_title_entry_reviews_link"><![CDATA[Reviews to <a href="{entry_link}">{title}</a>]]></string>
+    <string name="_bx_ads_page_block_title_entry_reviews_rating"><![CDATA[Rating]]></string>
+    <string name="_bx_ads_page_block_title_entry_location"><![CDATA[Location]]></string>
+    <string name="_bx_ads_page_block_title_entry_attachments"><![CDATA[Attachments]]></string>
+    <string name="_bx_ads_page_block_title_entry_polls"><![CDATA[Polls]]></string>
+    <string name="_bx_ads_page_block_title_entry_all_actions"><![CDATA[All Actions]]></string>
+    <string name="_bx_ads_page_title_sys_entries_popular"><![CDATA[Popular Ads]]></string>
+    <string name="_bx_ads_page_title_entries_popular"><![CDATA[Popular Ads]]></string>
+    <string name="_bx_ads_page_title_sys_entries_updated"><![CDATA[Updated Ads]]></string>
+    <string name="_bx_ads_page_title_entries_updated"><![CDATA[Updated Ads]]></string>
+    <string name="_bx_ads_page_title_sys_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_ads_page_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_ads_page_title_sys_entries_favorites"><![CDATA[Favorives Reviews]]></string>
+    <string name="_bx_ads_page_title_entries_favorites"><![CDATA[Favorives Reviews]]></string>
+    <string name="_bx_ads_page_block_title_search_form"><![CDATA[Search Form]]></string>
+    <string name="_bx_ads_page_block_title_search_results"><![CDATA[Search Results]]></string>
+    <string name="_bx_ads_page_block_title_search_form_cmts"><![CDATA[Comments Search Form]]></string>
+    <string name="_bx_ads_page_block_title_search_results_cmts"><![CDATA[Comments Search Results]]></string>
+    <string name="_bx_ads_page_block_title_popular_entries"><![CDATA[Popular Ads]]></string>
+    <string name="_bx_ads_page_block_title_popular_entries_view_extended"><![CDATA[Popular Ads (Extended View)]]></string>
+    <string name="_bx_ads_page_block_title_popular_entries_view_full"><![CDATA[Popular Ads (Full View)]]></string>
+    <string name="_bx_ads_page_block_title_updated_entries"><![CDATA[Updated Ads]]></string>
+    <string name="_bx_ads_page_block_title_favorites_entries"><![CDATA["{title}" List]]></string>
+    <string name="_bx_ads_page_block_title_favorites_entries_info"><![CDATA[Info]]></string>
+    <string name="_bx_ads_page_title_sys_view_entry_comments"><![CDATA[Ad Comments]]></string>
+    <string name="_bx_ads_page_title_view_entry_comments"><![CDATA[Comments to {title}]]></string>
+    <string name="_bx_ads_page_title_sys_view_entry_promotion"><![CDATA[Ad Promotion]]></string>
+    <string name="_bx_ads_page_title_view_entry_promotion"><![CDATA[Promotion for {title}]]></string>
+    <string name="_bx_ads_page_title_sys_entries_of_author"><![CDATA[Ads of author]]></string>
+    <string name="_bx_ads_page_title_entries_of_author"><![CDATA[Ads by {display_name}]]></string>
+    <string name="_bx_ads_page_title_sys_entries_in_context"><![CDATA[Ads in context]]></string>
+    <string name="_bx_ads_page_title_entries_in_context"><![CDATA[Ads in {display_name}]]></string>
+    <string name="_bx_ads_page_block_title_favorites_of_author"><![CDATA[Favorites by <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_ads_page_block_title_sys_favorites_of_author"><![CDATA[Favorites of author]]></string>
+    <string name="_bx_ads_page_block_title_entries_of_author"><![CDATA[Ads by <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_ads_page_block_title_sys_entries_of_author"><![CDATA[Ads of author]]></string>
+    <string name="_bx_ads_page_block_title_entries_in_context"><![CDATA[Ads in <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_ads_page_block_title_sys_entries_in_context"><![CDATA[Ads in context]]></string>
+    <string name="_bx_ads_page_block_title_entries_actions"><![CDATA[Ads actions]]></string>
+    <string name="_bx_ads_page_block_title_browse_cmts"><![CDATA[Ads' Comments]]></string>
+    <string name="_bx_ads_page_title_sys_manage"><![CDATA[Manage Own]]></string>
+    <string name="_bx_ads_page_title_sys_manage_administration"><![CDATA[Manage All]]></string>
+    <string name="_bx_ads_page_title_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_ads_page_block_title_system_manage"><![CDATA[Manage Own]]></string>
+    <string name="_bx_ads_page_block_title_system_manage_administration"><![CDATA[Manage All]]></string>
+    <string name="_bx_ads_page_block_title_manage"><![CDATA[Manage]]></string>    
+    <string name="_bx_ads_page_block_title_popular_keywords"><![CDATA[Popular Hashtags]]></string>
+    <string name="_bx_ads_page_block_title_categories"><![CDATA[Ads Categories]]></string>
+    <string name="_bx_ads_page_block_title_entry_info"><![CDATA[Info]]></string>
+    <string name="_bx_ads_page_block_title_sys_my_entries"><![CDATA[User Ads]]></string>
+    <string name="_bx_ads_page_block_title_my_entries"><![CDATA[{display_name} Ads]]></string>
+    <string name="_bx_ads_page_block_title_sys_recent_entries_view_showcase"><![CDATA[Latest Ads (Showcase View)]]></string>
+    <string name="_bx_ads_page_block_title_recent_entries_view_showcase"><![CDATA[Latest Ads]]></string>
+    <string name="_bx_ads_page_block_title_sys_popular_entries_view_showcase"><![CDATA[Popular Ads (Showcase View)]]></string>
+    <string name="_bx_ads_page_block_title_popular_entries_view_showcase"><![CDATA[Popular Ads]]></string>
+    <string name="_bx_ads_page_block_title_sys_featured_entries_view_showcase"><![CDATA[Featured Ads (Showcase View)]]></string>
+    <string name="_bx_ads_page_block_title_featured_entries_view_showcase"><![CDATA[Featured Ads]]></string>
+    <string name="_bx_ads_page_block_title_entry_offer_accepted"><![CDATA[Offer]]></string>
+    <string name="_bx_ads_page_block_title_sys_entry_context"><![CDATA[Posted in]]></string>
+    <string name="_bx_ads_page_block_title_entry_context"><![CDATA[Posted in]]></string>
+    <string name="_bx_ads_page_title_sys_entries_categories"><![CDATA[Ads Categories]]></string>
+    <string name="_bx_ads_page_title_entries_categories"><![CDATA[Ads Categories]]></string>
+    <string name="_bx_ads_page_block_title_entries_by_category"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_page_block_title_entries_by_category_mask"><![CDATA[Ads in: {0}]]></string>
+    <string name="_bx_ads_page_title_sys_licenses_administration"><![CDATA[Licenses]]></string>
+    <string name="_bx_ads_page_title_licenses_administration"><![CDATA[Licenses]]></string>
+    <string name="_bx_ads_page_block_title_licenses_administration"><![CDATA[Licenses]]></string>
+    <string name="_bx_ads_page_title_sys_licenses"><![CDATA[Licenses]]></string>
+    <string name="_bx_ads_page_title_licenses"><![CDATA[Licenses]]></string>
+    <string name="_bx_ads_page_block_title_licenses"><![CDATA[Licenses]]></string>
+    <string name="_bx_ads_page_block_title_licenses_note"><![CDATA[Note]]></string>
+    <string name="_bx_ads_page_block_content_licenses_note"><![CDATA[The licenses are for reference only.]]></string>
+    <string name="_bx_ads_page_title_sys_offers"><![CDATA[Offers]]></string>
+    <string name="_bx_ads_page_title_offers"><![CDATA[Offers]]></string>
+    <string name="_bx_ads_page_block_title_offers"><![CDATA[Offers]]></string>
+    <string name="_bx_ads_page_title_sys_offers_all"><![CDATA[All Offers]]></string>
+    <string name="_bx_ads_page_title_offers_all"><![CDATA[All Offers]]></string>
+    <string name="_bx_ads_page_block_title_offers_all"><![CDATA[All Offers]]></string>
+    <string name="_bx_ads_page_title_sys_sources"><![CDATA[Sources]]></string>
+    <string name="_bx_ads_page_title_sources"><![CDATA[Sources]]></string>
+    <string name="_bx_ads_page_block_title_system_sources_details"><![CDATA[Sources Details]]></string>
+    <string name="_bx_ads_page_block_title_sources_details"><![CDATA[Sources Details]]></string>
+    
+
+    <string name="_bx_ads_menu_title_entry_attachments"><![CDATA[Form Attachments]]></string>
+    <string name="_bx_ads_menu_set_title_entry_attachments"><![CDATA[Form Attachments]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_photo_simple"><![CDATA[Add Photo (simple)]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_photo_simple"><![CDATA[Add Photo]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_photo_html5"><![CDATA[Add Photo (html5)]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_photo_html5"><![CDATA[Add Photos]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_video_simple"><![CDATA[Add Video (simple)]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_video_simple"><![CDATA[Add Video]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_video_html5"><![CDATA[Add Video (html5)]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_video_html5"><![CDATA[Add Videos]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_video_record"><![CDATA[Record Video]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_video_record"><![CDATA[Record Video]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_file_simple"><![CDATA[Add File (simple)]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_file_simple"><![CDATA[Add File]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_file_html5"><![CDATA[Add File (html5)]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_file_html5"><![CDATA[Add Files]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_poll"><![CDATA[Add Poll]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_poll"><![CDATA[Add Poll]]></string>
+    <string name="_bx_ads_menu_item_title_system_cpa_link"><![CDATA[Add Link]]></string>
+    <string name="_bx_ads_menu_item_title_cpa_link"><![CDATA[Add Link]]></string>
+
+    <string name="_bx_ads_menu_set_title_view_entry"><![CDATA[Ad Actions]]></string>
+    <string name="_bx_ads_menu_title_view_entry"><![CDATA[Ad Actions]]></string>
+    <string name="_bx_ads_menu_title_submenu"><![CDATA[Ads Submenu]]></string>
+    <string name="_bx_ads_menu_set_title_submenu"><![CDATA[Ads Submenu]]></string>
+    <string name="_bx_ads_menu_item_title_system_create_entry"><![CDATA[Ad]]></string>
+    <string name="_bx_ads_menu_item_title_create_entry"><![CDATA[Ad]]></string>
+    <string name="_bx_ads_menu_item_title_system_interested_entry"><![CDATA[Interested]]></string>
+    <string name="_bx_ads_menu_item_title_interested_entry"><![CDATA[I'm interested]]></string>
+    <string name="_bx_ads_menu_item_title_system_add_to_cart"><![CDATA[Add to Cart]]></string>
+    <string name="_bx_ads_menu_item_title_add_to_cart"><![CDATA[Buy for {0}{1}]]></string>
+    <string name="_bx_ads_menu_item_title_system_make_offer"><![CDATA[Make offer]]></string>
+    <string name="_bx_ads_menu_item_title_make_offer"><![CDATA[Make offer]]></string>
+    <string name="_bx_ads_menu_item_title_system_view_offers"><![CDATA[Оffers]]></string>
+    <string name="_bx_ads_menu_item_title_view_offers"><![CDATA[Оffers]]></string>
+    <string name="_bx_ads_menu_item_title_system_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_ads_menu_item_title_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_ads_menu_item_title_system_edit_entry_budget"><![CDATA[Edit budget]]></string>
+    <string name="_bx_ads_menu_item_title_edit_entry_budget"><![CDATA[Edit budget]]></string>
+    <string name="_bx_ads_menu_item_title_system_view_promotion"><![CDATA[Promotion]]></string>
+    <string name="_bx_ads_menu_item_title_view_promotion"><![CDATA[Promotion]]></string>
+    <string name="_bx_ads_menu_item_title_system_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_ads_menu_item_title_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_ads_menu_item_title_system_mark_shipped"><![CDATA[Mark as shipped]]></string>
+    <string name="_bx_ads_menu_item_title_mark_shipped"><![CDATA[Mark as shipped]]></string>
+    <string name="_bx_ads_menu_item_title_system_mark_received"><![CDATA[Mark as received]]></string>
+    <string name="_bx_ads_menu_item_title_mark_received"><![CDATA[Mark as received]]></string>
+    <string name="_bx_ads_menu_item_title_system_review_entry"><![CDATA[Review]]></string>
+    <string name="_bx_ads_menu_item_title_system_view_entry"><![CDATA[View Ad]]></string>
+    <string name="_bx_ads_menu_item_title_system_entries_home"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_item_title_entries_home"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_item_title_system_entries_public"><![CDATA[Latest]]></string>
+    <string name="_bx_ads_menu_item_title_entries_public"><![CDATA[Latest]]></string>
+    <string name="_bx_ads_menu_item_title_system_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_ads_menu_item_title_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_ads_menu_item_title_system_entries_popular"><![CDATA[Popular]]></string>
+    <string name="_bx_ads_menu_item_title_entries_popular"><![CDATA[Popular]]></string>
+    <string name="_bx_ads_menu_item_title_system_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_ads_menu_item_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_ads_menu_title_view_entry_submenu"><![CDATA[View Ad Submenu]]></string>
+    <string name="_bx_ads_menu_set_title_view_entry_submenu"><![CDATA[View Ad Submenu]]></string>
+    <string name="_bx_ads_menu_item_title_view_entry_submenu_entry"><![CDATA[Ad]]></string>
+    <string name="_bx_ads_menu_item_title_view_entry_submenu_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_ads_menu_item_title_view_entry_submenu_promotion"><![CDATA[Promotion]]></string>
+    <string name="_bx_ads_menu_item_title_system_view_entry_comments"><![CDATA[Ad Comments]]></string>
+    <string name="_bx_ads_menu_item_title_system_view_entry_promotion"><![CDATA[Ad Promotion]]></string>
+    <string name="_bx_ads_menu_item_title_system_view_entries_author"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_item_title_view_entries_author"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_item_title_system_view_entries_in_context"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_item_title_view_entries_in_context"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_title_entries_my"><![CDATA[My Ads Action Menu]]></string>
+    <string name="_bx_ads_menu_set_title_entries_my"><![CDATA[My Ads Menu]]></string>
+    <string name="_bx_ads_menu_item_title_system_manage_my_posts"><![CDATA[My ads]]></string>
+    <string name="_bx_ads_menu_item_title_manage_my_posts"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_set_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_ads_menu_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_ads_menu_item_title_system_manage_account"><![CDATA[Manage account]]></string>
+    <string name="_bx_ads_menu_item_title_manage_account"><![CDATA[Manage account]]></string>
+    <string name="_bx_ads_menu_item_title_system_admt_ads"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_item_title_admt_ads"><![CDATA[Ads]]></string>
+    <string name="_bx_ads_menu_item_title_manage_my"><![CDATA[My Ads]]></string>
+    <string name="_bx_ads_menu_item_title_manage_all"><![CDATA[All Ads]]></string>
+    <string name="_bx_ads_menu_item_title_categories"><![CDATA[Categories]]></string>
+    <string name="_bx_ads_menu_item_title_system_entries_categories"><![CDATA[Categories]]></string>
+    <string name="_bx_ads_menu_item_title_entries_categories"><![CDATA[Categories]]></string>
+    <string name="_bx_ads_menu_item_title_system_sm_price"><![CDATA[Price]]></string>
+    <string name="_bx_ads_menu_item_title_sm_price"><![CDATA[Price]]></string>
+    <string name="_bx_ads_menu_title_licenses_submenu"><![CDATA[Licenses Submenu]]></string>
+    <string name="_bx_ads_menu_set_title_licenses_submenu"><![CDATA[Licenses Submenu]]></string>
+    <string name="_bx_ads_menu_item_title_system_ads_licenses_administration"><![CDATA[All Licenses]]></string>
+    <string name="_bx_ads_menu_item_title_ads_licenses_administration"><![CDATA[All Licenses]]></string>
+    <string name="_bx_ads_menu_item_title_system_ads_licenses"><![CDATA[My Licenses]]></string>
+    <string name="_bx_ads_menu_item_title_ads_licenses"><![CDATA[My Licenses]]></string>
+    <string name="_bx_ads_menu_item_title_system_licenses"><![CDATA[Ads Licenses]]></string>
+    <string name="_bx_ads_menu_item_title_licenses"><![CDATA[Ads Licenses]]></string>
+    <string name="_bx_ads_menu_item_title_system_offers_all"><![CDATA[Ads Offers]]></string>
+    <string name="_bx_ads_menu_item_title_offers_all"><![CDATA[Offers]]></string>
+    <string name="_bx_ads_menu_item_title_system_sources"><![CDATA[Sources]]></string>
+    <string name="_bx_ads_menu_item_title_sources"><![CDATA[Sources]]></string>
+    
+
+    <string name="_bx_ads_grid_column_title_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_ads_grid_column_title_title"><![CDATA[Title]]></string>
+    <string name="_bx_ads_grid_column_title_subcategories"><![CDATA[Subcategories]]></string>
+    <string name="_bx_ads_grid_column_value_subcategories"><![CDATA[{0} subcategories]]></string>
+    <string name="_bx_ads_grid_column_info_subcategories"><![CDATA[View subcategories]]></string>
+    <string name="_bx_ads_grid_action_title_add"><![CDATA[Add New]]></string>
+    <string name="_bx_ads_grid_action_title_back"><![CDATA[Back]]></string>
+    <string name="_bx_ads_grid_action_title_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_ads_grid_popup_title_add"><![CDATA[Add New Category]]></string>
+    <string name="_bx_ads_grid_popup_title_edit"><![CDATA[Edit '{0}' Category]]></string>
+    <string name="_bx_ads_grid_action_err_add_category"><![CDATA[Cannot add a category.]]></string>
+    <string name="_bx_ads_grid_action_err_edit_category"><![CDATA[Cannot edit the category.]]></string>
+
+    <string name="_bx_ads_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></string>
+    <string name="_bx_ads_grid_filter_item_title_adm_select_one_filter2"><![CDATA[Any Audience]]></string>
+    <string name="_bx_ads_grid_filter_item_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_ads_grid_filter_item_title_adm_hidden"><![CDATA[Hidden]]></string>
+    <string name="_bx_ads_grid_filter_item_title_adm_pending"><![CDATA[Pending]]></string>
+    <string name="_bx_ads_grid_filter_item_title_adm_unpaid"><![CDATA[Unpaid]]></string>
+    <string name="_bx_ads_grid_column_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_ads_grid_column_title_adm_title"><![CDATA[Title]]></string>
+    <string name="_bx_ads_grid_column_title_adm_added"><![CDATA[Added]]></string>
+    <string name="_bx_ads_grid_column_title_adm_author"><![CDATA[Author]]></string>
+    <string name="_bx_ads_grid_action_title_adm_promotion"><![CDATA[Promotion]]></string>
+    <string name="_bx_ads_grid_action_title_adm_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_ads_grid_action_title_adm_edit_budget"><![CDATA[Edit budget]]></string>
+    <string name="_bx_ads_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_ads_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
+    <string name="_bx_ads_grid_action_title_adm_audit_content"><![CDATA[Audit]]></string>
+    <string name="_bx_ads_grid_column_title_adm_status_admin"><![CDATA[Moderation]]></string>
+    <string name="_bx_ads_grid_action_err_delete"><![CDATA[Cannot delete the selected ad(s).]]></string>
+    <string name="_bx_ads_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
+
+    <string name="_bx_ads_grid_column_title_lcs_profile_id"><![CDATA[Owner]]></string>
+    <string name="_bx_ads_grid_column_title_lcs_transaction"><![CDATA[Order]]></string>
+    <string name="_bx_ads_grid_column_title_lcs_entry"><![CDATA[Ad]]></string>
+    <string name="_bx_ads_grid_column_title_lcs_count"><![CDATA[Qt.]]></string>
+    <string name="_bx_ads_grid_column_title_lcs_license"><![CDATA[License]]></string>
+    <string name="_bx_ads_grid_column_title_lcs_added"><![CDATA[Date]]></string>
+
+    <string name="_bx_ads_grid_column_title_ofr_author_id"><![CDATA[Offerer]]></string>
+    <string name="_bx_ads_grid_column_title_ofr_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_ads_grid_column_title_ofr_quantity"><![CDATA[Quantity]]></string>
+    <string name="_bx_ads_grid_column_title_ofr_message"><![CDATA[Message]]></string>
+    <string name="_bx_ads_grid_column_title_ofr_added"><![CDATA[Added]]></string>
+    <string name="_bx_ads_grid_column_title_ofr_status"><![CDATA[Status]]></string>
+    <string name="_bx_ads_grid_action_title_ofr_accept"><![CDATA[Accept the offer]]></string>
+    <string name="_bx_ads_grid_action_title_ofr_decline"><![CDATA[Decline the offer]]></string>
+
+    <string name="_bx_ads_grid_column_title_ofrs_content_id"><![CDATA[Ad]]></string>
+    <string name="_bx_ads_grid_column_title_ofrs_offers_awating"><![CDATA[Awating]]></string>
+    <string name="_bx_ads_grid_column_title_ofrs_offers_total"><![CDATA[Total]]></string>
+    <string name="_bx_ads_grid_action_title_ofr_view"><![CDATA[View offers]]></string>
+
+    <string name="_bx_ads_chart_growth"><![CDATA[Ads: Growth]]></string>
+    <string name="_bx_ads_chart_growth_speed"><![CDATA[Ads: Growth Speed]]></string>
+    <string name="_bx_ads_chart_promotion_growth"><![CDATA[Ad Promotion: Growth]]></string>
+    <string name="_bx_ads_chart_promotion_growth_speed"><![CDATA[Ad Promotion: Growth Speed]]></string>
+    <string name="_bx_ads_chart_label_impressions"><![CDATA[Impressions]]></string>
+    <string name="_bx_ads_chart_label_clicks"><![CDATA[Clicks]]></string>
+    <string name="_bx_ads_chart_label_roi"><![CDATA[ROI]]></string>
+    <string name="_bx_ads_chart_label_roi_local"><![CDATA[Local income]]></string>
+    <string name="_bx_ads_chart_label_roi_source"><![CDATA[External income]]></string>
+    <string name="_bx_ads_chart_label_roi_investment"><![CDATA[Investment]]></string>
+
+    <string name="_bx_ads_search_extended"><![CDATA[Ads Search]]></string>
+    <string name="_bx_ads_search_extended_cmts"><![CDATA[Ads Comments Search]]></string>
+    <string name="_bx_ads_search_extended_reviews"><![CDATA[Ads Reviews Search]]></string>
+
+    <!-- Ctegories -->
+    <string name="_bx_ads_cat_type_price"><![CDATA[With price]]></string>
+    <string name="_bx_ads_cat_type_price_year"><![CDATA[With price and year]]></string>
+
+    <string name="_bx_ads_cat_title_job"><![CDATA[Jobs]]></string>
+    <string name="_bx_ads_cat_title_accounting_finance"><![CDATA[Accounting / finance jobs]]></string>
+    <string name="_bx_ads_cat_title_education_nonprofit"><![CDATA[Education / nonprofit jobs]]></string>
+    <string name="_bx_ads_cat_title_government_legal"><![CDATA[Government / legal jobs]]></string>
+    <string name="_bx_ads_cat_title_programming_web_design"><![CDATA[Programming / web design jobs]]></string>
+
+    <string name="_bx_ads_cat_title_music"><![CDATA[Music Exchange]]></string>
+    <string name="_bx_ads_cat_title_instrument_sale"><![CDATA[Music instruments for sale]]></string>
+    <string name="_bx_ads_cat_title_instrument_wanted"><![CDATA[Music instruments wanted]]></string>
+    
+    <string name="_bx_ads_cat_title_housing"><![CDATA[Housing & Rentals]]></string>
+    <string name="_bx_ads_cat_title_apartments_housing"><![CDATA[Apartments / housing]]></string>
+    <string name="_bx_ads_cat_title_office_commercial"><![CDATA[Office / commercial]]></string>
+    <string name="_bx_ads_cat_title_real_estate_sale"><![CDATA[Real estate for sale]]></string>
+    <string name="_bx_ads_cat_title_roommate"><![CDATA[Roommates]]></string>
+    <string name="_bx_ads_cat_title_temporary_rental"><![CDATA[Temporary rental]]></string>
+    
+    <string name="_bx_ads_cat_title_service"><![CDATA[Services]]></string>
+    <string name="_bx_ads_cat_title_automotive"><![CDATA[Automotive services]]></string>
+    <string name="_bx_ads_cat_title_educational"><![CDATA[Educational services]]></string>
+    <string name="_bx_ads_cat_title_financial"><![CDATA[Financial services]]></string>
+    <string name="_bx_ads_cat_title_labor_move"><![CDATA[Labor / move services]]></string>
+    <string name="_bx_ads_cat_title_legal"><![CDATA[Legal services]]></string>
+
+    <string name="_bx_ads_cat_title_casting"><![CDATA[Casting Calls]]></string>
+    <string name="_bx_ads_cat_title_acting"><![CDATA[Acting castings]]></string>
+    <string name="_bx_ads_cat_title_dance"><![CDATA[Dance castings]]></string>
+    <string name="_bx_ads_cat_title_modeling"><![CDATA[Modeling castings]]></string>
+    <string name="_bx_ads_cat_title_musician"><![CDATA[Musicians castings]]></string>
+    <string name="_bx_ads_cat_title_reality_show"><![CDATA[Reality shows castings]]></string>
+
+    <string name="_bx_ads_cat_title_personal"><![CDATA[Personals]]></string>
+    <string name="_bx_ads_cat_title_men_women"><![CDATA[Men seeking women]]></string>
+    <string name="_bx_ads_cat_title_women_men"><![CDATA[Women seeking men]]></string>
+    <string name="_bx_ads_cat_title_missed_connection"><![CDATA[Missed connections]]></string>
+
+    <string name="_bx_ads_cat_title_sale"><![CDATA[For Sale]]></string>
+    <string name="_bx_ads_cat_title_barter"><![CDATA[Barter]]></string>
+    <string name="_bx_ads_cat_title_clothing"><![CDATA[Sale clothing]]></string>
+    <string name="_bx_ads_cat_title_collectible"><![CDATA[Sale collectibles]]></string>
+
+    <string name="_bx_ads_cat_title_sale_car"><![CDATA[Cars For Sale]]></string>
+    <string name="_bx_ads_cat_title_auto_part"><![CDATA[Sale auto parts]]></string>
+    <string name="_bx_ads_cat_title_auto_truck"><![CDATA[Sale autos / trucks]]></string>
+    <string name="_bx_ads_cat_title_motorcycle"><![CDATA[Sale motorcycles]]></string>
+
+    <string name="_bx_ads_et_txt_name_interested"><![CDATA[I'm interested.]]></string>
+    <string name="_bx_ads_et_txt_subject_interested"><![CDATA[{viewer_name} is interested in your ad.]]></string>
+    <string name="_bx_ads_et_txt_body_interested"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p><b><a href="{viewer_url}">{viewer_name}</a></b> is interested in your ad <b><a href="{ad_url}">{ad_name}</a></b>.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_et_txt_name_purchased"><![CDATA[Ad was purchased.]]></string>
+    <string name="_bx_ads_et_txt_subject_purchased"><![CDATA[You've purchased {entry_name} ad.]]></string>
+    <string name="_bx_ads_et_txt_body_purchased"><![CDATA[{email_header}
+<p>Hello, {client_name}.</p>
+<p>You've purchased <a href="{entry_url}">{entry_name}</a> ad ({count} item(s)) from <a href="{vendor_url}">{vendor_name}</a>.<br />Your license is <b>{license}</b>.</p>
+<p>{notes}</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_et_txt_name_shipped"><![CDATA[Ad was shipped.]]></string>
+    <string name="_bx_ads_et_txt_subject_shipped"><![CDATA[Ad {entry_name} was shipped.]]></string>
+    <string name="_bx_ads_et_txt_body_shipped"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p>The <a href="{entry_url}">{entry_name}</a> ad was shipped by <a href="{vendor_url}">{vendor_name}</a>.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_et_txt_name_received"><![CDATA[Ad received.]]></string>
+    <string name="_bx_ads_et_txt_subject_received"><![CDATA[Ad {entry_name} was received.]]></string>
+    <string name="_bx_ads_et_txt_body_received"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p>The <a href="{entry_url}">{entry_name}</a> ad was received by <a href="{client_url}">{client_name}</a>.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_et_txt_name_offer_added"><![CDATA[New offer was added.]]></string>
+    <string name="_bx_ads_et_txt_subject_offer_added"><![CDATA[{viewer_name} added an offer for your ad.]]></string>
+    <string name="_bx_ads_et_txt_body_offer_added"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p><b><a href="{viewer_url}">{viewer_name}</a></b> added an offer for your ad <b><a href="{entry_url}">{entry_name}</a></b>.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_et_txt_name_offer_accepted"><![CDATA[The offer was accepted.]]></string>
+    <string name="_bx_ads_et_txt_subject_offer_accepted"><![CDATA[Your offer for {entry_name} ad was accepted.]]></string>
+    <string name="_bx_ads_et_txt_body_offer_accepted"><![CDATA[{email_header}
+<p>Hello, {offerer_name}.</p>
+<p>Your offer for <b><a href="{entry_url}">{entry_name}</a></b> ad was accepted.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_et_txt_name_offer_declined"><![CDATA[The offer was declined.]]></string>
+    <string name="_bx_ads_et_txt_subject_offer_declined"><![CDATA[Your offer for {entry_name} ad was declined.]]></string>
+    <string name="_bx_ads_et_txt_body_offer_declined"><![CDATA[{email_header}
+<p>Hello, {offerer_name}.</p>
+<p>Your offer for <b><a href="{entry_url}">{entry_name}</a></b> ad was declined.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_et_txt_name_offer_canceled"><![CDATA[The offer was canceled.]]></string>
+    <string name="_bx_ads_et_txt_subject_offer_canceled"><![CDATA[{offerer_name} canceled the offer for {entry_name} ad.]]></string>
+    <string name="_bx_ads_et_txt_body_offer_canceled"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p><a href="{offerer_url}">{offerer_name}</a> canceled the offer for <b><a href="{entry_url}">{entry_name}</a></b> ad.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_ads_txt_all_entries_in"><![CDATA[<a href="{0}">All Ads in {1} ({2})</a>]]></string>
+    <string name="_bx_ads_txt_all_entries_by"><![CDATA[{0} ads]]></string>
+    <string name="_bx_ads_txt_sample_single"><![CDATA[ad]]></string>
+    <string name="_bx_ads_txt_sample_single_with_article"><![CDATA[an ad]]></string>
+    <string name="_bx_ads_txt_sample_comment_single"><![CDATA[comment to ad]]></string>
+    <string name="_bx_ads_txt_sample_vote_single"><![CDATA[vote to ad]]></string>
+    <string name="_bx_ads_txt_sample_reaction_single"><![CDATA['{0}' reaction to ad]]></string>
+    <string name="_bx_ads_txt_sample_score_up_single"><![CDATA[score up vote to ad]]></string>
+    <string name="_bx_ads_txt_sample_score_down_single"><![CDATA[score down vote to ad]]></string>
+    <string name="_bx_ads_txt_sample_interest_single"><![CDATA[interested in ad]]></string>
+    <string name="_bx_ads_txt_status_offer"><![CDATA[Reserved]]></string>
+    <string name="_bx_ads_txt_status_sold"><![CDATA[Sold]]></string>
+    <string name="_bx_ads_txt_display_title_add"><![CDATA[{0}: Add]]></string>
+    <string name="_bx_ads_txt_display_title_edit"><![CDATA[{0}: Edit]]></string>
+    <string name="_bx_ads_txt_display_title_view"><![CDATA[{0}: View]]></string>
+    <string name="_bx_ads_txt_processing_awaiting"><![CDATA[Ad will be published as soon as video is converted]]></string>
+    <string name="_bx_ads_txt_processing_failed"><![CDATA[Ad isn't published because video conversion failed, edit video to fix the issue.]]></string>
+    <string name="_bx_ads_txt_free"><![CDATA[Free]]></string>
+    <string name="_bx_ads_txt_cd_ct_product"><![CDATA[Payment for product]]></string>
+    <string name="_bx_ads_txt_cd_ct_promotion"><![CDATA[Payment for promotion]]></string>
+    <string name="_bx_ads_txt_purchased_note"><![CDATA[Contact <a href="{0}">{1}</a> if you have any questions or need support.]]></string>
+    <string name="_bx_ads_txt_offer_status_awaiting"><![CDATA[Awaiting]]></string>
+    <string name="_bx_ads_txt_offer_status_accepted"><![CDATA[Accepted]]></string>
+    <string name="_bx_ads_txt_offer_status_declined"><![CDATA[Declined]]></string>
+    <string name="_bx_ads_txt_offer_status_canceled"><![CDATA[Canceled]]></string>
+    <string name="_bx_ads_txt_offer_status_paid"><![CDATA[Paid]]></string>
+    <string name="_bx_ads_txt_your_offer"><![CDATA[Your offer]]></string>
+    <string name="_bx_ads_txt_accepted"><![CDATA[was accepted]]></string>
+    <string name="_bx_ads_txt_n_items"><![CDATA[{0} item(s)]]></string>
+    <string name="_bx_ads_txt_pay"><![CDATA[Pay]]></string>
+    <string name="_bx_ads_txt_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_ads_txt_msg_status_hidden"><![CDATA[This ad was hidden.]]></string>
+    <string name="_bx_ads_txt_msg_status_pending"><![CDATA[This ad is pending approval.]]></string>
+    <string name="_bx_ads_txt_msg_auction_offer"><![CDATA[This ad is under offer.]]></string>
+    <string name="_bx_ads_txt_msg_auction_sold"><![CDATA[This ad was sold.]]></string>
+    <string name="_bx_ads_txt_msg_author_notified"><![CDATA[Advertiser was notified, that you're interested in his ad. Please wait.]]></string>
+    <string name="_bx_ads_txt_msg_offer_added"><![CDATA[Your offer was added. Advertiser will be notified about it. Please wait.]]></string>
+    <string name="_bx_ads_txt_msg_offer_accepted"><![CDATA[The offer was accepted.]]></string>
+    <string name="_bx_ads_txt_msg_offer_declined"><![CDATA[The offer was declined.]]></string>
+    <string name="_bx_ads_txt_msg_offer_canceled"><![CDATA[The offer was canceled.]]></string>
+    <string name="_bx_ads_txt_msg_promotion_unpaid"><![CDATA[The promotion campaign for the ad still hasn't been paid]]></string>
+    <string name="_bx_ads_txt_err_offer_duplicate"><![CDATA[You already made an offer. Please wait for the results.]]></string>
+    <string name="_bx_ads_txt_err_offer_wrong_quantity"><![CDATA[Offered quantity is out of stock. Only {0} items are available.]]></string>
+    <string name="_bx_ads_txt_err_offer_accepted"><![CDATA[Your offer was already accepted.]]></string>
+    <string name="_bx_ads_txt_err_your_own"><![CDATA[You cannot do the action because it's your own ad.]]></string>
+    <string name="_bx_ads_txt_err_duplicate"><![CDATA[You already performed the action.]]></string>
+    <string name="_bx_ads_txt_err_cannot_perform_action"><![CDATA[Cannot perform action. Please report.]]></string>
+
+    <string name="_bx_ads_txt_poll_view_answers"><![CDATA[poll]]></string>
+    <string name="_bx_ads_txt_poll_view_results"><![CDATA[results]]></string>
+    <string name="_bx_ads_txt_poll_answer_vote_do_by"><![CDATA[Voted for]]></string>
+    <string name="_bx_ads_txt_poll_answer_vote_counter"><![CDATA[{0} votes]]></string>
+    <string name="_bx_ads_txt_poll_answer_vote_percent"><![CDATA[({0}%)]]></string>
+
+    <string name="_bx_ads_alert_action_doInterest_personal"><![CDATA[Someone interested in your ad]]></string>
+    <string name="_bx_ads_alert_action_info_doInterest_personal"><![CDATA[A member has shown interest in your advertisement.]]></string>
+    <string name="_bx_ads_txt_ntfs_subobject_interested"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> {subentry_sample} <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+
+    <string name="_bx_ads_alert_action_license_register_personal"><![CDATA[Your ad was purchased]]></string>
+    <string name="_bx_ads_alert_action_info_license_register_personal"><![CDATA[A member purchased the item or service you advertised.]]></string>
+    <string name="_bx_ads_txt_ntfs_object_license_register"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> purchased your ad <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+
+    <string name="_bx_ads_alert_action_shipped_personal"><![CDATA[Ad was shipped]]></string>
+    <string name="_bx_ads_txt_ntfs_object_shipped"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> shipped the ad <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+
+    <string name="_bx_ads_alert_action_received_personal"><![CDATA[Ad was received]]></string>
+    <string name="_bx_ads_txt_ntfs_object_received"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> received the ad <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+
+    <string name="_bx_ads_alert_action_offer_added_personal"><![CDATA[Offer was added]]></string>
+    <string name="_bx_ads_txt_ntfs_offer_added"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> added an offer to your ad <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+
+    <string name="_bx_ads_alert_action_offer_accepted_personal"><![CDATA[Offer was accepted]]></string>
+    <string name="_bx_ads_txt_ntfs_offer_accepted"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> accepted your offer for the ad <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+
+    <string name="_bx_ads_alert_action_offer_declined_personal"><![CDATA[Offer was declined]]></string>
+    <string name="_bx_ads_txt_ntfs_offer_declined"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> declined your offer for the ad <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+
+    <string name="_bx_ads_alert_action_offer_canceled_personal"><![CDATA[Offer was canceled]]></string>
+    <string name="_bx_ads_txt_ntfs_offer_canceled"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> canceled the offer for your ad <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
+</resources>

+ 1141 - 0
modules/boonex/ads/updates/15.0.2_15.0.3/source/install/sql/install.sql

@@ -0,0 +1,1141 @@
+-- TABLE: entries
+CREATE TABLE IF NOT EXISTS `bx_ads_entries` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `author` int(11) NOT NULL,
+  `added` int(11) NOT NULL,
+  `changed` int(11) NOT NULL,
+  `sold` int(11) NOT NULL,
+  `shipped` int(11) NOT NULL,
+  `received` int(11) NOT NULL,
+  `source_type` varchar(32) NOT NULL DEFAULT '',
+  `source` varchar(255) NOT NULL DEFAULT '',
+  `category` int(11) NOT NULL,
+  `thumb` int(11) NOT NULL,
+  `name` varchar(255) NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `url` varchar(255) NOT NULL,
+  `price` float NOT NULL,
+  `auction` tinyint(4) NOT NULL DEFAULT '0',
+  `quantity` int(11) NOT NULL default '1',
+  `single` tinyint(4) NOT NULL DEFAULT '1',
+  `year` int(11) NOT NULL,
+  `text` mediumtext NOT NULL,
+  `notes_purchased` text NOT NULL,
+  `labels` text NOT NULL,
+  `tags` text NOT NULL,
+  `location` text NOT NULL,
+  `budget_total` float NOT NULL default '0',
+  `budget_daily` float NOT NULL default '0',
+  `impressions` int(11) unsigned NOT NULL default '0',
+  `clicks` int(11) unsigned NOT NULL default '0',
+  `views` int(11) NOT NULL default '0',
+  `rate` float NOT NULL default '0',
+  `votes` int(11) NOT NULL default '0',
+  `rrate` float NOT NULL default '0',
+  `rvotes` int(11) NOT NULL default '0',
+  `score` int(11) NOT NULL default '0',
+  `sc_up` int(11) NOT NULL default '0',
+  `sc_down` int(11) NOT NULL default '0',
+  `favorites` int(11) NOT NULL default '0',
+  `comments` int(11) NOT NULL default '0',
+  `reviews` int(11) NOT NULL default '0',
+  `reviews_avg` float NOT NULL default '0',
+  `reports` int(11) NOT NULL default '0',
+  `featured` int(11) NOT NULL default '0',
+  `seg` tinyint(4) NOT NULL DEFAULT '0',
+  `seg_gender` tinyint(4) NOT NULL DEFAULT '0',
+  `seg_age_min` int(11) NOT NULL default '0',
+  `seg_age_max` int(11) NOT NULL default '0',
+  `seg_tags` tinyint(4) NOT NULL DEFAULT '0',
+  `seg_country` varchar(255) NOT NULL DEFAULT '',
+  `cf` int(11) NOT NULL default '1',
+  `allow_view_to` varchar(16) NOT NULL DEFAULT '3',
+  `status` enum('active','awaiting','offer','sold','hidden') NOT NULL DEFAULT 'active',
+  `status_admin` enum('active','hidden','pending','unpaid') NOT NULL DEFAULT 'active',
+  PRIMARY KEY (`id`),
+  FULLTEXT KEY `title_text` (`title`,`text`)
+);
+
+-- TABLE: types of categories
+CREATE TABLE IF NOT EXISTS `bx_ads_categories_types` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(64) NOT NULL DEFAULT '',
+  `title` varchar(255) NOT NULL DEFAULT '',
+  `display_add` varchar(255) NOT NULL DEFAULT '',
+  `display_edit` varchar(255) NOT NULL DEFAULT '',
+  `display_view` varchar(255) NOT NULL DEFAULT '',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`)
+);
+
+INSERT INTO `bx_ads_categories_types` (`id`, `name`, `title`, `display_add`, `display_edit`, `display_view`) VALUES
+(1, 'price', '_bx_ads_cat_type_price', 'bx_ads_entry_price_add', 'bx_ads_entry_price_edit', 'bx_ads_entry_price_view'),
+(2, 'price_year', '_bx_ads_cat_type_price_year', 'bx_ads_entry_price_year_add', 'bx_ads_entry_price_year_edit', 'bx_ads_entry_price_year_view');
+
+-- TABLE: categories
+CREATE TABLE IF NOT EXISTS `bx_ads_categories` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `parent_id` int(11) unsigned NOT NULL DEFAULT '0',
+  `level` tinyint(11) unsigned NOT NULL DEFAULT '0',
+  `type` int(11) NOT NULL DEFAULT '0',
+  `name` varchar(64) NOT NULL DEFAULT '',
+  `title` varchar(255) NOT NULL DEFAULT '',
+  `text` text NOT NULL,
+  `icon` varchar(255) NOT NULL DEFAULT '',
+  `items` int(11) NOT NULL DEFAULT '0',
+  `active` tinyint(4) NOT NULL DEFAULT '1',
+  `order` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`),
+  FULLTEXT KEY `title_text` (`title`,`text`)
+);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 1, 'job', '_bx_ads_cat_title_job', '', 'user-md', 1, 1);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 1, 'job_finance', '_bx_ads_cat_title_accounting_finance', '', '', 1, 1),
+(@iParentId, 1, 1, 'job_education', '_bx_ads_cat_title_education_nonprofit', '', '', 1, 2),
+(@iParentId, 1, 1, 'job_legal', '_bx_ads_cat_title_government_legal', '', '', 1, 3),
+(@iParentId, 1, 1, 'job_programming', '_bx_ads_cat_title_programming_web_design', '', '', 1, 4);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 2, 'music', '_bx_ads_cat_title_music', '', 'music', 1, 2);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 2, 'music_isale', '_bx_ads_cat_title_instrument_sale', '', '', 1, 1),
+(@iParentId, 1, 2, 'music_iwanted', '_bx_ads_cat_title_instrument_wanted', '', '', 1, 2);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 1, 'housing', '_bx_ads_cat_title_housing', '', 'home', 1, 3);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 1, 'housing_apartments', '_bx_ads_cat_title_apartments_housing', '', '', 1, 1),
+(@iParentId, 1, 1, 'housing_office', '_bx_ads_cat_title_office_commercial', '', '', 1, 2),
+(@iParentId, 1, 1, 'housing_re_sale', '_bx_ads_cat_title_real_estate_sale', '', '', 1, 3),
+(@iParentId, 1, 1, 'housing_roommate', '_bx_ads_cat_title_roommate', '', '', 1, 4),
+(@iParentId, 1, 1, 'housing_temp_rental', '_bx_ads_cat_title_temporary_rental', '', '', 1, 5);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 1, 'service', '_bx_ads_cat_title_service', '', 'wrench', 1, 4);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 1, 'service_automotive', '_bx_ads_cat_title_automotive', '', '', 1, 1),
+(@iParentId, 1, 1, 'service_educational', '_bx_ads_cat_title_educational', '', '', 1, 2),
+(@iParentId, 1, 1, 'service_financial', '_bx_ads_cat_title_financial', '', '', 1, 3),
+(@iParentId, 1, 1, 'service_labor', '_bx_ads_cat_title_labor_move', '', '', 1, 4),
+(@iParentId, 1, 1, 'service_legal', '_bx_ads_cat_title_legal', '', '', 1, 5);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 1, 'casting', '_bx_ads_cat_title_casting', '', 'eye', 1, 5);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 1, 'casting_acting', '_bx_ads_cat_title_acting', '', '', 1, 1),
+(@iParentId, 1, 1, 'casting_dance', '_bx_ads_cat_title_dance', '', '', 1, 2),
+(@iParentId, 1, 1, 'casting_modeling', '_bx_ads_cat_title_modeling', '', '', 1, 3),
+(@iParentId, 1, 1, 'casting_musician', '_bx_ads_cat_title_musician', '', '', 1, 4),
+(@iParentId, 1, 1, 'casting_rshow', '_bx_ads_cat_title_reality_show', '', '', 1, 5);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 2, 'personal', '_bx_ads_cat_title_personal', '', 'user', 1, 6);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 2, 'personal_mw', '_bx_ads_cat_title_men_women', '', '', 1, 1),
+(@iParentId, 1, 2, 'personal_wm', '_bx_ads_cat_title_women_men', '', '', 1, 2),
+(@iParentId, 1, 2, 'personal_missed', '_bx_ads_cat_title_missed_connection', '', '', 1, 3);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 2, 'sale', '_bx_ads_cat_title_sale', '', 'shopping-cart', 1, 7);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 2, 'sale_barter', '_bx_ads_cat_title_barter', '', '', 1, 1),
+(@iParentId, 1, 2, 'sale_clothing', '_bx_ads_cat_title_clothing', '', '', 1, 1),
+(@iParentId, 1, 2, 'sale_collectible', '_bx_ads_cat_title_collectible', '', '', 1, 1);
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(0, 0, 2, 'sale_car', '_bx_ads_cat_title_sale_car', '', 'truck', 1, 8);
+SET @iParentId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_categories` (`parent_id`, `level`, `type`, `name`, `title`, `text`, `icon`, `active`, `order`) VALUES 
+(@iParentId, 1, 2, 'sale_car_part', '_bx_ads_cat_title_auto_part', '', '', 1, 1),
+(@iParentId, 1, 2, 'sale_car_auto', '_bx_ads_cat_title_auto_truck', '', '', 1, 2),
+(@iParentId, 1, 2, 'sale_car_motorcycle', '_bx_ads_cat_title_motorcycle', '', '', 1, 3);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_interested_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `interested` (`entry_id`, `profile_id`)
+);
+
+-- TABLE: sources
+CREATE TABLE IF NOT EXISTS `bx_ads_sources` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(64) NOT NULL default '',
+  `caption` varchar(128) NOT NULL default '',
+  `description` varchar(128) NOT NULL default '',
+  `option_prefix` varchar(32) NOT NULL default '',
+  `active` tinyint(4) NOT NULL default '0',
+  `order` tinyint(4) NOT NULL default '0',
+  `class_name` varchar(128) NOT NULL default '',
+  `class_file` varchar(255) NOT NULL  default '',
+  PRIMARY KEY(`id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_sources_options` (
+  `id` int(11) NOT NULL auto_increment,
+  `source_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`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_sources_options_values` (
+  `id` int(11) NOT NULL auto_increment,
+  `profile_id` int(11) NOT NULL default '0',
+  `option_id` int(11) NOT NULL default '0',  
+  `value` varchar(255) NOT NULL default '',
+  PRIMARY KEY(`id`),
+  UNIQUE KEY `value`(`profile_id`, `option_id`)
+);
+
+-- Shopify (Admin API) source
+INSERT INTO `bx_ads_sources`(`name`, `caption`, `description`, `option_prefix`, `active`, `order`, `class_name`) VALUES
+('shopify_admin', '_bx_ads_src_cpt_shopify_admin', '_bx_ads_src_dsc_shopify_admin', 'shf_adm_', 1, 1, 'BxAdsSourceShopifyAdmin');
+SET @iSourceId = LAST_INSERT_ID();
+
+INSERT INTO `bx_ads_sources_options`(`source_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
+(@iSourceId, 'shf_adm_active', 'checkbox', '_bx_ads_src_opt_cpt_active', '_bx_ads_src_opt_dsc_active', '', '', '', '', 1),
+(@iSourceId, 'shf_adm_shop_domain', 'text', '_bx_ads_src_opt_cpt_shop_domain', '_bx_ads_src_opt_dsc_shop_domain', '', '', '', '', 2),
+(@iSourceId, 'shf_adm_access_token', 'text', '_bx_ads_src_opt_cpt_access_token', '_bx_ads_src_opt_dsc_access_token', '', '', '', '', 3);
+
+
+-- TABLE: types of categories
+CREATE TABLE IF NOT EXISTS `bx_ads_commodities` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `type` varchar(16) NOT NULL DEFAULT '',
+  `amount` float NOT NULL,
+  `added` int(11) NOT NULL,
+  PRIMARY KEY (`id`)
+);
+
+-- TABLE: licenses
+CREATE TABLE IF NOT EXISTS `bx_ads_licenses` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `entry_id` int(11) unsigned NOT NULL default '0',
+  `count` int(11) unsigned NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `license` varchar(32) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  `new` tinyint(1) NOT NULL default '1',
+  PRIMARY KEY (`id`),
+  KEY `product_id` (`entry_id`, `profile_id`),
+  KEY `license` (`license`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_licenses_deleted` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `entry_id` int(11) unsigned NOT NULL default '0',
+  `count` int(11) unsigned NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `license` varchar(32) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  `new` tinyint(1) NOT NULL default '1',
+  `reason` varchar(16) NOT NULL default '',
+  `deleted` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `product_id` (`entry_id`,`profile_id`),
+  KEY `license` (`license`)
+);
+
+-- TABLE: promotions
+CREATE TABLE IF NOT EXISTS `bx_ads_promo_licenses` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `commodity_id` int(11) unsigned NOT NULL default '0',
+  `entry_id` int(11) unsigned NOT NULL default '0',
+  `amount` float NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `license` varchar(32) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `license` (`license`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_promo_licenses_deleted` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `commodity_id` int(11) unsigned NOT NULL default '0',
+  `entry_id` int(11) unsigned NOT NULL default '0',
+  `amount` float NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `license` varchar(32) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  `reason` varchar(16) NOT NULL default '',
+  `deleted` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `license` (`license`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_promo_tracker` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) unsigned NOT NULL default '0',
+  `date` int(11) unsigned NOT NULL default '0',
+  `impressions` int(11) unsigned NOT NULL default '0',
+  `clicks` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `track` (`entry_id`, `date`)
+);
+
+
+-- TABLE: storages & transcoders
+CREATE TABLE IF NOT EXISTS `bx_ads_covers` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_files` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_photos` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_photos_resized` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_videos` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `dimensions` varchar(12) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_videos_resized` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+-- TABLES: LINKS
+CREATE TABLE IF NOT EXISTS `bx_ads_links` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `media_id` int(11) NOT NULL DEFAULT '0',
+  `url` varchar(255) NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `text` text NOT NULL,
+  `added` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `profile_id` (`profile_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_links2content` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `content_id` int(11) NOT NULL DEFAULT '0',
+  `link_id` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `link` (`link_id`, `content_id`)
+);
+
+-- TABLE: comments
+CREATE TABLE IF NOT EXISTS `bx_ads_cmts` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  `cmt_cf` int(11) NOT NULL default '1',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_cmts_notes` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+-- TABLE: reviews
+CREATE TABLE IF NOT EXISTS `bx_ads_reviews` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  `cmt_cf` int(11) NOT NULL default '1',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+-- TABLE: votes
+CREATE TABLE IF NOT EXISTS `bx_ads_votes` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_votes_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_reactions` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `reaction` varchar(32) NOT NULL default '',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),    
+  UNIQUE KEY `reaction` (`object_id`, `reaction`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_reactions_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `reaction` varchar(32) NOT NULL default '',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+-- TABLE: views
+CREATE TABLE IF NOT EXISTS `bx_ads_views_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,  
+  `object_id` int(11) NOT NULL default '0',
+  `viewer_id` int(11) NOT NULL default '0',
+  `viewer_nip` int(11) unsigned NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`), 
+  KEY `id` (`object_id`,`viewer_id`,`viewer_nip`)
+);
+
+-- TABLE: metas
+CREATE TABLE IF NOT EXISTS `bx_ads_meta_keywords` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,  
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`),  
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_meta_mentions` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(10) unsigned NOT NULL,
+  `profile_id` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),  
+  KEY `object_id` (`object_id`),
+  KEY `profile_id` (`profile_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_meta_locations` (
+  `object_id` int(10) unsigned NOT NULL,
+  `lat` double NOT NULL,
+  `lng` double NOT NULL,
+  `country` varchar(2) NOT NULL,
+  `state` varchar(255) NOT NULL,
+  `city` varchar(255) NOT NULL,
+  `zip` varchar(255) NOT NULL,
+  `street` varchar(255) NOT NULL,
+  `street_number` varchar(255) NOT NULL,
+  PRIMARY KEY (`object_id`),
+  KEY `country_state_city` (`country`,`state`(8),`city`(8))
+);
+
+-- TABLE: reports
+CREATE TABLE IF NOT EXISTS `bx_ads_reports` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_reports_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(32) NOT NULL default '',
+  `text` text NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  `checked_by` int(11) NOT NULL default '0',
+  `status` tinyint(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `report` (`object_id`, `author_nip`)
+);
+
+-- TABLES: favorites
+CREATE TABLE IF NOT EXISTS `bx_ads_favorites_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `list_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+   PRIMARY KEY (`id`),
+  KEY `id` (`object_id`,`author_id`)
+);
+
+CREATE TABLE `bx_ads_favorites_lists` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) NOT NULL,
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  `allow_view_favorite_list_to` varchar(16) NOT NULL DEFAULT '3',
+   PRIMARY KEY (`id`)
+);
+
+-- TABLE: scores
+CREATE TABLE IF NOT EXISTS `bx_ads_scores` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count_up` int(11) NOT NULL default '0',
+  `count_down` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_scores_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(8) NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+-- TABLE: polls
+CREATE TABLE IF NOT EXISTS `bx_ads_polls` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `author_id` int(11) NOT NULL default '0',
+  `content_id` int(11) NOT NULL default '0',
+  `text` text NOT NULL,
+  PRIMARY KEY (`id`),
+  FULLTEXT KEY `search_fields` (`text`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_polls_answers` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `poll_id` int(11) unsigned NOT NULL default '0',
+  `title` varchar(255) NOT NULL,
+  `rate` float NOT NULL default '0',
+  `votes` int(11) NOT NULL default '0',
+  `order` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  FULLTEXT KEY `title` (`title`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_polls_answers_votes` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_ads_polls_answers_votes_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+-- TABLE: offers
+CREATE TABLE IF NOT EXISTS `bx_ads_offers` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `content_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `added` int(11) NOT NULL default '0',
+  `changed` int(11) NOT NULL default '0',
+  `amount` float NOT NULL default '0',
+  `quantity` int(11) NOT NULL default '0',
+  `message` text NOT NULL,
+  `status` enum('accepted','awaiting','declined','canceled','paid') NOT NULL DEFAULT 'awaiting',
+  PRIMARY KEY (`id`)
+);
+
+
+-- STORAGES & TRANSCODERS
+SET @sStorageEngine = (SELECT `value` FROM `sys_options` WHERE `name` = 'sys_storage_default');
+
+INSERT INTO `sys_objects_storage` (`object`, `engine`, `params`, `token_life`, `cache_control`, `levels`, `table_files`, `ext_mode`, `ext_allow`, `ext_deny`, `quota_size`, `current_size`, `quota_number`, `current_number`, `max_file_size`, `ts`) VALUES
+('bx_ads_covers', @sStorageEngine, '', 360, 2592000, 3, 'bx_ads_covers', 'allow-deny', '{image}', '', 0, 0, 0, 0, 0, 0),
+
+('bx_ads_photos', @sStorageEngine, '', 360, 2592000, 3, 'bx_ads_photos', 'allow-deny', '{image}', '', 0, 0, 0, 0, 0, 0),
+('bx_ads_photos_resized', @sStorageEngine, '', 360, 2592000, 3, 'bx_ads_photos_resized', 'allow-deny', '{image}', '', 0, 0, 0, 0, 0, 0),
+
+('bx_ads_videos', @sStorageEngine, '', 360, 2592000, 3, 'bx_ads_videos', 'allow-deny', '{video}', '', 0, 0, 0, 0, 0, 0),
+('bx_ads_videos_resized', @sStorageEngine, '', 360, 2592000, 3, 'bx_ads_videos_resized', 'allow-deny', '{imagevideo}', '', 0, 0, 0, 0, 0, 0),
+
+('bx_ads_files', @sStorageEngine, '', 360, 2592000, 3, 'bx_ads_files', 'deny-allow', '', '{dangerous}', 0, 0, 0, 0, 0, 0);
+
+INSERT INTO `sys_objects_transcoder` (`object`, `storage_object`, `source_type`, `source_params`, `private`, `atime_tracking`, `atime_pruning`, `ts`, `override_class_name`, `override_class_file`) VALUES 
+('bx_ads_preview', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_covers";}', 'no', '1', '2592000', '0', '', ''),
+('bx_ads_gallery', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_covers";}', 'no', '1', '2592000', '0', '', ''),
+('bx_ads_cover', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_covers";}', 'no', '1', '2592000', '0', '', ''),
+
+('bx_ads_preview_photos', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_photos";}', 'no', '1', '2592000', '0', '', ''),
+('bx_ads_gallery_photos', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_photos";}', 'no', '1', '2592000', '0', '', ''),
+('bx_ads_view_photos', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_photos";}', 'no', '1', '2592000', '0', '', ''),
+
+('bx_ads_videos_poster', 'bx_ads_videos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_videos";}', 'no', '0', '0', '0', 'BxDolTranscoderVideo', ''),
+('bx_ads_videos_poster_preview', 'bx_ads_videos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_videos";}', 'no', '0', '0', '0', 'BxDolTranscoderVideo', ''),
+('bx_ads_videos_mp4', 'bx_ads_videos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_videos";}', 'no', '0', '0', '0', 'BxDolTranscoderVideo', ''),
+('bx_ads_videos_mp4_hd', 'bx_ads_videos_resized', 'Storage', 'a:1:{s:6:"object";s:13:"bx_ads_videos";}', 'no', '0', '0', '0', 'BxDolTranscoderVideo', ''),
+
+('bx_ads_preview_files', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:12:"bx_ads_files";}', 'no', '1', '2592000', '0', '', ''),
+('bx_ads_gallery_files', 'bx_ads_photos_resized', 'Storage', 'a:1:{s:6:"object";s:12:"bx_ads_files";}', 'no', '1', '2592000', '0', '', '');
+
+INSERT INTO `sys_transcoder_filters` (`transcoder_object`, `filter`, `filter_params`, `order`) VALUES 
+('bx_ads_preview', 'Resize', 'a:3:{s:1:"w";s:3:"300";s:1:"h";s:3:"200";s:11:"crop_resize";s:1:"1";}', '0'),
+('bx_ads_gallery', 'Resize', 'a:1:{s:1:"w";s:3:"500";}', '0'),
+('bx_ads_cover', 'Resize', 'a:1:{s:1:"w";s:4:"1200";}', '0'),
+
+('bx_ads_preview_photos', 'Resize', 'a:3:{s:1:"w";s:3:"300";s:1:"h";s:3:"200";s:11:"crop_resize";s:1:"1";}', '0'),
+('bx_ads_gallery_photos', 'Resize', 'a:4:{s:1:"w";s:3:"600";s:1:"h";s:3:"600";s:13:"square_resize";s:1:"1";s:10:"force_type";s:3:"jpg";}', '0'),
+('bx_ads_view_photos', 'Resize',  'a:2:{s:1:"w";s:4:"1200";s:1:"h";s:4:"1200";}', '0'),
+
+('bx_ads_videos_poster_preview', 'Resize', 'a:3:{s:1:"w";s:3:"300";s:1:"h";s:3:"200";s:13:"square_resize";s:1:"1";}', 10),
+('bx_ads_videos_poster_preview', 'Poster', 'a:2:{s:1:"h";s:3:"480";s:10:"force_type";s:3:"jpg";}', 0),
+('bx_ads_videos_poster', 'Poster', 'a:2:{s:1:"h";s:3:"318";s:10:"force_type";s:3:"jpg";}', 0),
+('bx_ads_videos_mp4', 'Mp4', 'a:2:{s:1:"h";s:3:"318";s:10:"force_type";s:3:"mp4";}', 0),
+('bx_ads_videos_mp4_hd', 'Mp4', 'a:3:{s:1:"h";s:3:"720";s:13:"video_bitrate";s:4:"1536";s:10:"force_type";s:3:"mp4";}', 0),
+
+('bx_ads_preview_files', 'Resize', 'a:3:{s:1:"w";s:3:"300";s:1:"h";s:3:"200";s:11:"crop_resize";s:1:"1";}', '0'),
+('bx_ads_gallery_files', 'Resize', 'a:4:{s:1:"w";s:3:"600";s:1:"h";s:3:"600";s:13:"square_resize";s:1:"1";s:10:"force_type";s:3:"jpg";}', '0');
+
+
+-- FORMS: sources details
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_ads_form_sources_details', 'bx_ads', '_bx_ads_form_sources_details', '', '', 'submit', '', 'id', '', '', 'a:1:{s:14:"checker_helper";s:36:"BxAdsSourcesDetailsFormCheckerHelper";}', 0, 1, 'BxAdsFormSourcesDetails', 'modules/boonex/ads/classes/BxAdsFormSourcesDetails.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_ads_form_sources_details_edit', 'bx_ads', 'bx_ads_form_sources_details', '_bx_ads_form_sources_details_display_edit', 0);
+
+-- FORMS: category
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_ads_category', 'bx_ads', '_bx_ads_form_category', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_ads_categories', 'id', '', '', 'a:1:{i:0;s:9:"do_submit";}', '', 0, 1, 'BxAdsFormCategory', 'modules/boonex/ads/classes/BxAdsFormCategory.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_ads_category', 'bx_ads_category_add', 'bx_ads', 0, '_bx_ads_form_category_display_add'),
+('bx_ads_category', 'bx_ads_category_delete', 'bx_ads', 0, '_bx_ads_form_category_display_delete'),
+('bx_ads_category', 'bx_ads_category_edit', 'bx_ads', 0, '_bx_ads_form_category_display_edit');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_ads_category', 'bx_ads', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_ads_form_category_input_sys_delete_confirm', '_bx_ads_form_category_input_delete_confirm', '_bx_ads_form_category_input_delete_confirm_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_category_input_delete_confirm_error', '', '', 1, 0),
+('bx_ads_category', 'bx_ads', 'parent_id', '', '', 0, 'select', '_bx_ads_form_category_input_sys_parent_id', '_bx_ads_form_category_input_parent_id', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_ads_category', 'bx_ads', 'title', '', '', 0, 'text_translatable', '_bx_ads_form_category_input_sys_title', '_bx_ads_form_category_input_title', '', 1, 0, 0, '', '', '', 'AvailTranslatable', 'a:1:{i:0;s:5:"title";}', '_bx_ads_form_category_input_title_err', 'Xss', '', 1, 0),
+('bx_ads_category', 'bx_ads', 'text', '', '', 0, 'textarea_translatable', '_bx_ads_form_category_input_sys_text', '_bx_ads_form_category_input_text', '', 0, 0, 2, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_ads_category', 'bx_ads', 'type', '', '', 0, 'select', '_bx_ads_form_category_input_sys_type', '_bx_ads_form_category_input_type', '_bx_ads_form_category_input_type_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_category_input_type_err', 'Int', '', 0, 0),
+('bx_ads_category', 'bx_ads', 'type_clone', 'on', '', 0, 'switcher', '_bx_ads_form_category_input_sys_type_clone', '_bx_ads_form_category_input_type_clone', '', 0, 0, 0, 'a:1:{s:8:"onchange";s:33:"oBxAdsStudio.onChangeClone(this);";}', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_category', 'bx_ads', 'type_title', '', '', 0, 'text_translatable', '_bx_ads_form_category_input_sys_type_title', '_bx_ads_form_category_input_type_title', '', 0, 0, 0, 'a:1:{s:8:"disabled";s:8:"disabled";}', 'a:1:{s:5:"style";s:13:"display:none;";}', '', '', '', '', '', '', 0, 0),
+('bx_ads_category', 'bx_ads', 'icon', '', '', 0, 'text', '_bx_ads_form_category_input_sys_icon', '_bx_ads_form_category_input_icon', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_ads_category', 'bx_ads', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_category', 'bx_ads', 'do_submit', '_bx_ads_form_category_input_do_submit', '', 0, 'submit', '_bx_ads_form_category_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads_category', 'bx_ads', 'do_cancel', '_bx_ads_form_category_input_do_cancel', '', 0, 'button', '_bx_ads_form_category_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES 
+('bx_ads_category_add', 'parent_id', 2147483647, 1, 1),
+('bx_ads_category_add', 'type', 2147483647, 1, 2),
+('bx_ads_category_add', 'type_clone', 2147483647, 1, 3),
+('bx_ads_category_add', 'type_title', 2147483647, 1, 4),
+('bx_ads_category_add', 'title', 2147483647, 1, 5),
+('bx_ads_category_add', 'text', 2147483647, 1, 6),
+('bx_ads_category_add', 'icon', 2147483647, 1, 7),
+('bx_ads_category_add', 'controls', 2147483647, 1, 8),
+('bx_ads_category_add', 'do_submit', 2147483647, 1, 9),
+('bx_ads_category_add', 'do_cancel', 2147483647, 1, 10),
+
+('bx_ads_category_delete', 'delete_confirm', 2147483647, 1, 1),
+('bx_ads_category_delete', 'do_submit', 2147483647, 1, 2),
+
+('bx_ads_category_edit', 'parent_id', 2147483647, 1, 1),
+('bx_ads_category_edit', 'title', 2147483647, 1, 2),
+('bx_ads_category_edit', 'text', 2147483647, 1, 3),
+('bx_ads_category_edit', 'icon', 2147483647, 1, 4),
+('bx_ads_category_edit', 'controls', 2147483647, 1, 5),
+('bx_ads_category_edit', 'do_submit', 2147483647, 1, 6),
+('bx_ads_category_edit', 'do_cancel', 2147483647, 1, 7);
+
+
+-- FORMS: entry (ad)
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_ads', 'bx_ads', '_bx_ads_form_entry', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_ads_entries', 'id', '', '', 'a:1:{i:0;s:9:"do_submit";}', '', 0, 1, 'BxAdsFormEntry', 'modules/boonex/ads/classes/BxAdsFormEntry.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_ads', 'bx_ads_entry_add', 'bx_ads', 0, '_bx_ads_form_entry_display_add'),
+('bx_ads', 'bx_ads_entry_edit_budget', 'bx_ads', 0, '_bx_ads_form_entry_display_edit_budget'),
+('bx_ads', 'bx_ads_entry_delete', 'bx_ads', 0, '_bx_ads_form_entry_display_delete'),
+
+('bx_ads', 'bx_ads_entry_price_add', 'bx_ads', 0, '_bx_ads_form_entry_price_display_add'),
+('bx_ads', 'bx_ads_entry_price_edit', 'bx_ads', 0, '_bx_ads_form_entry_price_display_edit'),
+('bx_ads', 'bx_ads_entry_price_view', 'bx_ads', 1, '_bx_ads_form_entry_price_display_view'),
+
+('bx_ads', 'bx_ads_entry_price_year_add', 'bx_ads', 0, '_bx_ads_form_entry_price_year_display_add'),
+('bx_ads', 'bx_ads_entry_price_year_edit', 'bx_ads', 0, '_bx_ads_form_entry_price_year_display_edit'),
+('bx_ads', 'bx_ads_entry_price_year_view', 'bx_ads', 1, '_bx_ads_form_entry_price_year_display_view');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_ads', 'bx_ads', 'cf', '1', '#!sys_content_filter', 0, 'select', '_sys_form_entry_input_sys_cf', '_sys_form_entry_input_cf', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_ads', 'bx_ads', 'allow_view_to', '', '', 0, 'custom', '_bx_ads_form_entry_input_sys_allow_view_to', '_bx_ads_form_entry_input_allow_view_to', '', 1, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_ads_form_entry_input_sys_delete_confirm', '_bx_ads_form_entry_input_delete_confirm', '_bx_ads_form_entry_input_delete_confirm_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_entry_input_delete_confirm_error', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'do_select', '_bx_ads_form_entry_input_do_select', '', 0, 'button', '_bx_ads_form_entry_input_sys_do_select', '', '', 0, 0, 0, 'a:1:{s:7:"onclick";s:32:"{js_object}.selectCategory(this)";}', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'do_submit', '_bx_ads_form_entry_input_do_submit', '', 0, 'submit', '_bx_ads_form_entry_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'do_cancel', '_sys_form_input_cancel', '', 0, 'button', '_sys_form_input_sys_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:41:"window.open(''{edit_cancel_url}'', ''_self'')";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0),
+('bx_ads', 'bx_ads', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '_sys_form_input_sys_controls', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads', 'bx_ads', 'location', '', '', 0, 'location', '_sys_form_input_sys_location', '_sys_form_input_location', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'covers', 'a:1:{i:0;s:12:"bx_ads_html5";}', 'a:1:{s:12:"bx_ads_html5";s:25:"_sys_uploader_html5_title";}', 0, 'files', '_bx_ads_form_entry_input_sys_covers', '_bx_ads_form_entry_input_covers', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'pictures', 'a:1:{i:0;s:19:"bx_ads_photos_html5";}', 'a:1:{s:19:"bx_ads_photos_html5";s:25:"_sys_uploader_html5_title";}', 0, 'files', '_bx_ads_form_entry_input_sys_pictures', '_bx_ads_form_entry_input_pictures', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'videos', 'a:2:{i:0;s:19:"bx_ads_videos_html5";i:1;s:26:"bx_ads_videos_record_video";}', 'a:2:{s:19:"bx_ads_videos_html5";s:25:"_sys_uploader_html5_title";s:26:"bx_ads_videos_record_video";s:32:"_sys_uploader_record_video_title";}', 0, 'files', '_bx_ads_form_entry_input_sys_videos', '_bx_ads_form_entry_input_videos', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'files', 'a:1:{i:0;s:18:"bx_ads_files_html5";}', 'a:1:{s:18:"bx_ads_files_html5";s:25:"_sys_uploader_html5_title";}', 0, 'files', '_bx_ads_form_entry_input_sys_files', '_bx_ads_form_entry_input_files', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'polls', '', '', 0, 'custom', '_bx_ads_form_entry_input_sys_polls', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'link', '', '', 0, 'custom', '_bx_ads_form_entry_input_sys_link', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'text', '', '', 0, 'textarea', '_bx_ads_form_entry_input_sys_text', '_bx_ads_form_entry_input_text', '', 1, 0, 2, '', '', '', 'Avail', '', '_bx_ads_form_entry_input_text_err', 'XssHtml', '', 1, 0),
+('bx_ads', 'bx_ads', 'title', '', '', 0, 'text', '_bx_ads_form_entry_input_sys_title', '_bx_ads_form_entry_input_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:3;s:3:"max";i:160;}', '_bx_ads_form_entry_input_title_err', 'Xss', '', 1, 0),
+('bx_ads', 'bx_ads', 'name', '', '', 0, 'text', '_bx_ads_form_entry_input_sys_name', '_bx_ads_form_entry_input_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_entry_input_name_err', 'Xss', '', 1, 0),
+('bx_ads', 'bx_ads', 'price', '', '', 0, 'price', '_bx_ads_form_entry_input_sys_price', '_bx_ads_form_entry_input_price', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_ads', 'bx_ads', 'auction', 1, '', 0, 'checkbox', '_bx_ads_form_entry_input_sys_auction', '_bx_ads_form_entry_input_auction', '_bx_ads_form_entry_input_auction_info', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_ads', 'bx_ads', 'quantity', '', '', 0, 'text', '_bx_ads_form_entry_input_sys_quantity', '_bx_ads_form_entry_input_quantity', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_entry_input_quantity_err', 'Xss', '', 1, 0),
+('bx_ads', 'bx_ads', 'year', '', '', 0, 'text', '_bx_ads_form_entry_input_sys_year', '_bx_ads_form_entry_input_year', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_ads', 'bx_ads', 'category', '', '', 0, 'hidden', '_bx_ads_form_entry_input_sys_category', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_ads', 'bx_ads', 'category_view', '', '', 0, 'text', '_bx_ads_form_entry_input_sys_category_view', '_bx_ads_form_entry_input_category_view', '', 0, 0, 0, 'a:1:{s:8:"disabled";s:8:"disabled";}', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'category_select', '', '', 0, 'select', '_bx_ads_form_entry_input_sys_category_select', '_bx_ads_form_entry_input_category_select', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_entry_input_category_select_err', 'Int', '', 0, 0),
+('bx_ads', 'bx_ads', 'notes_purchased', '', '', 0, 'textarea', '_bx_ads_form_entry_input_sys_notes_purchased', '_bx_ads_form_entry_input_notes_purchased', '_bx_ads_form_entry_input_notes_purchased_inf', 0, 0, 3, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_ads', 'bx_ads', 'added', '', '', 0, 'datetime', '_bx_ads_form_entry_input_sys_date_added', '_bx_ads_form_entry_input_date_added', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'changed', '', '', 0, 'datetime', '_bx_ads_form_entry_input_sys_date_changed', '_bx_ads_form_entry_input_date_changed', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'attachments', '', '', 0, 'custom', '_bx_ads_form_entry_input_sys_attachments', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'labels', '', '', 0, 'custom', '_sys_form_input_sys_labels', '_sys_form_input_labels', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'source_type', '', '', 0, 'hidden', '_bx_ads_form_entry_input_sys_source_type', '_bx_ads_form_entry_input_source_type', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_ads', 'bx_ads', 'source', '', '', 0, 'custom', '_bx_ads_form_entry_input_sys_source', '_bx_ads_form_entry_input_source', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_ads', 'bx_ads', 'url', '', '', 0, 'hidden', '_bx_ads_form_entry_input_sys_url', '', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_ads', 'bx_ads', 'budget_total', '', '', 0, 'text', '_bx_ads_form_entry_input_sys_budget_total', '_bx_ads_form_entry_input_budget_total', '', 0, 0, 0, '', '', '', '', '', '', 'Float', '', 1, 0),
+('bx_ads', 'bx_ads', 'budget_daily', '', '', 0, 'text', '_bx_ads_form_entry_input_sys_budget_daily', '_bx_ads_form_entry_input_budget_daily', '', 0, 0, 0, '', '', '', '', '', '', 'Float', '', 1, 0),
+('bx_ads', 'bx_ads', 'seg_header_beg', '', '', 0, 'block_header', '_bx_ads_form_entry_input_sys_header_beg_seg', '_bx_ads_form_entry_input_header_beg_seg', '', 0, 1, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'seg_header_end', '', '', 0, 'block_end', '_bx_ads_form_entry_input_sys_header_end_seg', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads', 'bx_ads', 'seg', 1, '', 0, 'switcher', '_bx_ads_form_entry_input_sys_seg', '_bx_ads_form_entry_input_seg', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_ads', 'bx_ads', 'seg_gender', '', '#!Sex', 0, 'checkbox_set', '_bx_ads_form_entry_input_sys_seg_gender', '_bx_ads_form_entry_input_seg_gender', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 1, 0),
+('bx_ads', 'bx_ads', 'seg_age', '', '', 0, 'doublerange', '_bx_ads_form_entry_input_sys_seg_age', '_bx_ads_form_entry_input_seg_age', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_ads', 'bx_ads', 'seg_country', '', '#!Country', 0, 'select', '_bx_ads_form_entry_input_sys_seg_country', '_bx_ads_form_entry_input_seg_country', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES 
+('bx_ads_entry_add', 'category_select', 2147483647, 1, 1),
+('bx_ads_entry_add', 'do_select', 2147483647, 1, 2),
+
+('bx_ads_entry_edit_budget', 'budget_total', 2147483647, 1, 1),
+('bx_ads_entry_edit_budget', 'budget_daily', 2147483647, 1, 2),
+('bx_ads_entry_edit_budget', 'do_submit', 2147483647, 1, 3),
+
+('bx_ads_entry_delete', 'delete_confirm', 2147483647, 1, 1),
+('bx_ads_entry_delete', 'do_submit', 2147483647, 1, 2),
+
+('bx_ads_entry_price_add', 'url', 2147483647, 1, 0),
+('bx_ads_entry_price_add', 'source_type', 2147483647, 1, 1),
+('bx_ads_entry_price_add', 'source', 2147483647, 1, 2),
+('bx_ads_entry_price_add', 'category', 2147483647, 1, 3),
+('bx_ads_entry_price_add', 'category_view', 2147483647, 1, 4),
+('bx_ads_entry_price_add', 'title', 2147483647, 1, 5),
+('bx_ads_entry_price_add', 'name', 2147483647, 1, 6),
+('bx_ads_entry_price_add', 'price', 2147483647, 1, 7),
+('bx_ads_entry_price_add', 'auction', 2147483647, 1, 8),
+('bx_ads_entry_price_add', 'quantity', 2147483647, 1, 9),
+('bx_ads_entry_price_add', 'text', 2147483647, 1, 10),
+('bx_ads_entry_price_add', 'attachments', 2147483647, 1, 11),
+('bx_ads_entry_price_add', 'pictures', 2147483647, 1, 12),
+('bx_ads_entry_price_add', 'videos', 2147483647, 1, 13),
+('bx_ads_entry_price_add', 'files', 2147483647, 1, 14),
+('bx_ads_entry_price_add', 'polls', 2147483647, 1, 15),
+('bx_ads_entry_price_add', 'link', 2147483647, 1, 16),
+('bx_ads_entry_price_add', 'covers', 2147483647, 1, 17),
+('bx_ads_entry_price_add', 'budget_total', 2147483647, 1, 18),
+('bx_ads_entry_price_add', 'budget_daily', 2147483647, 1, 19),
+('bx_ads_entry_price_add', 'allow_view_to', 2147483647, 1, 20),
+('bx_ads_entry_price_add', 'cf', 2147483647, 1, 21),
+('bx_ads_entry_price_add', 'notes_purchased', 2147483647, 1, 22),
+('bx_ads_entry_price_add', 'location', 2147483647, 1, 23),
+('bx_ads_entry_price_add', 'seg_header_beg', 2147483647, 1, 24),
+('bx_ads_entry_price_add', 'seg', 2147483647, 1, 25),
+('bx_ads_entry_price_add', 'seg_gender', 2147483647, 1, 26),
+('bx_ads_entry_price_add', 'seg_age', 2147483647, 1, 27),
+('bx_ads_entry_price_add', 'seg_country', 2147483647, 1, 28),
+('bx_ads_entry_price_add', 'seg_header_end', 2147483647, 1, 29),
+('bx_ads_entry_price_add', 'do_submit', 2147483647, 1, 30),
+
+('bx_ads_entry_price_edit', 'source_type', 2147483647, 1, 1),
+('bx_ads_entry_price_edit', 'source', 2147483647, 1, 2),
+('bx_ads_entry_price_edit', 'category_view', 2147483647, 1, 3),
+('bx_ads_entry_price_edit', 'title', 2147483647, 1, 4),
+('bx_ads_entry_price_edit', 'name', 2147483647, 1, 5),
+('bx_ads_entry_price_edit', 'price', 2147483647, 1, 6),
+('bx_ads_entry_price_edit', 'auction', 2147483647, 1, 7),
+('bx_ads_entry_price_edit', 'quantity', 2147483647, 1, 8),
+('bx_ads_entry_price_edit', 'text', 2147483647, 1, 9),
+('bx_ads_entry_price_edit', 'attachments', 2147483647, 1, 10),
+('bx_ads_entry_price_edit', 'pictures', 2147483647, 1, 11),
+('bx_ads_entry_price_edit', 'videos', 2147483647, 1, 12),
+('bx_ads_entry_price_edit', 'files', 2147483647, 1, 13),
+('bx_ads_entry_price_edit', 'polls', 2147483647, 1, 14),
+('bx_ads_entry_price_edit', 'link', 2147483647, 1, 15),
+('bx_ads_entry_price_edit', 'covers', 2147483647, 1, 16),
+('bx_ads_entry_price_edit', 'allow_view_to', 2147483647, 1, 17),
+('bx_ads_entry_price_edit', 'cf', 2147483647, 1, 18),
+('bx_ads_entry_price_edit', 'notes_purchased', 2147483647, 1, 19),
+('bx_ads_entry_price_edit', 'location', 2147483647, 1, 20),
+('bx_ads_entry_price_edit', 'seg_header_beg', 2147483647, 1, 21),
+('bx_ads_entry_price_edit', 'seg', 2147483647, 1, 22),
+('bx_ads_entry_price_edit', 'seg_gender', 2147483647, 1, 23),
+('bx_ads_entry_price_edit', 'seg_age', 2147483647, 1, 24),
+('bx_ads_entry_price_edit', 'seg_country', 2147483647, 1, 25),
+('bx_ads_entry_price_edit', 'seg_header_end', 2147483647, 1, 26),
+('bx_ads_entry_price_edit', 'controls', 2147483647, 1, 27),
+('bx_ads_entry_price_edit', 'do_submit', 2147483647, 1, 28),
+('bx_ads_entry_price_edit', 'do_cancel', 2147483647, 1, 29),
+
+('bx_ads_entry_price_view', 'category_view', 2147483647, 1, 1),
+('bx_ads_entry_price_view', 'price', 2147483647, 1, 2),
+('bx_ads_entry_price_view', 'added', 2147483647, 1, 3),
+('bx_ads_entry_price_view', 'changed', 2147483647, 1, 4),
+
+('bx_ads_entry_price_year_add', 'url', 2147483647, 1, 0),
+('bx_ads_entry_price_year_add', 'source_type', 2147483647, 1, 1),
+('bx_ads_entry_price_year_add', 'source', 2147483647, 1, 2),
+('bx_ads_entry_price_year_add', 'category', 2147483647, 1, 3),
+('bx_ads_entry_price_year_add', 'category_view', 2147483647, 1, 4),
+('bx_ads_entry_price_year_add', 'title', 2147483647, 1, 5),
+('bx_ads_entry_price_year_add', 'name', 2147483647, 1, 6),
+('bx_ads_entry_price_year_add', 'price', 2147483647, 1, 7),
+('bx_ads_entry_price_year_add', 'auction', 2147483647, 1, 8),
+('bx_ads_entry_price_year_add', 'quantity', 2147483647, 1, 9),
+('bx_ads_entry_price_year_add', 'year', 2147483647, 1, 10),
+('bx_ads_entry_price_year_add', 'text', 2147483647, 1, 11),
+('bx_ads_entry_price_year_add', 'attachments', 2147483647, 1, 12),
+('bx_ads_entry_price_year_add', 'pictures', 2147483647, 1, 13),
+('bx_ads_entry_price_year_add', 'videos', 2147483647, 1, 14),
+('bx_ads_entry_price_year_add', 'files', 2147483647, 1, 15),
+('bx_ads_entry_price_year_add', 'polls', 2147483647, 1, 16),
+('bx_ads_entry_price_year_add', 'link', 2147483647, 1, 17),
+('bx_ads_entry_price_year_add', 'covers', 2147483647, 1, 18),
+('bx_ads_entry_price_year_add', 'budget_total', 2147483647, 1, 19),
+('bx_ads_entry_price_year_add', 'budget_daily', 2147483647, 1, 20),
+('bx_ads_entry_price_year_add', 'allow_view_to', 2147483647, 1, 21),
+('bx_ads_entry_price_year_add', 'cf', 2147483647, 1, 22),
+('bx_ads_entry_price_year_add', 'notes_purchased', 2147483647, 1, 23),
+('bx_ads_entry_price_year_add', 'location', 2147483647, 1, 24),
+('bx_ads_entry_price_year_add', 'seg_header_beg', 2147483647, 1, 25),
+('bx_ads_entry_price_year_add', 'seg', 2147483647, 1, 26),
+('bx_ads_entry_price_year_add', 'seg_gender', 2147483647, 1, 27),
+('bx_ads_entry_price_year_add', 'seg_age', 2147483647, 1, 28),
+('bx_ads_entry_price_year_add', 'seg_country', 2147483647, 1, 29),
+('bx_ads_entry_price_year_add', 'seg_header_end', 2147483647, 1, 30),
+('bx_ads_entry_price_year_add', 'do_submit', 2147483647, 1, 31),
+
+('bx_ads_entry_price_year_edit', 'source_type', 2147483647, 1, 1),
+('bx_ads_entry_price_year_edit', 'source', 2147483647, 1, 2),
+('bx_ads_entry_price_year_edit', 'category_view', 2147483647, 1, 3),
+('bx_ads_entry_price_year_edit', 'title', 2147483647, 1, 4),
+('bx_ads_entry_price_year_edit', 'name', 2147483647, 1, 5),
+('bx_ads_entry_price_year_edit', 'price', 2147483647, 1, 6),
+('bx_ads_entry_price_year_edit', 'auction', 2147483647, 1, 7),
+('bx_ads_entry_price_year_edit', 'quantity', 2147483647, 1, 8),
+('bx_ads_entry_price_year_edit', 'year', 2147483647, 1, 9),
+('bx_ads_entry_price_year_edit', 'text', 2147483647, 1, 10),
+('bx_ads_entry_price_year_edit', 'attachments', 2147483647, 1, 11),
+('bx_ads_entry_price_year_edit', 'pictures', 2147483647, 1, 12),
+('bx_ads_entry_price_year_edit', 'videos', 2147483647, 1, 13),
+('bx_ads_entry_price_year_edit', 'files', 2147483647, 1, 14),
+('bx_ads_entry_price_year_edit', 'polls', 2147483647, 1, 15),
+('bx_ads_entry_price_year_edit', 'link', 2147483647, 1, 16),
+('bx_ads_entry_price_year_edit', 'covers', 2147483647, 1, 17),
+('bx_ads_entry_price_year_edit', 'allow_view_to', 2147483647, 1, 18),
+('bx_ads_entry_price_year_edit', 'cf', 2147483647, 1, 19),
+('bx_ads_entry_price_year_edit', 'notes_purchased', 2147483647, 1, 20),
+('bx_ads_entry_price_year_edit', 'location', 2147483647, 1, 21),
+('bx_ads_entry_price_year_edit', 'seg_header_beg', 2147483647, 1, 22),
+('bx_ads_entry_price_year_edit', 'seg', 2147483647, 1, 23),
+('bx_ads_entry_price_year_edit', 'seg_gender', 2147483647, 1, 24),
+('bx_ads_entry_price_year_edit', 'seg_age', 2147483647, 1, 25),
+('bx_ads_entry_price_year_edit', 'seg_country', 2147483647, 1, 26),
+('bx_ads_entry_price_year_edit', 'seg_header_end', 2147483647, 1, 27),
+('bx_ads_entry_price_year_edit', 'controls', 2147483647, 1, 28),
+('bx_ads_entry_price_year_edit', 'do_submit', 2147483647, 1, 29),
+('bx_ads_entry_price_year_edit', 'do_cancel', 2147483647, 1, 30),
+
+('bx_ads_entry_price_year_view', 'category_view', 2147483647, 1, 1),
+('bx_ads_entry_price_year_view', 'price', 2147483647, 1, 2),
+('bx_ads_entry_price_year_view', 'quantity', 2147483647, 1, 3),
+('bx_ads_entry_price_year_view', 'year', 2147483647, 1, 4),
+('bx_ads_entry_price_year_view', 'added', 2147483647, 1, 5),
+('bx_ads_entry_price_year_view', 'changed', 2147483647, 1, 6);
+
+-- FORMS: poll
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_ads_poll', 'bx_ads', '_bx_ads_form_poll', '', '', 'do_submit', 'bx_ads_polls', 'id', '', '', 'a:1:{s:14:"checker_helper";s:26:"BxAdsFormPollCheckerHelper";}', 0, 1, 'BxAdsFormPoll', 'modules/boonex/ads/classes/BxAdsFormPoll.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_ads_poll_add', 'bx_ads', 'bx_ads_poll', '_bx_ads_form_poll_display_add', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_ads_poll', 'bx_ads', 'text', '', '', 0, 'text', '_bx_ads_form_poll_input_sys_text', '_bx_ads_form_poll_input_text', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_poll_input_text_err', 'Xss', '', 1, 0),
+('bx_ads_poll', 'bx_ads', 'answers', '', '', 0, 'custom', '_bx_ads_form_poll_input_sys_answers', '_bx_ads_form_poll_input_answers', '', 1, 0, 0, '', '', '', 'AvailAnswers', '', '_bx_ads_form_poll_input_answers_err', '', '', 1, 0),
+('bx_ads_poll', 'bx_ads', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_poll', 'bx_ads', 'do_submit', '_bx_ads_form_poll_input_do_submit', '', 0, 'submit', '_bx_ads_form_poll_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_poll', 'bx_ads', 'do_cancel', '_bx_ads_form_poll_input_do_cancel', '', 0, 'button', '_bx_ads_form_poll_input_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_ads_poll_add', 'text', 2147483647, 1, 1),
+('bx_ads_poll_add', 'answers', 2147483647, 1, 2),
+('bx_ads_poll_add', 'controls', 2147483647, 1, 3),
+('bx_ads_poll_add', 'do_submit', 2147483647, 1, 4),
+('bx_ads_poll_add', 'do_cancel', 2147483647, 1, 5);
+
+-- FORMS: link
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_ads_attach_link', 'bx_ads', '_bx_ads_form_attach_link', '', '', 'do_submit', 'bx_ads_links', 'id', '', '', '', 0, 1, '', '');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_ads_attach_link_add', 'bx_ads', 'bx_ads_attach_link', '_bx_ads_form_attach_link_display_add', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_ads_attach_link', 'bx_ads', 'content_id', '0', '', 0, 'hidden', '_bx_ads_form_attach_link_input_sys_content_id', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_attach_link', 'bx_ads', 'url', '', '', 0, 'text', '_bx_ads_form_attach_link_input_sys_url', '_bx_ads_form_attach_link_input_url', '', 0, 0, 0, '', '', '', 'Preg', 'a:1:{s:4:"preg";s:0:"";}', '_bx_ads_form_attach_link_input_url_err', '', '', 0, 0),
+('bx_ads_attach_link', 'bx_ads', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_attach_link', 'bx_ads', 'do_submit', '_bx_ads_form_attach_link_input_do_submit', '', 0, 'submit', '_bx_ads_form_attach_link_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_attach_link', 'bx_ads', 'do_cancel', '_bx_ads_form_attach_link_input_do_cancel', '', 0, 'button', '_bx_ads_form_attach_link_input_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_ads_attach_link_add', 'content_id', 2147483647, 1, 1),
+('bx_ads_attach_link_add', 'url', 2147483647, 1, 2),
+('bx_ads_attach_link_add', 'controls', 2147483647, 1, 3),
+('bx_ads_attach_link_add', 'do_submit', 2147483647, 1, 4),
+('bx_ads_attach_link_add', 'do_cancel', 2147483647, 1, 5);
+
+-- FORMS: offer
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_ads_offer', 'bx_ads', '_bx_ads_form_offer', '', '', 'do_submit', 'bx_ads_offers', 'id', '', '', '', 0, 1, 'BxAdsFormOffer', 'modules/boonex/ads/classes/BxAdsFormOffer.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_ads_offer_add', 'bx_ads', 'bx_ads_offer', '_bx_ads_form_offer_display_add', 0),
+('bx_ads_offer_view', 'bx_ads', 'bx_ads_offer', '_bx_ads_form_offer_display_view', 1);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_ads_offer', 'bx_ads', 'amount', '', '', 0, 'text', '_bx_ads_form_offer_input_sys_amount', '_bx_ads_form_offer_input_amount', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_offer_input_amount_err', 'Xss', '', 1, 0),
+('bx_ads_offer', 'bx_ads', 'quantity', '1', '', 0, 'text', '_bx_ads_form_offer_input_sys_quantity', '_bx_ads_form_offer_input_quantity', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_ads_form_offer_input_quantity_err', 'Xss', '', 1, 0),
+('bx_ads_offer', 'bx_ads', 'total', '', '', 0, 'text', '_bx_ads_form_offer_input_sys_total', '_bx_ads_form_offer_input_total', '', 0, 0, 0, 'a:1:{s:8:"readonly";s:8:"readonly";}', '', '', '', '', '', '', '', 1, 0),
+('bx_ads_offer', 'bx_ads', 'message', '', '', 0, 'textarea', '_bx_ads_form_offer_input_sys_message', '_bx_ads_form_offer_input_message', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_ads_offer', 'bx_ads', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_offer', 'bx_ads', 'do_submit', '_bx_ads_form_offer_input_do_submit', '', 0, 'submit', '_bx_ads_form_offer_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_ads_offer', 'bx_ads', 'do_cancel', '_bx_ads_form_offer_input_do_cancel', '', 0, 'button', '_bx_ads_form_offer_input_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_ads_offer_add', 'amount', 2147483647, 1, 1),
+('bx_ads_offer_add', 'quantity', 2147483647, 1, 2),
+('bx_ads_offer_add', 'total', 2147483647, 1, 3),
+('bx_ads_offer_add', 'message', 2147483647, 1, 4),
+('bx_ads_offer_add', 'controls', 2147483647, 1, 5),
+('bx_ads_offer_add', 'do_submit', 2147483647, 1, 6),
+('bx_ads_offer_add', 'do_cancel', 2147483647, 1, 7),
+
+('bx_ads_offer_view', 'amount', 2147483647, 1, 1),
+('bx_ads_offer_view', 'quantity', 2147483647, 1, 2),
+('bx_ads_offer_view', 'total', 2147483647, 1, 3),
+('bx_ads_offer_view', 'message', 2147483647, 1, 4);
+
+
+-- COMMENTS
+INSERT INTO `sys_objects_cmts` (`Name`, `Module`, `Table`, `CharsPostMin`, `CharsPostMax`, `CharsDisplayMax`, `Html`, `PerView`, `PerViewReplies`, `BrowseType`, `IsBrowseSwitch`, `PostFormPosition`, `NumberOfLevels`, `IsDisplaySwitch`, `IsRatable`, `ViewingThreshold`, `IsOn`, `RootStylePrefix`, `BaseUrl`, `ObjectVote`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldTitle`, `TriggerFieldComments`, `ClassName`, `ClassFile`) VALUES
+('bx_ads', 'bx_ads', 'bx_ads_cmts', 1, 5000, 1000, 3, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-ad&id={object_id}', '', 'bx_ads_entries', 'id', 'author', 'title', 'comments', '', ''),
+('bx_ads_notes', 'bx_ads', 'bx_ads_cmts_notes', 1, 5000, 1000, 0, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-ad&id={object_id}', '', 'bx_ads_entries', 'id', 'author', 'title', '', 'BxTemplCmtsNotes', ''),
+('bx_ads_reviews', 'bx_ads', 'bx_ads_reviews', 1, 5000, 1000, 3, 5, 3, 'tail', 1, 'bottom', 1, 0, 1, -3, 1, 'cmt', 'page.php?i=view-ad&id={object_id}', '', 'bx_ads_entries', 'id', 'author', 'title', 'reviews', 'BxTemplCmtsReviews', '');
+
+-- VOTES
+INSERT INTO `sys_objects_vote` (`Name`, `Module`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
+('bx_ads', 'bx_ads', 'bx_ads_votes', 'bx_ads_votes_track', '604800', '1', '1', '0', '1', 'bx_ads_entries', 'id', 'author', 'rate', 'votes', '', ''),
+('bx_ads_reactions', 'bx_ads', 'bx_ads_reactions', 'bx_ads_reactions_track', '604800', '1', '1', '1', '1', 'bx_ads_entries', 'id', 'author', 'rrate', 'rvotes', 'BxTemplVoteReactions', ''),
+('bx_ads_poll_answers', 'bx_ads', 'bx_ads_polls_answers_votes', 'bx_ads_polls_answers_votes_track', '604800', '1', '1', '0', '1', 'bx_ads_polls_answers', 'id', '', 'rate', 'votes', 'BxAdsVotePollAnswers', 'modules/boonex/ads/classes/BxAdsVotePollAnswers.php');
+
+-- SCORES
+INSERT INTO `sys_objects_score` (`name`, `module`, `table_main`, `table_track`, `post_timeout`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_score`, `trigger_field_cup`, `trigger_field_cdown`, `class_name`, `class_file`) VALUES 
+('bx_ads', 'bx_ads', 'bx_ads_scores', 'bx_ads_scores_track', '604800', '0', 'bx_ads_entries', 'id', 'author', 'score', 'sc_up', 'sc_down', '', '');
+
+-- REPORTS
+INSERT INTO `sys_objects_report` (`name`, `module`, `table_main`, `table_track`, `is_on`, `base_url`, `object_comment`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_ads', 'bx_ads', 'bx_ads_reports', 'bx_ads_reports_track', '1', 'page.php?i=view-ad&id={object_id}', 'bx_ads_notes', 'bx_ads_entries', 'id', 'author', 'reports', '', '');
+
+-- VIEWS
+INSERT INTO `sys_objects_view` (`name`, `module`, `table_track`, `period`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_ads', 'bx_ads', 'bx_ads_views_track', '86400', '1', 'bx_ads_entries', 'id', 'author', 'views', '', '');
+
+-- FAFORITES
+INSERT INTO `sys_objects_favorite` (`name`, `table_track`, `table_lists`, `is_on`, `is_undo`, `is_public`, `base_url`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_ads', 'bx_ads_favorites_track', 'bx_ads_favorites_lists', '1', '1', '1', 'page.php?i=view-ad&id={object_id}', 'bx_ads_entries', 'id', 'author', 'favorites', '', '');
+
+-- FEATURED
+INSERT INTO `sys_objects_feature` (`name`, `module`, `is_on`, `is_undo`, `base_url`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_flag`, `class_name`, `class_file`) VALUES 
+('bx_ads', 'bx_ads', '1', '1', 'page.php?i=view-ad&id={object_id}', 'bx_ads_entries', 'id', 'author', 'featured', '', '');
+
+-- CONTENT INFO
+INSERT INTO `sys_objects_content_info` (`name`, `title`, `alert_unit`, `alert_action_add`, `alert_action_update`, `alert_action_delete`, `class_name`, `class_file`) VALUES
+('bx_ads', '_bx_ads', 'bx_ads', 'added', 'edited', 'deleted', '', ''),
+('bx_ads_cmts', '_bx_ads_cmts', 'bx_ads', 'commentPost', 'commentUpdated', 'commentRemoved', 'BxDolContentInfoCmts', ''),
+('bx_ads_reviews', '_bx_ads_reviews', 'bx_ads_reviews', 'commentPost', 'commentUpdated', 'commentRemoved', 'BxDolContentInfoCmts', '');
+
+INSERT INTO `sys_content_info_grids` (`object`, `grid_object`, `grid_field_id`, `condition`, `selection`) VALUES
+('bx_ads', 'bx_ads_administration', 'id', '', ''),
+('bx_ads', 'bx_ads_common', 'id', '', '');
+
+-- SEARCH EXTENDED
+INSERT INTO `sys_objects_search_extended` (`object`, `object_content_info`, `module`, `title`, `active`, `class_name`, `class_file`) VALUES
+('bx_ads', 'bx_ads', 'bx_ads', '_bx_ads_search_extended', 1, '', ''),
+('bx_ads_cmts', 'bx_ads_cmts', 'bx_ads', '_bx_ads_search_extended_cmts', 1, 'BxTemplSearchExtendedCmts', ''),
+('bx_ads_reviews', 'bx_ads_reviews', 'bx_ads', '_bx_ads_search_extended_reviews', 1, 'BxTemplSearchExtendedCmts', '');
+
+-- STUDIO: page & widget
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_ads', '_bx_ads', '_bx_ads', 'bx_ads@modules/boonex/ads/|std-icon.svg');
+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`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_ads', 'content', '{url_studio}module.php?name=bx_ads', '', 'bx_ads@modules/boonex/ads/|std-icon.svg', '_bx_ads', '', '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));

+ 51 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Albums',
+    'version_from' => '15.0.1',
+    'version_to' => '15.0.2',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/albums/updates/update_15.0.1_15.0.2/',
+    'home_uri' => 'albums_update_1501_1502',
+
+    'module_dir' => 'boonex/albums/',
+    'module_uri' => 'albums',
+
+    'db_prefix' => 'bx_albums_',
+    'class_prefix' => 'BxAlbums',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Albums',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxAlbumsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 4 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/install/langs/en.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_upd name="_bx_albums_form_entry_input_title_err"><![CDATA[Caption is essential min 3 max 160.]]></string_upd>
+</resources>

+ 2 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/install/sql/install.sql

@@ -0,0 +1,2 @@
+-- FORMS
+UPDATE `sys_form_inputs` SET `checker_func`='Length', `checker_params`='a:2:{s:3:"min";i:3;s:3:"max";i:160;}' WHERE `object`='bx_albums' AND `name`='title';

+ 171 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/source/classes/BxAlbumsDb.php

@@ -0,0 +1,171 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Albums Albums
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/*
+ * Module database queries
+ */
+class BxAlbumsDb extends BxBaseModTextDb
+{
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+
+    public function associateFileWithContent($iContentId, $iFileId, $iProfileId, $sTitle, $sData = '', $sExif = '')
+    {
+        $sQuery = $this->prepare ("SELECT MAX(`order`) FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` WHERE `content_id` = ?", $iContentId);
+        $iOrder = 1 + (int)$this->getOne($sQuery);
+
+        $sQuery = $this->prepare ("INSERT INTO `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` SET `content_id` = ?, `file_id` = ?, `author` = ?, `title` = ?, `data` = ?, `exif` = ?, `order` = ? ON DUPLICATE KEY UPDATE `title` = ?, `data` = ?, `exif` = ?", $iContentId, $iFileId, $iProfileId, $sTitle, $sData, $sExif, $iOrder, $sTitle, $sData, $sExif);
+        return $this->res($sQuery);
+    }
+
+    public function deassociateFileWithContent($iContentId, $iFileId)
+    {
+    	$aBindings = array();
+
+        $sWhere = '';
+        if ($iContentId) {
+        	$aBindings['content_id'] = $iContentId;
+
+            $sWhere .= " AND `content_id` = :content_id";
+        }
+
+        if ($iFileId) {
+        	$aBindings['file_id'] = $iFileId;
+
+            $sWhere .= " AND `file_id` = :file_id";
+        }
+
+        $sQuery = "DELETE FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` WHERE 1 " . $sWhere;
+        return $this->query($sQuery, $aBindings);
+    }
+
+    public function getFileTitle($iFileId)
+    {
+        $sQuery = $this->prepare ("SELECT `title` FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` WHERE `file_id` = ?", $iFileId);
+        return $this->getOne($sQuery);
+    }
+
+    public function getMediaInfoById($iMediaId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sSelectClause = "`f2e`.*, `f`.`added`";
+        $sJoinClause = "INNER JOIN `" . $CNF['TABLE_FILES'] . "` AS `f` ON (`f`.`id` = `f2e`.`file_id`) INNER JOIN `" . $CNF['TABLE_ENTRIES'] . "` AS `e` ON (`e`.`id` = `f2e`.`content_id`)";
+
+        if($CNF['PARAM_ORDER_BY_GHOSTS']) {
+            $sSelectClause .= ", `g`.`order` as `gorder`";
+            $sJoinClause .= $this->prepareAsString("INNER JOIN `sys_storage_ghosts` AS `g` ON `g`.`id`=`f2e`.`file_id` AND `g`.`content_id`=`f2e`.`content_id` AND `g`.`object`=?", $CNF['OBJECT_STORAGE']);
+        }
+
+        $sQuery = $this->prepare("SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_FILES2ENTRIES'] . "` AS `f2e` " . $sJoinClause . " WHERE `f2e`.`id` = ?", $iMediaId);
+        return $this->getRow($sQuery);
+    }
+
+    public function getMediaInfoSimpleByFileId($iFileId)
+    {
+        $sQuery = $this->prepare ("SELECT * FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` WHERE `file_id` = ?", $iFileId);
+        return $this->getRow($sQuery);
+    }
+
+    public function getMediaCountByContentId($iContentId)
+    {
+        $sQuery = $this->prepare ("SELECT COUNT(*) FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` WHERE `content_id` = ? LIMIT 1", $iContentId);
+        return $this->getOne($sQuery);
+    }
+
+    public function getMediaListByContentId($iContentId, $iLimit = false)
+    {
+        $sLimitQuery = '';
+        $aBindings = array('id' => $iContentId);
+        if ((int)$iLimit) {
+            $sLimitQuery = ' LIMIT :limit';
+            $aBindings = array_merge($aBindings, array('limit' => (int)$iLimit));
+        }
+            
+        $sQuery = "SELECT * FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` WHERE `content_id` = :id ORDER BY `order`" . $sLimitQuery;
+        return $this->getAll($sQuery, $aBindings);
+    }
+
+    function getMediaBy($aParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aMethod = array('name' => 'getAll', 'params' => array(0 => 'query', 1 => array()));
+        $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        $sSelectClause = "`{$CNF['TABLE_FILES2ENTRIES']}`.*";
+
+        switch($aParams['type']) {
+            case 'id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1]['id'] = (int)$aParams['id'];
+
+                $sWhereClause .= " AND `{$CNF['TABLE_FILES2ENTRIES']}`.`id` = :id";
+                break;
+
+            case 'search_ids':
+                $aMethod['name'] = 'getColumn';
+                
+                $sSelectClause = "`{$CNF['TABLE_FILES2ENTRIES']}`.`id`";
+
+                if (!empty($aParams['start']) && !empty($aParams['per_page']))
+                    $sLimitClause = $this->prepareAsString("?, ?", $aParams['start'], $aParams['per_page']);
+                elseif (!empty($aParams['per_page']))
+                    $sLimitClause = $this->prepareAsString("?", $aParams['per_page']);
+
+                BxDolSearchExtended::processParams($aParams['search_params'], [
+                    'table' => $CNF['TABLE_FILES2ENTRIES'],
+                    'bindings' => &$aMethod['params'][1],
+                    'where_clause' => &$sWhereClause,
+                    'bindings_ref' => &$aMethod['params'][1],
+                    'where_clause_ref' => &$sWhereClause
+                ]);
+
+                $sOrderClause .=  "`{$CNF['TABLE_FILES2ENTRIES']}`.`id` ASC";
+                break;
+
+            case 'all_ids':
+                $aMethod['name'] = 'getColumn';
+
+                $sSelectClause = "`{$CNF['TABLE_FILES2ENTRIES']}`.`id`";
+                $sOrderClause .=  "`{$CNF['TABLE_FILES2ENTRIES']}`.`id` ASC";
+                break;
+
+            case 'all':
+                $sOrderClause .=  "`{$CNF['TABLE_FILES2ENTRIES']}`.`id` ASC";
+                break;
+        }
+
+        if(!empty($sOrderClause))
+            $sOrderClause = 'ORDER BY ' . $sOrderClause;
+
+        if(!empty($sLimitClause))
+            $sLimitClause = 'LIMIT ' . $sLimitClause;
+
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `{$CNF['TABLE_FILES2ENTRIES']}` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+		return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+    
+    public function updateMedia($aParamsSet, $aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet) || empty($aParamsWhere))
+            return false;
+
+        $sSql = "UPDATE `" . $CNF['TABLE_FILES'] . "` SET " . $this->arrayToSQL($aParamsSet) . " WHERE " . $this->arrayToSQL($aParamsWhere, " AND ");
+        return $this->query($sSql);
+    }
+}
+
+/** @} */

+ 288 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/source/classes/BxAlbumsSearchResultMedia.php

@@ -0,0 +1,288 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Albums Albums
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAlbumsSearchResultMedia extends BxBaseModTextSearchResult
+{
+    protected $sOrderDirection = 'DESC';
+
+    function __construct($sMode = '', $aParams = array())
+    {
+        $this->sUnitTemplateLiveSearch = 'unit_media_live_search.html';
+
+        $aParams['unit_views'] = array('gallery' => 'unit_media.html', 'showcase' => 'unit_showcase.html');
+        parent::__construct($sMode, $aParams);
+
+        $this->aCurrent = array(
+            'name' => 'bx_albums_media',
+            'module_name' => 'bx_albums',
+            'object_metatags' => 'bx_albums_media',
+            'title' => _t('_bx_albums_media'),
+            'table' => 'bx_albums_files2albums',
+            'ownFields' => array('id', 'title', 'data', 'content_id', 'file_id', 'order', 'views'),
+            'searchFields' => array('title'),
+            'restriction_sql' => '',
+            'restriction' => array(
+                'author' => array('value' => '', 'field' => 'author', 'operator' => '='),
+                'album' => array('value' => '', 'field' => 'content_id', 'operator' => '='),
+                'featured' => array('value' => '', 'field' => 'featured', 'operator' => '<>'),
+                'status' => array('value' => 'active', 'field' => 'status', 'operator' => '=', 'table' => 'bx_albums_albums'),
+                'statusAdmin' => array('value' => 'active', 'field' => 'status_admin', 'operator' => '=', 'table' => 'bx_albums_albums'),
+            ),
+            'join' => array(
+                'albums' => array(
+                    'type' => 'INNER',
+                    'table' => 'bx_albums_albums',
+                    'mainField' => 'content_id',
+                    'onField' => 'id',
+                    'joinFields' => array(),
+                ),
+                'files' => array(
+                    'type' => 'INNER',
+                    'table' => 'bx_albums_files',
+                    'table_alias' => 'f',
+                    'mainField' => 'file_id',
+                    'onField' => 'id',
+                    'joinFields' => array('added'),
+                ),
+            ),
+            'paginate' => array('perPage' => getParam('bx_albums_per_page_browse'), 'start' => 0),
+            'sorting' => 'last',
+            'rss' => array(
+                'title' => '',
+                'link' => '',
+                'image' => '',
+                'profile' => 0,
+                'fields' => array (
+                    'Guid' => 'link',
+                    'Link' => 'link',
+                    'Title' => 'title',
+                    'DateTimeUTS' => 'added',
+                    'Desc' => 'title',
+                ),
+            ),
+            'ident' => 'id',
+        );
+
+        $this->sFilterName = 'bx_albums_filter';
+        $this->oModule = $this->getMain();
+
+        $oProfileAuthor = isset($aParams['author']) ? BxDolProfile::getInstance((int)$aParams['author']) : null;
+
+        $CNF = &$this->oModule->_oConfig->CNF;
+
+        switch ($sMode) {
+            case 'album':
+                $iAlbumId = (int)$aParams['album_id'];
+                $this->aCurrent['restriction']['album']['value'] = $iAlbumId;
+                $this->aCurrent['restriction']['status']['value'] = '';
+                $this->aCurrent['restriction']['statusAdmin']['value'] = '';
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iAlbumId);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_media_in_album');
+                $this->aCurrent['rss']['link'] = 'modules/?r=albums/rss_media/' . $sMode . '/' . $iAlbumId;
+                $this->aCurrent['sorting'] = 'order';
+                $this->sOrderDirection = 'ASC';
+                $this->setProcessPrivateContent(true);
+
+                if($CNF['PARAM_ORDER_BY_GHOSTS'])
+                    $this->_updateCurrentForOrderByGhosts();
+                break;
+
+            case 'favorite':
+                if(!$this->_updateCurrentForFavorite($sMode, array_merge($aParams, array('system' => $CNF['OBJECT_FAVORITES_MEDIA'])), $oProfileAuthor)) {
+                    $this->isError = true;
+                    break;
+                }
+                $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+                break;
+
+            case 'recent':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_RECENT_MEDIA']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_recent_media');
+                $this->aCurrent['rss']['link'] = 'modules/?r=albums/rss_media/' . $sMode; 
+                $this->aCurrent['sorting'] = 'last';
+                $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+                break;
+
+            case 'featured':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_RECENT_MEDIA']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_featured_media');
+                $this->aCurrent['restriction']['featured']['value'] = '0';
+                $this->aCurrent['rss']['link'] = 'modules/?r=albums/rss_media/' . $sMode;
+                $this->aCurrent['sorting'] = 'featured';
+                $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+                break;
+
+            case 'popular':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_POPULAR_MEDIA']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_popular_media');
+                $this->aCurrent['rss']['link'] = BxDolPermalinks::getInstance()->permalink('modules/?r=albums/rss_media/' . $sMode);
+                $this->aCurrent['sorting'] = 'popular';
+                $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+                break;
+
+            case 'top':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_TOP_MEDIA']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_top_media');
+                $this->aCurrent['rss']['link'] = BxDolPermalinks::getInstance()->permalink('modules/?r=albums/rss_media/' . $sMode);
+                $this->aCurrent['sorting'] = 'top';
+                $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+                break;
+
+            case '': // search results
+                $this->sBrowseUrl = BX_DOL_SEARCH_KEYWORD_PAGE;
+                unset($this->aCurrent['paginate']['perPage'], $this->aCurrent['rss']);
+                break;
+
+            default:
+                $sMode = '';
+                $this->isError = true;
+        }
+
+        $this->sBrowseUrl = $this->_replaceMarkers($this->sBrowseUrl);
+        $this->aCurrent['title'] = $this->_replaceMarkers($this->aCurrent['title']); 
+
+        $this->removeContainerClass('bx-def-margin-bottom-neg');
+        $this->addContainerClass(array('bx-def-margin-sec-lefttopright-neg', 'bx-def-margin-sec-bottom-neg', 'bx-albums-medias-wrapper'));
+
+        $this->addCustomConditions($CNF, $oProfileAuthor, $sMode, $aParams);
+    }
+
+    function getAlterOrder()
+    {
+        $CNF = &$this->oModule->_oConfig->CNF;
+
+        $sTable = $this->aCurrent['table'];
+        $sWay = $this->sOrderDirection;
+
+        $aSql = array();
+        switch ($this->aCurrent['sorting']) {
+            case 'order':
+                $aSql['order'] = " ORDER BY `" . $sTable . "`.`order` " . $sWay . ", `" . $sTable . "`.`id` " . $sWay . " ";
+                break;
+
+            case 'order_by_ghosts':
+                $aSql['order'] = " ORDER BY `g`.`order` " . $sWay . ", `" . $sTable . "`.`id` " . $sWay . " ";
+                break;
+
+            case 'last':
+                $aSql['order'] = " ORDER BY `f`.`added` " . $sWay . ", `" . $sTable . "`.`id` " . $sWay . " ";
+                break;
+
+            case 'featured':
+                $aSql['order'] = " ORDER BY `" . $sTable . "`.`featured` " . $sWay . ", `" . $sTable . "`.`id` " . $sWay . "";
+                break;
+
+            case 'popular':
+                $aSql['order'] = " ORDER BY `" . $sTable . "`.`views` " . $sWay . ", `" . $sTable . "`.`id` " . $sWay . "";
+                break;
+
+            case 'top':
+                $aSql['order'] = '';
+
+                $aPartsUp = $aPartsDown = array();
+                if(!empty($CNF['OBJECT_VOTES_MEDIA']) && ($oVote = BxDolVote::getObjectInstance($CNF['OBJECT_VOTES_MEDIA'], 0, false)) !== false) {
+                    $aVote = $oVote->getSystemInfo();
+                    if(!empty($aVote['trigger_table']) && !empty($aVote['trigger_field_count']))
+                        $aPartsUp[] = '`' . $aVote['trigger_table'] . '`.`' . $aVote['trigger_field_count'] . '`';
+                }
+
+                if(!empty($CNF['OBJECT_SCORES_MEDIA']) && ($oScore = BxDolScore::getObjectInstance($CNF['OBJECT_SCORES_MEDIA'], 0, false)) !== false) {
+                    $aScore = $oScore->getSystemInfo();
+                    if(!empty($aScore['trigger_table']) && !empty($aScore['trigger_field_cup']) && !empty($aScore['trigger_field_cdown'])) {
+                        $aPartsUp[] = '`' . $aScore['trigger_table'] . '`.`' . $aScore['trigger_field_cup'] . '`';
+                        $aPartsDown[] = '`' . $aScore['trigger_table'] . '`.`' . $aScore['trigger_field_cdown'] . '`';
+                    }
+                }
+
+                if(empty($aPartsUp) && empty($aPartsDown))
+                    break;
+
+                $aSql['order'] = ' ORDER BY ' . pow(10, 8) . ' * ((' . implode(' + ', $aPartsUp) . ') - (' . implode(' + ', $aPartsDown) . ')) / (UNIX_TIMESTAMP() - `f`.`added`) ' . $sWay;
+                break;
+        }
+
+        return $aSql;
+    }
+
+    public function getNextPrevItem($aMediaInfo, $isNext)
+    {
+        $sOper = $isNext ? '<' : '>';
+
+        $this->sOrderDirection = $isNext ? 'DESC' : 'ASC';
+        $this->aCurrent['paginate']['perPage'] = 1;
+        $this->aCurrent['restriction']['next_prev2'] = array('value' => $aMediaInfo['id'], 'field' => 'id', 'operator' => '!=');
+
+        $this->setProcessPrivateContent(true);
+        $aRestrictions = array_keys($this->aCurrent['restriction']);
+        foreach ($aRestrictions as $sKey) 
+            if (0 === strpos($sKey, 'privacy_'))
+                unset($this->aCurrent['restriction'][$sKey]);
+
+        switch ($this->aCurrent['sorting']) {
+            case 'order':
+                $this->sOrderDirection = $isNext ? 'ASC' : 'DESC';
+                $sOper = $isNext ? '>' : '<';
+                $this->aCurrent['restriction_sql'] = " AND (`{$this->aCurrent['table']}`.`order` {$sOper} {$aMediaInfo['order']} OR (`{$this->aCurrent['table']}`.`order` = {$aMediaInfo['order']} AND `{$this->aCurrent['table']}`.`id` {$sOper} {$aMediaInfo['id']})) ";
+                break;
+            case 'order_by_ghosts':
+                $this->sOrderDirection = $isNext ? 'ASC' : 'DESC';
+                $sOper = $isNext ? '>' : '<';
+                $this->aCurrent['restriction_sql'] = " AND (`g`.`order` {$sOper} {$aMediaInfo['gorder']} OR (`g`.`order` = {$aMediaInfo['gorder']} AND `{$this->aCurrent['table']}`.`id` {$sOper} {$aMediaInfo['id']})) ";
+                break;
+            case 'last':
+                $this->aCurrent['restriction_sql'] = " AND (`f`.`added` {$sOper} {$aMediaInfo['added']} OR (`f`.`added` = {$aMediaInfo['added']} AND `{$this->aCurrent['table']}`.`id` {$sOper} {$aMediaInfo['id']})) ";
+                break;
+            case 'featured':
+                $this->aCurrent['restriction_sql'] = " AND (`{$this->aCurrent['table']}`.`featured` {$sOper} {$aMediaInfo['featured']} OR (`{$this->aCurrent['table']}`.`featured` = {$aMediaInfo['featured']} AND `{$this->aCurrent['table']}`.`id` {$sOper} {$aMediaInfo['id']})) ";
+                break;
+            case 'popular':
+                $this->aCurrent['restriction_sql'] = " AND (`{$this->aCurrent['table']}`.`views` {$sOper} {$aMediaInfo['views']} OR (`{$this->aCurrent['table']}`.`views` = {$aMediaInfo['views']} AND `{$this->aCurrent['table']}`.`id` {$sOper} {$aMediaInfo['id']})) ";
+                break;
+        }
+
+        $aData = $this->getSearchData();
+        if (count($aData) > 0)
+            return array_shift($aData);
+
+        return false;
+    }
+
+    function getRssUnitLink (&$a)
+    {
+        return bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $this->oModule->_oConfig->CNF['URI_VIEW_MEDIA'] . '&id=' . $a['id']));
+    }
+
+    protected function _updateCurrentForOrderByGhosts()
+    {
+        $CNF = &$this->oModule->_oConfig->CNF;
+
+        $this->aCurrent['join']['ghosts'] = [
+            'type' => 'INNER',
+            'table' => 'sys_storage_ghosts',
+            'table_alias' => 'g',
+            'mainField' => 'file_id',
+            'on_sql' => $this->oModule->_oDb->prepareAsString(" `g`.`id`=`bx_albums_files2albums`.`file_id` AND `g`.`content_id`=`bx_albums_files2albums`.`content_id` AND `g`.`object`=? ", $CNF['OBJECT_STORAGE']),
+            'joinFields' => array('order'),
+        ];
+        $this->aCurrent['sorting'] = 'order_by_ghosts';
+        $this->sOrderDirection = 'ASC';
+    }
+
+/*
+    function displaySearchUnit ($aData)
+    {
+        $oMain = $this->getMain();
+        return $oMain->_oTemplate->unit($aData, $this->bProcessPrivateContent, $this->_bLiveSearch ? $this->sUnitTemplateLiveSearch : $this->sUnitTemplate, $this->aUnitParams);
+    }
+*/
+}
+
+/** @} */

+ 143 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/source/install/config.php

@@ -0,0 +1,143 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Albums Albums
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_albums',
+    'title' => 'Albums',
+    'note' => 'Photos & videos',
+    'version' => '15.0.2',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/albums/',
+    'home_uri' => 'albums',
+
+    'db_prefix' => 'bx_albums_',
+    'class_prefix' => 'BxAlbums',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Albums',
+
+    /**
+     * List of page triggers.
+     */
+    'page_triggers' => array (
+    	'trigger_page_profile_view_entry',
+    ),
+
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+    	'trigger_profile_view_submenu',
+        'trigger_group_view_submenu',
+    ),
+
+	/**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+    	'bx_albums_files'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+        'bx_albums_preview',
+        'bx_albums_browse',
+        'bx_albums_big',
+        'bx_albums_video_poster_browse',
+        'bx_albums_video_poster_preview',
+        'bx_albums_video_poster_big',
+        'bx_albums_video_mp4',
+        'bx_albums_video_mp4_hd',
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_albums',
+    	'bx_albums_media',
+    	'bx_albums_cmts',
+        'bx_albums_media_cmts'
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+        'process_menu_triggers' => 1,
+        'process_page_triggers' => 1,
+    	'process_esearches' => 1,
+    	'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+    	'unregister_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Connections Section
+     */
+    'relations' => array(
+    	'bx_timeline',
+    	'bx_notifications'
+    ),
+
+);
+
+/** @} */

+ 311 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/source/install/langs/en.xml

@@ -0,0 +1,311 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+
+	<string name="_bx_albums"><![CDATA[Albums]]></string>
+	<string name="_bx_albums_cmts"><![CDATA[Albums' Comments]]></string>
+        <string name="_bx_albums_reactions"><![CDATA[Albums' Reactions]]></string>
+	<string name="_bx_albums_media"><![CDATA[Images]]></string>
+	<string name="_bx_albums_media_cmts"><![CDATA[Images' Comments]]></string>
+
+        <string name="_bx_albums_option_enable_auto_approve"><![CDATA[Auto approve an album after creation]]></string>
+	<string name="_bx_albums_option_summary_chars"><![CDATA[Number of characters in auto-cropped album summary]]></string>
+	<string name="_bx_albums_option_plain_summary_chars"><![CDATA[Number of characters in plain album description when album hasn't thumb]]></string>
+	<string name="_bx_albums_option_per_page_browse"><![CDATA[Number of items per page]]></string>
+	<string name="_bx_albums_option_per_page_profile"><![CDATA[Number of items on profile page]]></string>
+	<string name="_bx_albums_option_album_browsing_unit"><![CDATA[Number of items in album card]]></string>
+	<string name="_bx_albums_option_rss_num"><![CDATA[Number of items in RSS feed]]></string>
+	<string name="_bx_albums_option_searchable_fields"><![CDATA[Searchable fields for keyword search]]></string>
+  <string name="_bx_albums_option_per_page_for_favorites_lists"><![CDATA[Number of favorite lists per page]]></string>
+
+	<string name="_bx_albums_form_entry"><![CDATA[Albums]]></string>
+	<string name="_bx_albums_form_entry_display_add"><![CDATA[Add Album]]></string>
+    <string name="_bx_albums_form_entry_display_edit"><![CDATA[Edit Album]]></string>
+	<string name="_bx_albums_form_entry_display_add_images"><![CDATA[Add Images To Album]]></string>
+	<string name="_bx_albums_form_entry_display_delete"><![CDATA[Delete Album]]></string>
+	<string name="_bx_albums_form_entry_display_view"><![CDATA[View Album]]></string>
+	<string name="_bx_albums_form_entry_input_author"><![CDATA[Author]]></string>
+	<string name="_bx_albums_form_entry_input_sys_do_submit"><![CDATA[Submit]]></string>
+	<string name="_bx_albums_form_entry_input_do_submit"><![CDATA[Submit]]></string>
+	<string name="_bx_albums_form_entry_input_sys_title"><![CDATA[Caption]]></string>
+	<string name="_bx_albums_form_entry_input_title"><![CDATA[Caption]]></string>
+	<string name="_bx_albums_form_entry_input_title_err"><![CDATA[Caption is essential min 3 max 160.]]></string>
+	<string name="_bx_albums_form_entry_input_sys_text"><![CDATA[Album Description]]></string>
+	<string name="_bx_albums_form_entry_input_text"><![CDATA[Album Description]]></string>
+	<string name="_bx_albums_form_entry_input_sys_delete_confirm"><![CDATA[Delete confirmation]]></string>
+	<string name="_bx_albums_form_entry_input_delete_confirm"><![CDATA[I am sure I want to delete this album]]></string>
+	<string name="_bx_albums_form_entry_input_delete_confirm_info"><![CDATA[This action can not be undone]]></string>
+	<string name="_bx_albums_form_entry_input_delete_confirm_error"><![CDATA[Please check this checkbox if you really want to delete the album]]></string>
+	<string name="_bx_albums_form_entry_input_sys_pictures"><![CDATA[Pictures]]></string>
+	<string name="_bx_albums_form_entry_input_pictures"><![CDATA[Pictures]]></string>
+    <string name="_bx_albums_form_entry_input_sys_allow_view_to"><![CDATA[Visibility]]></string>
+    <string name="_bx_albums_form_entry_input_allow_view_to"><![CDATA[Visibility]]></string>
+    <string name="_bx_albums_form_entry_input_allow_view_favorite_list"><![CDATA[Visibility]]></string>
+    <string name="_bx_albums_form_entry_file_title"><![CDATA[Caption]]></string>
+	<string name="_bx_albums_form_entry_input_sys_date_added"><![CDATA[Created]]></string>
+	<string name="_bx_albums_form_entry_input_date_added"><![CDATA[Created]]></string>
+	<string name="_bx_albums_form_entry_input_sys_date_changed"><![CDATA[Updated]]></string>
+    <string name="_bx_albums_form_entry_input_date_changed"><![CDATA[Updated]]></string>
+
+        <string name="_bx_albums_form_media"><![CDATA[Medias]]></string>
+        <string name="_bx_albums_form_media_display_edit"><![CDATA[Edit Media]]></string>
+        <string name="_bx_albums_form_media_display_move"><![CDATA[Move Media]]></string>
+        <string name="_bx_albums_form_media_input_sys_title"><![CDATA[Caption]]></string>
+	<string name="_bx_albums_form_media_input_title"><![CDATA[Caption]]></string>
+        <string name="_bx_albums_form_media_input_sys_content_id"><![CDATA[Album]]></string>
+	<string name="_bx_albums_form_media_input_content_id"><![CDATA[Album]]></string>
+        <string name="_bx_albums_form_media_input_sys_do_submit"><![CDATA[Submit]]></string>
+	<string name="_bx_albums_form_media_input_do_submit"><![CDATA[Submit]]></string>
+        <string name="_bx_albums_form_media_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+	<string name="_bx_albums_form_media_input_do_cancel"><![CDATA[Cancel]]></string>
+
+	<string name="_bx_albums_acl_action_create_entry"><![CDATA[Create Album]]></string>
+	<string name="_bx_albums_acl_action_delete_entry"><![CDATA[Delete Album]]></string>
+	<string name="_bx_albums_acl_action_view_entry"><![CDATA[View Album]]></string>
+	<string name="_bx_albums_acl_action_set_thumb"><![CDATA[Set Album Cover]]></string>
+	<string name="_bx_albums_acl_action_edit_any_entry"><![CDATA[Edit Any Album]]></string>
+	<string name="_bx_albums_acl_action_delete_any_entry"><![CDATA[Delete Any Album]]></string>
+
+	<string name="_bx_albums_page_title_sys_create_entry"><![CDATA[New Album]]></string>
+	<string name="_bx_albums_page_title_create_entry"><![CDATA[New Album]]></string>
+	<string name="_bx_albums_page_block_title_create_entry"><![CDATA[New Album]]></string>
+	<string name="_bx_albums_page_title_sys_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_albums_page_title_edit_entry"><![CDATA[Edit]]></string>
+	<string name="_bx_albums_page_title_sys_add_images"><![CDATA[Add/Edit Pictures]]></string>
+	<string name="_bx_albums_page_title_add_images"><![CDATA[Add/Edit Pictures]]></string>
+    <string name="_bx_albums_page_block_title_edit_entry"><![CDATA[Edit]]></string>
+	<string name="_bx_albums_page_block_title_add_images"><![CDATA[Add/Edit Pictures]]></string>
+    <string name="_bx_albums_page_title_sys_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_albums_page_title_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_albums_page_block_title_delete_entry"><![CDATA[Delete]]></string>
+	<string name="_bx_albums_page_title_sys_view_entry"><![CDATA[View Album]]></string>
+	<string name="_bx_albums_page_title_view_entry"><![CDATA[{title}]]></string>
+	<string name="_bx_albums_page_title_sys_view_media"><![CDATA[View Image]]></string>
+	<string name="_bx_albums_page_title_view_media"><![CDATA[{title}]]></string>
+	<string name="_bx_albums_page_block_title_entry_view_media"><![CDATA[View Image]]></string>
+	<string name="_bx_albums_page_block_title_entry_view_media_exif"><![CDATA[EXIF]]></string>
+	<string name="_bx_albums_page_block_title_entry_attachments"><![CDATA[Attachments]]></string>
+	<string name="_bx_albums_page_block_title_entry_text"><![CDATA[Album Description]]></string>
+	<string name="_bx_albums_page_title_browse"><![CDATA[Albums]]></string>
+	<string name="_bx_albums_page_title_search_results"><![CDATA[Albums matching "{0}"]]></string>
+	<string name="_bx_albums_page_title_browse_by_author"><![CDATA[{display_name}'s Albums]]></string>
+  <string name="_bx_albums_page_title_browse_by_context"><![CDATA[Albums in {display_name}]]></string>
+	<string name="_bx_albums_page_title_browse_featured"><![CDATA[Featured Albums]]></string>
+	<string name="_bx_albums_page_title_browse_featured_media"><![CDATA[Featured Media]]></string>
+    <string name="_bx_albums_page_title_browse_recent"><![CDATA[Latest Albums]]></string>
+    <string name="_bx_albums_page_title_browse_popular"><![CDATA[Popular Albums]]></string>
+    <string name="_bx_albums_page_title_browse_popular_media"><![CDATA[Popular Images]]></string>
+    <string name="_bx_albums_page_title_browse_top"><![CDATA[Top Albums]]></string>
+    <string name="_bx_albums_page_title_browse_top_media"><![CDATA[Top Images]]></string>
+    <string name="_bx_albums_page_title_browse_recent_media"><![CDATA[Recent Images]]></string>
+    <string name="_bx_albums_page_title_browse_media_in_album"><![CDATA[Images]]></string>
+    <string name="_bx_albums_page_title_browse_updated"><![CDATA[Updated Albums]]></string>
+    <string name="_bx_albums_page_title_sys_home"><![CDATA[Albums Home]]></string>
+    <string name="_bx_albums_page_title_home"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_page_block_title_featured_entries_view_extended"><![CDATA[Featured Albums]]></string>
+    <string name="_bx_albums_page_block_title_featured_entries_view_gallery_media"><![CDATA[Featured Media]]></string>
+    <string name="_bx_albums_page_block_title_recent_entries"><![CDATA[Latest Albums]]></string>
+    <string name="_bx_albums_page_block_title_entry_all_actions"><![CDATA[All Actions]]></string>
+    <string name="_bx_albums_page_block_title_entry_social_sharing"><![CDATA[Share]]></string>
+    <string name="_bx_albums_page_block_title_entry_author"><![CDATA[Author]]></string>
+    <string name="_bx_albums_page_block_title_entry_info"><![CDATA[Info]]></string>
+    <string name="_bx_albums_page_block_title_entry_actions"><![CDATA[Actions]]></string>
+    <string name="_bx_albums_page_block_title_entry_comments"><![CDATA[Comments]]></string>
+	<string name="_bx_albums_page_block_title_entry_reports"><![CDATA[Reports]]></string>
+    <string name="_bx_albums_page_block_title_entry_comments_link"><![CDATA[Comments to <a href="{entry_link}">{title}</a>]]></string>
+    <string name="_bx_albums_page_block_title_entry_location"><![CDATA[Location]]></string>
+    <string name="_bx_albums_page_title_sys_entries_popular"><![CDATA[Popular]]></string>
+    <string name="_bx_albums_page_title_entries_popular"><![CDATA[Popular]]></string>
+    <string name="_bx_albums_page_title_sys_entries_popular_media"><![CDATA[Popular Images]]></string>
+    <string name="_bx_albums_page_title_entries_popular_media"><![CDATA[Popular Images]]></string>
+    <string name="_bx_albums_page_block_title_popular_entries"><![CDATA[Popular]]></string>
+    <string name="_bx_albums_page_block_title_popular_media"><![CDATA[Popular Images]]></string>
+    <string name="_bx_albums_page_title_sys_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_albums_page_title_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_albums_page_title_sys_entries_top_media"><![CDATA[Top Images]]></string>
+    <string name="_bx_albums_page_title_entries_top_media"><![CDATA[Top Images]]></string>
+    <string name="_bx_albums_page_block_title_top_entries"><![CDATA[Top]]></string>
+    <string name="_bx_albums_page_block_title_top_media"><![CDATA[Top Images]]></string>
+    <string name="_bx_albums_page_block_title_recent_media"><![CDATA[Recent Images]]></string>
+    <string name="_bx_albums_page_block_title_updated_entries"><![CDATA[Updated Albums]]></string>
+    <string name="_bx_albums_page_block_title_sys_favorites_entries"><![CDATA[Favorite List]]></string>
+    <string name="_bx_albums_page_block_title_favorites_entries"><![CDATA["{title}" List]]></string>
+    <string name="_bx_albums_page_block_title_favorites_entries_info"><![CDATA[Info]]></string>
+    <string name="_bx_albums_page_block_title_favorites_entries_actions"><![CDATA[Favorites List Actions]]></string>
+    <string name="_bx_albums_page_title_sys_view_entry_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_albums_page_title_view_entry_comments"><![CDATA[Comments to {title}]]></string>
+    <string name="_bx_albums_page_title_sys_entries_of_author"><![CDATA[Albums of author]]></string>
+    <string name="_bx_albums_page_title_entries_of_author"><![CDATA[{display_name}'s albums]]></string>
+    <string name="_bx_albums_page_title_sys_entries_in_context"><![CDATA[Albums in context]]></string>
+    <string name="_bx_albums_page_title_entries_in_context"><![CDATA[Albums in {display_name}]]></string>
+    <string name="_bx_albums_page_block_title_favorites_of_author"><![CDATA[Favorite albums by <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_albums_page_block_title_sys_favorites_of_author"><![CDATA[Favorite albums of author]]></string>
+    <string name="_bx_albums_page_block_title_favorites_of_author_media"><![CDATA[Favorite media by <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_albums_page_block_title_sys_favorites_of_author_media"><![CDATA[Favorite media of author]]></string>
+    <string name="_bx_albums_page_block_title_entries_of_author"><![CDATA[Albums by <a href="{profile_link}">{display_name}</a>]]></string>   
+    <string name="_bx_albums_page_block_title_sys_entries_of_author"><![CDATA[Albums of author]]></string>
+    <string name="_bx_albums_page_block_title_entries_in_context"><![CDATA[Albums in <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_albums_page_block_title_sys_entries_in_context"><![CDATA[Albums in context]]></string>
+    <string name="_bx_albums_page_block_title_entries_actions"><![CDATA[Albums actions]]></string>
+    <string name="_bx_albums_page_block_title_browse_cmts"><![CDATA[Albums' Comments]]></string>
+    <string name="_bx_albums_page_block_title_browse_cmts_media"><![CDATA[Images Comments]]></string>
+	<string name="_bx_albums_page_title_sys_manage"><![CDATA[Manage Own Albums]]></string>
+	<string name="_bx_albums_page_title_sys_manage_administration"><![CDATA[Manage All Albums]]></string>
+	<string name="_bx_albums_page_title_manage"><![CDATA[Manage]]></string>
+	<string name="_bx_albums_page_block_title_system_manage"><![CDATA[Manage Own Albums]]></string>
+	<string name="_bx_albums_page_block_title_system_manage_administration"><![CDATA[Manage All Albums]]></string>  
+	<string name="_bx_albums_page_block_title_manage"><![CDATA[Manage]]></string>
+	<string name="_bx_albums_page_block_title_popular_keywords_albums"><![CDATA[Popular Albums Hashtags]]></string>
+	<string name="_bx_albums_page_block_title_popular_keywords_media"><![CDATA[Popular Media Hashtags]]></string>
+	<string name="_bx_albums_page_block_title_popular_keywords_media_camera"><![CDATA[Popular Cameras]]></string>
+	<string name="_bx_albums_page_block_title_sys_my_entries"><![CDATA[User Albums]]></string>
+        <string name="_bx_albums_page_block_title_my_entries"><![CDATA[{display_name} Albums]]></string>
+  	<string name="_bx_albums_page_title_sys_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_albums_page_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_albums_page_title_sys_entries_favorites"><![CDATA[Favorites Media]]></string>
+    <string name="_bx_albums_page_title_entries_favorites"><![CDATA[Favorites Media]]></string>
+    <string name="_bx_albums_page_block_title_search_form"><![CDATA[Search Form]]></string>
+    <string name="_bx_albums_page_block_title_search_results"><![CDATA[Search Results]]></string>
+    <string name="_bx_albums_page_block_title_search_form_media"><![CDATA[Images Search Form]]></string>
+    <string name="_bx_albums_page_block_title_search_results_media"><![CDATA[Images Search Results]]></string>
+    <string name="_bx_albums_page_block_title_search_form_cmts"><![CDATA[Comments Search Form]]></string>
+    <string name="_bx_albums_page_block_title_search_results_cmts"><![CDATA[Comments Search Results]]></string>
+    <string name="_bx_albums_page_block_title_search_form_media_cmts"><![CDATA[Images Comments Search Form]]></string>
+    <string name="_bx_albums_page_block_title_search_results_media_cmts"><![CDATA[Images Comments Search Results]]></string>
+    <string name="_bx_albums_page_block_title_sys_recent_media_view_showcase"><![CDATA[Recent Images (Showcase View)]]></string>
+    <string name="_bx_albums_page_block_title_recent_media_view_showcase"><![CDATA[Recent Images]]></string>
+    <string name="_bx_albums_page_block_title_sys_popular_media_view_showcase"><![CDATA[Top Images (Showcase View)]]></string>
+    <string name="_bx_albums_page_block_title_popular_media_view_showcase"><![CDATA[Top Images]]></string>
+    <string name="_bx_albums_page_block_title_sys_featured_media_view_showcase"><![CDATA[Featured Images (Showcase View)]]></string>
+    <string name="_bx_albums_page_block_title_featured_media_view_showcase"><![CDATA[Featured Images]]></string>
+	<string name="_bx_albums_page_block_title_sys_entry_context"><![CDATA[Posted in]]></string>
+  	<string name="_bx_albums_page_block_title_entry_context"><![CDATA[Posted in]]></string>
+
+    <string name="_bx_albums_menu_set_title_view_entry"><![CDATA[Album Actions]]></string>
+    <string name="_bx_albums_menu_title_view_entry"><![CDATA[Album Actions]]></string>
+    <string name="_bx_albums_menu_set_title_view_media"><![CDATA[Media Actions]]></string>
+    <string name="_bx_albums_menu_title_view_media"><![CDATA[Media Actions]]></string>
+    <string name="_bx_albums_menu_set_title_view_actions_media"><![CDATA[Media All Actions]]></string>
+    <string name="_bx_albums_menu_title_view_actions_media"><![CDATA[Media All Actions]]></string>
+    <string name="_bx_albums_menu_set_title_view_actions_media_unit"><![CDATA[Media Card Actions]]></string>
+    <string name="_bx_albums_menu_title_view_actions_media_unit"><![CDATA[Media Card Actions]]></string>
+    <string name="_bx_albums_menu_title_submenu"><![CDATA[Albums Submenu]]></string>
+    <string name="_bx_albums_menu_set_title_submenu"><![CDATA[Albums Submenu]]></string>
+    <string name="_bx_albums_menu_item_title_system_create_entry"><![CDATA[Album]]></string>
+    <string name="_bx_albums_menu_item_title_create_entry"><![CDATA[Album]]></string>
+    <string name="_bx_albums_menu_item_title_system_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_albums_menu_item_title_edit_entry"><![CDATA[Edit]]></string>
+    <string name="_bx_albums_menu_item_title_system_edit_album"><![CDATA[Edit Album]]></string>
+    <string name="_bx_albums_menu_item_title_edit_album"><![CDATA[Edit Album]]></string>
+    <string name="_bx_albums_menu_item_title_system_add_images"><![CDATA[Add/Edit Pictures]]></string>
+    <string name="_bx_albums_menu_item_title_add_images"><![CDATA[Add/Edit Pictures]]></string>
+    <string name="_bx_albums_menu_item_title_system_edit_image"><![CDATA[Edit Image]]></string>
+    <string name="_bx_albums_menu_item_title_edit_image"><![CDATA[Edit Image]]></string>
+    <string name="_bx_albums_menu_item_title_system_delete_image"><![CDATA[Delete Image]]></string>
+    <string name="_bx_albums_menu_item_title_delete_image"><![CDATA[Delete Image]]></string>
+    <string name="_bx_albums_menu_item_title_system_move_image"><![CDATA[Move Image To]]></string>
+    <string name="_bx_albums_menu_item_title_move_image"><![CDATA[Move Image To]]></string>
+    <string name="_bx_albums_menu_item_title_system_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_albums_menu_item_title_delete_entry"><![CDATA[Delete]]></string>
+    <string name="_bx_albums_menu_item_title_system_view_entry"><![CDATA[View Album]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_home"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_item_title_entries_home"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_recent"><![CDATA[Latest]]></string>
+    <string name="_bx_albums_menu_item_title_entries_recent"><![CDATA[Latest]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_albums_menu_item_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_albums_menu_item_title_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_popular"><![CDATA[Popular]]></string>
+    <string name="_bx_albums_menu_item_title_entries_popular"><![CDATA[Popular]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_popular_media"><![CDATA[Popular Images]]></string>
+    <string name="_bx_albums_menu_item_title_entries_popular_media"><![CDATA[Popular Images]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_albums_menu_item_title_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_albums_menu_item_title_system_entries_top_media"><![CDATA[Top Images]]></string>
+    <string name="_bx_albums_menu_item_title_entries_top_media"><![CDATA[Top Images]]></string>    
+    <string name="_bx_albums_menu_title_view_entry_submenu"><![CDATA[View Album Submenu]]></string>
+    <string name="_bx_albums_menu_set_title_view_entry_submenu"><![CDATA[View Album Submenu]]></string>
+    <string name="_bx_albums_menu_item_title_view_entry_submenu_entry"><![CDATA[Album]]></string>
+    <string name="_bx_albums_menu_item_title_view_entry_submenu_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_albums_menu_item_title_system_view_entry_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_albums_menu_item_title_system_view_entries_author"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_item_title_view_entries_author"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_item_title_system_view_entries_in_context"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_item_title_view_entries_in_context"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_title_entries_my"><![CDATA[My Albums Action Menu]]></string>
+    <string name="_bx_albums_menu_set_title_entries_my"><![CDATA[My Albums Menu]]></string>
+    <string name="_bx_albums_menu_item_title_system_manage_my_albums"><![CDATA[My albums]]></string>
+    <string name="_bx_albums_menu_item_title_manage_my_albums"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_set_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_albums_menu_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_albums_menu_item_title_system_admt_albums"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_item_title_admt_albums"><![CDATA[Albums]]></string>
+    <string name="_bx_albums_menu_item_title_manage_my"><![CDATA[My Albums]]></string>
+    <string name="_bx_albums_menu_item_title_manage_all"><![CDATA[All Albums]]></string>
+    <string name="_bx_albums_menu_item_title_system_sm_items"><![CDATA[Items]]></string>
+    <string name="_bx_albums_menu_item_title_sm_items"><![CDATA[{0} items]]></string>
+
+    <string name="_bx_albums_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></string>
+    <string name="_bx_albums_grid_filter_item_title_adm_select_one_filter2"><![CDATA[Any Audience]]></string>
+    <string name="_bx_albums_grid_filter_item_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_albums_grid_filter_item_title_adm_hidden"><![CDATA[Hidden]]></string>
+    <string name="_bx_albums_grid_filter_item_title_adm_pending"><![CDATA[Pending]]></string>
+    <string name="_bx_albums_grid_column_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_albums_grid_column_title_adm_title"><![CDATA[Caption]]></string>
+    <string name="_bx_albums_grid_column_title_adm_added"><![CDATA[Added]]></string>
+    <string name="_bx_albums_grid_column_title_adm_author"><![CDATA[Author]]></string>
+    <string name="_bx_albums_grid_action_title_adm_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_albums_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_albums_grid_action_title_adm_clear_reports"><![CDATA[Сlear reports]]></string>
+    <string name="_bx_albums_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
+    <string name="_bx_albums_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
+    <string name="_bx_albums_grid_column_title_adm_status_admin"><![CDATA[Moderation]]></string>
+    <string name="_bx_albums_grid_action_err_delete"><![CDATA[Cannot delete the selected album(s).]]></string>
+    <string name="_bx_albums_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
+
+    <string name="_bx_albums_chart_growth"><![CDATA[Albums: Growth]]></string>
+    <string name="_bx_albums_chart_growth_speed"><![CDATA[Albums: Growth Speed]]></string>
+    <string name="_bx_albums_chart_growth_media"><![CDATA[Images: Growth]]></string>
+    <string name="_bx_albums_chart_growth_speed_media"><![CDATA[Images: Growth Speed]]></string>
+
+    <string name="_bx_albums_search_extended"><![CDATA[Albums Search]]></string>
+    <string name="_bx_albums_search_extended_media"><![CDATA[Images Search]]></string>
+    <string name="_bx_albums_search_extended_cmts"><![CDATA[Albums Comments Search]]></string>
+    <string name="_bx_albums_search_extended_media_cmts"><![CDATA[Images Comments Search]]></string>
+
+    <string name="_bx_albums_txt_all_entries_by"><![CDATA[{0} albums]]></string>
+    <string name="_bx_albums_txt_all_entries_in"><![CDATA[<a href="{0}">All Albums in {1} ({2})</a>]]></string>
+    <string name="_bx_albums_txt_sample_single"><![CDATA[album]]></string>
+    <string name="_bx_albums_txt_sample_single_with_article"><![CDATA[an album]]></string>
+    <string name="_bx_albums_txt_sample_comment_single"><![CDATA[comment to album]]></string>
+    <string name="_bx_albums_txt_sample_vote_single"><![CDATA[vote to album]]></string>
+    <string name="_bx_albums_txt_sample_reaction_single"><![CDATA['{0}' reaction to album]]></string>
+    <string name="_bx_albums_txt_sample_score_up_single"><![CDATA[score up vote to album]]></string>
+    <string name="_bx_albums_txt_sample_score_down_single"><![CDATA[score down vote to album]]></string>
+    <string name="_bx_albums_txt_sample_action_changed"><![CDATA[changed {0}]]></string>
+    <string name="_bx_albums_txt_media_single"><![CDATA[image]]></string>
+    <string name="_bx_albums_txt_media_comment_single"><![CDATA[image's comment]]></string>
+    <string name="_bx_albums_txt_media_vote_single"><![CDATA[image's vote]]></string>
+    <string name="_bx_albums_txt_media_score_up_single"><![CDATA[image's score up vote]]></string>
+    <string name="_bx_albums_txt_media_score_down_single"><![CDATA[image's score down vote]]></string>
+    <string name="_bx_albums_txt_media_title_alt"><![CDATA[{0}'s media]]></string>
+    <string name="_bx_albums_txt_media_title_empty"><![CDATA[No Caption]]></string>
+    <string name="_bx_albums_txt_media_album_link"><![CDATA[<a href="{0}">{1}</a> album by <a href="{2}">{3}</a>]]></string>
+    <string name="_bx_albums_txt_media_album_camera"><![CDATA[Camera]]></string>
+    <string name="_bx_albums_txt_media_album_focal_length"><![CDATA[Focal length]]></string>
+    <string name="_bx_albums_txt_media_album_focal_length_value"><![CDATA[{0}mm]]></string>
+    <string name="_bx_albums_txt_media_album_aperture"><![CDATA[Aperture]]></string>
+    <string name="_bx_albums_txt_media_album_shutter_speed"><![CDATA[Exposure time]]></string>
+    <string name="_bx_albums_txt_media_album_shutter_speed_value"><![CDATA[{0}s]]></string>
+    <string name="_bx_albums_txt_media_album_iso"><![CDATA[ISO]]></string>
+    <string name="_bx_albums_txt_processing_awaiting"><![CDATA[Scheduled for publishing as soon as video conversion is complete.]]></string>
+    <string name="_bx_albums_txt_processing_failed"><![CDATA[Album isn't published because video conversion failed, edit video to fix the issue.]]></string>
+
+    <string name="_bx_albums_txt_msg_status_hidden"><![CDATA[This album was hidden.]]></string>
+    <string name="_bx_albums_txt_msg_status_pending"><![CDATA[This album is pending approval.]]></string>
+    <string name="_bx_albums_txt_err_cannot_perform_action"><![CDATA[Cannot perform the action. Please report.]]></string>
+
+    <string name="_bx_albums_alert_action_medias_added"><![CDATA[add images]]></string>
+    <string name="_bx_albums_alert_action_medias_added_follow_member"><![CDATA[New media]]></string>
+    <string name="_bx_albums_alert_action_medias_added_follow_context"><![CDATA[New media]]></string>
+</resources>

+ 609 - 0
modules/boonex/albums/updates/15.0.1_15.0.2/source/install/sql/install.sql

@@ -0,0 +1,609 @@
+-- TABLE: entries
+CREATE TABLE IF NOT EXISTS `bx_albums_albums` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `author` int(10) unsigned NOT NULL,
+  `added` int(11) NOT NULL,
+  `changed` int(11) NOT NULL,
+  `thumb` int(11) NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `text` text NOT NULL,
+  `labels` text NOT NULL,
+  `location` text NOT NULL,
+  `views` int(11) NOT NULL default '0',
+  `rate` float NOT NULL default '0',
+  `votes` int(11) NOT NULL default '0',
+  `rrate` float NOT NULL default '0',
+  `rvotes` int(11) NOT NULL default '0',
+  `score` int(11) NOT NULL default '0',
+  `sc_up` int(11) NOT NULL default '0',
+  `sc_down` int(11) NOT NULL default '0',
+  `favorites` int(11) NOT NULL default '0',
+  `comments` int(11) NOT NULL default '0',
+  `reports` int(11) NOT NULL default '0',
+  `featured` int(11) NOT NULL default '0',
+  `cf` int(11) NOT NULL default '1',
+  `allow_view_to` varchar(16) NOT NULL DEFAULT '3',
+  `status` enum('active','awaiting','failed','hidden') NOT NULL DEFAULT 'active',
+  `status_admin` enum('active','hidden','pending') NOT NULL DEFAULT 'active',
+  PRIMARY KEY (`id`),
+  FULLTEXT KEY `title_text` (`title`,`text`)
+);
+
+-- TABLE: storages & transcoders
+CREATE TABLE IF NOT EXISTS `bx_albums_files` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `dimensions` varchar(12) NOT NULL,
+  `duration` int(11) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_photos_resized` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_files2albums` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `content_id` int(10) unsigned NOT NULL,
+  `file_id` int(11) NOT NULL,
+  `author` int(10) unsigned NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `views` int(11) NOT NULL,
+  `rate` float NOT NULL,
+  `votes` int(11) NOT NULL,
+  `score` int(11) NOT NULL default '0',
+  `sc_up` int(11) NOT NULL default '0',
+  `sc_down` int(11) NOT NULL default '0',
+  `favorites` int(11) NOT NULL default '0',
+  `comments` int(11) NOT NULL,
+  `reports` int(11) NOT NULL default '0',
+  `featured` int(11) NOT NULL default '0',
+  `cf` int(11) NOT NULL default '1',
+  `data` text NOT NULL,
+  `exif` text NOT NULL,
+  `order` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `file_content` (`file_id`,`content_id`),
+  KEY `content_id` (`content_id`),
+  FULLTEXT KEY `search_fields` (`title`)
+);
+
+-- TABLE: comments
+CREATE TABLE IF NOT EXISTS `bx_albums_cmts` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  `cmt_cf` int(11) NOT NULL default '1',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_cmts_notes` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_cmts_media` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  `cmt_cf` int(11) NOT NULL default '1',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+-- TABLE: votes
+CREATE TABLE IF NOT EXISTS `bx_albums_votes` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_votes_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_votes_media` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_votes_media_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_reactions` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `reaction` varchar(32) NOT NULL default '',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `reaction` (`object_id`, `reaction`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_reactions_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `reaction` varchar(32) NOT NULL default '',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+-- TABLE: views
+CREATE TABLE `bx_albums_views_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `viewer_id` int(11) NOT NULL default '0',
+  `viewer_nip` int(11) unsigned NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `id` (`object_id`,`viewer_id`,`viewer_nip`)
+);
+
+CREATE TABLE `bx_albums_views_media_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `viewer_id` int(11) NOT NULL default '0',
+  `viewer_nip` int(11) unsigned NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `id` (`object_id`,`viewer_id`,`viewer_nip`)
+);
+
+-- TABLE: metas
+CREATE TABLE `bx_albums_meta_keywords` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+);
+
+CREATE TABLE `bx_albums_meta_keywords_media` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+);
+
+CREATE TABLE `bx_albums_meta_keywords_media_camera` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+);
+
+CREATE TABLE `bx_albums_meta_locations` (
+  `object_id` int(10) unsigned NOT NULL,
+  `lat` double NOT NULL,
+  `lng` double NOT NULL,
+  `country` varchar(2) NOT NULL,
+  `state` varchar(255) NOT NULL,
+  `city` varchar(255) NOT NULL,
+  `zip` varchar(255) NOT NULL,
+  `street` varchar(255) NOT NULL,
+  `street_number` varchar(255) NOT NULL,
+  PRIMARY KEY (`object_id`),
+  KEY `country_state_city` (`country`,`state`(8),`city`(8))
+);
+
+CREATE TABLE `bx_albums_meta_mentions` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(10) unsigned NOT NULL,
+  `profile_id` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `profile_id` (`profile_id`)
+);
+
+-- TABLE: reports
+CREATE TABLE IF NOT EXISTS `bx_albums_reports` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_reports_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(32) NOT NULL default '',
+  `text` text NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  `checked_by` int(11) NOT NULL default '0',
+  `status` tinyint(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `report` (`object_id`, `author_nip`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_reports_media` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_reports_media_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(32) NOT NULL default '',
+  `text` text NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  `checked_by` int(11) NOT NULL default '0',
+  `status` tinyint(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `report` (`object_id`, `author_nip`)
+);
+
+-- TABLE: favorites
+CREATE TABLE `bx_albums_favorites_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `list_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `id` (`object_id`,`author_id`)
+);
+
+CREATE TABLE `bx_albums_favorites_media_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `id` (`object_id`,`author_id`)
+);
+
+CREATE TABLE `bx_albums_favorites_lists` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) NOT NULL,
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  `allow_view_favorite_list_to` varchar(16) NOT NULL DEFAULT '3',
+   PRIMARY KEY (`id`)
+);
+
+-- TABLE: scores
+CREATE TABLE IF NOT EXISTS `bx_albums_scores` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count_up` int(11) NOT NULL default '0',
+  `count_down` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_scores_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(8) NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_scores_media` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count_up` int(11) NOT NULL default '0',
+  `count_down` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_albums_scores_media_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(8) NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+
+-- STORAGES & TRANSCODERS
+SET @sStorageEngine = (SELECT `value` FROM `sys_options` WHERE `name` = 'sys_storage_default');
+
+INSERT INTO `sys_objects_storage` (`object`, `engine`, `params`, `token_life`, `cache_control`, `levels`, `table_files`, `ext_mode`, `ext_allow`, `ext_deny`, `quota_size`, `current_size`, `quota_number`, `current_number`, `max_file_size`, `ts`) VALUES
+('bx_albums_files', @sStorageEngine, '', 360, 2592000, 3, 'bx_albums_files', 'allow-deny', '{imagevideo}', '', 0, 0, 0, 0, 0, 0),
+('bx_albums_photos_resized', @sStorageEngine, '', 360, 2592000, 3, 'bx_albums_photos_resized', 'allow-deny', '{imagevideo}', '', 0, 0, 0, 0, 0, 0);
+
+INSERT INTO `sys_objects_transcoder` (`object`, `storage_object`, `source_type`, `source_params`, `private`, `atime_tracking`, `atime_pruning`, `ts`, `override_class_name`, `override_class_file`) VALUES
+('bx_albums_preview', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', '1', '2592000', '0', '', ''),
+('bx_albums_browse', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', '1', '2592000', '0', '', ''),
+('bx_albums_big', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', '1', '2592000', '0', '', ''),
+('bx_albums_video_poster_browse', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', 0, 0, 0, 'BxDolTranscoderVideo', ''),
+('bx_albums_video_poster_preview', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', 0, 0, 0, 'BxDolTranscoderVideo', ''),
+('bx_albums_video_poster_big', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', 0, 0, 0, 'BxDolTranscoderVideo', ''),
+('bx_albums_video_mp4', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', 0, 0, 0, 'BxDolTranscoderVideo', ''),
+('bx_albums_video_mp4_hd', 'bx_albums_photos_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_albums_files";}', 'no', 0, 0, 0, 'BxDolTranscoderVideo', ''),
+('bx_albums_proxy_preview', 'bx_albums_photos_resized', 'Proxy', 'a:4:{s:6:"object";s:15:"bx_albums_files";s:5:"image";s:17:"bx_albums_preview";s:12:"video_poster";s:30:"bx_albums_video_poster_preview";s:5:"video";a:4:{i:0;s:19:"bx_albums_video_mp4";i:1;s:22:"bx_albums_video_mp4_hd";i:2;s:29:"bx_albums_video_poster_browse";i:3;s:26:"bx_albums_video_poster_big";}}', 'no', 0, 0, 0, 'BxDolTranscoderProxy', ''),
+('bx_albums_proxy_browse', 'bx_albums_photos_resized', 'Proxy', 'a:4:{s:6:"object";s:15:"bx_albums_files";s:5:"image";s:16:"bx_albums_browse";s:12:"video_poster";s:29:"bx_albums_video_poster_browse";s:5:"video";a:2:{i:0;s:19:"bx_albums_video_mp4";i:1;s:22:"bx_albums_video_mp4_hd";}}', 'no', 0, 0, 0, 'BxDolTranscoderProxy', ''),
+('bx_albums_proxy_cover', 'bx_albums_photos_resized', 'Proxy', 'a:4:{s:6:"object";s:15:"bx_albums_files";s:5:"image";s:13:"bx_albums_big";s:12:"video_poster";s:26:"bx_albums_video_poster_big";s:5:"video";a:2:{i:0;s:19:"bx_albums_video_mp4";i:1;s:22:"bx_albums_video_mp4_hd";}}', 'no', 0, 0, 0, 'BxDolTranscoderProxy', '');
+
+INSERT INTO `sys_transcoder_filters` (`transcoder_object`, `filter`, `filter_params`, `order`) VALUES 
+('bx_albums_preview', 'Resize', 'a:3:{s:1:"w";s:3:"300";s:1:"h";s:3:"300";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_albums_browse', 'Resize', 'a:1:{s:1:"h";s:3:"180";}', '0'),
+('bx_albums_big', 'Resize', 'a:2:{s:1:"w";s:4:"1280";s:1:"h";s:4:"1280";}', '0'),
+('bx_albums_video_poster_browse', 'Poster', 'a:2:{s:1:"h";s:3:"180";s:10:"force_type";s:3:"jpg";}', 0),
+('bx_albums_video_poster_preview', 'Poster', 'a:2:{s:1:"h";s:3:"480";s:10:"force_type";s:3:"jpg";}', 0),
+('bx_albums_video_poster_preview', 'Resize', 'a:3:{s:1:"w";s:3:"300";s:1:"h";s:3:"300";s:13:"square_resize";s:1:"1";}', 10),
+('bx_albums_video_poster_big', 'Poster', 'a:2:{s:1:"h";s:3:"480";s:10:"force_type";s:3:"jpg";}', 0),
+('bx_albums_video_mp4', 'Mp4', 'a:2:{s:1:"h";s:3:"480";s:10:"force_type";s:3:"mp4";}', 0),
+('bx_albums_video_mp4_hd', 'Mp4', 'a:3:{s:1:"h";s:3:"720";s:13:"video_bitrate";s:4:"1536";s:10:"force_type";s:3:"mp4";}', 0);
+
+
+-- FORMS
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_albums', 'bx_albums', '_bx_albums_form_entry', '', 'a:1:{s:7:\"enctype\";s:19:\"multipart/form-data\";}', 'bx_albums_albums', 'id', '', '', 'do_submit', '', 0, 1, 'BxAlbumsFormEntry', 'modules/boonex/albums/classes/BxAlbumsFormEntry.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_albums', 'bx_albums_entry_add', 'bx_albums', 0, '_bx_albums_form_entry_display_add'),
+('bx_albums', 'bx_albums_entry_edit', 'bx_albums', 0, '_bx_albums_form_entry_display_edit'),
+('bx_albums', 'bx_albums_entry_add_images', 'bx_albums', 0, '_bx_albums_form_entry_display_add_images'),
+('bx_albums', 'bx_albums_entry_delete', 'bx_albums', 0, '_bx_albums_form_entry_display_delete'),
+('bx_albums', 'bx_albums_entry_view', 'bx_albums', 1, '_bx_albums_form_entry_display_view');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_albums', 'bx_albums', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_albums_form_entry_input_sys_delete_confirm', '_bx_albums_form_entry_input_delete_confirm', '_bx_albums_form_entry_input_delete_confirm_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_albums_form_entry_input_delete_confirm_error', '', '', 1, 0),
+('bx_albums', 'bx_albums', 'do_submit', '_bx_albums_form_entry_input_do_submit', '', 0, 'submit', '_bx_albums_form_entry_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_albums', 'bx_albums', 'do_cancel', '_sys_form_input_cancel', '', 0, 'button', '_sys_form_input_sys_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:41:"window.open(''{edit_cancel_url}'', ''_self'')";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0),
+('bx_albums', 'bx_albums', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '_sys_form_input_sys_controls', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_albums', 'bx_albums', 'title', '', '', 0, 'text', '_bx_albums_form_entry_input_sys_title', '_bx_albums_form_entry_input_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:3;s:3:"max";i:160;}', '_bx_albums_form_entry_input_title_err', 'Xss', '', 1, 0),
+('bx_albums', 'bx_albums', 'text', '', '', 0, 'textarea', '_bx_albums_form_entry_input_sys_text', '_bx_albums_form_entry_input_text', '', 0, 0, 2, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_albums', 'bx_albums', 'pictures', 'a:2:{i:0;s:15:"bx_albums_html5";i:1;s:22:"bx_albums_record_video";}', 'a:3:{s:15:"bx_albums_html5";s:25:"_sys_uploader_html5_title";s:22:"bx_albums_record_video";s:32:"_sys_uploader_record_video_title";s:14:"bx_albums_crop";s:24:"_sys_uploader_crop_title";}', 0, 'files', '_bx_albums_form_entry_input_sys_pictures', '_bx_albums_form_entry_input_pictures', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_albums', 'bx_albums', 'cf', '1', '#!sys_content_filter', 0, 'select', '_sys_form_entry_input_sys_cf', '_sys_form_entry_input_cf', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_albums', 'bx_albums', 'allow_view_to', '', '', 0, 'custom', '_bx_albums_form_entry_input_sys_allow_view_to', '_bx_albums_form_entry_input_allow_view_to', '', 1, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_albums', 'bx_albums', 'location', '', '', 0, 'location', '_sys_form_input_sys_location', '_sys_form_input_location', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_albums', 'bx_albums', 'added', '', '', 0, 'datetime', '_bx_albums_form_entry_input_sys_date_added', '_bx_albums_form_entry_input_date_added', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_albums', 'bx_albums', 'changed', '', '', 0, 'datetime', '_bx_albums_form_entry_input_sys_date_changed', '_bx_albums_form_entry_input_date_changed', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_albums', 'bx_albums', 'labels', '', '', 0, 'custom', '_sys_form_input_sys_labels', '_sys_form_input_labels', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_albums_entry_add', 'delete_confirm', 2147483647, 0, 1),
+('bx_albums_entry_add', 'title', 2147483647, 1, 2),
+('bx_albums_entry_add', 'text', 2147483647, 1, 3),
+('bx_albums_entry_add', 'pictures', 2147483647, 1, 4),
+('bx_albums_entry_add', 'allow_view_to', 2147483647, 1, 5),
+('bx_albums_entry_add', 'cf', 2147483647, 1, 6),
+('bx_albums_entry_add', 'location', 2147483647, 1, 7),
+('bx_albums_entry_add', 'do_submit', 2147483647, 1, 8),
+
+('bx_albums_entry_edit', 'delete_confirm', 2147483647, 0, 1),
+('bx_albums_entry_edit', 'title', 2147483647, 1, 2),
+('bx_albums_entry_edit', 'text', 2147483647, 1, 3),
+('bx_albums_entry_edit', 'pictures', 2147483647, 1, 4),
+('bx_albums_entry_edit', 'allow_view_to', 2147483647, 1, 5),
+('bx_albums_entry_edit', 'cf', 2147483647, 1, 6),
+('bx_albums_entry_edit', 'location', 2147483647, 1, 7),
+('bx_albums_entry_edit', 'controls', 2147483647, 1, 8),
+('bx_albums_entry_edit', 'do_submit', 2147483647, 1, 9),
+('bx_albums_entry_edit', 'do_cancel', 2147483647, 1, 10),
+
+('bx_albums_entry_add_images', 'delete_confirm', 2147483647, 0, 1),
+('bx_albums_entry_add_images', 'title', 2147483647, 0, 2),
+('bx_albums_entry_add_images', 'text', 2147483647, 0, 3),
+('bx_albums_entry_add_images', 'allow_view_to', 2147483647, 0, 4),
+('bx_albums_entry_add_images', 'location', 2147483647, 0, 5),
+('bx_albums_entry_add_images', 'pictures', 2147483647, 1, 6),
+('bx_albums_entry_add_images', 'do_submit', 2147483647, 1, 7),
+
+('bx_albums_entry_view', 'delete_confirm', 2147483647, 0, 0),
+('bx_albums_entry_view', 'allow_view_to', 2147483647, 0, 0),
+('bx_albums_entry_view', 'do_submit', 2147483647, 0, 0),
+('bx_albums_entry_view', 'text', 2147483647, 0, 0),
+('bx_albums_entry_view', 'title', 2147483647, 0, 0),
+('bx_albums_entry_view', 'pictures', 2147483647, 0, 0),
+('bx_albums_entry_view', 'added', 2147483647, 1, 1),
+('bx_albums_entry_view', 'changed', 2147483647, 1, 2),
+
+('bx_albums_entry_delete', 'delete_confirm', 2147483647, 1, 1),
+('bx_albums_entry_delete', 'do_submit', 2147483647, 1, 2),
+('bx_albums_entry_delete', 'allow_view_to', 2147483647, 0, 0),
+('bx_albums_entry_delete', 'pictures', 2147483647, 0, 0),
+('bx_albums_entry_delete', 'text', 2147483647, 0, 0),
+('bx_albums_entry_delete', 'title', 2147483647, 0, 0);
+
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_albums_media', 'bx_albums', '_bx_albums_form_media', '', 'a:1:{s:7:\"enctype\";s:19:\"multipart/form-data\";}', 'bx_albums_files2albums', 'id', '', '', 'do_submit', '', 0, 1, 'BxAlbumsFormMedia', 'modules/boonex/albums/classes/BxAlbumsFormMedia.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_albums_media', 'bx_albums_media_edit', 'bx_albums', 0, '_bx_albums_form_media_display_edit'),
+('bx_albums_media', 'bx_albums_media_move', 'bx_albums', 0, '_bx_albums_form_media_display_move');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_albums_media', 'bx_albums', 'title', '', '', 0, 'text', '_bx_albums_form_media_input_sys_title', '_bx_albums_form_media_input_title', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_albums_media', 'bx_albums', 'content_id', '', '', 0, 'select', '_bx_albums_form_media_input_sys_content_id', '_bx_albums_form_media_input_content_id', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_albums_media', 'bx_albums', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_albums_media', 'bx_albums', 'do_submit', '_bx_albums_form_media_input_do_submit', '', 0, 'submit', '_bx_albums_form_media_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_albums_media', 'bx_albums', 'do_cancel', '_bx_albums_form_media_input_do_cancel', '', 0, 'button', '_bx_albums_form_media_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_albums_media_edit', 'title', 2147483647, 1, 1),
+('bx_albums_media_edit', 'controls', 2147483647, 1, 2),
+('bx_albums_media_edit', 'do_submit', 2147483647, 1, 3),
+('bx_albums_media_edit', 'do_cancel', 2147483647, 1, 4),
+
+('bx_albums_media_move', 'content_id', 2147483647, 1, 1),
+('bx_albums_media_move', 'controls', 2147483647, 1, 2),
+('bx_albums_media_move', 'do_submit', 2147483647, 1, 3),
+('bx_albums_media_move', 'do_cancel', 2147483647, 1, 4);
+
+
+-- COMMENTS
+INSERT INTO `sys_objects_cmts` (`Name`, `Module`, `Table`, `CharsPostMin`, `CharsPostMax`, `CharsDisplayMax`, `Html`, `PerView`, `PerViewReplies`, `BrowseType`, `IsBrowseSwitch`, `PostFormPosition`, `NumberOfLevels`, `IsDisplaySwitch`, `IsRatable`, `ViewingThreshold`, `IsOn`, `RootStylePrefix`, `BaseUrl`, `ObjectVote`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldTitle`, `TriggerFieldComments`, `ClassName`, `ClassFile`) VALUES
+('bx_albums', 'bx_albums', 'bx_albums_cmts', 1, 5000, 1000, 3, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-album&id={object_id}', '', 'bx_albums_albums', 'id', 'author', 'title', 'comments', '', ''),
+('bx_albums_notes', 'bx_albums', 'bx_albums_cmts_notes', 1, 5000, 1000, 0, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-post&id={object_id}', '', 'bx_albums_albums', 'id', 'author', 'title', '', 'BxTemplCmtsNotes', ''),
+('bx_albums_media', 'bx_albums', 'bx_albums_cmts_media', 1, 5000, 1000, 3, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-album-media&id={object_id}', '', 'bx_albums_files2albums', 'id', 'author', 'title', 'comments', 'BxAlbumsCmtsMedia', 'modules/boonex/albums/classes/BxAlbumsCmtsMedia.php');
+
+
+-- VOTES
+INSERT INTO `sys_objects_vote` (`Name`, `Module`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
+('bx_albums', 'bx_albums', 'bx_albums_votes', 'bx_albums_votes_track', '604800', '1', '1', '0', '1', 'bx_albums_albums', 'id', 'author', 'rate', 'votes', '', ''),
+('bx_albums_media', 'bx_albums', 'bx_albums_votes_media', 'bx_albums_votes_media_track', '604800', '1', '1', '0', '1', 'bx_albums_files2albums', 'id', 'author', 'rate', 'votes', 'BxAlbumsVoteLikesMedia', 'modules/boonex/albums/classes/BxAlbumsVoteLikesMedia.php'),
+
+('bx_albums_reactions', 'bx_albums', 'bx_albums_reactions', 'bx_albums_reactions_track', '604800', '1', '1', '1', '1', 'bx_albums_albums', 'id', 'author', 'rrate', 'rvotes', 'BxTemplVoteReactions', '');
+
+
+-- SCORES
+INSERT INTO `sys_objects_score` (`name`, `module`, `table_main`, `table_track`, `post_timeout`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_score`, `trigger_field_cup`, `trigger_field_cdown`, `class_name`, `class_file`) VALUES 
+('bx_albums', 'bx_albums', 'bx_albums_scores', 'bx_albums_scores_track', '604800', '0', 'bx_albums_albums', 'id', 'author', 'score', 'sc_up', 'sc_down', '', ''),
+('bx_albums_media', 'bx_albums', 'bx_albums_scores_media', 'bx_albums_scores_media_track', '604800', '0', 'bx_albums_files2albums', 'id', 'author', 'score', 'sc_up', 'sc_down', '', '');
+
+
+-- REPORTS
+INSERT INTO `sys_objects_report` (`name`, `module`, `table_main`, `table_track`, `is_on`, `base_url`, `object_comment`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_albums', 'bx_albums', 'bx_albums_reports', 'bx_albums_reports_track', '1', 'page.php?i=view-album&id={object_id}', 'bx_albums_notes', 'bx_albums_albums', 'id', 'author', 'reports', '', ''),
+('bx_albums_media', 'bx_albums', 'bx_albums_reports_media', 'bx_albums_reports_media_track', '1', 'page.php?i=view-album-media&id={object_id}', '', 'bx_albums_files2albums', 'id', 'author', 'reports', '', '');
+
+
+-- VIEWS
+INSERT INTO `sys_objects_view` (`name`, `module`, `table_track`, `period`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_albums', 'bx_albums', 'bx_albums_views_track', '86400', '1', 'bx_albums_albums', 'id', '', 'views', '', ''),
+('bx_albums_media', 'bx_albums', 'bx_albums_views_media_track', '86400', '1', 'bx_albums_files2albums', 'id', 'author', 'views', '', '');
+
+
+-- FAFORITES
+INSERT INTO `sys_objects_favorite` (`name`, `table_track`, `table_lists`, `is_on`, `is_undo`, `is_public`, `base_url`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_albums', 'bx_albums_favorites_track', 'bx_albums_favorites_lists', '1', '1', '1', 'page.php?i=view-album&id={object_id}', 'bx_albums_albums', 'id', 'author', 'favorites', '', ''),
+('bx_albums_media', 'bx_albums_favorites_media_track', '', '1', '1', '1', 'page.php?i=view-album-media&id={object_id}', 'bx_albums_files2albums', 'id', 'author', 'favorites', '', '');
+
+
+-- FEATURED
+INSERT INTO `sys_objects_feature` (`name`, `module`, `is_on`, `is_undo`, `base_url`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_flag`, `class_name`, `class_file`) VALUES 
+('bx_albums', 'bx_albums', '1', '1', 'page.php?i=view-album&id={object_id}', 'bx_albums_albums', 'id', 'author', 'featured', '', ''),
+('bx_albums_media', 'bx_albums', '1', '1', 'page.php?i=view-album-media&id={object_id}', 'bx_albums_files2albums', 'id', '', 'featured', '', '');
+
+
+-- CONTENT INFO
+INSERT INTO `sys_objects_content_info` (`name`, `title`, `alert_unit`, `alert_action_add`, `alert_action_update`, `alert_action_delete`, `class_name`, `class_file`) VALUES
+('bx_albums', '_bx_albums', 'bx_albums', 'added', 'edited', 'deleted', '', ''),
+('bx_albums_media', '_bx_albums_media', 'bx_albums', 'media_added', '', 'media_deleted', 'BxAlbumsContentInfoMedia', 'modules/boonex/albums/classes/BxAlbumsContentInfoMedia.php'),
+('bx_albums_cmts', '_bx_albums_cmts', 'bx_albums', 'commentPost', 'commentUpdated', 'commentRemoved', 'BxDolContentInfoCmts', ''),
+('bx_albums_media_cmts', '_bx_albums_media_cmts', 'bx_albums_media', 'commentPost', 'commentUpdated', 'commentRemoved', 'BxDolContentInfoCmts', '');
+
+INSERT INTO `sys_content_info_grids` (`object`, `grid_object`, `grid_field_id`, `condition`, `selection`) VALUES
+('bx_albums', 'bx_albums_administration', 'id', '', ''),
+('bx_albums', 'bx_albums_common', 'id', '', '');
+
+
+-- SEARCH EXTENDED
+INSERT INTO `sys_objects_search_extended` (`object`, `object_content_info`, `module`, `title`, `active`, `class_name`, `class_file`) VALUES
+('bx_albums', 'bx_albums', 'bx_albums', '_bx_albums_search_extended', 1, '', ''),
+('bx_albums_media', 'bx_albums_media', 'bx_albums', '_bx_albums_search_extended_media', 1, '', ''),
+('bx_albums_cmts', 'bx_albums_cmts', 'bx_albums', '_bx_albums_search_extended_cmts', 1, 'BxTemplSearchExtendedCmts', ''),
+('bx_albums_media_cmts', 'bx_albums_media_cmts', 'bx_albums', '_bx_albums_search_extended_media_cmts', 1, 'BxTemplSearchExtendedCmts', '');
+
+
+-- STUDIO: page & widget
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_albums', '_bx_albums', '_bx_albums', 'bx_albums@modules/boonex/albums/|std-icon.svg');
+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`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_albums', 'content', '{url_studio}module.php?name=bx_albums', '', 'bx_albums@modules/boonex/albums/|std-icon.svg', '_bx_albums', '', '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));
+

+ 51 - 0
modules/boonex/analytics/updates/15.0.0_15.0.1/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Analytics',
+    'version_from' => '15.0.0',
+    'version_to' => '15.0.1',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/analytics/updates/update_15.0.0_15.0.1/',
+    'home_uri' => 'analytics_update_1500_1501',
+
+    'module_dir' => 'boonex/analytics/',
+    'module_uri' => 'analytics',
+
+    'db_prefix' => 'bx_analytics_',
+    'class_prefix' => 'BxAnalytics',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Analytics',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/analytics/updates/15.0.0_15.0.1/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxAnalyticsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 576 - 0
modules/boonex/analytics/updates/15.0.0_15.0.1/source/classes/BxAnalyticsModule.php

@@ -0,0 +1,576 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+* Copyright (c) UNA, Inc - https://una.io
+* MIT License - https://opensource.org/licenses/MIT
+*
+* @defgroup    Analytics Analytics
+* @ingroup     UnaModules
+*
+* @{
+*/
+
+/**
+ * Analytics module
+ */     
+define('BX_ANALYTICS_SYSTEM', 'system');
+define('BX_ANALYTICS_TOP_BY_LIKES', 'top_by_likes');
+define('BX_ANALYTICS_TOP_BY_VIEWS', 'top_by_views');
+define('BX_ANALYTICS_CONTENT_TOTAL', 'content_total');
+define('BX_ANALYTICS_CONTENT_SPEED', 'content_speed');
+define('BX_ANALYTICS_MOST_FOLLOWED_PROFILES', 'top_by_followers');
+define('BX_ANALYTICS_MOST_ACTIVE_PROFILES', 'top_by_activity');
+class BxAnalyticsModule extends BxDolModule
+{
+    protected $aColors = array('#3366CC', '#DC3912', '#FF9900', '#109618', '#990099');
+          
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+          
+    /**
+    * Service methods
+    */
+          
+    /**
+    * @page service Service Calls
+    * @section bx_analytics Analytics
+    * @subsection bx_analytics-other Other
+    * @subsubsection bx_analytics get_canvas
+    * 
+    * @code bx_srv('bx_analytics', 'get_canvas', [...]); @endcode
+    * 
+    * Get canvas  
+    * 
+    * @return an html. 
+    * 
+    * @see BxAnalyticsModule::serviceGetCanvas
+    */
+    /** 
+    * @ref bx_analytics-get_canvas "get_canvas"
+    */
+    public function serviceGetCanvas()
+    {
+		$sMsg = $this->checkAllowed();
+		if($sMsg !== CHECK_ACTION_RESULT_ALLOWED){
+			$this->_oTemplate->displayAccessDenied($sMsg, BX_PAGE_EMBED);
+			exit;
+		}
+		
+        return $this->_oTemplate->getCanvas();
+    }
+	
+	/**
+    * @page service Service Calls
+    * @section bx_analytics Analytics
+    * @subsection bx_analytics-other Other
+    * @subsubsection bx_analytics is_avaliable
+    * 
+    * @code bx_srv('bx_analytics', 'is_avaliable', [...]); @endcode
+    * 
+    * Check avalability for current profile  
+    * 
+    * @return boolean. 
+    * 
+    * @see BxAnalyticsModule::serviceIsAvaliable
+    */
+    /** 
+    * @ref bx_analytics-is_avaliable "is_avaliable"
+    */
+	public function serviceIsAvaliable()
+    {
+		return $this->checkAllowed() === CHECK_ACTION_RESULT_ALLOWED ? true : false;
+    }
+	
+          
+    /**
+    * @page service Service Calls
+    * @section bx_analytics Analytics
+    * @subsection bx_analytics-other Other
+    * @subsubsection bx_analytics get_modules
+    * 
+    * @code bx_srv('bx_analytics', 'get_modules', [...]); @endcode
+    * 
+    * Get modules list  
+    * 
+    * @return an html. 
+    * 
+    * @see BxAnalyticsModule::serviceGetModules
+    */
+    /** 
+    * @ref bx_analytics-get_modules "get_modules"
+    */
+    public function serviceGetModules()
+    {
+        $aResult = array();
+        $aResult[BX_ANALYTICS_SYSTEM] = _t('_bx_analytics_system_text');
+        $BxDolModuleQuery = BxDolModuleQuery::getInstance();
+        $aModules = $BxDolModuleQuery->getModulesBy(array('type' => 'modules', 'active' => 1));
+        foreach($aModules as $aModule){
+            $oModule = BxDolModule::getInstance($aModule['name']);
+            if($oModule instanceof iBxDolContentInfoService){
+                $aResult[$aModule['name']] = $aModule['title'];
+            }
+        }
+        
+        /**
+         * @hooks
+         * @hookdef hook-bx_analytics-get_modules 'bx_analytics', 'get_modules' - hook on get modules list with avalaible analytics
+         * - $unit_name - equals `get_modules`
+         * - $action - equals `get_modules` 
+         * - $object_id - not used
+         * - $sender_id -not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `list` - [array] by ref, array of modules for analytics as [module_name] = module_title, can be overridden in hook processing
+         * @hook @ref hook-bx_analytics-get_modules
+         */
+        bx_alert($this->_aModule['name'], 'get_modules', 0, 0, array(
+            'list' => &$aResult,
+            'list_ref' => &$aResult,
+        ));
+        
+        return $aResult;
+    }
+          
+    /**
+    * Action methods
+    */
+          
+    public function actionGetReports($sModuleName)
+    {
+		if(!$this->serviceIsAvaliable())
+            return '';
+            
+        header('Content-Type: application/json');
+             
+        $aSystemsVote = BxDolVote::getSystems();
+        $aRv = array();
+        if ($sModuleName == BX_ANALYTICS_SYSTEM){
+            $aRv[BX_ANALYTICS_TOP_BY_LIKES] = _t('_bx_analytics_type_top_contents_by_likes');
+            $aRv[BX_ANALYTICS_TOP_BY_VIEWS] = _t('_bx_analytics_type_top_by_views');
+            $aRv[BX_ANALYTICS_CONTENT_TOTAL] = _t('_bx_analytics_type_accounts_growth');
+            $aRv[BX_ANALYTICS_CONTENT_SPEED] = _t('_bx_analytics_type_accounts_speed');
+        }
+        else{
+            $oModule = BxDolModule::getInstance($sModuleName);
+                  
+            if(BxDolRequest::serviceExists($sModuleName, 'act_as_profile') && BxDolService::call($sModuleName, 'act_as_profile') == true){
+                $aRv[BX_ANALYTICS_MOST_FOLLOWED_PROFILES] = _t('_bx_analytics_type_most_followed_profiles');
+                $aRv[BX_ANALYTICS_MOST_ACTIVE_PROFILES] = _t('_bx_analytics_type_most_active_profiles');
+            }
+                  
+            if(isset($oModule->_oConfig->CNF['OBJECT_VOTES']) && isset($aSystemsVote[$oModule->_oConfig->CNF['OBJECT_VOTES']])){
+                $aRv[BX_ANALYTICS_TOP_BY_LIKES] = _t('_bx_analytics_type_top_contents_by_likes');
+            }
+                  
+            if(isset($oModule->_oConfig->CNF['OBJECT_VIEWS'])){
+                $aRv[BX_ANALYTICS_TOP_BY_VIEWS] = _t('_bx_analytics_type_top_by_views');
+            }
+                  
+            $aRv[BX_ANALYTICS_CONTENT_TOTAL] = _t('_bx_analytics_type_content_growth');
+            $aRv[BX_ANALYTICS_CONTENT_SPEED] = _t('_bx_analytics_type_content_speed');
+        }
+        /**
+         * @hooks
+         * @hookdef hook-bx_analytics-get_reports 'bx_analytics', 'get_reports' - hook on get avalaible reports for specified module
+         * - $unit_name - equals `bx_analytics`
+         * - $action - equals `get_reports` 
+         * - $object_id - not used
+         * - $sender_id -not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `module` - [string] module name
+         *      - `list` - [array] by ref, array of avalaible reports for analytics as [report_name] = report_title, can be overridden in hook processing
+         * @hook @ref hook-bx_analytics-get_reports
+         */
+        bx_alert($this->_aModule['name'], 'get_reports', 0, 0, array(
+            'module' => $sModuleName,
+            'list' => &$aRv,
+            'list_ref' => &$aRv,
+        ));
+        
+        echo json_encode($aRv);
+    }
+          
+    public function actionGetReportsData($sModuleName, $sReportName, $sDateFrom, $sDateTo, $sType = '')
+    {
+        if(!$this->serviceIsAvaliable())
+            return '';
+        
+        if ($sType == 'csv'){
+            header('Content-type: text/csv');
+            header('Content-disposition: attachment;filename=' . $sModuleName . '_' . $sReportName . '_' . $sDateFrom . '_' . $sDateTo . '.csv');
+            echo $this->actionGetReportsDataCsv($sModuleName, $sReportName, $sDateFrom, $sDateTo);
+            
+        }
+        
+        if ($sType == ''){
+            header('Content-Type: application/json');
+            echo $this->actionGetReportsDataJson($sModuleName, $sReportName, $sDateFrom, $sDateTo);
+        } 
+    }
+    
+    private function actionGetReportsDataCsv($sModuleName, $sReportName, $sDateFrom, $sDateTo)
+    {
+        $sRv = '';
+        $sColumn = ";";
+        $sRow = "\n";
+        $sData = $this->actionGetReportsDataJson($sModuleName, $sReportName, $sDateFrom, $sDateTo);
+        $oRv = json_decode($sData, true); 
+        $oData = $oRv['data'];
+        $sCol1 = $oRv['strings'][0];
+        $iColumnCount = count($oData['datasets']);
+        $sRv .= $sCol1 . $sColumn;
+        foreach($oData['datasets'] as $aItem){
+            $sRv .= ($aItem['label'] != '' ? $aItem['label'] : $oRv['strings'][1]) . $sColumn;
+        }
+   
+        $sRv .= $sRow;
+        $iK = 0;
+        foreach($oData['datasets'][0]['data'] as $sDx => $aItemData){
+            $sTxt = "";
+            if (is_array($oData['datasets'][0]['data'][$sDx]))
+                $sTxt = $oData['datasets'][0]['data'][$sDx]['x'];
+            else
+                $sTxt = $oData['labels'][$sDx];
+            if (isset($oRv['links']) && count($oRv['links'])>0) {
+                $sTxt .= ' (' . $oRv['links'][$sDx] . ')';
+            }
+            
+            $sRv .= $sTxt . $sColumn;
+            for ($i = 0; $i < $iColumnCount; $i++){
+                $sText = '';
+                if (is_array($aItemData)) {
+                    $sText = $aItemData['y'];
+                }
+                else {
+                    $sText = $aItemData;
+                }
+                $sRv .= $sText . $sColumn;
+            }
+            $sRv .= $sRow;
+            $iK++;
+        }
+        echo $sRv;
+    }
+    
+    private function actionGetReportsDataJson($sModuleName, $sReportName, $sDateFrom, $sDateTo)
+    { 
+        $iDateFrom = strtotime($sDateFrom);
+        $iDateTo = strtotime($sDateTo) + 86400;
+        $sType = "bar";
+        $bIsTimeX = false;
+        $iMinValueY = 0;
+        $iMaxValueY = 0;
+        $bDefaultFillData = true;
+        $sReportType = $sReportName;
+        
+        $aValues = array('labels' => array(), 'values' => array(array('legend' => '', 'data' => array())), 'links' => array(), 'strings' => array(0 => _t('_bx_analytics_txt_item'), 1 => _t('_bx_analytics_txt_value')));
+   
+        if ($sModuleName != BX_ANALYTICS_SYSTEM){
+            $oModule = BxDolModule::getInstance($sModuleName);
+            $aValues['strings'][0] = $oModule->_aModule['title'];
+            $aValues['strings'][1] = _t('_bx_analytics_type_' . $sReportType . '_label');
+        }
+        else{
+            $aValues['strings'][0] = _t('_bx_analytics_txt_total_content');
+            $aValues['strings'][1] = _t('_bx_analytics_type_' . $sReportType . '_label');
+        }
+              
+        switch ($sReportType) {
+            case BX_ANALYTICS_TOP_BY_LIKES:
+                $bDefaultFillData = false;
+                $this->getViewsOrVotes(BxDolVote::getSystems(), 'OBJECT_VOTES', $sModuleName, $iDateFrom, $iDateTo, $aValues, $iMaxValueY, $iMinValueY);
+                break;
+                  
+            case BX_ANALYTICS_TOP_BY_VIEWS:
+                $bDefaultFillData = false;
+                $this->getViewsOrVotes(BxDolView::getSystems(), 'OBJECT_VIEWS', $sModuleName, $iDateFrom, $iDateTo, $aValues, $iMaxValueY, $iMinValueY);
+                break;
+                  
+            case BX_ANALYTICS_MOST_FOLLOWED_PROFILES:
+                $oModule = BxDolModule::getInstance($sModuleName);
+                $aData = $this->_oDb->getMostFollowedProfiles($sModuleName, $iDateFrom, $iDateTo);
+                break;
+                  
+            case BX_ANALYTICS_MOST_ACTIVE_PROFILES:
+                $aData = array();
+                $oModule = BxDolModule::getInstance($sModuleName);
+                $aTmp = $this->getSelectedModules();
+                foreach($aTmp[0] as $sContentModule){
+                    $oContentModule = BxDolModule::getInstance($sContentModule);
+                    if ($oContentModule && isset($oContentModule->_oConfig->CNF['TABLE_ENTRIES']) && isset($oContentModule->_oConfig->CNF['FIELD_AUTHOR'])){
+                        $sContentTable = $oContentModule->_oConfig->CNF['TABLE_ENTRIES'];
+                        $sColumnAuthor = $oContentModule->_oConfig->CNF['FIELD_AUTHOR'];
+                        $sColumnAdded = $oContentModule->_oConfig->CNF['FIELD_ADDED'];
+                        if (!empty($sContentTable) && !empty($sColumnAuthor) && !empty($sColumnAdded) && (!BxDolRequest::serviceExists($sContentModule, 'act_as_profile') || BxDolService::call($sContentModule, 'act_as_profile') == false)){
+                            $aData1 = $this->_oDb->getMostActiveProfiles($sModuleName, $sContentModule, $sContentTable, $sColumnAuthor, $sColumnAuthor, $sColumnAdded, $iDateFrom, $iDateTo);
+                            foreach($aData1 as $aTmp3){
+                                if (array_key_exists($aTmp3['object_id'], $aData))
+                                    $aData[$aTmp3['object_id']]['value'] += $aTmp3['value'];
+                                else
+                                    $aData[$aTmp3['object_id']] = $aTmp3;
+                            }
+                        }
+                    }
+                }
+                $aData = $this->sortArrayOfArraysAndSlice($aData, 'value', intval(getParam('bx_analytics_items_count')));
+                break;
+                
+            default:
+                $sType = "line";
+                $sTableName = '';
+                $aValues['strings'][0] = _t('_bx_analytics_txt_date');
+                $bDefaultFillData = false;
+                if ($sModuleName != BX_ANALYTICS_SYSTEM){
+                    $oModule = BxDolModule::getInstance($sModuleName);
+                    if (isset($oModule->_oConfig->CNF['TABLE_ENTRIES']))
+                        $sTableName = $oModule->_oConfig->CNF['TABLE_ENTRIES'];
+                    $aValues['strings'][1] = ($sReportType == BX_ANALYTICS_CONTENT_TOTAL ? _t('_bx_analytics_txt_total_count', $oModule->_aModule['title']) : _t('_bx_analytics_txt_count', $oModule->_aModule['title']));
+                }
+                else{
+                    $sTableName = 'sys_accounts';
+                    $aValues['strings'][1] = ($sReportType == BX_ANALYTICS_CONTENT_TOTAL ? _t('_bx_analytics_txt_total_count', _t('_bx_analytics_txt_accounts')) : _t('_bx_analytics_txt_count', _t('_bx_analytics_txt_accounts')));
+                }
+                if (isset($oModule) && isset($oModule->_oConfig->CNF['FIELD_ADDED']))
+                    $sColumnAdded = $oModule->_oConfig->CNF['FIELD_ADDED'];
+                else
+                    $sColumnAdded = 'added';
+                
+                $aData = array();
+                $iValuePrev = 0;
+                
+                /**
+                 * @hooks
+                 * @hookdef hook-bx_analytics-get_chart_data_line 'bx_analytics', 'get_chart_data_line' - hook on get avalaible reports for specified module
+                 * - $unit_name - equals `bx_analytics`
+                 * - $action - equals `get_chart_data_line` 
+                 * - $object_id - not used
+                 * - $sender_id -not used
+                 * - $extra_params - array of additional params with the following array keys:
+                 *      - `module` - [string] module name
+                 *      - `report_name` - [string] report name
+                 *      - `report_type` - [string]  by ref, report name, can be overridden in hook processing
+                 *      - `date_from` - [int] report's start of interval in php time format
+                 *      - `date_to` - [int] report's end of interval in php time format
+                 *      - `data` - [array] by ref, array of data for chart, can be overridden in hook processing
+                 *      - `prev_value` - [int] by ref, previous value for chart (started value), can be overridden in hook processing
+                 * @hook @ref hook-bx_analytics-get_chart_data_line
+                 */
+                bx_alert($this->_aModule['name'], 'get_chart_data_line', 0, 0, array(
+                    'module' => $sModuleName,
+                    'report_name' => $sReportName,
+                    'report_type' => &$sReportType,
+                    'report_type_ref' => &$sReportType,
+                    'date_from' => $iDateFrom,
+                    'date_to' => $iDateTo,
+                    'data' => &$aData,
+                    'data_ref' => &$aData,
+                    'prev_value' => &$iValuePrev,
+                    'prev_value_ref' => &$iValuePrev,
+                ));
+                
+                
+                
+                if ($sColumnAdded != '' && count($aData) == 0){
+                    $aData = $this->_oDb->getGrowth($sTableName, $sColumnAdded, $iDateFrom, $iDateTo);
+                    $iValuePrev = $this->_oDb->getGrowthInitValue($sTableName, $sColumnAdded, $iDateFrom);
+                }
+                
+                if (count($aData) > 0){
+                    $aTmpDates = array();
+                    foreach ($aData as $aValue) {
+                        $sX = $aValue['period'];
+                        $aTmpDates[$sX] = $aValue['count'];
+                    }
+                    for ($i = $iDateFrom; $i < $iDateTo ; $i = $i + 86400 ){
+                        $sX = date('Y-m-d', $i);
+                        if (!array_key_exists($sX, $aTmpDates)){
+                            array_push($aValues['values'][0]['data'], array('x' => $sX, 'y' => $sReportType == BX_ANALYTICS_CONTENT_TOTAL ? $iValuePrev : 0));
+                        }
+                        else{
+                            array_push($aValues['values'][0]['data'], array('x' => $sX, 'y' => $sReportType == BX_ANALYTICS_CONTENT_TOTAL ? ($iValuePrev + $aTmpDates[$sX]) : $aTmpDates[$sX]));
+                            $iValuePrev += $aTmpDates[$sX];
+                        }
+                        //array_push($aValues['labels'], $sX);
+                        if ($sReportType == BX_ANALYTICS_CONTENT_TOTAL){
+                            if ($iValuePrev > $iMaxValueY)
+                                $iMaxValueY = $iValuePrev;
+                        }
+                        else{
+                            if (array_key_exists($sX, $aTmpDates) && $aTmpDates[$sX] > $iMaxValueY)
+                                $iMaxValueY = $aTmpDates[$sX];
+                        } 
+                    }
+                }
+                break;
+        }
+          
+        if ($bDefaultFillData){
+            foreach ($aData as $aValue) {
+                array_push($aValues['labels'], $this->getItemName($oModule->serviceGetTitle($aValue['object_id'])));
+                array_push($aValues['values'][0]['data'], $aValue['value']);
+                array_push($aValues['links'], $oModule->serviceGetLink($aValue['object_id']));
+                if ($aValue['value'] > $iMaxValueY)
+                    $iMaxValueY = $aValue['value'];
+            }
+        }
+        $iMaxValueY = ceil($iMaxValueY * 1.1);
+        
+        $aDataForChartXAxes = array();
+        if ($bIsTimeX){
+            $sUnit = 'day';
+            $iInterval = ($iDateTo - $iDateFrom) / 86400;
+            if ($iInterval > 50)
+                $sUnit = 'week';
+            if ($iInterval > 100)
+                $sUnit = 'month';
+            
+            $aDataForChartXAxes = array(
+                'type' => 'time',
+                'time' => array(
+                    'tooltipFormat' => 'DD.MM.YYYY',
+                    'unit' => $sUnit,
+                 ),
+                'display' => true,
+                'distribution' => 'linear',
+                'ticks' => array(
+                    'display' => true, 
+                    'autoSkip' => true, 
+                )
+            );
+        }
+        else{
+            $aDataForChartXAxes = array(
+                'ticks' => array(
+                    'autoSkip' => true
+                ),
+                'display' => true
+            );
+        }
+       
+        $aDataForChartDatasets = array();
+        for($i = 0; $i < count($aValues['values']); $i++){
+            $aDataForChartDatasets[] = array(
+                'label' => $aValues['values'][$i]['legend'],
+                'fill' => false,
+                'backgroundColor' => $this->aColors[$i],
+                'borderColor' => $this->aColors[$i],
+                'borderWidth' => 2,
+                'data' => $aValues['values'][$i]['data']
+            );
+        }
+            
+        $aDataForChart = array(
+            'type' => $sType,
+            'data' => array(
+                'labels' => $aValues['labels'], 
+                'datasets' => $aDataForChartDatasets
+            ),
+            'options' => array(
+                'legend' => array(
+                    'position' => 'bottom', 
+                    'display' => count($aValues['values']) == 1 ? false :true
+                ),
+                'elements' => array(
+                    'point'=> array(
+                        'radius' => 0
+                    )
+                ),
+                'scales' => array(
+                    'y' => array(
+                        'ticks' => array(
+                            'max' => $iMaxValueY, 
+                            'min' => $iMinValueY, 
+                            'stepSize' => $this->getStep($iMinValueY, $iMaxValueY), 
+                            'autoSkip' => true
+                        )
+                    ),
+                    'x' => $aDataForChartXAxes
+                )
+            ),
+            'links' => $aValues['links'],
+            'strings' => $aValues['strings'],
+        );
+        
+        return json_encode($aDataForChart);
+    }
+	
+	public function checkAllowed($isPerformAction = false)
+    {
+        $aCheck = checkActionModule(bx_get_logged_profile_id(), 'use analytics', $this->getName(), $isPerformAction);
+        if ($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+             return _t('_sys_txt_access_denied');
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+          
+    public function getSelectedModules()
+    {
+        $aModulesList = $this->serviceGetModules();
+        $aModules = array_keys($aModulesList);
+        $sModulesDisabled = explode(',', getParam('bx_analytics_modules_disabled'));
+        return array(array_diff($aModules, $sModulesDisabled), $aModulesList);
+    }
+
+    private function getViewsOrVotes($aSystems, $sSystem, $sModuleName, $iDateFrom, $iDateTo, &$aValues, &$iMaxValueY, &$iMinValueY)
+    {
+        $aData = array();
+        if ($sModuleName != BX_ANALYTICS_SYSTEM){
+            $oModule = BxDolModule::getInstance($sModuleName);
+            $sSystem = $oModule->_oConfig->CNF['OBJECT_VIEWS'];
+            if(isset($aSystems[$sSystem]))
+                $aData = $this->_oDb->getTopContentByLikes($sModuleName, $aSystems[$sSystem]['table_track'], $iDateFrom, $iDateTo);
+        }
+        else{
+            $aTmp = $this->getSelectedModules();
+            foreach ($aTmp[0] as $sModule) {
+                if ($sModule != BX_ANALYTICS_SYSTEM){
+                    $oModule = BxDolModule::getInstance($sModule);
+                    if (isset($oModule->_oConfig->CNF['OBJECT_VIEWS'])){
+                        $sSystem = $oModule->_oConfig->CNF['OBJECT_VIEWS'];
+                        if(isset($aSystems[$sSystem])){
+                            $aData1 = $this->_oDb->getTopContentByLikes($sModule, $aSystems[$sSystem]['table_track'], $iDateFrom, $iDateTo);
+                            $aData = array_merge($aData, $aData1);
+                        }
+                    }
+                }
+            }
+            $aData = $this->sortArrayOfArraysAndSlice($aData, 'value', intval(getParam('bx_analytics_items_count')));
+        }
+        foreach ($aData as $aValue) {
+            $oModule = BxDolModule::getInstance($aValue['module']);
+            array_push($aValues['labels'], $this->getItemName($oModule->serviceGetTitle($aValue['object_id'])));
+            array_push($aValues['values'][0]['data'], $aValue['value']);
+            array_push($aValues['links'], $oModule->serviceGetLink($aValue['object_id']));
+            if ($aValue['value'] > $iMaxValueY)
+                $iMaxValueY = $aValue['value'];
+        }
+    }
+          
+    private function getStep($iMin, $iMax)
+    {
+        $iCount = 8;
+        return ceil(($iMax - $iMin) / $iCount);
+    }
+          
+    private function sortArrayOfArraysAndSlice($aArr, $sFld, $Count)
+    {
+        $aDataSort = array();
+        foreach ($aArr as $sKey => $oRow)
+        {
+            $aDataSort[$sKey] = $oRow[$sFld];
+        }
+        array_multisort($aDataSort, SORT_DESC, $aArr);
+        return array_slice($aArr, 0, $Count);
+    }
+          
+    private function getItemName($sString)
+    {
+        $sString = strip_tags($sString);
+        if ($sString == ''){
+            $sString = _t('_bx_analytics_txt_empty_title_item');
+        }
+        return $sString;
+    }
+}
+
+/** @} */

+ 77 - 0
modules/boonex/analytics/updates/15.0.0_15.0.1/source/install/config.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Analytics Analytics
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_analytics',
+    'title' => 'Analytics',
+    'note' => 'Analytics module.',
+    'version' => '15.0.1',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/analytics/',
+    'home_uri' => 'analytics',
+
+    'db_prefix' => 'bx_analytics_',
+    'class_prefix' => 'BxAnalytics',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Analytics',
+
+    /**
+     * 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,
+    ),
+    'enable_success' => array(
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Antispam',
+    'version_from' => '14.0.2',
+    'version_to' => '15.0.0',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/antispam/updates/update_14.0.2_15.0.0/',
+    'home_uri' => 'antispam_update_1402_1500',
+
+    'module_dir' => 'boonex/antispam/',
+    'module_uri' => 'antispam',
+
+    'db_prefix' => 'bx_antispam_',
+    'class_prefix' => 'BxAntispam',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Antispam',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxAntispamUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 4 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/install/langs/en.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_upd name="_bx_antispam_rule_note_spamhaus_org"><![CDATA[Detect spammy IPs: to use the service for free, your server must use its own private DNS server and have a reverse PTR record for its public IP.]]></string_upd>
+</resources>

+ 4 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/install/sql/install.sql

@@ -0,0 +1,4 @@
+-- TABLES
+ALTER TABLE `bx_antispam_dnsbl_rules` MODIFY `postvresp` varchar(255) NOT NULL;
+
+UPDATE `bx_antispam_dnsbl_rules` SET `postvresp`='127.0.0.2,127.0.0.3,127.0.0.4,127.0.0.9', `recheck`='https://check.spamhaus.org/results/?query=%s' WHERE `chain`='spammers' AND `zonedomain`='sbl.spamhaus.org.';

+ 54 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/source/classes/BxAntispamAlertsResponse.php

@@ -0,0 +1,54 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Antispam Antispam
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * alerts handler
+ */
+class BxAntispamAlertsResponse extends BxDolAlertsResponse
+{
+    public function response($oAlert)
+    {
+        if ('account' == $oAlert->sUnit) {
+
+            switch ($oAlert->sAction) {
+                case 'check_login':
+                    $oAlert->aExtras['error_msg'] = BxDolService::call('bx_antispam', 'check_login');
+                    break;
+                case 'check_join':
+                    $oAlert->aExtras['error_msg'] = BxDolService::call('bx_antispam', 'check_join', array($oAlert->aExtras['email'], &$oAlert->aExtras['approve']));
+                    break;
+            }
+
+        } elseif ('system' == $oAlert->sUnit) {
+            
+            switch ($oAlert->sAction) {
+                case 'check_spam_url':
+                    $oAlert->aExtras['is_spam'] = BxDolService::call('bx_antispam', 'is_spam_url', array(&$oAlert->aExtras['content']));
+                    break;
+                case 'check_spam':
+                    $oAlert->aExtras['content'] = BxDolService::call('bx_antispam', 'filter_spam', array($oAlert->aExtras['content']));
+                    if ((!isset($oAlert->aExtras['type']) || 'textarea' == $oAlert->aExtras['type']) && is_string($oAlert->aExtras['content']))
+                        $oAlert->aExtras['is_spam'] = BxDolService::call('bx_antispam', 'is_spam', array(&$oAlert->aExtras['content']));
+                    break;
+                case 'form_check':
+                    BxDolService::call('bx_antispam', 'check_form_for_toxicity', array(&$oAlert->aExtras['object']));
+                    break;
+                case 'form_submitted':
+                    BxDolService::call('bx_antispam', 'on_form_submitted', array($oAlert->aExtras['module'], $oAlert->aExtras['entry_id'], $oAlert->aExtras['action'], &$oAlert->aExtras['form_object']));
+                    break;
+            }
+        } elseif ('comment' == $oAlert->sUnit && BxDolService::call('bx_antispam', 'is_last_form_submitted_toxic', array())) {
+            BxDolService::call('bx_antispam', 'set_comment_as_pending', array($oAlert->iObject));
+        }
+    }
+}
+
+/** @} */

+ 206 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/source/classes/BxAntispamDNSBlacklists.php

@@ -0,0 +1,206 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Antispam Antispam
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define('BX_DOL_DNSBL_NEGATIVE', 0);   // negative
+define('BX_DOL_DNSBL_POSITIVE', 1);   // positive match
+define('BX_DOL_DNSBL_FAILURE', 2);    // generic failure, not enabled or configured
+
+// Types of queries for dnsbl_lookup_ip() and dnsbl_lookup_domain()
+define('BX_DOL_DNSBL_ANYPOSTV_RETFIRST', 0);   // Any positive from chain, stop and return first
+define('BX_DOL_DNSBL_ANYPOSTV_RETEVERY', 1);   // Any positive, check all and return every positive
+define('BX_DOL_DNSBL_ALLPOSTV_RETEVERY', 2);   // All must check positive, return every positive
+
+define('BX_DOL_DNSBL_MATCH_ANY', "any");
+
+define('BX_DOL_DNSBL_CHAIN_SPAMMERS', "spammers");
+define('BX_DOL_DNSBL_CHAIN_WHITELIST', "whitelist");
+define('BX_DOL_DNSBL_CHAIN_URIDNS', "uridns");
+
+/**
+ *  Spam detection based on spammer IP
+ *
+ *
+ * Example of usage:
+ *
+ *  if (DNSBL_POSITIVE == $o->dnsbl_lookup_ip(DNSBL_CHAIN_SPAMMERS, $sCurIP) && DNSBL_POSITIVE != $o->dnsbl_lookup_ip(DNSBL_CHAIN_WHITELIST, $sCurIP))
+ *  {
+ *    // positive detection - block this ip
+ *  }
+ *  // continue script execution
+ *
+ *
+ *  There is more handy function available:
+ *  @see bx_is_ip_dns_blacklisted
+ */
+class BxAntispamDNSBlacklists extends BxDol
+{
+    private $aChains = array ();
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->initChains();
+    }
+
+    public function dnsbl_lookup_ip($mixedChain, $sIp, $querymode = BX_DOL_DNSBL_ANYPOSTV_RETFIRST)
+    {
+        $lookupkey = $this->ipreverse($sIp);
+        if (false === $lookupkey)
+            return BX_DOL_DNSBL_FAILURE;	// unable to prepare lookup string from address
+
+        if (is_array($mixedChain))
+            $aChain = $mixedChain;
+        else
+            $aChain = &$this->aChains[$mixedChain];
+        return $this->dnsbl_lookup($aChain, $lookupkey, $querymode);
+    }
+
+    public function dnsbl_lookup_uri($sUri, $mixedChain = BX_DOL_DNSBL_CHAIN_URIDNS, $querymode = BX_DOL_DNSBL_ANYPOSTV_RETFIRST)
+    {
+        if (!$sUri)
+            return BX_DOL_DNSBL_FAILURE;
+
+        if (is_array($mixedChain))
+            $aChain = $mixedChain;
+        else
+            $aChain = &$this->aChains[$mixedChain];
+        return $this->dnsbl_lookup($aChain, $sUri, $querymode);
+    }
+
+    public function onPositiveDetection ($sIP, $sNote = '', $sType = 'dnsbl')
+    {
+        $iIP = sprintf("%u", ip2long($sIP));
+        $oDb = BxDolDb::getInstance();
+        $sQuery = $oDb->prepare("INSERT INTO `bx_antispam_block_log` SET `ip` = ?, `profile_id` = ?, `type` = ?, `extra` = ?, `added` = ?", $iIP, bx_get_logged_profile_id(), $sType, $sNote, time());
+        return $oDb->query($sQuery);
+    }
+
+    public function getRules ($aChains)
+    {
+        bx_import('BxDolLanguages');
+        $oDb = BxDolDb::getInstance();
+        $sChains = $oDb->implode_escape($aChains);
+        $a = $oDb->getAll("SELECT * FROM `bx_antispam_dnsbl_rules` WHERE `chain` IN($sChains) AND `active` = 1 ORDER BY `chain`, `added`");
+        foreach ($a as $k => $r) {
+            $a[$k]['chain_title'] = _t('_bx_antispam_chain_' . $a[$k]['chain']);
+        }
+        return $a;
+    }
+
+    public function getRule ($iId)
+    {
+        $oDb = BxDolDb::getInstance();
+        return $oDb->getAll("SELECT `zonedomain`, `postvresp` FROM `bx_antispam_dnsbl_rules` WHERE `id` = '" . (int)$iId . "' AND `active` = 1");
+    }
+
+    public function clearCache ()
+    {
+        $oDb = BxDolDb::getInstance();
+        $oDb->cleanCache('sys_dnsbl_'.BX_DOL_DNSBL_CHAIN_SPAMMERS);
+        $oDb->cleanCache('sys_dnsbl_'.BX_DOL_DNSBL_CHAIN_WHITELIST);
+    }
+
+    public function getDNSBLConfigValues ()
+    {
+        return array (
+            'block' => _t('_bx_antispam_dnsbl_behaviour_login_block'),
+            'log' => _t('_bx_antispam_dnsbl_behaviour_login_log'),
+        );
+    }
+
+    /*************** private function ***************/
+
+    private function dnsbl_lookup(&$zones, $key, $querymode)
+    {
+        $numpositive = 0;
+        $numservers = count ($zones);
+        $servers = $zones;
+
+        if (!$servers)
+            return BX_DOL_DNSBL_FAILURE; // no servers defined
+
+        if (($querymode!=BX_DOL_DNSBL_ANYPOSTV_RETFIRST) && ($querymode!=BX_DOL_DNSBL_ANYPOSTV_RETEVERY)
+             && ($querymode!=BX_DOL_DNSBL_ALLPOSTV_RETEVERY))
+             return BX_DOL_DNSBL_FAILURE;	// invalid querymode
+
+        foreach ($servers as $r) {
+            $resultaddr = gethostbyname ($key . "." . $r['zonedomain']);
+            if ($resultaddr && $resultaddr != $key . "." . $r['zonedomain']) {
+
+                bx_log('bx_antispam_dnsbl', $key . "." . $r['zonedomain'] . " => " . $resultaddr);
+
+                // we got some result from the DNS query, not NXDOMAIN. should we consider 'positive'?
+                $postvresp = $r['postvresp'];	// check positive match criteria
+                $bPositive = false;
+                if (strpos($postvresp, ',') !== false) {
+                    $postvresp = explode(',', $postvresp);
+                    if (in_array($resultaddr, $postvresp)) {
+                        $bPositive = true;
+                    }
+                }
+                else {
+                    $bPositive = (
+                        BX_DOL_DNSBL_MATCH_ANY === $postvresp ||
+                        (preg_match("/^\d+\.\d+\.\d+\.\d+$/", $postvresp) && $resultaddr == $postvresp) ||
+                        (is_numeric($postvresp) && (ip2long($resultaddr) & $postvresp))
+                    );
+                }
+                if ($bPositive) {
+                    $numpositive++;
+                    if ($querymode == BX_DOL_DNSBL_ANYPOSTV_RETFIRST)
+                        return BX_DOL_DNSBL_POSITIVE;	// found one positive, returning single
+                }
+            }
+        }
+        // all servers were queried
+        if ($numpositive == $numservers)
+            return BX_DOL_DNSBL_POSITIVE;
+        else if (($querymode == BX_DOL_DNSBL_ANYPOSTV_RETEVERY) && ($numpositive > 0))
+            return BX_DOL_DNSBL_POSITIVE;
+        else
+            return BX_DOL_DNSBL_NEGATIVE;
+    }
+
+    private function ipreverse ($sIp)
+    {
+        if (!preg_match ('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/', $sIp, $m))
+            return false;
+
+        return "{$m[4]}.{$m[3]}.{$m[2]}.{$m[1]}";
+    }
+
+    private function initChains()
+    {
+        $oDb = BxDolDb::getInstance();
+
+        if (!isset($GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_SPAMMERS])) {
+            $sQuery = $oDb->prepare("SELECT `zonedomain`, `postvresp` FROM `bx_antispam_dnsbl_rules` WHERE `chain` = ? AND `active` = 1", BX_DOL_DNSBL_CHAIN_SPAMMERS);
+            $GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_SPAMMERS] = $oDb->fromCache('sys_dnsbl_'.BX_DOL_DNSBL_CHAIN_SPAMMERS, 'getAll', $sQuery);
+        }
+
+        if (!isset($GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_WHITELIST]))
+            $GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_WHITELIST] = $oDb->fromCache('sys_dnsbl_'.BX_DOL_DNSBL_CHAIN_WHITELIST, 'getAll', "SELECT `zonedomain`, `postvresp` FROM `bx_antispam_dnsbl_rules` WHERE `chain` = '".BX_DOL_DNSBL_CHAIN_WHITELIST."' AND `active` = 1");
+
+        if (!isset($GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_URIDNS]))
+            $GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_URIDNS] = $oDb->fromCache('sys_dnsbl_'.BX_DOL_DNSBL_CHAIN_URIDNS, 'getAll', "SELECT `zonedomain`, `postvresp` FROM `bx_antispam_dnsbl_rules` WHERE `chain` = '".BX_DOL_DNSBL_CHAIN_URIDNS."' AND `active` = 1");
+
+        $this->aChains[BX_DOL_DNSBL_CHAIN_SPAMMERS] = &$GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_SPAMMERS];
+        $this->aChains[BX_DOL_DNSBL_CHAIN_WHITELIST] = &$GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_WHITELIST];
+        $this->aChains[BX_DOL_DNSBL_CHAIN_URIDNS] = &$GLOBALS['bx_dol_dnsbl_'.BX_DOL_DNSBL_CHAIN_URIDNS];
+
+    }
+
+}
+
+/** @} */

+ 634 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/source/classes/BxAntispamModule.php

@@ -0,0 +1,634 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Antispam Antispam
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAntispamModule extends BxDolModule
+{
+    private $_bLastSubmittedFormWasToxic;
+
+    public function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+        $this->_bLastSubmittedFormWasToxic = false;
+    }
+
+    public function actionContentChecked()
+    {
+        $iResult = 404;
+
+        if($this->_oConfig->getAntispamOption('lm_enable') == 'on') {
+            $oLassoModeration = bx_instance('BxAntispamLassoModeration', [], $this->getName());
+            $iResult = $oLassoModeration->processEvent();    
+        }
+
+        http_response_code($iResult);
+    }
+
+    public function serviceUpdateDisposableDomainsLists ()
+    {
+        $o = bx_instance('BxAntispamDisposableEmailDomains', array(), 'bx_antispam');
+
+        $o->updateList('blacklist', 'https://raw.githubusercontent.com/martenson/disposable-email-domains/master/disposable_email_blocklist.conf');
+
+        // TODO: uncomment after adding interface for whitelisting
+        // $o->updateList('whitelist', 'https://raw.githubusercontent.com/martenson/disposable-email-domains/master/whitelist.conf');
+    }
+    
+    public function serviceIpTable ()
+    {
+        $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
+        $s = _t('_bx_antispam_ip_table_status', mb_strtolower($o->getIpTableConfigTitle((int)getParam('bx_antispam_ip_list_type'))));
+        $s .= $this->_grid('bx_antispam_grid_ip_table');
+        return $s;
+    }
+
+    public function serviceDnsblList ()
+    {
+        $s = _t('_bx_antispam_dnsbl_status',
+             BxTemplFunctions::getInstance()->statusOnOff((bool)$this->_oConfig->getAntispamOption('dnsbl_enable'), true),
+             BxTemplFunctions::getInstance()->statusOnOff((bool)$this->_oConfig->getAntispamOption('uridnsbl_enable'), true),
+             mb_strtolower(_t('_bx_antispam_dnsbl_behaviour_login_' . $this->_oConfig->getAntispamOption('dnsbl_behaviour_login'))),
+             mb_strtolower(_t('_bx_antispam_dnsbl_behaviour_join_' . $this->_oConfig->getAntispamOption('dnsbl_behaviour_join')))
+        );
+        $s .= $this->_grid('bx_antispam_grid_dnsbl');
+        return $s;
+    }
+
+    public function serviceBlockLog ()
+    {
+        return $this->_grid('bx_antispam_grid_block_log');
+    }
+
+    /**
+     * Filter undesired words
+     *
+     * @param $sContent content to filter
+     * @param $sIp IP address of content poster
+     * @return modified or the same content.
+     */
+    public function serviceFilterSpam ($mContent, $sIp = '')
+    {      
+        if (defined('BX_DOL_CRON_EXECUTE') || isAdmin() || 'on' != $this->_oConfig->getAntispamOption('profanity_enable'))
+            return $mContent;
+        
+        $oProfanityFilter = bx_instance('BxAntispamProfanityFilter', array(), $this->_aModule);
+        return $oProfanityFilter->censor($mContent); 
+    }
+    
+    /**
+     * Check text for spam URL.
+     * It checks URLs found in text for DNSURI black lists (@see BxAntispamDNSURIBlacklists),
+     *
+     * @param $sContent content to check for spam
+     * @return true if spam detected, false if spam isn't detected, null if detection wasn't performed.
+     */
+    public function serviceIsSpamUrl ($sContent)
+    {
+        $bRet = null;
+        if ('on' == $this->_oConfig->getAntispamOption('uridnsbl_enable')) {
+            $oDNSURIBlacklists = bx_instance('BxAntispamDNSURIBlacklists', array(), $this->_aModule);
+            if ($oDNSURIBlacklists->isSpam($sContent)) {
+                $oDNSURIBlacklists->onPositiveDetection($sContent);
+                $bRet = true;
+            } 
+            else {
+                $bRet = false;
+            }
+        }
+        return $bRet;
+    }
+
+    /**
+     * Check text for spam.
+     * First it check if IP is whitelisted(or under cron execution or user is admin) - for whitelisted IPs check for spam isn't performed,
+     * then it checks URLs found in text for DNSURI black lists (@see BxAntispamDNSURIBlacklists),
+     * then it checks text in Akismet service (@see BxAntispamAkismet).
+     * It can send report if spam is found or tries to inform caller to block the content (depending on configuration).
+     *
+     * @param $sContent content to check for spam
+     * @param $sIp IP address of content poster
+     * @param $isStripSlashes slashes parameter:
+     *          BX_SLASHES_AUTO - automatically detect magic_quotes_gpc setting
+     *          BX_SLASHES_NO_ACTION - do not perform any action with slashes
+     * @return true if spam detected and content shouln't be recorded, false if content should be processed as usual.
+     */
+    public function serviceIsSpam ($sContent, $sIp = '', $isStripSlashes = BX_SLASHES_AUTO)
+    {      
+        if (defined('BX_DOL_CRON_EXECUTE') || isAdmin())
+            return false;
+
+        if ($this->serviceIsIpWhitelisted($sIp))
+            return false;
+
+        if (version_compare(phpversion(), '7.4.0', '<') && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() && $isStripSlashes == BX_SLASHES_AUTO)
+            $sContent = stripslashes($sContent);
+        
+        $bRet = false;
+        if ('on' == $this->_oConfig->getAntispamOption('uridnsbl_enable')) {
+            $oDNSURIBlacklists = bx_instance('BxAntispamDNSURIBlacklists', array(), $this->_aModule);
+            if ($oDNSURIBlacklists->isSpam($sContent)) {
+                $oDNSURIBlacklists->onPositiveDetection($sContent);
+                $bRet = true;
+            }
+        }
+
+        if (!$bRet && 'on' == $this->_oConfig->getAntispamOption('akismet_enable')) {
+            $oAkismet = bx_instance('BxAntispamAkismet', array(), $this->_aModule);
+            if ($oAkismet->isSpam($sContent)) {
+                $oAkismet->onPositiveDetection($sContent);
+                $bRet = true;
+            }
+        }
+
+        if ($bRet && 'on' == $this->_oConfig->getAntispamOption('antispam_report')) {
+
+            $oProfile = BxDolProfile::getInstance();
+            $aPlus = array(
+                'SpammerUrl' => $oProfile->getUrl(),
+                'SpammerNickName' => $oProfile->getDisplayName(),
+                'Page' => htmlspecialchars_adv($_SERVER['PHP_SELF']),
+                'Get' => print_r($_GET, true),
+                'Post' => print_r($_POST, true),
+                'SpamContent' => htmlspecialchars_adv($sContent),
+            );
+
+            $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('bx_antispam_spam_report', $aPlus);
+            if (!$aTemplate)
+                trigger_error('Email template or translation missing: bx_antispam_spam_report', E_USER_ERROR);
+
+            sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body']);
+        }
+
+        if ($bRet && 'on' == $this->_oConfig->getAntispamOption('antispam_block'))
+            return true;
+
+        return false;
+    }
+
+    /**
+     * Check text for toxicity.
+     * It can send report if toxic content is found or tries to inform caller to block the content (depending on configuration).
+     *
+     * @param $sContent content to check for spam
+     * @param $sIp IP address of content poster
+     * @param $isStripSlashes slashes parameter:
+     *          BX_SLASHES_AUTO - automatically detect magic_quotes_gpc setting
+     *          BX_SLASHES_NO_ACTION - do not perform any action with slashes
+     * @return true if toxic content detected and content shouln't be recorded, false if content should be processed as usual.
+     */
+    public function serviceIsToxic ($sContent, $sIp = '', $isStripSlashes = BX_SLASHES_AUTO, $bSupressNotification = false)
+    {
+        if (defined('BX_DOL_CRON_EXECUTE') || isAdmin())
+            return false;
+
+        static $aQuickCache;
+        $key = md5($sContent);
+        if (isset($aQuickCache[$key]))
+            return $aQuickCache[$key];
+
+        $aQuickCache[$key] = false;
+
+        if (!$this->serviceIsIpWhitelisted($sIp)) {
+            if (version_compare(phpversion(), '7.4.0', '<') && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() && $isStripSlashes == BX_SLASHES_AUTO)
+                $sContent = stripslashes($sContent);
+
+            if ('on' == $this->_oConfig->getAntispamOption('toxicity_enable')) {
+                $oPerspectiveAPI = bx_instance('BxAntispamPerspectiveAPI', array(), $this->_aModule);
+                if ($oPerspectiveAPI->isToxic($sContent)) {
+                    $oPerspectiveAPI->onPositiveDetection($sContent);
+                    $aQuickCache[$key] = true;
+
+                    if (!$bSupressNotification) $this->serviceOnToxicContentFound($sContent);
+                }
+            }
+        }
+
+        return $aQuickCache[$key];
+    }
+
+    public function serviceCheckFormForToxicity(&$oForm) {
+        $this->_bLastSubmittedFormWasToxic = false;
+
+        if (!$oForm->isValid() || !$oForm->isSubmitted()) return false;
+
+        $sSubmitName = false;
+        $sFormText = '';
+        foreach ($oForm->aInputs as $k => $a) {
+            if (isset($a['visible_for_levels']) && !BxDolForm::isVisible($a))
+                continue;
+
+            if (empty($a['name']) || 'submit' == $a['type'] || 'reset' == $a['type'] || 'button' == $a['type'] || 'value' == $a['type']) {
+                if (isset($a['type']) && 'submit' == $a['type'])
+                    $sSubmitName = $k;
+                continue;
+            }
+
+            if ('input_set' == $a['type'])
+                foreach ($a as $r)
+                    if (isset($r['type']) && 'submit' == $r['type'])
+                        $sSubmitName = $k;
+
+            $a['name'] = str_replace('[]', '', $a['name']);
+
+            if ($a['type'] != 'textarea' && $a['type'] != 'text')
+                continue;
+
+            $val = BxDolForm::getSubmittedValue($a['name'], $oForm->aFormAttrs['method']);
+            if (!$val)
+                continue;
+
+            $sFormText .= " ".$val; // collect all text fields into a single text
+        }
+
+        if ($this->serviceIsToxic($sFormText, '', BX_SLASHES_AUTO, true)) {
+            $this->_bLastSubmittedFormWasToxic = true;
+
+            $sActionrequired = $this->_oConfig->getAntispamOption('toxicity_action');
+
+            if ($sActionrequired == 'block' || $sActionrequired == 'disapprove' && !$oForm->isStatusFieldSupported()) {
+                $oForm->setValid(false);
+                $this->serviceOnToxicContentFound($sFormText);
+
+                if ($sSubmitName)
+                    $oForm->aInputs[$sSubmitName]['error'] = _t('_bx_antispam_form_submission_error');
+            } elseif ($sActionrequired == 'disapprove' && $oForm->isStatusFieldSupported()) {
+                $oForm->setForceSetToPending(true);
+                return false;
+            }
+        }
+    }
+
+    public function serviceIsLastFormSubmittedToxic() {
+        return $this->_bLastSubmittedFormWasToxic;
+    }
+
+    public function serviceSetCommentAsPending($iCmtId) {
+        return $this->_oDb->setCommentStatus($iCmtId, BX_CMT_STATUS_PENDING);
+    }
+
+    public function serviceOnFormSubmitted($sModule, $iEntry, $sAction, &$oForm)
+    {
+        if ($this->_bLastSubmittedFormWasToxic) {
+            $this->serviceOnToxicContentPosted($sModule, $iEntry);
+        }
+
+        if($this->_oConfig->getAntispamOption('lm_enable') == 'on' && (bx_srv('system', 'is_module_content', [$sModule]) || bx_srv('system', 'is_module_context', [$sModule]) || in_array($sModule, ['bx_timeline'])) && ($oModule = BxDolModule::getInstance($sModule)) !== null) {
+            $CNF = &$oModule->_oConfig->CNF;
+
+            if(($iAuthorId = bx_get_logged_profile_id()) !== false && ($oAuthor = BxDolProfile::getInstance()) !== false) {
+                $iDataAdded = time();
+
+                $sText = '';
+                if(isset($CNF['FIELD_TEXT'], $oForm->aInputs[$CNF['FIELD_TEXT']]))
+                    $sText = $oForm->getCleanValue($CNF['FIELD_TEXT']);
+                if(empty($sText) && isset($oForm->aInputs['text']))
+                    $sText = $oForm->getCleanValue('text');
+
+                $aMedias = [
+                    'images' => ['fields' => ['FIELD_PHOTO', 'FIELD_PICTURE', 'FIELD_COVER'], 'data' => []], 
+                    'videos' => ['fields' => ['FIELD_VIDEO'], 'data' => []]
+                ];
+                foreach($aMedias as $sName => $aMedia) 
+                    foreach($aMedia['fields'] as $sField) {
+                        if(!isset($CNF[$sField], $oForm->aInputs[$CNF[$sField]]))
+                            continue;
+
+                        $oStorage = BxDolStorage::getObjectInstance($oForm->aInputs[$CNF[$sField]]['storage_object']);
+                        if(!$oStorage)
+                            continue;
+
+                        $aGhosts = $oStorage->getGhosts($iAuthorId, $sAction == 'insert' ? 0 : $iEntry);
+                        foreach($aGhosts as $aGhost)
+                            $aMedias[$sName]['data'][] = $oStorage->getFileUrlById($aGhost['id']);
+                    }
+
+                $oLassoModeration = bx_instance('BxAntispamLassoModeration', [], $this->getName());
+                $oLassoModeration->addContent($sModule, $iEntry, [
+                    'author_id' => $iAuthorId,
+                    'author_name' => $oAuthor->getDisplayName(),
+                    'date_added' => $iDataAdded,
+                    'text' => $sText,
+                    'images' => $aMedias['images']['data'], 
+                    'videos' => $aMedias['videos']['data']
+                ]);
+            }
+        }
+    }
+
+    public function serviceOnToxicContentFound(&$sText) {
+        if ('on' == $this->_oConfig->getAntispamOption('toxicity_report')) {
+            $oProfile = BxDolProfile::getInstance();
+            if (!$oProfile) return;
+            $aPlus = array(
+                'AuthorUrl' => $oProfile->getUrl(),
+                'AuthorNickName' => $oProfile->getDisplayName(),
+                'Page' => htmlspecialchars_adv($_SERVER['PHP_SELF']),
+                'Content' => htmlspecialchars_adv(strip_tags($sText)),
+            );
+
+            $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('bx_antispam_toxicity_blocked_report', $aPlus);
+            if (!$aTemplate)
+                trigger_error('Email template or translation missing: bx_antispam_toxicity_blocked_report', E_USER_ERROR);
+
+            sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body']);
+
+            bx_alert('bx_antispam', 'toxic_content_found', 0, bx_get_logged_profile_id(), array(
+                'text' => $sText,
+            ));
+        }
+    }
+
+    public function serviceOnToxicContentPosted($sModule, $iContentId) {
+        if ('on' == $this->_oConfig->getAntispamOption('toxicity_report') && $sModule) {
+            $oModule = BxDolModule::getInstance($sModule);
+            $CNF = &$oModule->_oConfig->CNF;
+            $sContentUrl = isset($CNF['URI_VIEW_ENTRY']) ? bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iContentId)) : false;
+            $sManageContentUrl = isset($CNF['URL_MANAGE_ADMINISTRATION']) ? bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URL_MANAGE_ADMINISTRATION'])) : false;
+
+            $oProfile = BxDolProfile::getInstance();
+            if (!$oProfile) return;
+
+            $aPlus = array(
+                'AuthorUrl' => $oProfile->getUrl(),
+                'AuthorNickName' => $oProfile->getDisplayName(),
+                'Page' => htmlspecialchars_adv($_SERVER['PHP_SELF']),
+                'bx_if:content_url' => [
+                    'condition' => boolval($sContentUrl),
+                    'content' => ['c_url' => $sContentUrl],
+                ],
+                'bx_if:manage_content_url' => [
+                    'condition' => boolval($sManageContentUrl),
+                    'content' => ['m_url' => $sManageContentUrl],
+                ],
+            );
+
+            $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('bx_antispam_toxicity_posted_report', $aPlus);
+            if (!$aTemplate)
+                trigger_error('Email template or translation missing: bx_antispam_toxicity_posted_report', E_USER_ERROR);
+
+            sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body']);
+
+            bx_alert('bx_antispam', 'toxic_content_posted', $iContentId, bx_get_logged_profile_id(), array(
+                'module' => $sModule,
+                'entry_id' => $iContentId,
+                'entry_url' => $sContentUrl,
+            ));
+        }
+    }
+
+    /**
+     * Perform complex check if user is allowed to login.
+     * First it checks if IP is directly blocked (@see serviceIsIpBlocked),
+     * then it checks in DNS black lists (@see serviceIsIpDnsBlacklisted).
+     *
+     * @param $sIp IP to check, or empty for current IP
+     * @return empty string - if join should be allowed, error message - if join should be blocked
+     */
+    public function serviceCheckLogin ($sIp = '')
+    {
+        $bLoginBlock = ('block' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_login'));
+        $sErrorMsg = '';
+        $sNote = $bLoginBlock ? 'login block' : 'login log';
+
+        if (!$sIp)
+            $sIp = getVisitorIP();
+
+        if (!$sErrorMsg && $this->serviceIsIpBlocked($sIp))
+            $sErrorMsg = $this->getErrorMessageIpBlocked();
+
+        if (!$sErrorMsg && 'on' == $this->_oConfig->getAntispamOption('dnsbl_enable') && $this->serviceIsIpDnsBlacklisted($sIp, $sNote) && $bLoginBlock)
+            $sErrorMsg = $this->getErrorMessageSpam();
+
+        return $sErrorMsg;
+    }
+
+    /**
+     * Perform complex check if user is allowed to join.
+     * First it checks if IP is directly blocked (@see serviceIsIpBlocked),
+     * then it checks in DNS black lists (@see serviceIsIpDnsBlacklisted),
+     * then it checks in StopForumSpam service (@see BxAntispamStopForumSpam).
+     *
+     * @param $sCurIP IP to check, or empty for current IP
+     * @return empty string - if join should be allowed, error message - if join should be blocked
+     */
+    public function serviceCheckJoin ($sEmail, &$bApproval, $sIp = '')
+    {
+        $bJoinBlock = ('block' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_join'));
+        $sErrorMsg = '';
+        $sNote = $bJoinBlock ? 'join block' : 'join approval';
+
+        if (!$sIp)
+            $sIp = getVisitorIP();
+
+        // check if IP is blocked
+        if (!$sErrorMsg && $this->serviceIsIpBlocked($sIp))
+            $sErrorMsg = $this->getErrorMessageIpBlocked();
+
+        // check in DNSBL lists
+        if (!$sErrorMsg && 'on' == $this->_oConfig->getAntispamOption('dnsbl_enable') && $this->serviceIsIpDnsBlacklisted($sIp, $sNote)) {
+            if ('approval' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_join'))
+                $bApproval = true;
+            else
+                $sErrorMsg = $this->getErrorMessageSpam();
+        }
+
+        // check in StopForumSpam service
+        if (!$sErrorMsg) {
+            $oStopForumSpam = bx_instance('BxAntispamStopForumSpam', array(), $this->_aModule);
+            if ($oStopForumSpam->isSpammer(array('email' => $sEmail, 'ip' => $sIp), $sNote))
+                $sErrorMsg = $this->getErrorMessageSpam();
+        }
+
+        // check for disposable email domains
+        if (!$sErrorMsg && 'disable' != ($sMode = $this->_oConfig->getAntispamOption('disposable_email_domains_mode'))) {
+
+            $bDisposableEmailDomainsBehaviour = $this->_oConfig->getAntispamOption('disposable_email_domains_behaviour_join');
+            $oDisposableEmailDomains = bx_instance('BxAntispamDisposableEmailDomains', array(), 'bx_antispam');
+            
+            if ('blacklist' == $sMode && $oDisposableEmailDomains->isBlacklisted($sEmail)) {
+                if ('approval' == $bDisposableEmailDomainsBehaviour)
+                    $bApproval = true;
+                else
+                    $sErrorMsg = $oDisposableEmailDomains->getErrorMessageBlacklisted();
+            }
+            elseif ('whitelist' == $sMode && !$oDisposableEmailDomains->isWhitelisted($sEmail)) {
+                if ('approval' == $bDisposableEmailDomainsBehaviour)
+                    $bApproval = true;
+                else
+                    $sErrorMsg = $oDisposableEmailDomains->getErrorMessageNotWhitelisted();
+            }
+
+        }
+
+        return $sErrorMsg;
+    }
+
+    /**
+     * Check if IP is blacklisted in some DNS chain (@see BxAntispamDNSBlacklists).
+     *
+     * @param $sCurIP IP to check, or empty for current IP
+     * @param $sNote [optional] place where checking is performed, for example 'join', 'login'
+     * @return true if IP blacklisted and not whiteloisted, or false if under cron execution or if IP isn't blacklisted
+     */
+    public function serviceIsIpDnsBlacklisted($sCurIP = '', $sNote = '')
+    {
+        if (defined('BX_DOL_CRON_EXECUTE'))
+            return false;
+
+        if (!$sCurIP)
+            $sCurIP = getVisitorIP();
+
+        if ($this->serviceIsIpWhitelisted($sCurIP))
+            return false;
+
+        $o = bx_instance('BxAntispamDNSBlacklists', array(), $this->_aModule);
+
+        if (BX_DOL_DNSBL_POSITIVE == $o->dnsbl_lookup_ip(BX_DOL_DNSBL_CHAIN_SPAMMERS, $sCurIP) && BX_DOL_DNSBL_POSITIVE != $o->dnsbl_lookup_ip(BX_DOL_DNSBL_CHAIN_WHITELIST, $sCurIP)) {
+            $o->onPositiveDetection ($sCurIP, $sNote);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @see BxAntispamIP::isIpWhitelisted
+     */
+    public function serviceIsIpWhitelisted($sIp = '')
+    {
+        $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
+        return $o->isIpWhitelisted($sIp);
+    }
+
+    /**
+     * @see BxAntispamIP::isIpBlocked
+     */
+    public function serviceIsIpBlocked($sIp = '')
+    {
+        $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
+        return $o->isIpBlocked($sIp);
+    }
+
+    /**
+     * @see BxAntispamIP::blockIp
+     */
+    public function serviceBlockIp($mixedIP, $iExpirationInSec = 86400, $sComment = '')
+    {
+        $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
+        return $o->blockIp($mixedIP, $iExpirationInSec, $sComment);
+    }
+
+    /**
+     * @see BxAntispamIP::pruning
+     */
+    public function servicePruning()
+    {
+        $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
+        return $o->pruning();
+    }
+
+    /**
+     * @see BxAntispamIP::pruning
+     */
+    public function serviceConfigValues($s)
+    {
+        switch ($s) {
+            case 'ip_table':
+                $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
+                return $o->getIpTableConfigValues();
+            case 'dnsbl_login':
+                $o = bx_instance('BxAntispamDNSBlacklists', array(), $this->_aModule);
+                return $o->getDNSBLConfigValues();
+            case 'dnsbl_join':
+                $o = bx_instance('BxAntispamDNSURIBlacklists', array(), $this->_aModule);
+                return $o->getURIDNSBLConfigValues();
+            case 'disposable_email_domains_join':
+                $o = bx_instance('BxAntispamDisposableEmailDomains', array(), $this->_aModule);
+                return $o->getJoinBehaviourValues();                
+            case 'disposable_email_domains_mode':
+                $o = bx_instance('BxAntispamDisposableEmailDomains', array(), $this->_aModule);
+                return $o->getJoinBehaviourModes();                
+        }
+    }
+    
+    /**
+     * @return array with avaliable dictionaries languages
+     */
+    public function serviceGetProfanityFilterDicts ()
+    {
+        $oProfanityFilter = bx_instance('BxAntispamProfanityFilter', array(), $this->_aModule);
+        return $oProfanityFilter->getDicts(); 
+    }
+
+    /**
+     * @return array with avaliable toxicity filter actions
+     */
+    public function serviceGetToxicityFilterActions ()
+    {
+        return [
+            'none' => _t('_bx_antispam_option_toxicity_filter_action_none'),
+            'block' => _t('_bx_antispam_option_toxicity_filter_action_block'),
+            'disapprove' => _t('_bx_antispam_option_toxicity_filter_action_disapprove'),
+        ];
+    }
+
+    /**
+     * @return array with avaliable Lasso Moderation actions
+     */
+    public function serviceGetLassoModerationActions ()
+    {
+        return [
+            'none' => _t('_bx_antispam_option_lasso_moderation_action_none'),
+            'disapprove' => _t('_bx_antispam_option_lasso_moderation_action_disapprove'),
+        ];
+    }
+
+    public function log($mixedContents, $sSection = '', $sTitle = '')
+    {
+        if(is_array($mixedContents))
+            $mixedContents = var_export($mixedContents, true);	
+        else if(is_object($mixedContents))
+            $mixedContents = json_encode($mixedContents);
+
+        if(empty($sSection))
+            $sSection = "Core";
+
+        $sTitle .= "\n";
+
+        bx_log('bx_antispam', ":\n[" . $sSection . "] " . $sTitle . $mixedContents);
+    }
+
+    protected function getErrorMessageIpBlocked ()
+    {
+        bx_import('BxDolLanguages');
+        return _t('_bx_antispam_ip_blocked', $this->getErrorMessageSubmitFalsePositiveReport());
+    }
+
+    protected function getErrorMessageSpam ()
+    {
+        bx_import('BxDolLanguages');
+        return _t('_bx_antispam_spam_detected', $this->getErrorMessageSubmitFalsePositiveReport());
+    }
+
+    protected function getErrorMessageSubmitFalsePositiveReport ()
+    {
+        if (BxDolRequest::serviceExists('bx_contact', 'get_contact_page_url') && ($sUrl = BxDolService::call('bx_contact', 'get_contact_page_url')))
+            return _t('_bx_antispam_submit_false_positive_report', $sUrl);
+        return '';
+    }
+
+    protected function _grid ($sObjectGrid)
+    {
+        $oGrid = BxDolGrid::getObjectInstance($sObjectGrid);
+        return $oGrid ? $oGrid->getCode() : '';
+    }
+}
+
+/** @} */

+ 68 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/source/install/config.php

@@ -0,0 +1,68 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Antispam Antispam
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_antispam',
+    'title' => 'Antispam',
+    'note' => 'Antispam tools',
+    'version' => '15.0.0',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/antispam/',
+    'home_uri' => 'antispam',
+
+    'db_prefix' => 'bx_antispam_',
+    'class_prefix' => 'BxAntispam',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Antispam',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 233 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/source/install/langs/en.xml

@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+
+	<string name="_bx_antispam"><![CDATA[Antispam]]></string>
+    <string name="_bx_antispam_help"><![CDATA[Help]]></string>
+    <string name="_bx_antispam_help_text">
+      <![CDATA[This module allows to limit spam on the site, there are different build-in tools for this:
+<ul>
+    <li><b>IP table</b> - manual IP blocking/whitelisting</li>
+    <li><b>DNSBL</b> - automatic blocking by IP from different block lists</li>
+    <li><b>URI DNSBL</b> - automatic blocking of spam content by spam URLs in the text</li>
+    <li><b>Akismet</b> - automatic spam content blocking using <a target="_blank" href="http://akismet.com">Akismet service</a></li>
+    <li><b>StopForumSpam</b> - automatic spam content blocking using <a target="_blank" href="http://www.stopforumspam.com">StopForumSpam service</a></li>
+    <li><b>Profanity filter</b> - profanity filter, based on a predefined list of words with the ability to add to words this list</a></li>
+    <li><b>Toxicity filter</b> - toxicity filter, based on a <a target="_blank" href="https://www.perspectiveapi.com/">Perspective API</a> provided by Google</li>
+    <li><b>Harmful filter</b> - harmfulness filter, based on a <a target="_blank" href="https://www.lassomoderation.com/">Lasso Moderation API</a></li>
+</ul>
+]]></string>
+    <string name="_bx_antispam_help_dnsbl"><![CDATA[
+DNSBL - <a href="http://en.wikipedia.org/wiki/DNSBL">Domain Name Service Block Lists</a>.
+This functionality allows to block by IP address if it exists in one of available blocklists.
+<br /><br />
+A URI DNSBL is a DNSBL that lists the domain names and IP addresses which are found in the "clickable" links contained in the body of spams, but generally not found inside legitimate messages.
+This antispam method scan submitted content for the URLs and check them if any of them is a link to spam site. If such URL detected in the text then content is not submitted.
+<br /><br />
+<button class="bx-btn" style="float:none;" onclick ="$('.bx-popup-applied:visible').dolPopupHide();">Close</button>
+]]></string>
+    <string name="_bx_antispam_dnsbl_status"><![CDATA[
+<table style="width:100%;"><tr>
+    <td>DNSBL: {0}</td>
+    <td>URI DNSBL: {1}</td>
+    <td>Login behavior: {2}</td>
+    <td>Join behavior: {3}</td>
+</tr></table>
+]]></string>
+    <string name="_bx_antispam_ip_table_status"><![CDATA[IP table mode: {0}]]></string>
+    <string name="_bx_antispam_ip_blocked"><![CDATA[IP address is blocked. {0}]]></string>
+    <string name="_bx_antispam_spam_detected"><![CDATA[Spam detected. {0}]]></string>
+    <string name="_bx_antispam_submit_false_positive_report"><![CDATA[If you believe that this is a mistake, please submit a false positive report here: {0}]]></string>
+	<string name="_bx_antispam_ip_allow"><![CDATA[Allow]]></string>
+	<string name="_bx_antispam_ip_deny"><![CDATA[Deny]]></string>
+	<string name="_bx_antispam_ip_table"><![CDATA[IP table]]></string>
+	<string name="_bx_antispam_dnsbl_list"><![CDATA[DNSBL list]]></string>
+	<string name="_bx_antispam_block_log"><![CDATA[Block log]]></string>
+	<string name="_bx_antispam_chain_whitelist"><![CDATA[IP whitelist]]></string>
+	<string name="_bx_antispam_chain_spammers"><![CDATA[IP blacklist]]></string>
+	<string name="_bx_antispam_chain_uridns"><![CDATA[Spam text]]></string>
+	<string name="_bx_antispam_rule_note_country"><![CDATA[Country: {0}]]></string>
+	<string name="_bx_antispam_rule_note_spamhaus_org"><![CDATA[Detect spammy IPs: to use the service for free, your server must use its own private DNS server and have a reverse PTR record for its public IP.]]></string>
+	<string name="_bx_antispam_rule_note_zomgbl_spameatingmonkey_net"><![CDATA[Everything is blacklisted, <u>use it only if something is whitelisted!</u>]]></string>
+	<string name="_bx_antispam_rule_note_surbl_org"><![CDATA[Detect spammer URLs from SURBLs (lists of web sites that have appeared in unsolicited messages)]]></string>
+	<string name="_bx_antispam_rule_note_dnsbl_tornevall_org"><![CDATA[Detect anonymous/elite proxies and abuse IPs]]></string>
+	<string name="_bx_antispam_dnsbl"><![CDATA[DNSBL]]></string>
+	<string name="_bx_antispam_uri_dnsbl"><![CDATA[URI DNSBL]]></string>
+	<string name="_bx_antispam_type_dnsbl"><![CDATA[DNSBL]]></string>
+	<string name="_bx_antispam_type_dnsbluri"><![CDATA[URI DNSBL]]></string>
+	<string name="_bx_antispam_type_akismet"><![CDATA[Akismet]]></string>
+	<string name="_bx_antispam_type_stopforumspam"><![CDATA[StopForumSpam]]></string>
+	<string name="_bx_antispam_ip_table_disabled"><![CDATA[Disabled]]></string>
+	<string name="_bx_antispam_ip_table_all_allowed_except_listed"><![CDATA[All allowed except listed]]></string>
+	<string name="_bx_antispam_ip_table_all_blocked_except_listed"><![CDATA[All blocked except listed]]></string>
+	<string name="_bx_antispam_dnsbl_behaviour_login_block"><![CDATA[Block]]></string>
+	<string name="_bx_antispam_dnsbl_behaviour_login_log"><![CDATA[Log]]></string>
+	<string name="_bx_antispam_dnsbl_behaviour_join_block"><![CDATA[Block]]></string>
+    <string name="_bx_antispam_dnsbl_behaviour_join_approval"><![CDATA[Approval]]></string>
+	<string name="_bx_antispam_disposable_email_domains_mode_disable"><![CDATA[Disable]]></string>
+	<string name="_bx_antispam_disposable_email_domains_mode_whitelist"><![CDATA[Allow whitelisted only]]></string>
+	<string name="_bx_antispam_disposable_email_domains_mode_blacklist"><![CDATA[Disallow blacklisted]]></string>
+	<string name="_bx_antispam_disposable_email_domains_behaviour_join_block"><![CDATA[Block]]></string>
+	<string name="_bx_antispam_disposable_email_domains_behaviour_join_approval"><![CDATA[Approval]]></string>
+    <string name="_bx_antispam_disposable_email_domains_msg_blacklisted"><![CDATA[Your email domain isn't allowed for registration on this site]]></string>
+
+	<string name="_bx_antispam_popup_help"><![CDATA[DNSBL help]]></string>
+	<string name="_bx_antispam_popup_dnsbl_recheck"><![CDATA[Recheck IP/URL in DNSBL/URIDNSBL]]></string>
+	<string name="_bx_antispam_popup_dnsbl_add"><![CDATA[Add country based rule]]></string>
+	<string name="_bx_antispam_popup_block_log"><![CDATA[Block log]]></string>
+
+	<string name="_bx_antispam_page_title_sys_ip_table"><![CDATA[IP table]]></string>
+	<string name="_bx_antispam_page_title_ip_table"><![CDATA[IP table]]></string>
+	<string name="_bx_antispam_page_title_sys_dnsbl_list"><![CDATA[DNSBL list]]></string>
+	<string name="_bx_antispam_page_title_dnsbl_list"><![CDATA[DNSBL list]]></string>
+	<string name="_bx_antispam_page_title_sys_block_log"><![CDATA[Block log]]></string>
+	<string name="_bx_antispam_page_title_block_log"><![CDATA[Block log]]></string>
+
+	<string name="_bx_antispam_page_block_title_ip_table"><![CDATA[IP table]]></string>
+	<string name="_bx_antispam_page_block_title_dnsbl_list"><![CDATA[DNSBL list]]></string>
+	<string name="_bx_antispam_page_block_title_block_log"><![CDATA[Block log]]></string>
+
+	<string name="_bx_antispam_grid_action_delete"><![CDATA[Delete]]></string>
+	<string name="_bx_antispam_grid_action_add"><![CDATA[Add]]></string>
+	<string name="_bx_antispam_grid_action_log"><![CDATA[Log]]></string>
+	<string name="_bx_antispam_grid_action_recheck"><![CDATA[Re-check]]></string>
+	<string name="_bx_antispam_grid_action_help"><![CDATA[Help]]></string>
+
+	<string name="_bx_antispam_field_id"><![CDATA[ID]]></string>
+	<string name="_bx_antispam_field_ip"><![CDATA[IP]]></string>
+	<string name="_bx_antispam_field_ip_from"><![CDATA[IP from]]></string>
+	<string name="_bx_antispam_field_ip_to"><![CDATA[IP to]]></string>
+	<string name="_bx_antispam_field_action"><![CDATA[Action]]></string>
+	<string name="_bx_antispam_field_expiration"><![CDATA[Expiration]]></string>
+	<string name="_bx_antispam_field_note"><![CDATA[Note]]></string>
+	<string name="_bx_antispam_field_chain"><![CDATA[Rule]]></string>
+	<string name="_bx_antispam_field_ip_url"><![CDATA[IP for DNSBL or URL for URI DNSBL]]></string>
+	<string name="_bx_antispam_field_dnsbl_uridnsbl"><![CDATA[Check in]]></string>
+	<string name="_bx_antispam_field_profile_id"><![CDATA[Profile]]></string>
+	<string name="_bx_antispam_field_type"><![CDATA[Type]]></string>
+	<string name="_bx_antispam_field_when"><![CDATA[When]]></string>
+	<string name="_bx_antispam_field_country"><![CDATA[Country]]></string>
+	<string name="_bx_antispam_field_zonedomain"><![CDATA[ZONE domain]]></string>
+
+    <string name="_bx_antispam_form_ip_table"><![CDATA[IP table directive]]></string>
+    <string name="_bx_antispam_form_ip_table_add"><![CDATA[Add IP table directive]]></string>
+    <string name="_bx_antispam_form_ip_table_edit"><![CDATA[Edit IP table directive]]></string>
+    <string name="_bx_antispam_form_buttons"><![CDATA[Form buttons]]></string>
+
+    <string name="_bx_antispam_field_ip_err_msg"><![CDATA[Please enter correct IP address, for example 192.168.4.5]]></string>
+    <string name="_bx_antispam_field_expiration_err_msg"><![CDATA[Please enter correct date time value]]></string>
+
+    <string name="_bx_antispam_data_list_ip_table_actions"><![CDATA[IP table actions]]></string>
+
+    <string name="_bx_antispam_adm_stg_cpt_type"><![CDATA[Antispam]]></string>
+    <string name="_bx_antispam_adm_stg_cpt_category_general"><![CDATA[General]]></string>
+    <string name="_bx_antispam_adm_stg_cpt_category_ip_table"><![CDATA[IP table]]></string>
+    <string name="_bx_antispam_adm_stg_cpt_category_dnsbl"><![CDATA[DNSBL]]></string>
+    <string name="_bx_antispam_adm_stg_cpt_category_akismet"><![CDATA[Akismet]]></string>
+    <string name="_bx_antispam_adm_stg_cpt_category_stopforumspam"><![CDATA[StopForumSpam]]></string>
+    <string name="_bx_antispam_adm_stg_cpt_category_disposable_email_domains"><![CDATA[Disposable email domains]]></string>
+    <string name="_bx_antispam_adm_stg_cpt_category_profanity_filter"><![CDATA[Profanity filter]]></string>
+    
+	<string name="_bx_antispam_option_block"><![CDATA[Total block all spam content]]></string>
+	<string name="_bx_antispam_option_report"><![CDATA[Send report to admin if spam content discovered]]></string>
+	<string name="_bx_antispam_option_ip_list_type"><![CDATA[IP table mode]]></string>
+	<string name="_bx_antispam_option_dnsbl_enable"><![CDATA[Enable DNS Block Lists]]></string>
+	<string name="_bx_antispam_option_dnsbl_behaviour_login"><![CDATA[User login behaviour if listed in DNS Block Lists]]></string>
+	<string name="_bx_antispam_option_dnsbl_behaviour_join"><![CDATA[User join behaviour if listed in DNS Block Lists]]></string>
+	<string name="_bx_antispam_option_uridnsbl_enable"><![CDATA[Enable URI DNS Block Lists]]></string>
+	<string name="_bx_antispam_option_akismet_enable"><![CDATA[Enable Akismet]]></string>
+	<string name="_bx_antispam_option_akismet_api_key"><![CDATA[Akismet API Key]]></string>
+	<string name="_bx_antispam_option_stopforumspam_enable"><![CDATA[Enable "Stop Forum Spam"]]></string>
+    <string name="_bx_antispam_option_stopforumspam_api_key"><![CDATA["Stop Forum Spam" API Key]]></string> 
+	<string name="_bx_antispam_option_disposable_email_domains_mode"><![CDATA[Mode]]></string>
+	<string name="_bx_antispam_option_disposable_email_domains_behaviour_join"><![CDATA[Join behaviour]]></string>
+  <string name="_bx_antispam_option_profanity_filter_enable"><![CDATA[Enable Profanity filter]]></string>
+  <string name="_bx_antispam_option_profanity_filter_dicts"><![CDATA[Dictionaries]]></string>
+  <string name="_bx_antispam_option_profanity_filter_bad_words_list"><![CDATA[Bad words list (comma separated)]]></string>
+  <string name="_bx_antispam_option_profanity_filter_char_replace"><![CDATA[Char to replace]]></string>
+  <string name="_bx_antispam_option_profanity_filter_white_words_list"><![CDATA[White words list (comma separated)]]></string>
+  <string name="_bx_antispam_option_profanity_filter_full_words_only"><![CDATA[Full words only]]></string>
+
+  <string name="_bx_antispam_spam_report_name"><![CDATA[Spam report message]]></string>
+    <string name="_bx_antispam_spam_report_subject"><![CDATA[Automatic spam report]]></string>
+    <string name="_bx_antispam_spam_report_body"><![CDATA[{email_header}
+<b>Profile:</b> <a href="{SpammerUrl}">{SpammerNickName}</a><br />
+
+<b>Page:</b> {Page}<br /><br />
+
+<b>GET variables:</b>
+<pre>
+{Get}
+</pre>
+
+<b>POST variables:</b>
+<pre>
+{Post}
+</pre>
+
+<b>Spam content:</b>
+<pre>
+{SpamContent}
+</pre>
+{email_footer}]]></string>
+
+	<string name="_bx_antispam_type_toxicity_filter"><![CDATA[Toxicity Filter]]></string>
+	<string name="_bx_antispam_adm_stg_cpt_category_toxicity_filter"><![CDATA[Toxicity filter]]></string>
+	<string name="_bx_antispam_option_toxicity_filter_enable"><![CDATA[Enable toxicity filter]]></string>
+	<string name="_bx_antispam_option_toxicity_filter_action"><![CDATA[Action]]></string>
+	<string name="_bx_antispam_option_toxicity_filter_action_none"><![CDATA[None]]></string>
+	<string name="_bx_antispam_option_toxicity_filter_action_block"><![CDATA[Block all toxic content]]></string>
+	<string name="_bx_antispam_option_toxicity_filter_action_disapprove"><![CDATA[Disapprove all toxic content if possible, block the rest]]></string>
+	<string name="_bx_antispam_option_toxicity_report"><![CDATA[Send report to admin if toxic content discovered]]></string>
+	<string name="_bx_antispam_option_toxicity_filter_api_key"><![CDATA[Perspective API key (see <a href="https://developers.perspectiveapi.com/s/docs-get-started">this</a> page on how obtain it)]]></string>
+	<string name="_bx_antispam_option_toxicity_filter_threshold"><![CDATA[Toxicity threshold to consider a text as toxic (0 - 100)]]></string>
+	<string name="_bx_antispam_form_submission_error"><![CDATA[Incorrect info. The form seems to contain some toxic content. Please, check your inputs and try to submit again.]]></string>
+
+	<string name="_bx_antispam_toxicity_blocked_report_name"><![CDATA[Toxicity blocked report message]]></string>
+    <string name="_bx_antispam_toxicity_blocked_report_subject"><![CDATA[Toxic content has been blocked]]></string>
+    <string name="_bx_antispam_toxicity_blocked_report_body"><![CDATA[{email_header}
+<b>Profile:</b> <a href="{AuthorUrl}">{AuthorNickName}</a><br />
+<b>Page:</b> {Page}<br /><br />
+<b>Content:</b>
+<pre>
+{Content}
+</pre>
+{email_footer}]]></string>
+
+	<string name="_bx_antispam_toxicity_posted_report_name"><![CDATA[Toxicity posted report message]]></string>
+    <string name="_bx_antispam_toxicity_posted_report_subject"><![CDATA[Toxic content has been posted]]></string>
+    <string name="_bx_antispam_toxicity_posted_report_body"><![CDATA[{email_header}
+<b>Profile:</b> <a href="{AuthorUrl}">{AuthorNickName}</a><br />
+<b>Page:</b> {Page}<br /><br />
+<bx_if:content_url>
+	<b>Content:</b> <a href="{c_url}">{c_url}</a><br /><br />
+</bx_if:content_url>
+<bx_if:manage_content_url>
+	<b>Manage:</b> <a href="{m_url}">{m_url}</a><br /><br />
+</bx_if:manage_content_url>
+{email_footer}]]></string>
+
+    <string name="_bx_antispam_adm_stg_cpt_category_lasso_moderation"><![CDATA[Lasso Moderation]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_enable"><![CDATA[Enable]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_api_key"><![CDATA[API key]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_webhook_secret"><![CDATA[Webhook Secret]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_webhook_url"><![CDATA[Webhook URL]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_action"><![CDATA[Action]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_action_none"><![CDATA[None]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_action_disapprove"><![CDATA[Disapprove]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_report"><![CDATA[Send report to admin if harmful content discovered]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_threshold_toxicity"><![CDATA[Toxicity threshold (0 - 100)]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_threshold_threat"><![CDATA[Threat threshold (0 - 100)]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_threshold_identity_attack"><![CDATA[Identity attack threshold (0 - 100)]]></string>
+    <string name="_bx_antispam_option_lasso_moderation_threshold_profanity"><![CDATA[Profanity threshold (0 - 100)]]></string>
+    <string name="_bx_antispam_et_lasso_moderation_report_name"><![CDATA[Harmful content posted report message]]></string>
+    <string name="_bx_antispam_et_lasso_moderation_report_subject"><![CDATA[Harmful content has been posted]]></string>
+    <string name="_bx_antispam_et_lasso_moderation_report_body"><![CDATA[{email_header}
+<b>Profile:</b> <a href="{AuthorUrl}">{AuthorNickName}</a><br />
+<bx_if:content_url>
+	<b>Content:</b> <a href="{c_url}">{c_url}</a><br /><br />
+</bx_if:content_url>
+<bx_if:manage_content_url>
+	<b>Manage:</b> <a href="{m_url}">{m_url}</a><br /><br />
+</bx_if:manage_content_url>
+{email_footer}]]></string>
+</resources>

+ 73 - 0
modules/boonex/antispam/updates/14.0.2_15.0.0/source/install/sql/install.sql

@@ -0,0 +1,73 @@
+
+CREATE TABLE `bx_antispam_ip_table` (
+  `ID` int(11) NOT NULL auto_increment,
+  `From` int(10) unsigned NOT NULL,
+  `To` int(10) unsigned NOT NULL,
+  `Type` enum('allow','deny') NOT NULL default 'deny',
+  `LastDT` int(11) unsigned NOT NULL,
+  `Desc` varchar(128) NOT NULL,
+  PRIMARY KEY  (`ID`),
+  KEY `From` (`From`),
+  KEY `To` (`To`)
+);
+
+
+CREATE TABLE `bx_antispam_dnsbl_rules` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `chain` enum('spammers','whitelist','uridns') NOT NULL,
+  `zonedomain` varchar(255) NOT NULL,
+  `postvresp` varchar(255) NOT NULL,
+  `url` varchar(255) NOT NULL,
+  `recheck` varchar(255) NOT NULL,
+  `comment` varchar(255) NOT NULL,
+  `added` int(11) NOT NULL,
+  `active` tinyint(4) NOT NULL,
+  PRIMARY KEY (`id`)
+);
+
+INSERT INTO `bx_antispam_dnsbl_rules` (`chain`, `zonedomain`, `postvresp`, `url`, `recheck`, `comment`, `added`, `active`) VALUES
+('spammers', 'sbl.spamhaus.org.', '127.0.0.2,127.0.0.3,127.0.0.4,127.0.0.9', 'http://www.spamhaus.org/sbl/', 'https://check.spamhaus.org/results/?query=%s', '_bx_antispam_rule_note_spamhaus_org', 0, 1),
+('spammers', 'dnsbl.tornevall.org.', '230', 'http://dnsbl.tornevall.org/', '', '_bx_antispam_rule_note_dnsbl_tornevall_org', 0, 0),
+('uridns', 'multi.surbl.org.', 'any', 'http://www.surbl.org/', 'https://surbl.org/surbl-analysis', '_bx_antispam_rule_note_surbl_org', 0, 1),
+('spammers', 'zomgbl.spameatingmonkey.net.', 'any', 'http://spameatingmonkey.com/index.html', '', '_bx_antispam_rule_note_zomgbl_spameatingmonkey_net', 0, 0);
+
+CREATE TABLE `bx_antispam_dnsbluri_zones` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `level` tinyint(4) NOT NULL,
+  `zone` varchar(64) NOT NULL,
+  PRIMARY KEY (`id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_antispam_disposable_email_domains` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `domain` varchar(255) NOT NULL,
+  `list` enum('blacklist','custom_blacklist','whitelist','custom_whitelist') NOT NULL DEFAULT 'custom_blacklist',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `domain` (`domain`(191))
+);
+
+CREATE TABLE `bx_antispam_block_log` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `ip` int(10) unsigned NOT NULL,
+  `profile_id` int(10) unsigned NOT NULL,
+  `type` varchar(32) NOT NULL,
+  `extra` text NOT NULL,
+  `added` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `ip` (`ip`),
+  KEY `profile_id` (`profile_id`)
+);
+
+-- Studio page and widget
+
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_antispam', '_bx_antispam', '_bx_antispam', 'bx_antispam@modules/boonex/antispam/|std-icon.svg');
+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`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_antispam', 'extensions', '{url_studio}module.php?name=bx_antispam', '', 'bx_antispam@modules/boonex/antispam/|std-icon.svg', '_bx_antispam', '', '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));
+

+ 51 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Channels',
+    'version_from' => '15.0.0',
+    'version_to' => '15.0.1',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/channels/updates/update_15.0.0_15.0.1/',
+    'home_uri' => 'channels_update_1500_1501',
+
+    'module_dir' => 'boonex/channels/',
+    'module_uri' => 'channels',
+
+    'db_prefix' => 'bx_cnl_',
+    'class_prefix' => 'BxCnl',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Channels',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxCnlUpdater extends BxDolStudioUpdater
+{
+    public function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 12 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/install/sql/enable.sql

@@ -0,0 +1,12 @@
+SET @sName = 'bx_channels';
+
+
+-- MENUS
+UPDATE `sys_menu_items` SET `icon`='hashtag' WHERE `set_name`='sys_site' AND `name`='channels-home' AND `icon`='hashtag col-red2';
+UPDATE `sys_menu_items` SET `icon`='hashtag' WHERE `set_name`='sys_homepage' AND `name`='channels-home' AND `icon`='hashtag col-red2';
+UPDATE `sys_menu_items` SET `icon`='hashtag' WHERE `set_name`='sys_profile_followings' AND `name`='channels' AND `icon`='hashtag col-red2';
+UPDATE `sys_menu_items` SET `icon`='hashtag' WHERE `set_name`='trigger_profile_view_submenu' AND `name`='channels-author' AND `icon`='hashtag col-red2';
+UPDATE `sys_menu_items` SET `icon`='hashtag' WHERE `set_name` LIKE '%_view_submenu' AND `name`='channels-author' AND `icon`='hashtag col-red2';
+
+-- STATS
+UPDATE `sys_statistics` SET `icon`='hashtag' WHERE `name`='bx_channels' AND `icon`='hashtag col-red2';

+ 203 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/source/classes/BxCnlConfig.php

@@ -0,0 +1,203 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defdroup    Channels Channels
+ * @indroup     UnaModules
+ *
+ * @{
+ */
+
+class BxCnlConfig extends BxBaseModGroupsConfig
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_aMenuItems2MethodsActions = array_merge($this->_aMenuItems2MethodsActions, array(
+            'view-channel-profile' => 'checkAllowedView',
+            'edit-channel-profile' => 'checkAllowedEdit',
+            'edit-channel-cover' => 'checkAllowedChangeCover',
+            'invite-to-channel' => 'checkAllowedInvite',
+            'delete-channel-profile' => 'checkAllowedDelete',
+        ));
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'hashtag',
+
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'data',
+            'TABLE_ENTRIES_FULLTEXT' => 'search_fields',
+            'TABLE_CONTENT' => $aModule['db_prefix'] . 'content',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => 'author',
+            'FIELD_ADDED' => 'added',
+            'FIELD_CHANGED' => 'changed',
+            'FIELD_NAME' => 'channel_name',
+            'FIELD_TITLE' => 'channel_name',
+            'FIELD_PICTURE' => 'picture',
+            'FIELD_COVER' => 'cover',
+            'FIELD_COVER_POSITION' => 'cover_data',
+            'FIELD_ALLOW_VIEW_TO' => 'allow_view_to',
+            'FIELD_CF' => 'cf',
+            'FIELD_VIEWS' => 'views',
+            'FIELD_VOTES' => 'votes',
+            'FIELD_REACTIONS' => 'rvotes',
+            'FIELD_SCORE' => 'score',
+            'FIELD_SCORE_UP' => 'sc_up',
+            'FIELD_SCORE_DOWN' => 'sc_down',
+            'FIELD_COMMENTS' => 'comments',
+            'FIELD_STATUS' => 'status',
+            'FIELD_STATUS_ADMIN' => 'status_admin',
+            'FIELDS_QUICK_SEARCH' => array('channel_name'),
+            'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
+
+            // page URIs
+            'URI_VIEW_ENTRY' => 'view-channel-profile',
+            'URI_ADD_ENTRY' => 'create-channel-profile',
+            'URI_EDIT_ENTRY' => 'edit-channel-profile',
+            'URI_JOINED_ENTRIES' => 'channels-author',
+
+            'URL_HOME' => 'page.php?i=channels-home',
+            'URL_MANAGE_COMMON' => 'page.php?i=channels-manage',
+            'URL_MANAGE_ADMINISTRATION' => 'page.php?i=channels-administration',
+
+            // some params
+            'PARAM_USE_COVER_AS_THUMB' => 'bx_channels_use_cover_as_thumb',
+            'PARAM_DEFAULT_AUTHOR' => 'bx_channels_default_author',
+            'PARAM_NUM_RSS' => 'bx_channels_num_rss',
+            'PARAM_SEARCHABLE_FIELDS' => 'bx_channels_searchable_fields',
+            'PARAM_PER_PAGE_BROWSE_SHOWCASE' => 'bx_channels_per_page_browse_showcase',
+            'PARAM_PER_PAGE_BROWSE_RECOMMENDED' => 'bx_channels_per_page_browse_recommended',
+            'PARAM_BROWSE_ACTIVE_N_POSTS' => 'bx_channels_browse_active_n_posts',
+            'PARAM_BROWSE_ACTIVE_X_HOURS' => 'bx_channels_browse_active_x_hours',
+            'PARAM_BROWSE_TRENDING_X_HOURS' => 'bx_channels_browse_trending_x_hours',
+
+            // objects
+            'OBJECT_STORAGE' => 'bx_channels_pics',
+            'OBJECT_STORAGE_COVER' => 'bx_channels_pics',
+            'OBJECT_IMAGES_TRANSCODER_THUMB' => 'bx_channels_thumb',
+            'OBJECT_IMAGES_TRANSCODER_ICON' => 'bx_channels_icon',
+            'OBJECT_IMAGES_TRANSCODER_AVATAR' => 'bx_channels_avatar',
+            'OBJECT_IMAGES_TRANSCODER_AVATAR_BIG' => 'bx_channels_avatar_big',
+            'OBJECT_IMAGES_TRANSCODER_PICTURE' => 'bx_channels_picture',
+            'OBJECT_IMAGES_TRANSCODER_COVER' => 'bx_channels_cover',
+            'OBJECT_IMAGES_TRANSCODER_COVER_THUMB' => 'bx_channels_cover_thumb',
+            'OBJECT_IMAGES_TRANSCODER_GALLERY' => 'bx_channels_gallery',
+            'OBJECT_VIEWS' => 'bx_channels',
+            'OBJECT_VOTES' => 'bx_channels',
+            'OBJECT_REACTIONS' => 'bx_channels_reactions',
+            'OBJECT_SCORES' => 'bx_channels',
+            'OBJECT_FAVORITES' => 'bx_channels',
+            'OBJECT_FEATURED' => 'bx_channels',
+            'OBJECT_COMMENTS' => 'bx_channels',
+            'OBJECT_REPORTS' => 'bx_channels',
+            'OBJECT_METATAGS' => 'bx_channels',
+            'OBJECT_NOTES' => 'bx_cnl_notes',
+            'OBJECT_FORM_ENTRY' => 'bx_channel',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW' => 'bx_channel_view',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW_FULL' => 'bx_channel_view_full', // for "info" tab on view channel page
+            'OBJECT_FORM_ENTRY_DISPLAY_ADD' => 'bx_channel_add',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT' => 'bx_channel_edit',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT_COVER' => 'bx_channel_edit_cover',
+            'OBJECT_FORM_ENTRY_DISPLAY_DELETE' => 'bx_channel_delete',
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY' => 'bx_channels_view_actions', // actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY_MORE' => 'bx_channels_view_actions_more', // actions menu on view entry page for "more" popup
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY_ALL' => 'bx_channels_view_actions_all', // all actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_MY_ENTRIES' => 'bx_channels_my', // actions menu on profile entries page
+            'OBJECT_MENU_SUBMENU' => 'bx_channels_submenu', // main module submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY' => '',  // view entry submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY_MAIN_SELECTION' => 'channels-home', // first item in view entry submenu from main module submenu
+            'OBJECT_MENU_MANAGE_VIEW_ENTRY' => '',
+            'OBJECT_MENU_VIEW_ENTRY_META' => 'bx_channels_view_meta', // meta menu on view entry page
+            'OBJECT_MENU_SNIPPET_META' => 'bx_channels_snippet_meta', // menu for snippet meta info
+            'OBJECT_MENU_MANAGE_TOOLS' => 'bx_channels_menu_manage_tools', //manage menu in content administration tools
+            'OBJECT_PAGE_VIEW_ENTRY' => 'bx_channels_view_profile',
+            'OBJECT_PAGE_VIEW_ENTRY_CLOSED' => 'bx_channels_view_profile_closed',
+            'OBJECT_PAGE_JOINED_ENTRY' => 'bx_channels_join_profile',
+            'OBJECT_PRIVACY_VIEW' => 'bx_channels_allow_view_to',
+            'OBJECT_PRIVACY_VIEW_NOTIFICATION_EVENT' => 'bx_channels_allow_view_notification_to',
+            'OBJECT_GRID_ADMINISTRATION' => 'bx_channels_administration',
+            'OBJECT_UPLOADERS_COVER' => array('bx_channels_cover_crop'),
+            'OBJECT_UPLOADERS_PICTURE' => array('bx_channels_picture_crop'),
+      
+
+            'TRIGGER_MENU_PROFILE_VIEW_SUBMENU' => 'trigger_group_view_submenu',
+            'TRIGGER_MENU_PROFILE_SNIPPET_META' => 'trigger_group_snippet_meta',
+            'TRIGGER_MENU_PROFILE_VIEW_ACTIONS' => 'trigger_group_view_actions',
+            'TRIGGER_PAGE_VIEW_ENTRY' => 'trigger_page_group_view_entry',
+
+            // menu items which visibility depends on custom visibility checking
+            'MENU_ITEM_TO_METHOD' => array (
+                'bx_channels_view_actions' => $this->_aMenuItems2MethodsActions,
+                'bx_channels_view_actions_more' => $this->_aMenuItems2MethodsActions,
+            ),
+
+            // informer messages
+            'INFORMERS' => array (
+                'status' => array (
+                    'name' => 'bx-channels-status-not-active',
+                    'map' => array (
+                        BX_PROFILE_STATUS_PENDING => '_bx_channels_txt_account_pending',
+                        BX_PROFILE_STATUS_SUSPENDED => '_bx_channels_txt_account_suspended',
+                    ),
+                ),
+            ),
+
+            // some language keys
+            'T' => array (
+                'txt_sample_single' => '_bx_channels_txt_sample_single',
+                'txt_sample_single_with_article' => '_bx_channels_txt_sample_single_with_article',
+                'txt_sample_comment_single' => '_bx_channels_txt_sample_comment_single',
+                'txt_sample_vote_single' => '_bx_channels_txt_sample_vote_single',
+                'txt_sample_score_up_single' => '_bx_channels_txt_sample_score_up_single',
+                'txt_sample_score_down_single' => '_bx_channels_txt_sample_score_down_single',
+                'txt_private_group' => '_bx_channels_txt_private_channel',
+                'txt_ntfs_timeline_post_common' => '_bx_channels_txt_ntfs_timeline_post_common',
+                'form_field_author' => '_bx_channels_form_entry_input_author',
+                'menu_item_title_befriend_sent' => '_bx_channels_menu_item_title_befriend_sent',
+                'menu_item_title_unfriend_cancel_request' => '_bx_channels_menu_item_title_unfriend_cancel_request',
+                'menu_item_title_befriend_confirm' => '_bx_channels_menu_item_title_befriend_confirm',
+                'menu_item_title_unfriend_reject_request' => '_bx_channels_menu_item_title_unfriend_reject_request',
+                'menu_item_title_befriend' => '_bx_channels_menu_item_title_befriend',
+                'menu_item_title_unfriend' => '_bx_channels_menu_item_title_unfriend',
+                'grid_action_err_delete' => '_bx_channels_grid_action_err_delete',
+                'grid_txt_account_manager' => '_bx_channels_grid_txt_account_manager',
+                'filter_item_active' => '_bx_channels_grid_filter_item_title_adm_active',
+                'filter_item_hidden' => '_bx_channels_grid_filter_item_title_adm_hidden',
+                'filter_item_pending' => '_bx_channels_grid_filter_item_title_adm_pending',
+                'filter_item_suspended' => '_bx_channels_grid_filter_item_title_adm_suspended',
+                'filter_item_select_one_filter1' => '_bx_channels_grid_filter_item_title_adm_select_one_filter1',
+                'filter_item_select_one_filter2' => '_bx_channels_grid_filter_item_title_adm_select_one_filter2',
+                'menu_item_manage_my' => '_bx_channels_menu_item_title_manage_my',
+                'menu_item_manage_all' => '_bx_channels_menu_item_title_manage_all',
+                'txt_all_entries_by_author' => '_bx_channels_page_title_browse_by_author'
+            ),
+
+        );
+
+        $this->_aJsClasses = array(
+            'manage_tools' => 'BxCnlManageTools'
+        );
+
+        $this->_aJsObjects = array(
+            'manage_tools' => 'oBxCnlManageTools'
+        );
+
+        $this->_aGridObjects = array(
+            'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION']
+        );
+    }
+
+    public function isFans()
+    {
+        return false;
+    }
+}
+
+/** @} */

+ 664 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/source/classes/BxCnlModule.php

@@ -0,0 +1,664 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defdroup    Channels Channels
+ * @indroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Channels profiles module.
+ */
+
+class BxCnlModule extends BxBaseModGroupsModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    public function _modGroupsCheckAllowedSubscribeAdd(&$aDataEntry, $isPerformAction = false)
+    {
+        return parent::_modProfileCheckAllowedSubscribeAdd($aDataEntry, $isPerformAction);
+    }
+
+    /**
+     * Process Hash Tag
+     * 
+     * @param string $sHashtag - hashtag to be processed.
+     * @param string $sModuleName - module name.
+     * @param integer $iContentId - ID of the content which has the hashtag.
+     * @param integer $iSenderId - action's sender (author) id.
+     */
+    function processHashtag($sHashtag, $sModuleName, $iContentId, $iSenderId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        /*
+         * Note! For now metatag object name is used here as module name, because usually it's equal to module's name. This should be changed in Ticket #1596
+         * For now if module cannot be created then a channel for such tag shouldn't be created too.
+         */
+
+        $oModule = BxDolModule::getInstance($sModuleName);
+        if(empty($oModule) && $sModuleName != 'sys_cmts')
+            return;
+
+        $iAuthorId = 0;
+        if(($sModuleMethod = 'act_as_profile') && bx_is_srv($sModuleName, $sModuleMethod) && ($oAuthor = BxDolProfile::getInstanceByContentAndType($iContentId, $sModuleName)) !== false)
+            $iAuthorId = $oAuthor->id();
+        else if(($sModuleMethod = 'get_author') && bx_is_srv($sModuleName, $sModuleMethod))
+            $iAuthorId = abs(bx_srv($sModuleName, $sModuleMethod, [$iContentId]));
+        if(empty($iAuthorId) && !empty($iSenderId))
+            $iAuthorId = $iSenderId;
+
+        $aCheck = checkActionModule($iAuthorId, 'create channel auto', $this->getName(), false);
+        $mixedCnlId = $this->_oDb->getChannelIdByName($sHashtag);
+        if(empty($mixedCnlId) && ($aCheck[CHECK_ACTION_RESULT] == CHECK_ACTION_RESULT_ALLOWED)) {
+            $iProfileId = (int)$this->_oDb->getParam($CNF['PARAM_DEFAULT_AUTHOR']);
+            if(empty($iProfileId)) {
+                $aOperators = BxDolAccountQuery::getInstance()->getOperators();
+                if(count($aOperators) > 0)
+                    $iProfileId = BxDolProfile::getInstanceByAccount(array_shift($aOperators))->id();
+            }
+
+            $aContent = $this->serviceEntityAdd($iProfileId, array($CNF['FIELD_NAME'] => $sHashtag, $CNF['FIELD_CF'] => 1));
+            checkActionModule($iAuthorId, 'create channel auto', $this->getName(), true);
+            if(isset($aContent['content']) && isset($aContent['content']['id']))
+                $mixedCnlId = $aContent['content']['id'];
+        }
+
+        if(empty($mixedCnlId) || (int)$this->_oDb->checkContentInChannel($iContentId, $mixedCnlId, $sModuleName, $iAuthorId) != 0)
+            return;
+
+        $iId = (int)$this->_oDb->addContentToChannel($iContentId, $mixedCnlId, $sModuleName, $iAuthorId);
+        if($iId == 0)
+            return;
+
+        $oCnlProfile = BxDolProfile::getInstanceByContentAndType($mixedCnlId, $this->_oConfig->getName());
+        if(!$oCnlProfile)
+            return;
+
+        $iCnlProfileId = $oCnlProfile->id();
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_channels-hashtag_added 'bx_channels', 'hashtag_added' - hook on found hashtag in content
+         * - $unit_name - equals `bx_channels`
+         * - $action - equals `hashtag_added` 
+         * - $object_id - content_id in channel
+         * - $sender_id - channel profile_id
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `object_author_id` - [int] profile_id for content's author
+         *      - `privacy_view` - [string] equal - channel_profile_id
+         *      - `subobject_id` - [int]  content_id in channel
+         *      - `content_module` - [string] module name for content
+         *      - `content_id` - [int] content_id
+         *      - `content_author_id` - [int] equal with object_author_id
+         *      - `timeline_group` - [array] parameters for group event in timeline  ['by' => $sModuleName . '_' . $iAuthorId . '_' . $iContentId, 'field' => 'owner_id']
+         * @hook @ref hook-bx_channels-hashtag_added
+         */
+        bx_alert($this->_aModule['name'], 'hashtag_added', $iId, $iCnlProfileId, array(
+            'object_author_id' => $iAuthorId, 
+            'privacy_view' => -$iCnlProfileId,
+            'subobject_id' => $iId,
+            'content_module' => $sModuleName,
+            'content_id' => $iContentId,
+            'content_author_id' => $iAuthorId,
+            'timeline_group' => array(
+                'by' => $sModuleName . '_' . $iAuthorId . '_' . $iContentId,
+                'field' => 'owner_id'
+            )
+        ));
+
+        $aParams = [
+            'object_author_id' => $iAuthorId, 
+            'privacy_view' => -$iCnlProfileId, 
+            'subobject_id' => $iId,
+            'content_module' => $sModuleName,
+            'content_id' => $iContentId,
+            'content_author_id' => $iAuthorId
+        ];
+        /**
+         * @hooks
+         * @hookdef hook-system-prepare_alert_params 'system', 'prepare_alert_params' - hook to override alert (hook) params
+         * - $unit_name - equals `system`
+         * - $action - equals `prepare_alert_params`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `unit` - [string] unit name
+         *      - `action` - [string] equals to 'hashtag_added_notif'
+         *      - `object_id` - [int] object id
+         *      - `sender_id` - [int] action performer profile id
+         *      - `extras` - [array] by ref, extra params array as key&value pairs, can be overridden in hook processing
+         * @hook @ref hook-system-prepare_alert_params
+         */
+        bx_alert('system', 'prepare_alert_params', 0, 0, [
+            'unit'=> $this->_aModule['name'], 
+            'action' => 'hashtag_added_notif', 
+            'object_id' => $mixedCnlId, 
+            'sender_id' => $iCnlProfileId, 
+            'extras' => &$aParams,
+            'extras_ref' => &$aParams
+        ]);
+        
+        /**
+         * @hooks
+         * @hookdef hook-bx_channels-hashtag_added_notif 'bx_channels', 'hashtag_added_notif' - hook on after found hashtag in content
+         * - $unit_name - equals `bx_channels`
+         * - $action - equals `hashtag_added_notif` 
+         * - $object_id - channel id
+         * - $sender_id - channel profile_id
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `object_author_id` - [int] profile_id for content's author
+         *      - `privacy_view` - [string] equal - channel_profile_id
+         *      - `subobject_id` - [int]  content_id in channel
+         *      - `content_module` - [string] module name for content
+         *      - `content_id` - [int] content_id
+         *      - `content_author_id` - [int] equal with object_author_id
+         * @hook @ref hook-bx_channels-hashtag_added_notif
+         */
+        bx_alert($this->_aModule['name'], 'hashtag_added_notif', $mixedCnlId, $iCnlProfileId, $aParams);
+    }
+    
+    function removeContentFromChannel($iContentId, $sModuleName)
+    {
+        $oDolProfileQuery = BxDolProfileQuery::getInstance();
+        
+        $aData = $this->_oDb->getDataByContent($iContentId, $sModuleName);
+        foreach ($aData as $aRow) {
+            $iProfileInfo = $oDolProfileQuery->getProfileByContentAndType($aRow['cnl_id'], $this->_aModule['name']);
+            if(is_array($iProfileInfo)){
+                /**
+                 * @hooks
+                 * @hookdef hook-bx_channels-hashtag_deleted 'bx_channels', 'hashtag_deleted' - hook on before delete content from channel
+                 * - $unit_name - equals `bx_channels`
+                 * - $action - equals `hashtag_deleted` 
+                 * - $object_id - content id in channel
+                 * - $sender_id - channel profile_id
+                 * @hook @ref hook-bx_channels-hashtag_deleted
+                 */
+                bx_alert($this->_aModule['name'], 'hashtag_deleted', $aRow['id'], $iProfileInfo['id']);
+                 /**
+                 * @hooks
+                 * @hookdef hook-bx_channels-hashtag_deleted_notif 'bx_channels', 'hashtag_deleted_notif' - hook on before delete content from channel
+                 * - $unit_name - equals `bx_channels`
+                 * - $action - equals `hashtag_deleted_notif` 
+                 * - $object_id - channel id
+                 * - $sender_id - channel profile_id
+                 * - $extra_params - array of additional params with the following array keys:
+                 *      - `subobject_id` - [int] content id in channel
+                 * @hook @ref hook-bx_channels-hashtag_deleted_notif
+                 */
+                bx_alert($this->_aModule['name'], 'hashtag_deleted_notif', $aRow['cnl_id'], $iProfileInfo['id'], array('subobject_id' => $aRow['id']));
+            }
+        }
+        
+        return $this->_oDb->removeContentFromChannel($iContentId, $sModuleName);
+    }
+
+    public function serviceGetSafeServices()
+    {
+        $a = parent::serviceGetSafeServices();
+        unset($a['GetCreatePostForm']);
+        unset($a['EntityCreate']);
+        return array_merge($a, array (
+            'EntityBreadcrumb' => '',
+            'EntityParent' => '',
+            'EntityChilds' => '',
+            'SearchResultByHashtag' => '',
+        ));
+    }
+
+    public function serviceGetWidgetNotices()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iResult = 0;
+        if(empty($this->_oDb->getParam($CNF['PARAM_DEFAULT_AUTHOR'])))
+            $iResult += 1;
+
+        return $iResult;
+    }
+
+    public function serviceGetOptionsDefaultAuthor()
+    {
+        $aResult = array(
+            array('key' => '', 'value' => _t('_Select_one'))
+        );
+
+        $aAccountsIds = BxDolAccountQuery::getInstance()->getOperators();
+        foreach($aAccountsIds as $iAccountId) {
+            $aProfilesIds = BxDolAccount::getInstance($iAccountId)->getProfilesIds();
+            foreach($aProfilesIds as $iProfileId)
+                $aResult[] = array(
+                    'key' => $iProfileId,
+                    'value' => BxDolProfile::getInstance($iProfileId)->getDisplayName()
+                );
+        }
+
+        return $aResult;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_channels Channels
+     * @subsection bx_channels-page_blocks Page Blocks
+     * @subsubsection bx_channels-entity_breadcrumb entity_breadcrumb
+     * 
+     * @code bx_srv('bx_channels', 'entity_breadcrumb', [...]); @endcode
+     * 
+     * Display channel breadcrumb
+     * @param $iContentId channel content ID
+     * 
+     * @see BxCnlModule::serviceEntityBreadcrumb
+     */
+    /** 
+     * @ref bx_channels-entity_breadcrumb "entity_breadcrumb"
+     */
+    public function serviceEntityBreadcrumb($iContentId = 0)
+    {
+    	if(!$iContentId)
+            $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        if(!$iContentId)
+            return false;
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(!$aContentInfo)
+            return false;
+
+        return $this->_oTemplate->entryBreadcrumb($aContentInfo);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_channels Channels
+     * @subsection bx_channels-page_blocks Page Blocks
+     * @subsubsection bx_channels-entity_parent entity_parent
+     * 
+     * @code bx_srv('bx_channels', 'entity_parent', [...]); @endcode
+     * 
+     * Display block with parent channel
+     * @param $iContentId channel content ID
+     * 
+     * @see BxCnlModule::serviceEntityParent
+     */
+    /** 
+     * @ref bx_channels-entity_parent "entity_parent"
+     */
+    public function serviceEntityParent($iContentId = 0)
+    {
+        return $this->_serviceTemplateFunc('entryParent', $iContentId);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_channels Channels
+     * @subsection bx_channels-page_blocks Page Blocks
+     * @subsubsection bx_channels-entity_childs entity_childs
+     * 
+     * @code bx_srv('bx_channels', 'entity_childs', [...]); @endcode
+     * 
+     * Display block with child channels
+     * @param $iContentId channel content ID
+     * 
+     * @see BxCnlModule::serviceEntityChilds
+     */
+    /** 
+     * @ref bx_channels-entity_childs "entity_childs"
+     */
+    public function serviceEntityChilds($iContentId = 0)
+    {
+        return $this->_serviceTemplateFunc('entryChilds', $iContentId);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_channels Channels
+     * @subsection bx_channels-page_blocks Page Blocks
+     * @subsubsection bx_channels-search_result_by_hashtag search_result_by_hashtag
+     * 
+     * @code bx_srv('bx_channels', 'search_result_by_hashtag', [...]); @endcode
+     * 
+     * Display search results by hashtag for particular channel
+     * @param $iContentId channel content ID
+     * 
+     * @see BxCnlModule::serviceSearchResultByHashtag
+     */
+    /** 
+     * @ref bx_channels-search_result_by_hashtag "search_result_by_hashtag"
+     */
+    public function serviceSearchResultByHashtag($iContentId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        
+        $oSearch = new BxTemplSearch();
+        $oSearch->setLiveSearch(0);
+        $oSearch->setMetaType('keyword');
+        $aContentInfo = $this->_oDb->getContentInfoById(bx_get('id'));
+        $_GET['keyword'] = $aContentInfo[$CNF['FIELD_NAME']];
+        $sCode = $oSearch->response();
+        if (!$sCode)
+            $sCode = $oSearch->getEmptyResult();
+        
+        return $sCode;
+    }
+    
+    /**
+     * Data for Timeline module
+     */
+    public function serviceGetTimelineData()
+    {
+        $sModule = $this->_aModule['name'];
+
+        return array(
+            'handlers' => array(
+                array('group' => $sModule . '_hastag', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'hashtag_added', 'module_name' => $sModule, 'module_method' => 'get_timeline_post_hashtag', 'module_class' => 'Module',  'groupable' => 0, 'group_by' => ''),
+                array('group' => $sModule . '_hastag', 'type' => 'delete', 'alert_unit' => $sModule, 'alert_action' => 'hashtag_deleted')
+            ),
+            'alerts' => array(
+                array('unit' => $sModule, 'action' => 'hashtag_added'),
+                array('unit' => $sModule, 'action' => 'hashtag_deleted')
+            )
+        );
+    }
+
+    public function serviceGetTimelinePostAllowedView ($aEvent)
+    {
+        $sError = _t('_sys_txt_access_denied');
+
+        if(empty($aEvent) || !is_array($aEvent))
+            return $sError;
+
+        $aEventContent = $this->_oDb->getContentById($aEvent['object_id']);
+        
+        if ($aEventContent['module_name'] == 'sys_cmts')
+            return CHECK_ACTION_RESULT_ALLOWED;
+            
+        if(empty($aEventContent) || !is_array($aEventContent))
+            return $sError;
+
+        if(!BxDolRequest::serviceExists($aEventContent['module_name'], 'get_timeline_post_allowed_view'))
+            return $sError;
+
+        return BxDolService::call($aEventContent['module_name'], 'get_timeline_post_allowed_view', array(array('id' => $aEvent['id'], 'owner_id' => $aEventContent['author_id'], 'object_id' => $aEventContent['content_id'])));
+    }
+
+    public function serviceGetTimelinePostHashtag($aEvent, $aBrowseParams = [])
+    {
+        if(empty($aEvent) || !is_array($aEvent))
+            return false;
+
+        $aEventContent = $this->_oDb->getContentById($aEvent['object_id']);
+        if(empty($aEventContent) || !is_array($aEventContent))
+            return false;
+
+        $sModule = $aEventContent['module_name'];
+        $sClass = 'Module';
+        if($sModule == 'sys_cmts') {
+            $sModule = 'system';
+            $sClass = 'TemplCmtsServices';
+        }
+
+        if(!BxDolRequest::serviceExists($sModule, 'get_timeline_post', $sClass))
+            return false;
+
+        $iContentId = (int)$aEventContent['content_id'];
+        $iContentAuthor = 0;
+        if(bx_is_srv($sModule, 'get_author', $sClass))
+            $iContentAuthor = bx_srv($sModule, 'get_author', [$iContentId], $sClass);
+
+        /**
+         * Prepare fake event array (only mandatory parameters) to get
+         * necessary data (related to an 'original' event with hashtag/label) 
+         * from associated content module.
+         */
+        $iOwnerId = $aEvent['owner_id'];
+        $mixedObjectPrivacyView = BX_DOL_PG_ALL;
+        if($iContentAuthor > 0) {
+            $iOwnerId = (int)$aEventContent['author_id'];
+            if(bx_is_srv($sModule, 'get_privacy_view', $sClass)) {
+                $mixedAllowViewTo = bx_srv($sModule, 'get_privacy_view', [$iContentId]);
+                if($mixedAllowViewTo !== false)
+                    $mixedObjectPrivacyView = $mixedAllowViewTo;
+
+                if(is_numeric($mixedAllowViewTo) && (int)$mixedAllowViewTo < 0)
+                    $iOwnerId = abs($mixedAllowViewTo);
+            }
+        }
+
+        $aResult = bx_srv($sModule, 'get_timeline_post', [[
+            'owner_id' => $iOwnerId,
+            'object_id' => $iContentId,
+            'object_privacy_view' => $mixedObjectPrivacyView
+        ], $aBrowseParams], $sClass);
+
+        if(empty($aResult) || !is_array($aResult))
+            return $aResult;
+        
+        /**
+         * Note. The context shouldn't be changed therefore 
+         * use input event's context (owner_id) in returned results.
+         */
+        return array_merge($aResult, [
+            'owner_id' => $aEvent['owner_id']
+        ]);
+    }
+    
+    /**
+     * Data for Notifications module
+     */
+    public function serviceGetNotificationsData()
+    {      
+        $a = parent::serviceGetNotificationsData();
+
+        $sModule = $this->_aModule['name'];
+
+        $a['handlers'][] = array('group' => $sModule . '_hastag_notif', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'hashtag_added_notif', 'module_name' => $sModule, 'module_method' => 'get_notifications_post_hashtag', 'module_class' => 'Module');
+        $a['handlers'][] = array('group' => $sModule . '_hastag_notif', 'type' => 'delete', 'alert_unit' => $sModule, 'alert_action' => 'hashtag_deleted_notif');
+
+        $a['settings'][] = array('group' => 'hastag', 'unit' => $sModule, 'action' => 'hashtag_added_notif', 'types' => array('follow_context'));
+
+        $a['alerts'][] = array('unit' => $sModule, 'action' => 'hashtag_added_notif');
+        $a['alerts'][] = array('unit' => $sModule, 'action' => 'hashtag_deleted_notif');
+
+        return $a;
+    }
+
+    public function serviceGetNotificationsPostHashtag($aEvent)
+    {
+        if(empty($aEvent) || !is_array($aEvent))
+            return '';
+
+        $aContentEvent = $this->_oDb->getContentById($aEvent['subobject_id']);
+        if(empty($aContentEvent) || !is_array($aContentEvent))
+            return '';
+
+        $oModule = BxDolModule::getInstance($aContentEvent['module_name']);
+        if ($oModule) {
+            if (isset($oModule->_oConfig->CNF['OBJECT_PRIVACY_VIEW'])){
+                $oPrivacy = BxDolPrivacy::getObjectInstance($oModule->_oConfig->CNF['OBJECT_PRIVACY_VIEW']);
+                if (!$oPrivacy->check($aContentEvent['content_id']))
+                    return '';
+            }
+
+            $aRv = $oModule->serviceGetNotificationsPost(array(
+                'object_id' => $aContentEvent['content_id']
+            ));
+
+            $aRv['lang_key'] = '_bx_channels_ntfs_txt_subobject_added';
+            if(method_exists($oModule, 'serviceActAsProfile') && $oModule->serviceActAsProfile())
+                $aRv['lang_key'] = '_bx_channels_ntfs_txt_subobject_added_profile';
+
+            $aRv['entry_url'] = bx_absolute_url(str_replace(BX_DOL_URL_ROOT, '', $this->serviceGetLink($aContentEvent['cnl_id'])), '{bx_url_root}');
+            return $aRv;
+        }
+
+        return '';
+    }
+
+    public function serviceBrowseActive ($bDisplayEmptyMsg = false, $bAjaxPaginate = false)
+    {
+        return $this->_serviceBrowse ('active', false, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
+    }
+
+    public function serviceBrowseTrending ($bDisplayEmptyMsg = false, $bAjaxPaginate = false)
+    {
+        return $this->_serviceBrowse ('trending', false, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
+    }
+
+    public function serviceBrowseByLevel ($iLevelId = 0, $bDisplayEmptyMsg = false)
+    {
+        return $this->_serviceBrowse ('level', array('level' => $iLevelId), BX_DB_PADDING_DEF, $bDisplayEmptyMsg);
+    }
+    
+    public function serviceBrowseFollowed($iProfileId = 0, $aParams = array())
+    {
+        return $this->_serviceBrowseWithParam ('followed_entries', 'profile_id', $iProfileId, $aParams);
+    }
+
+    public function serviceBrowseAuthor($iProfileId = 0, $aParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($iProfileId))
+            $iProfileId = bx_get_logged_profile_id();
+
+        $sResult = isset($aParams['empty_message']) && (bool)$aParams['empty_message'] === true ? MsgBox(_t('_Empty')) : '';
+
+        $oConnection = BxDolConnection::getObjectInstance('sys_profiles_subscriptions');
+        if(!$oConnection)
+            return $sResult;
+
+        $aProfiles = $oConnection->getConnectedContentByType($iProfileId, $this->getName());
+
+        $aTmplVars = [];
+        foreach ($aProfiles as $iProfileId) {
+            $oProfile = BxDolProfile::getInstance($iProfileId);
+            if(!$oProfile)
+                continue;
+
+            $aTmplVars[] = [
+                'unit' => $oProfile->getUnit(0, ['template' => 'unit_wo_cover'])
+            ];
+        }
+
+        if(empty($aTmplVars) || !is_array($aTmplVars))
+            return $sResult;
+
+        return $this->_oTemplate->parseHtmlByName('my_channels.html', [
+            'bx_repeat:items' => $aTmplVars
+        ]);
+    }
+
+    public function serviceDeleteProfileFromFansAndAdmins ($iProfileId)
+    {
+        return true;
+    }
+
+    public function serviceGetFollowingChannelsNames($iProfileId)
+    {
+        if (!($oConn = BxDolConnection::getObjectInstance('sys_profiles_subscriptions')))
+            return array();
+
+        if (!($aIds = $oConn->getConnectedContent($iProfileId)))
+            return array();
+
+        $a = array();
+        foreach ($aIds as $iId) {
+            if (!($oProfile = BxDolProfile::getInstance($iId)))
+                continue;
+            if ($oProfile->getModule() != $this->getName())
+                continue;
+            $a[] = $oProfile->getDisplayName();
+        }
+        return $a;
+    }
+
+    public function checkAllowedCompose(&$aDataEntry, $isPerformAction = false)
+    {
+        return _t('_sys_txt_access_denied');
+    }
+
+    public function checkAllowedContact($aDataEntry, $isPerformAction = false)
+    {
+        return _t('_sys_txt_access_denied');
+    }
+
+    public function followLabels($sModule, $iContentId)
+    {
+        if (!getParam('bx_channels_labels_autofollow'))
+            return;
+        
+        if (!($oModuleProfiles = BxDolModule::getInstance($sModule)))
+            return;
+
+        if (!($aContentInfo = $oModuleProfiles->_oDb->getContentInfoById($iContentId)) || !($oProfile = BxDolProfile::getInstanceByContentAndType($iContentId, $sModule)))
+            return;
+
+        $CNF = &$oModuleProfiles->_oConfig->CNF;
+        
+        if (!$oModuleProfiles->serviceActAsProfile() || !isset($CNF['FIELD_LABELS']) || empty($aContentInfo[$CNF['FIELD_ID']]))
+            return;
+
+        if (!($oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS'])) || !$oMetatags->keywordsIsEnabled() || !($aLabels = $oMetatags->keywordsGet($aContentInfo[$CNF['FIELD_ID']])))
+            return;
+
+        if (!($oConn = BxDolConnection::getObjectInstance('sys_profiles_subscriptions')))
+            return;
+
+        foreach ($aLabels as $sLabel) {
+
+            if (!($iContentIdChannel = $this->_oDb->getChannelIdByName($sLabel)))
+                continue;
+
+            if (!($oProfileChannel = BxDolProfile::getInstanceByContentAndType($iContentIdChannel, 'bx_channels')))
+                continue;
+
+            $oConn->addConnection($oProfile->id(), $oProfileChannel->id());
+        }
+    }
+
+    /** Returns profile's subscribed channels  React Jot
+     *
+     * @param int $iProfileId
+     * @param array $aParams
+     * @return array
+     */
+
+    public function serviceBrowseAuthorChannels($iProfileId = 0, $aParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($iProfileId))
+            $iProfileId = bx_get_logged_profile_id();
+
+        $oConnection = BxDolConnection::getObjectInstance('sys_profiles_subscriptions');
+        $aProfile = $oConnection->getConnectedContent($iProfileId);
+        $aVars = array();
+        foreach ($aProfile as $iProfileId) {
+            $oProfile = BxDolProfile::getInstance($iProfileId);
+            if (!$oProfile || $oProfile->getModule() != $this->getName())
+                continue;
+
+            $iContentId = $oProfile->getContentId();
+            $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+
+            if (isset($aContentInfo[$CNF['FIELD_NAME']]))
+                array_push($aVars, array(
+                    'title' => $aContentInfo[$CNF['FIELD_NAME']],
+                    'link' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iContentId)),
+                    'followers' => $oConnection -> getConnectedInitiatorsCount($iProfileId),
+                    'id' => $iProfileId,
+                    'icon' => $oProfile -> getThumb()
+                ));
+        }
+
+        return $aVars;
+    }
+}
+
+/** @} */

+ 153 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/source/install/config.php

@@ -0,0 +1,153 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defdroup    Channels Channels
+ * @indroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_channels',
+    'title' => 'Channels',
+    'note' => 'Channels functionality.',
+    'version' => '15.0.1',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use channels in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/channels/',
+    'home_uri' => 'channels',
+
+    'db_prefix' => 'bx_cnl_',
+    'class_prefix' => 'BxCnl',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Channels',
+
+    /**
+     * Connections.
+     */
+    'connections' => array(
+        'sys_profiles_friends' => array ('type' => 'profiles'),
+        'sys_profiles_subscriptions' => array ('type' => 'profiles'),
+    ),
+
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+        'trigger_profile_view_submenu',
+        'trigger_group_view_submenu',
+        'trigger_group_snippet_meta',
+        'trigger_group_view_actions',
+    ),
+
+    /**
+     * Page triggers.
+     */
+    'page_triggers' => array (
+        'trigger_page_profile_view_entry',
+        'trigger_page_group_view_entry',
+    ),
+
+    /**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+        'bx_channels_pics'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+        'bx_channels_icon',
+        'bx_channels_thumb',
+        'bx_channels_avatar',
+        'bx_channels_avatar_big',
+        'bx_channels_picture',
+        'bx_channels_cover',
+        'bx_channels_cover_thumb',
+        'bx_channels_gallery'
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_channels',
+        'bx_channels_cmts',
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'process_connections' => 1,
+        'process_deleted_profiles' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+        'process_menu_triggers' => 1,
+        'process_page_triggers' => 1,
+        'process_esearches' => 1,
+        'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'unregister_transcoders' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+        'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Connections Section
+     */
+    'relations' => array(
+        'bx_timeline',
+        'bx_notifications',
+    ),
+);
+
+/** @} */

+ 266 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/source/install/langs/en.xml

@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_channels"><![CDATA[Channels]]></string>
+    <string name="_bx_channels_cmts"><![CDATA[Channels' Comments]]></string>
+
+    <string name="_bx_channels_option_default_author"><![CDATA[Default author for automatically created channels]]></string>
+    <string name="_bx_channels_option_use_cover_as_thumb"><![CDATA[Use cover image only]]></string>
+    <string name="_bx_channels_option_use_cover_as_thumb_inf"><![CDATA[After disabling don't forget to activate 'Upload Picture' field in Create/Edit channel forms.]]></string>
+    <string name="_bx_channels_option_num_connections_quick"><![CDATA[Number of items in friends block]]></string>
+    <string name="_bx_channels_option_per_page_browse"><![CDATA[Number of items per page]]></string>
+    <string name="_bx_channels_option_num_rss"><![CDATA[Number of items in RSS feed]]></string>
+    <string name="_bx_channels_option_searchable_fields"><![CDATA[Searchable fields]]></string>
+    <string name="_bx_channels_option_labels_autofollow"><![CDATA[Auto-follow channels when used as labels in profiles]]></string>
+    <string name="_bx_channels_option_browse_active_n_posts"><![CDATA[Browse Active: Min number of new posts in channel during the period of time]]></string>
+    <string name="_bx_channels_option_browse_active_x_hours"><![CDATA[Browse Active: The period of time to count new posts in channel]]></string>
+    <string name="_bx_channels_option_browse_trending_x_hours"><![CDATA[Browse Trending: The period of time to count new posts in channel]]></string>
+
+	<string name="_bx_channels_form_profile"><![CDATA[Channels]]></string>
+	<string name="_bx_channels_form_profile_display_add"><![CDATA[Add Channel]]></string>
+	<string name="_bx_channels_form_profile_display_edit"><![CDATA[Edit Channel]]></string>
+	<string name="_bx_channels_form_profile_display_edit_cover"><![CDATA[Edit Cover]]></string>
+	<string name="_bx_channels_form_profile_display_delete"><![CDATA[Delete Channel]]></string>
+	<string name="_bx_channels_form_profile_display_view"><![CDATA[View Channel]]></string>
+	<string name="_bx_channels_form_profile_display_view_full"><![CDATA[Full View Channel]]></string>
+	<string name="_bx_channels_form_profile_display_invite"><![CDATA[Invite to Channel]]></string>
+	<string name="_bx_channels_form_entry_input_author"><![CDATA[Author]]></string>
+	<string name="_bx_channels_form_profile_input_sys_do_submit"><![CDATA[Submit]]></string>
+	<string name="_bx_channels_form_profile_input_sys_allow_view_to"><![CDATA[Privacy]]></string>
+	<string name="_bx_channels_form_profile_input_allow_view_to"><![CDATA[Privacy]]></string>
+	<string name="_bx_channels_form_profile_input_allow_view_to_desc"><![CDATA[Closed - anyone can find the channel and see who's in it, only members can see posts. Secret - only members can find the channel and see posts.]]></string>
+	<string name="_bx_channels_form_profile_input_sys_initial_members"><![CDATA[Invite Members]]></string>
+	<string name="_bx_channels_form_profile_input_initial_members"><![CDATA[Invite Members]]></string>
+  	<string name="_bx_channels_form_profile_input_sys_channel_name"><![CDATA[Channel Name]]></string>
+	<string name="_bx_channels_form_profile_input_channel_name"><![CDATA[Channel Name]]></string>
+	<string name="_bx_channels_form_profile_input_channel_name_err"><![CDATA[Please, fill in this field. Channel name must be unique.]]></string>
+	<string name="_bx_channels_form_profile_input_sys_picture"><![CDATA[Channel Picture]]></string>
+	<string name="_bx_channels_form_profile_input_picture"><![CDATA[Upload New Channel Picture]]></string>
+	<string name="_bx_channels_form_profile_input_picture_err"><![CDATA[Channel picture is mandatory.]]></string>
+	<string name="_bx_channels_form_profile_input_sys_cover"><![CDATA[Cover Picture]]></string>
+	<string name="_bx_channels_form_profile_input_cover"><![CDATA[Upload New Cover Picture]]></string>
+	<string name="_bx_channels_form_profile_input_sys_delete_confirm"><![CDATA[Delete confirmation]]></string>
+	<string name="_bx_channels_form_profile_input_delete_confirm"><![CDATA[I am sure I want to delete this channel]]></string>
+	<string name="_bx_channels_form_profile_input_delete_confirm_info"><![CDATA[This action can not be undone.]]></string>
+	<string name="_bx_channels_form_profile_input_delete_confirm_error"><![CDATA[Please check this checkbox if you really want to delete channel.]]></string>
+	
+	<string name="_bx_channels_acl_action_delete_profile"><![CDATA[Delete Channel]]></string>
+	<string name="_bx_channels_acl_action_view_profile"><![CDATA[View Channel]]></string>
+	<string name="_bx_channels_acl_action_edit_any_profile"><![CDATA[Edit Any Channel]]></string>
+	<string name="_bx_channels_acl_action_delete_any_profile"><![CDATA[Delete Any Channel]]></string>
+  <string name="_bx_channels_acl_action_create_channel_auto"><![CDATA[Create Channel Auto]]></string>
+  <string name="_bx_channels_acl_action_create_channel_auto_info"><![CDATA[Automatically create new channel when new hashtag is mentioned by anyone with this level]]></string>
+ 
+  <string name="_bx_channels_page_title_sys_edit_profile"><![CDATA[Edit Channel]]></string>
+	<string name="_bx_channels_page_title_edit_profile"><![CDATA[Edit Channel]]></string>
+	<string name="_bx_channels_page_title_sys_edit_profile_cover"><![CDATA[Edit Channel Cover]]></string>
+	<string name="_bx_channels_page_title_edit_profile_cover"><![CDATA[Edit Cover]]></string>
+	<string name="_bx_channels_page_title_sys_delete_profile"><![CDATA[Delete Channel]]></string>
+	<string name="_bx_channels_page_title_delete_profile"><![CDATA[Delete Channel]]></string>
+	<string name="_bx_channels_page_title_sys_view_profile_closed"><![CDATA[View Closed Channel]]></string>
+	<string name="_bx_channels_page_title_sys_view_profile"><![CDATA[View Channel]]></string>
+	<string name="_bx_channels_page_title_view_profile"><![CDATA[{display_name}]]></string>
+	<string name="_bx_channels_page_title_browse"><![CDATA[Channels]]></string>
+	<string name="_bx_channels_page_title_browse_by_author"><![CDATA[Channels of {display_name}]]></string>
+	<string name="_bx_channels_page_title_browse_featured"><![CDATA[Featured Channels]]></string>
+	<string name="_bx_channels_page_title_browse_top"><![CDATA[Top Channels]]></string>
+        <string name="_bx_channels_page_title_browse_active"><![CDATA[Active Channels]]></string>
+        <string name="_bx_channels_page_title_browse_trending"><![CDATA[Trending Channels]]></string>
+	<string name="_bx_channels_page_title_browse_recent"><![CDATA[New Channels]]></string>
+	<string name="_bx_channels_page_title_browse_connections"><![CDATA[{0} connections]]></string>
+	<string name="_bx_channels_page_title_browse_connections_mutual"><![CDATA[{0} and {1} mutual connections]]></string>
+	<string name="_bx_channels_page_title_sys_profile_friends"><![CDATA[Friends]]></string>
+	<string name="_bx_channels_page_title_profile_friends"><![CDATA[Friends]]></string>
+	<string name="_bx_channels_page_title_sys_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_channels_page_block_title_entry_reports"><![CDATA[Reports]]></string>
+	<string name="_bx_channels_page_title_profile_comments"><![CDATA[Comments]]></string>
+	<string name="_bx_channels_page_title_sys_profile_info"><![CDATA[Channel Info]]></string>
+	<string name="_bx_channels_page_title_profile_info"><![CDATA[Info about {display_name}]]></string>
+	<string name="_bx_channels_page_title_sys_recent"><![CDATA[New Channels]]></string>
+	<string name="_bx_channels_page_title_recent"><![CDATA[New Channels]]></string>
+	<string name="_bx_channels_page_title_sys_manage"><![CDATA[Manage Own Channels]]></string>
+	<string name="_bx_channels_page_title_sys_manage_administration"><![CDATA[Manage All Channels]]></string>
+	<string name="_bx_channels_page_title_manage"><![CDATA[Manage Channels]]></string>
+	<string name="_bx_channels_page_title_sys_top"><![CDATA[Top Channels]]></string>
+	<string name="_bx_channels_page_title_top"><![CDATA[Top Channels]]></string>
+	<string name="_bx_channels_page_title_sys_entries_search"><![CDATA[Search]]></string>
+	<string name="_bx_channels_page_title_entries_search"><![CDATA[Search]]></string>
+  <string name="_bx_channels_page_title_sys_entries_toplevel"><![CDATA[Top level]]></string>
+  <string name="_bx_channels_page_title_entries_toplevel"><![CDATA[Top level]]></string>
+  <string name="_bx_channels_page_title_sys_entries_of_author"><![CDATA[Сhannels of author]]></string>
+  <string name="_bx_channels_page_title_entries_of_author"><![CDATA[Сhannels by {display_name}]]></string>
+
+    <string name="_bx_channels_page_block_title_system_profile_subscribed_me"><![CDATA[Followers]]></string>
+    <string name="_bx_channels_page_block_title_profile_subscribed_me"><![CDATA[Followers]]></string>
+    <string name="_bx_channels_page_block_title_sys_entries_actions"><![CDATA[Channels actions]]></string>
+    <string name="_bx_channels_page_block_title_entries_actions"><![CDATA[Channels actions]]></string>
+    <string name="_bx_channels_page_block_title_sys_entries_of_author"><![CDATA[Followed Сhannels]]></string>
+    <string name="_bx_channels_page_block_title_entries_of_author"><![CDATA[Followed Сhannels]]></string>
+    <string name="_bx_channels_page_block_title_favorites_of_author"><![CDATA[Favorite Channels]]></string>
+    <string name="_bx_channels_page_block_title_sys_favorites_of_author"><![CDATA[Favorite Channels]]></string>
+    <string name="_bx_channels_page_block_title_edit_profile"><![CDATA[Edit Channel]]></string>
+    <string name="_bx_channels_page_block_title_edit_profile_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_channels_page_block_title_delete_profile"><![CDATA[Delete Channel]]></string>
+    <string name="_bx_channels_page_block_title_profile_info_link"><![CDATA[About <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_channels_page_block_title_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_channels_page_block_title_system_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_channels_page_block_title_profile_description"><![CDATA[Description]]></string>
+    <string name="_bx_channels_page_block_title_profile_friends_link"><![CDATA[Friends of <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_channels_page_block_title_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_channels_page_block_title_system_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_channels_page_block_title_admins"><![CDATA[Administrators]]></string>
+    <string name="_bx_channels_page_block_title_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_channels_page_block_title_profile_comments_link"><![CDATA[Comments to <a href="{entry_link}">{title}</a>]]></string>
+    <string name="_bx_channels_page_block_title_profile_actions"><![CDATA[Actions]]></string>
+    <string name="_bx_channels_page_block_title_featured_profiles"><![CDATA[Featured Channels]]></string>
+    <string name="_bx_channels_page_block_title_latest_profiles"><![CDATA[New Channels]]></string>
+    <string name="_bx_channels_page_block_title_system_manage"><![CDATA[Manage Own Channels]]></string>
+    <string name="_bx_channels_page_block_title_system_manage_administration"><![CDATA[Manage All Channels]]></string>
+    <string name="_bx_channels_page_block_title_manage"><![CDATA[Manage Channels]]></string>
+    <string name="_bx_channels_page_block_title_top_profiles"><![CDATA[Top Channels]]></string>
+    <string name="_bx_channels_page_block_title_entry_all_actions"><![CDATA[All Actions]]></string>
+    <string name="_bx_channels_page_block_title_entry_social_sharing"><![CDATA[Share]]></string>
+    <string name="_bx_channels_page_block_title_entry_breadcrumb"><![CDATA[Breadcrumb]]></string>
+    <string name="_bx_channels_page_block_title_entry_parent"><![CDATA[Parent Channel]]></string>
+    <string name="_bx_channels_page_block_title_entry_childs"><![CDATA[Child Channels]]></string>
+    <string name="_bx_channels_page_block_title_search_form"><![CDATA[Search Form]]></string>
+    <string name="_bx_channels_page_block_title_toplevel"><![CDATA[Top level]]></string>
+    <string name="_bx_channels_page_block_title_entries_my"><![CDATA[My Followed Channels]]></string>
+    <string name="_bx_channels_page_trigger_block_title_entries_my"><![CDATA[My Followed Channels]]></string>
+    <string name="_bx_channels_page_block_title_search_results"><![CDATA[Search Results]]></string>
+    <string name="_bx_channels_page_block_title_search_results_by_hashtag"><![CDATA[Search Results By Hashtag]]></string>
+    <string name="_bx_channels_page_block_title_search_form_cmts"><![CDATA[Comments Search Form]]></string>
+    <string name="_bx_channels_page_block_title_search_results_cmts"><![CDATA[Comments Search Results]]></string>
+    <string name="_bx_channels_page_block_title_sys_featured_entries_view_showcase"><![CDATA[Featured Channels (Showcase View)]]></string>
+    <string name="_bx_channels_page_block_title_featured_entries_view_showcase"><![CDATA[Featured Channels]]></string>
+    <string name="_bx_channels_page_block_title_sys_recommended_entries_view_showcase"><![CDATA[Recommended Channels (Showcase View)]]></string>
+    <string name="_bx_channels_page_block_title_recommended_entries_view_showcase"><![CDATA[Recommended Channels]]></string>
+    <string name="_bx_channels_page_block_title_sys_active_entries"><![CDATA[Active Channels]]></string>
+    <string name="_bx_channels_page_block_title_active_entries"><![CDATA[Active Channels]]></string>
+    <string name="_bx_channels_page_block_title_sys_trending_entries"><![CDATA[Trending Channels]]></string>
+    <string name="_bx_channels_page_block_title_trending_entries"><![CDATA[Trending Channels]]></string>
+    <string name="_bx_channels_page_block_title_sys_cover_block"><![CDATA[Cover]]></string>
+    <string name="_bx_channels_page_block_title_cover_block"><![CDATA[Cover]]></string>
+
+	<string name="_bx_channels_menu_title_submenu"><![CDATA[Channels Submenu]]></string>
+        <string name="_bx_channels_menu_title_public_submenu"><![CDATA[Channels]]></string>
+	<string name="_bx_channels_menu_set_title_submenu"><![CDATA[Channels Submenu]]></string>
+	<string name="_bx_channels_menu_set_title_view_profile_actions"><![CDATA[Channel Actions]]></string>
+	<string name="_bx_channels_menu_title_view_profile_actions"><![CDATA[Channel Actions]]></string>
+	<string name="_bx_channels_menu_set_title_view_profile_actions_more"><![CDATA[Channel More Actions]]></string>
+	<string name="_bx_channels_menu_title_view_profile_actions_more"><![CDATA[Channel More Actions]]></string>
+        <string name="_bx_channels_menu_title_view_profile_meta"><![CDATA[Channel Meta]]></string>
+        <string name="_bx_channels_menu_set_title_view_profile_meta"><![CDATA[Channel Meta]]></string>
+	<string name="_bx_channels_menu_set_title_view_profile_submenu"><![CDATA[View Channel Submenu]]></string>
+	<string name="_bx_channels_menu_title_entries_my"><![CDATA[Profile Channels Action Menu]]></string>
+	<string name="_bx_channels_menu_set_title_entries_my"><![CDATA[Profile Channels Menu]]></string>
+	<string name="_bx_channels_menu_set_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+	<string name="_bx_channels_menu_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+	<string name="_bx_channels_menu_item_title_system_delete"><![CDATA[Delete]]></string>
+	<string name="_bx_channels_menu_item_title_delete"><![CDATA[Delete]]></string>
+	<string name="_bx_channels_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
+	<string name="_bx_channels_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+	<string name="_bx_channels_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
+	<string name="_bx_channels_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
+	<string name="_bx_channels_menu_item_title_system_edit_profile"><![CDATA[Edit Channel]]></string>
+	<string name="_bx_channels_menu_item_title_edit_profile"><![CDATA[Edit Channel]]></string>
+	<string name="_bx_channels_menu_item_title_system_delete_profile"><![CDATA[Delete Channel]]></string>
+	<string name="_bx_channels_menu_item_title_delete_profile"><![CDATA[Delete Channel]]></string>
+	<string name="_bx_channels_menu_item_title_system_delete_account"><![CDATA[Delete Account]]></string>
+	<string name="_bx_channels_menu_item_title_delete_account"><![CDATA[Delete Account]]></string>
+	<string name="_bx_channels_menu_item_title_system_profile_type"><![CDATA[Channel]]></string>
+	<string name="_bx_channels_menu_item_title_profile_type"><![CDATA[Channel]]></string>
+	<string name="_bx_channels_menu_item_title_system_view_profile_friends"><![CDATA[Friends]]></string>
+	<string name="_bx_channels_menu_item_title_view_profile_friends"><![CDATA[Friends]]></string>
+	<string name="_bx_channels_menu_item_title_system_view_profile_comments"><![CDATA[Comments]]></string>
+	<string name="_bx_channels_menu_item_title_view_profile_comments"><![CDATA[Comments]]></string>
+	<string name="_bx_channels_menu_item_title_system_view_profile_info"><![CDATA[Info]]></string>
+	<string name="_bx_channels_menu_item_title_view_profile_info"><![CDATA[Info]]></string>
+	<string name="_bx_channels_menu_item_title_system_view_profile_view"><![CDATA[Channel]]></string>
+	<string name="_bx_channels_menu_item_title_view_profile_view"><![CDATA[Channel]]></string>
+        <string name="_bx_channels_menu_item_title_system_view_profile_more_auto"><![CDATA[More]]></string>
+        <string name="_bx_channels_menu_item_title_view_profile_more_auto"><![CDATA[More]]></string>
+	<string name="_bx_channels_menu_item_title_system_invite"><![CDATA[Invite]]></string>
+	<string name="_bx_channels_menu_item_title_invite"><![CDATA[Invite]]></string>
+	
+	<string name="_bx_channels_menu_item_title_system_befriend"><![CDATA[Add friend]]></string>
+	<string name="_bx_channels_menu_item_title_befriend"><![CDATA[Add friend]]></string>
+	<string name="_bx_channels_menu_item_title_befriend_sent"><![CDATA[Add friend (requested)]]></string>
+	<string name="_bx_channels_menu_item_title_befriend_confirm"><![CDATA[Accept friend request]]></string>
+	<string name="_bx_channels_menu_item_title_system_unfriend"><![CDATA[Unfriend]]></string>
+	<string name="_bx_channels_menu_item_title_unfriend"><![CDATA[Unfriend]]></string>
+	<string name="_bx_channels_menu_item_title_unfriend_cancel_request"><![CDATA[Cancel friend request]]></string>
+	<string name="_bx_channels_menu_item_title_unfriend_reject_request"><![CDATA[Reject friend request]]></string>
+	
+        <string name="_bx_channels_menu_item_title_system_subscriptions"><![CDATA[Following]]></string>
+	<string name="_bx_channels_menu_item_title_system_subscribe"><![CDATA[Follow]]></string>
+	<string name="_bx_channels_menu_item_title_subscribe"><![CDATA[Follow]]></string>
+	<string name="_bx_channels_menu_item_title_system_unsubscribe"><![CDATA[Unfollow]]></string>
+	<string name="_bx_channels_menu_item_title_unsubscribe"><![CDATA[Unfollow]]></string>
+	<string name="_bx_channels_menu_item_title_system_more_actions"><![CDATA[More Actions]]></string>
+	<string name="_bx_channels_menu_item_title_more_actions"><![CDATA[]]></string>
+	<string name="_bx_channels_menu_item_title_system_friends"><![CDATA[Friends]]></string>
+	<string name="_bx_channels_menu_item_title_friends"><![CDATA[Friends]]></string>
+	<string name="_bx_channels_menu_item_title_system_manage_my_channels"><![CDATA[My channels]]></string>
+	<string name="_bx_channels_menu_item_title_manage_my_channels"><![CDATA[{addon} channels]]></string>
+	<string name="_bx_channels_menu_item_title_system_entries_recent"><![CDATA[New]]></string>
+	<string name="_bx_channels_menu_item_title_entries_recent"><![CDATA[New]]></string>
+	<string name="_bx_channels_menu_item_title_system_entries_search"><![CDATA[Search]]></string>
+	<string name="_bx_channels_menu_item_title_entries_search"><![CDATA[Search]]></string>
+        <string name="_bx_channels_menu_item_title_system_entries_toplevel"><![CDATA[Top level]]></string>
+        <string name="_bx_channels_menu_item_title_entries_toplevel"><![CDATA[Top level]]></string>
+        <string name="_bx_channels_menu_item_title_system_entries_manage"><![CDATA[Manage]]></string>
+	<string name="_bx_channels_menu_item_title_entries_manage"><![CDATA[Manage]]></string>
+	<string name="_bx_channels_menu_item_title_system_entries_home"><![CDATA[Channels]]></string>
+	<string name="_bx_channels_menu_item_title_entries_home"><![CDATA[Channels]]></string>
+	<string name="_bx_channels_menu_item_title_system_admt_channels"><![CDATA[Channels]]></string>
+	<string name="_bx_channels_menu_item_title_admt_channels"><![CDATA[Channels]]></string>
+	<string name="_bx_channels_menu_item_title_manage_my"><![CDATA[My Channels]]></string>
+	<string name="_bx_channels_menu_item_title_manage_all"><![CDATA[All Channels]]></string>
+	<string name="_bx_channels_menu_item_title_system_entries_top"><![CDATA[Top]]></string>
+	<string name="_bx_channels_menu_item_title_entries_top"><![CDATA[Top]]></string>
+        <string name="_bx_channels_menu_item_title_system_view_entries_author"><![CDATA[Channels]]></string>
+        <string name="_bx_channels_menu_item_title_view_entries_author"><![CDATA[Channels]]></string>
+        <string name="_bx_channels_menu_item_title_system_followings"><![CDATA[Channels]]></string>
+        <string name="_bx_channels_menu_item_title_followings"><![CDATA[Channels]]></string>
+
+	<string name="_bx_channels_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></string>
+        <string name="_bx_channels_grid_filter_item_title_adm_select_one_filter2"><![CDATA[Any Audience]]></string>
+	<string name="_bx_channels_grid_filter_item_title_adm_active"><![CDATA[Active]]></string>
+        <string name="_bx_channels_grid_filter_item_title_adm_hidden"><![CDATA[Hidden]]></string>
+	<string name="_bx_channels_grid_filter_item_title_adm_pending"><![CDATA[Pending]]></string>
+	<string name="_bx_channels_grid_filter_item_title_adm_suspended"><![CDATA[Suspended]]></string>
+	<string name="_bx_channels_grid_column_title_adm_active"><![CDATA[Active]]></string>
+	<string name="_bx_channels_grid_column_title_adm_name"><![CDATA[Name]]></string>
+	<string name="_bx_channels_grid_column_title_adm_added"><![CDATA[Created]]></string>
+	<string name="_bx_channels_grid_column_title_adm_account"><![CDATA[Account]]></string>
+	<string name="_bx_channels_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+	<string name="_bx_channels_grid_action_title_adm_delete_with_content"><![CDATA[Delete With Content]]></string>
+	<string name="_bx_channels_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
+  <string name="_bx_channels_grid_action_title_adm_audit_context"><![CDATA[Audit actions in context]]></string>
+  <string name="_bx_channels_grid_action_title_adm_audit_content"><![CDATA[Audit actions with content]]></string>
+	<string name="_bx_channels_grid_action_err_delete"><![CDATA[Cannot delete the selected channel(s).]]></string>
+	<string name="_bx_channels_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
+	
+	<string name="_bx_channels_chart_growth"><![CDATA[Channels: Growth]]></string>
+	<string name="_bx_channels_chart_growth_speed"><![CDATA[Channels: Growth Speed]]></string>
+	
+	<string name="_bx_channels_search_extended"><![CDATA[Channels Search]]></string>
+	<string name="_bx_channels_search_extended_cmts"><![CDATA[Channels Comments Search]]></string>
+
+	<string name="_bx_channels_txt_account_pending"><![CDATA[This channel is pending approval, please wait until it is activated.]]></string>
+	<string name="_bx_channels_txt_account_suspended"><![CDATA[This channel is suspended.]]></string>
+	<string name="_bx_channels_txt_sample_single"><![CDATA[channel]]></string>
+	<string name="_bx_channels_txt_sample_single_with_article"><![CDATA[a channel]]></string>
+	<string name="_bx_channels_txt_sample_comment_single"><![CDATA[comment to channel]]></string>
+	<string name="_bx_channels_txt_sample_vote_single"><![CDATA[vote to channel]]></string>
+        <string name="_bx_channels_txt_sample_score_up_single"><![CDATA[score up vote to channel]]></string>
+        <string name="_bx_channels_txt_sample_score_down_single"><![CDATA[score down vote to channel]]></string>
+        <string name="_bx_channels_msg_empty_author"><![CDATA['Default author for automatically created channels' is empty. You need to select some profile in this setting.]]></string>
+
+    <string name="_bx_channels_txt_ntfs_timeline_post_common"><![CDATA[New post <b class="bx-ntfs-content-entry">{subentry_sample}</b> in <b class="bx-ntfs-content-profile">{owner_name}</b> channel timeline]]></string>
+    <string name="_bx_channels_ntfs_txt_subobject_added"><![CDATA[New content <b class="bx-ntfs-content-entry">{entry_caption}</b> in <b class="bx-ntfs-content-profile">{owner_name}</b> channel]]></string>
+    <string name="_bx_channels_ntfs_txt_subobject_added_profile"><![CDATA[<b class="bx-ntfs-content-entry">{entry_caption}</b> was added to <b class="bx-ntfs-content-profile">{owner_name}</b> channel]]></string>
+    <string name="_bx_channels_alert_action_hashtag_added"><![CDATA[New hashtags]]></string>
+    <string name="_bx_channels_alert_action_hashtag_added_notif_follow_context"><![CDATA[New hashtags]]></string>
+    <string name="_bx_channels_alert_action_info_hashtag_added_notif_follow_context"><![CDATA[New hashtags were added or used in a space you follow.]]></string>
+</resources>

+ 444 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/source/install/sql/enable.sql

@@ -0,0 +1,444 @@
+
+-- 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', 'bx_channels', '_bx_channels', 'bx_channels@modules/boonex/channels/|std-icon.svg', IF(ISNULL(@iTypeOrder), 1, @iTypeOrder + 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_channels', '_bx_channels', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `info`, `type`, `extra`, `check`, `check_params`, `check_error`, `order`) VALUES
+('bx_channels_default_author', '', @iCategId, '_bx_channels_option_default_author', '', 'select', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:26:"get_options_default_author";}', '', '', '', 1),
+('bx_channels_use_cover_as_thumb', 'on', @iCategId, '_bx_channels_option_use_cover_as_thumb', '_bx_channels_option_use_cover_as_thumb_inf', 'checkbox', '', '', '', '', 5),
+('bx_channels_num_connections_quick', '6', @iCategId, '_bx_channels_option_num_connections_quick', '', 'digit', '', '', '', '', 10),
+('bx_channels_per_page_browse', '24', @iCategId, '_bx_channels_option_per_page_browse', '', 'digit', '', '', '', '', 11),
+('bx_channels_num_rss', '10', @iCategId, '_bx_channels_option_num_rss', '', 'digit', '', '', '', '', 12),
+('bx_channels_per_page_browse_showcase', '32', @iCategId, '_sys_option_per_page_browse_showcase', '', 'digit', '', '', '', '', 15),
+('bx_channels_per_page_browse_recommended', '10', @iCategId, '_sys_option_per_page_browse_recommended', '', 'digit', '', '', '', '', 16),
+('bx_channels_browse_active_n_posts', '1', @iCategId, '_bx_channels_option_browse_active_n_posts', '', 'digit', '', '', '', '', 20),
+('bx_channels_browse_active_x_hours', '24', @iCategId, '_bx_channels_option_browse_active_x_hours', '', 'digit', '', '', '', '', 21),
+('bx_channels_browse_trending_x_hours', '24', @iCategId, '_bx_channels_option_browse_trending_x_hours', '', 'digit', '', '', '', '', 23),
+('bx_channels_searchable_fields', 'channel_name', @iCategId, '_bx_channels_option_searchable_fields', '', 'list', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:21:"get_searchable_fields";}', '', '', '', 30),
+('bx_channels_labels_autofollow', 'on', @iCategId, '_bx_channels_option_labels_autofollow', '', 'checkbox', '', '', '', '', 40);
+
+
+-- PAGES
+
+-- PAGE: view profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_view_profile', 'view-channel-profile', '_bx_channels_page_title_sys_view_profile', '_bx_channels_page_title_view_profile', 'bx_channels', 10, 2147483647, 1, 'page.php?i=view-channel-profile', '', '', '', 0, 1, 0, 'BxCnlPageEntry', 'modules/boonex/channels/classes/BxCnlPageEntry.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_channels_view_profile', 1, 'bx_channels', '', '_bx_channels_page_block_title_entry_social_sharing', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:21:\"entity_social_sharing\";}', 0, 0, 0, 0),
+('bx_channels_view_profile', 1, 'bx_channels', '', '_bx_channels_page_block_title_entry_all_actions', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:18:\"entity_all_actions\";}', 0, 0, 0, 0),
+('bx_channels_view_profile', 1, 'bx_channels', '', '_bx_channels_page_block_title_entry_breadcrumb', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:17:"entity_breadcrumb";}', 0, 0, 1, 1),
+('bx_channels_view_profile', 2, 'bx_channels', '', '_bx_channels_page_block_title_entry_parent', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:13:"entity_parent";}', 0, 0, 1, 1),
+('bx_channels_view_profile', 3, 'bx_channels', '', '_bx_channels_page_block_title_entry_childs', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:13:"entity_childs";}', 0, 0, 1, 1),
+('bx_channels_view_profile', 4, 'bx_channels', '', '_bx_channels_page_block_title_search_results_by_hashtag', 0, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:24:\"search_result_by_hashtag\";}', 0, 0, 0, 0),
+('bx_channels_view_profile', 4, 'bx_channels', '', '_bx_channels_page_block_title_profile_comments', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:15:\"entity_comments\";}', 0, 0, 0, 1),
+('bx_channels_view_profile', 4, 'bx_channels', '', '_bx_channels_page_block_title_profile_subscribed_me', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:21:\"profile_subscribed_me\";}', 0, 1, 1, 0),
+('bx_channels_view_profile', 4, 'bx_channels', '', '_bx_channels_page_block_title_entry_reports', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:14:\"entity_reports\";}', 0, 0, 1, 6);
+
+-- PAGE: view closed profile 
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_view_profile_closed', 'view-channel-profile-closed', '_bx_channels_page_title_sys_view_profile_closed', '_bx_channels_page_title_view_profile', 'bx_channels', 10, 2147483647, 1, 'page.php?i=view-channel-profile', '', '', '', 0, 1, 0, 'BxCnlPageEntry', 'modules/boonex/channels/classes/BxCnlPageEntry.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_channels_view_profile_closed', 2, 'bx_channels', '', '_bx_channels_page_block_title_profile_info', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:11:\"entity_info\";}', 0, 0, 1, 0);
+
+-- PAGE: edit profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_edit_profile', 'edit-channel-profile', '_bx_channels_page_title_sys_edit_profile', '_bx_channels_page_title_edit_profile', 'bx_channels', 5, 2147483647, 1, 'page.php?i=edit-channel-profile', '', '', '', 0, 1, 0, 'BxCnlPageEntry', 'modules/boonex/channels/classes/BxCnlPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_channels_edit_profile', 1, 'bx_channels', '_bx_channels_page_block_title_edit_profile', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:11:\"entity_edit\";}', 0, 0, 0);
+
+
+-- PAGE: delete profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_delete_profile', 'delete-channel-profile', '_bx_channels_page_title_sys_delete_profile', '_bx_channels_page_title_delete_profile', 'bx_channels', 5, 2147483647, 1, 'page.php?i=delete-channel-profile', '', '', '', 0, 1, 0, 'BxCnlPageEntry', 'modules/boonex/channels/classes/BxCnlPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_channels_delete_profile', 1, 'bx_channels', '_bx_channels_page_block_title_delete_profile', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:13:\"entity_delete\";}', 0, 0, 0);
+
+-- PAGE: view entry comments
+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_channels_profile_comments', '_bx_channels_page_title_sys_profile_comments', '_bx_channels_page_title_profile_comments', 'bx_channels', 5, 2147483647, 1, 'channel-profile-comments', '', '', '', '', 0, 1, 0, 'BxCnlPageEntry', 'modules/boonex/channels/classes/BxCnlPageEntry.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_channels_profile_comments', 1, 'bx_channels', '_bx_channels_page_block_title_profile_comments', '_bx_channels_page_block_title_profile_comments_link', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:15:\"entity_comments\";}', 0, 0, 0, 1);
+
+-- PAGE: entries of author
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_author', 'channels-author', '_bx_channels_page_title_sys_entries_of_author', '_bx_channels_page_title_entries_of_author', 'bx_channels', 5, 2147483647, 1, '', '', '', '', 0, 1, 0, 'BxCnlPageAuthor', 'modules/boonex/channels/classes/BxCnlPageAuthor.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_channels_author', 1, 'bx_channels', '_bx_channels_page_block_title_sys_favorites_of_author', '_bx_channels_page_block_title_favorites_of_author', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:11:"bx_channels";s:6:"method";s:15:"browse_favorite";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 1, 1, 1),
+('bx_channels_author', 1, 'bx_channels', '_bx_channels_page_block_title_sys_entries_of_author', '_bx_channels_page_block_title_entries_of_author', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:11:"bx_channels";s:6:"method";s:13:"browse_author";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;a:1:{s:13:"empty_message";b:1;}}}}', 0, 0, 1, 2);
+
+-- PAGE: module home
+
+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_channels_home', '_bx_channels_page_title_sys_recent', '_bx_channels_page_title_recent', 'bx_channels', 5, 2147483647, 1, 'channels-home', 'page.php?i=channels-home', '', '', '', 0, 1, 0, 'BxCnlPageBrowse', 'modules/boonex/channels/classes/BxCnlPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('bx_channels_home', 1, 'bx_channels', '_bx_channels_page_block_title_featured_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:11:"bx_channels";s:6:"method";s:15:"browse_featured";s:6:"params";a:1:{i:0;s:7:"gallery";}}', 0, 1, 1, 0),
+('bx_channels_home', 1, 'bx_channels', '_bx_channels_page_block_title_latest_profiles', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:22:\"browse_recent_profiles\";s:6:"params";a:1:{i:0;b:1;}}', 0, 1, 1, 1),
+('bx_channels_home', 1, 'bx_channels', '_bx_channels_page_block_title_entries_my', 11, 2147483646, 'service', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:13:"browse_author";}',  0, 1, 0, 0);
+
+-- PAGE: top profiles
+
+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_channels_top', '_bx_channels_page_title_sys_top', '_bx_channels_page_title_top', 'bx_channels', 5, 2147483647, 1, 'channels-top', 'page.php?i=channels-top', '', '', '', 0, 1, 0, 'BxCnlPageBrowse', 'modules/boonex/channels/classes/BxCnlPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_channels_top', 1, 'bx_channels', '_bx_channels_page_block_title_top_profiles', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:19:\"browse_top_profiles\";s:6:"params";a:1:{i:0;b:1;}}', 0, 1, 0);
+
+-- PAGE: top level entries
+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_channels_toplevel', '_bx_channels_page_title_sys_entries_toplevel', '_bx_channels_page_title_entries_toplevel', 'bx_channels', 5, 2147483647, 1, 'channels-toplevel', 'page.php?i=toplevel', '', '', '', 0, 1, 0, 'BxCnlPageBrowse', 'modules/boonex/channels/classes/BxCnlPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_channels_toplevel', 1, 'bx_channels', '_bx_channels_page_block_title_toplevel', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:15:\"browse_by_level\";s:6:"params";a:2:{i:0;i:0;i:1;b:1;}}', 0, 1, 1, 1);
+
+-- PAGE: search for entries
+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_channels_search', '_bx_channels_page_title_sys_entries_search', '_bx_channels_page_title_entries_search', 'bx_channels', 5, 2147483647, 1, 'channels-search', 'page.php?i=channels-search', '', '', '', 0, 1, 0, 'BxCnlPageBrowse', 'modules/boonex/channels/classes/BxCnlPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_channels_search', 1, 'bx_channels', '_bx_channels_page_block_title_search_form', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:8:"get_form";s:6:"params";a:1:{i:0;a:1:{s:6:"object";s:11:"bx_channels";}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 1, 1),
+('bx_channels_search', 1, 'bx_channels', '_bx_channels_page_block_title_search_results', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_results";s:6:"params";a:1:{i:0;a:2:{s:6:"object";s:11:"bx_channels";s:10:"show_empty";b:1;}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 1, 2),
+('bx_channels_search', 1, 'bx_channels', '_bx_channels_page_block_title_search_form_cmts', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:8:"get_form";s:6:"params";a:1:{i:0;a:1:{s:6:"object";s:16:"bx_channels_cmts";}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 0, 3),
+('bx_channels_search', 1, 'bx_channels', '_bx_channels_page_block_title_search_results_cmts', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_results";s:6:"params";a:1:{i:0;a:2:{s:6:"object";s:16:"bx_channels_cmts";s:10:"show_empty";b:1;}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 0, 4);
+
+-- PAGE: module manage all
+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_channels_administration', '_bx_channels_page_title_sys_manage_administration', '_bx_channels_page_title_manage', 'bx_channels', 5, 128, 1, 'channels-administration', 'page.php?i=channels-administration', '', '', '', 0, 1, 0, 'BxCnlPageBrowse', 'modules/boonex/channels/classes/BxCnlPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_channels_administration', 1, 'bx_channels', '_bx_channels_page_block_title_system_manage_administration', '_bx_channels_page_block_title_manage', 11, 192, 'service', 'a:3:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:12:\"manage_tools\";s:6:\"params\";a:1:{i:0;s:14:\"administration\";}}', 0, 1, 0);
+
+-- PAGE: add block to homepage
+SET @iBlockOrder = (SELECT `order` FROM `sys_pages_blocks` WHERE `object` = 'sys_home' AND `cell_id` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('sys_home', 1, 'bx_channels', '', '_bx_channels_page_block_title_latest_profiles', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:22:"browse_recent_profiles";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 1),
+('sys_home', 1, 'bx_channels', '_bx_channels_page_block_title_sys_recommended_entries_view_showcase', '_bx_channels_page_block_title_recommended_entries_view_showcase', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:18:"browse_recommended";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 2);
+
+-- PAGE: service blocks
+SET @iBlockOrder = (SELECT `order` FROM `sys_pages_blocks` WHERE `object` = '' AND `cell_id` = 0 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('', 0, 'bx_channels', '_bx_channels_page_block_title_sys_featured_entries_view_showcase', '_bx_channels_page_block_title_featured_entries_view_showcase', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:15:\"browse_featured\";s:6:\"params\";a:3:{s:9:\"unit_view\";s:8:\"showcase\";s:13:\"empty_message\";b:0;s:13:\"ajax_paginate\";b:0;}}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 1),
+('', 0, 'bx_channels', '_bx_channels_page_block_title_sys_recommended_entries_view_showcase', '_bx_channels_page_block_title_recommended_entries_view_showcase', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:18:\"browse_recommended\";s:6:\"params\";a:3:{s:9:\"unit_view\";s:8:\"showcase\";s:13:\"empty_message\";b:0;s:13:\"ajax_paginate\";b:0;}}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 2),
+('', 0, 'bx_channels', '_bx_channels_page_block_title_sys_active_entries', '_bx_channels_page_block_title_active_entries', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:13:\"browse_active\";}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 3),
+('', 0, 'bx_channels', '_bx_channels_page_block_title_sys_trending_entries', '_bx_channels_page_block_title_trending_entries', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:15:\"browse_trending\";}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 4),
+('', 0, 'bx_channels', '_bx_channels_page_block_title_sys_cover_block', '_bx_channels_page_block_title_cover_block', 3, 2147483647, 'service', 'a:2:{s:6:\"module\";s:11:\"bx_channels\";s:6:\"method\";s:12:\"entity_cover\";}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 5);
+
+-- PAGES: add page block to profiles modules (trigger* page objects are processed separately upon modules enable/disable)
+SET @iPBCellProfile = 3;
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('trigger_page_profile_view_entry', @iPBCellProfile, 'bx_channels', '_bx_channels_page_trigger_block_title_entries_my', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:11:"bx_channels";s:6:"method";s:13:"browse_author";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;a:1:{s:13:"empty_message";b:0;}}}', 0, 0, 0);
+
+
+-- MENU
+
+-- MENU: add to site menu
+
+SET @iSiteMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_site' AND `active` = 1 AND `order` < 9999 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_site', 'bx_channels', 'channels-home', '_bx_channels_menu_item_title_system_entries_home', '_bx_channels_menu_item_title_entries_home', 'page.php?i=channels-home', '', '', 'hashtag', 'bx_channels_submenu', 2147483647, 1, 1, IFNULL(@iSiteMenuOrder, 0) + 1);
+
+-- MENU: add to homepage menu
+
+SET @iHomepageMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_homepage' AND `active` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_homepage', 'bx_channels', 'channels-home', '_bx_channels_menu_item_title_system_entries_home', '_bx_channels_menu_item_title_entries_home', 'page.php?i=channels-home', '', '', 'hashtag', 'bx_channels_submenu', 2147483647, 1, 1, IFNULL(@iHomepageMenuOrder, 0) + 1);
+
+-- MENU: view actions
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_view_actions', '_bx_channels_menu_title_view_profile_actions', 'bx_channels_view_actions', 'bx_channels', 9, 0, 1, 'BxCnlMenuViewActions', 'modules/boonex/channels/classes/BxCnlMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_view_actions', 'bx_channels', '_bx_channels_menu_set_title_view_profile_actions', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES 
+('bx_channels_view_actions', 'bx_channels', 'profile-subscribe-add', '_bx_channels_menu_item_title_system_subscribe', '_bx_channels_menu_item_title_subscribe', 'javascript:void(0)', 'bx_conn_action(this, \'sys_profiles_subscriptions\', \'add\', \'{profile_id}\')', '', 'check', '', 0, 2147483647, 1, 0, 1, 20),
+('bx_channels_view_actions', 'bx_channels', 'profile-subscribe-remove', '_bx_channels_menu_item_title_system_unsubscribe', '_bx_channels_menu_item_title_unsubscribe', 'javascript:void(0)', 'bx_conn_action(this, \'sys_profiles_subscriptions\', \'remove\', \'{profile_id}\')', '', 'check', '', 0, 2147483647, 1, 0, 1, 21),
+('bx_channels_view_actions', 'bx_channels', 'profile-actions-more', '_bx_channels_menu_item_title_system_more_actions', '_bx_channels_menu_item_title_more_actions', 'javascript:void(0)', 'bx_menu_popup(''bx_channels_view_actions_more'', this, {}, {profile_id:{profile_id}});', '', 'cog', 'bx_channels_view_actions_more', 1, 2147483647, 1, 0, 1, 9999);
+
+-- MENU: view actions more
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_view_actions_more', '_bx_channels_menu_title_view_profile_actions_more', 'bx_channels_view_actions_more', 'bx_channels', 6, 0, 1, 'BxCnlMenuViewActions', 'modules/boonex/channels/classes/BxCnlMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_view_actions_more', 'bx_channels', '_bx_channels_menu_set_title_view_profile_actions_more', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_channels_view_actions_more', 'bx_channels', 'notes', '_sys_menu_item_title_system_va_notes', '_sys_menu_item_title_va_notes', 'javascript:void(0)', 'javascript:bx_get_notes(this,  ''{module_uri}'', {content_id});', '', 'exclamation-triangle', '', 192, 1, 0, 20),
+('bx_channels_view_actions_more', 'bx_channels', 'edit-channel-profile', '_bx_channels_menu_item_title_system_edit_profile', '_bx_channels_menu_item_title_edit_profile', 'page.php?i=edit-channel-profile&id={content_id}', '', '', 'pencil-alt', '', 2147483647, 0, 0, 40),
+('bx_channels_view_actions_more', 'bx_channels', 'delete-channel-profile', '_bx_channels_menu_item_title_system_delete_profile', '_bx_channels_menu_item_title_delete_profile', 'page.php?i=delete-channel-profile&id={content_id}', '', '', 'remove', '', 2147483647, 1, 0, 50);
+
+-- MENU: all actions menu for view entry 
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `persistent`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_view_actions_all', '_sys_menu_title_view_actions', 'bx_channels_view_actions_all', 'bx_channels', 15, 1, 0, 1, 'BxCnlMenuViewActionsAll', 'modules/boonex/channels/classes/BxCnlMenuViewActionsAll.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_view_actions_all', 'bx_channels', '_sys_menu_set_title_view_actions', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_channels_view_actions_all', 'bx_channels', 'profile-subscriptions', '_bx_channels_menu_item_title_system_subscriptions', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 10),
+('bx_channels_view_actions_all', 'bx_channels', 'profile-subscribe-add', '_bx_channels_menu_item_title_system_subscribe', '', '', '', '', '', '', '', 0, 2147483647, 0, 0, 12),
+('bx_channels_view_actions_all', 'bx_channels', 'profile-subscribe-remove', '_bx_channels_menu_item_title_system_unsubscribe', '', '', '', '', '', '', '', 0, 2147483647, 0, 0, 14),
+('bx_channels_view_actions_all', 'bx_channels', 'comment', '_sys_menu_item_title_system_va_comment', '', '', '', '', '', '', '', 0, 2147483647, 0, 0, 200),
+('bx_channels_view_actions_all', 'bx_channels', 'view', '_sys_menu_item_title_system_va_view', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 210),
+('bx_channels_view_actions_all', 'bx_channels', 'vote', '_sys_menu_item_title_system_va_vote', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 220),
+('bx_channels_view_actions_all', 'bx_channels', 'reaction', '_sys_menu_item_title_system_va_reaction', '', '', '', '', '', '', '', 0, 2147483647, 0, 0, 225),
+('bx_channels_view_actions_all', 'bx_channels', 'score', '_sys_menu_item_title_system_va_score', '', '', '', '', '', '', '', 0, 2147483647, 0, 0, 230),
+('bx_channels_view_actions_all', 'bx_channels', 'favorite', '_sys_menu_item_title_system_va_favorite', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 240),
+('bx_channels_view_actions_all', 'bx_channels', 'feature', '_sys_menu_item_title_system_va_feature', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 250),
+('bx_channels_view_actions_all', 'bx_channels', 'report', '_sys_menu_item_title_system_va_report', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 260),
+('bx_channels_view_actions_all', 'bx_channels', 'social-sharing', '_sys_menu_item_title_system_social_sharing', '_sys_menu_item_title_social_sharing', 'javascript:void(0)', 'oBxDolPage.share(this, \'{url_encoded}\')', '', 'share', '', '', 0, 2147483647, 1, 0, 300),
+('bx_channels_view_actions_all', 'bx_channels', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, 1, 0, 9999);
+
+-- MENU: meta (counters) menu for view entry
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_view_meta', '_bx_channels_menu_title_view_profile_meta', 'bx_channels_view_meta', 'bx_channels', 15, 0, 1, 'BxCnlMenuViewMeta', 'modules/boonex/channels/classes/BxCnlMenuViewMeta.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_view_meta', 'bx_channels', '_bx_channels_menu_set_title_view_profile_meta', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_channels_view_meta', 'bx_channels', 'subscribers', '_sys_menu_item_title_system_sm_subscribers', '_sys_menu_item_title_sm_subscribers', '', '', '', '', '', 0, 2147483647, '', 1, 0, 20),
+('bx_channels_view_meta', 'bx_channels', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 0, 2147483647, '', 1, 0, 30),
+('bx_channels_view_meta', 'bx_channels', 'votes', '_sys_menu_item_title_system_sm_votes', '_sys_menu_item_title_sm_votes', '', '', '', '', '', 0, 2147483647, '', 1, 0, 40),
+('bx_channels_view_meta', 'bx_channels', 'reactions', '_sys_menu_item_title_system_sm_reactions', '_sys_menu_item_title_sm_reactions', '', '', '', '', '', 0, 2147483647, '', 0, 0, 43),
+('bx_channels_view_meta', 'bx_channels', 'scores', '_sys_menu_item_title_system_sm_scores', '_sys_menu_item_title_sm_scores', '', '', '', '', '', 0, 2147483647, '', 0, 0, 46),
+('bx_channels_view_meta', 'bx_channels', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 0, 2147483647, '', 1, 0, 50);
+
+-- MENU: actions menu for my entries
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_my', '_bx_channels_menu_title_entries_my', 'bx_channels_my', 'bx_channels', 9, 0, 1, 'BxCnlMenu', 'modules/boonex/channels/classes/BxCnlMenu.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_my', 'bx_channels', '_bx_channels_menu_set_title_entries_my', 0);
+
+-- MENU: module sub-menu
+INSERT INTO `sys_objects_menu`(`object`, `title`, `title_public`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_submenu', '_bx_channels_menu_title_submenu', '_bx_channels_menu_title_public_submenu', 'bx_channels_submenu', 'bx_channels', 8, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_submenu', 'bx_channels', '_bx_channels_menu_set_title_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_channels_submenu', 'bx_channels', 'channels-home', '_bx_channels_menu_item_title_system_entries_recent', '_bx_channels_menu_item_title_entries_recent', 'page.php?i=channels-home', '', '', '', '', 2147483647, 1, 1, 1),
+('bx_channels_submenu', 'bx_channels', 'channels-top', '_bx_channels_menu_item_title_system_entries_top', '_bx_channels_menu_item_title_entries_top', 'page.php?i=channels-top', '', '', '', '', 2147483647, 1, 1, 2),
+('bx_channels_submenu', 'bx_channels', 'channels-toplevel', '_bx_channels_menu_item_title_system_entries_toplevel', '_bx_channels_menu_item_title_entries_toplevel', 'page.php?i=channels-toplevel', '', '', '', '', 2147483647, 1, 1, 3),
+('bx_channels_submenu', 'bx_channels', 'channels-search', '_bx_channels_menu_item_title_system_entries_search', '_bx_channels_menu_item_title_entries_search', 'page.php?i=channels-search', '', '', '', '', 2147483647, 1, 1, 4),
+('bx_channels_submenu', 'bx_channels', 'channels-administration', '_bx_channels_menu_item_title_system_entries_manage', '_bx_channels_menu_item_title_entries_manage', 'page.php?i=channels-administration', '', '', '', '', 192, 1, 1, 5);
+
+-- MENU: custom menu for snippet meta info
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_snippet_meta', '_sys_menu_title_snippet_meta', 'bx_channels_snippet_meta', 'bx_channels', 15, 0, 1, 'BxCnlMenuSnippetMeta', 'modules/boonex/channels/classes/BxCnlMenuSnippetMeta.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_snippet_meta', 'bx_channels', '_sys_menu_set_title_snippet_meta', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES
+('bx_channels_snippet_meta', 'bx_channels', 'date', '_sys_menu_item_title_system_sm_date', '_sys_menu_item_title_sm_date', '', '', '', '', '', 2147483647, 0, 0, 1, 1),
+('bx_channels_snippet_meta', 'bx_channels', 'tags', '_sys_menu_item_title_system_sm_tags', '_sys_menu_item_title_sm_tags', '', '', '', '', '', 2147483647, 0, 0, 1, 10),
+('bx_channels_snippet_meta', 'bx_channels', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 2147483647, 0, 0, 1, 15),
+('bx_channels_snippet_meta', 'bx_channels', 'votes', '_sys_menu_item_title_system_sm_votes', '_sys_menu_item_title_sm_votes', '', '', '', '', '', 2147483647, 0, 0, 1, 16),
+('bx_channels_snippet_meta', 'bx_channels', 'reactions', '_sys_menu_item_title_system_sm_reactions', '_sys_menu_item_title_sm_reactions', '', '', '', '', '', 2147483647, 0, 0, 1, 17),
+('bx_channels_snippet_meta', 'bx_channels', 'scores', '_sys_menu_item_title_system_sm_scores', '_sys_menu_item_title_sm_scores', '', '', '', '', '', 2147483647, 0, 0, 1, 18),
+('bx_channels_snippet_meta', 'bx_channels', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 2147483647, 0, 0, 1, 20),
+('bx_channels_snippet_meta', 'bx_channels', 'members', '_sys_menu_item_title_system_sm_members', '_sys_menu_item_title_sm_members', '', '', '', '', '', 2147483647, 1, 0, 1, 30),
+('bx_channels_snippet_meta', 'bx_channels', 'subscribers', '_sys_menu_item_title_system_sm_subscribers', '_sys_menu_item_title_sm_subscribers', '', '', '', '', '', 2147483647, 1, 0, 1, 40),
+('bx_channels_snippet_meta', 'bx_channels', 'nl', '_sys_menu_item_title_system_sm_nl', '_sys_menu_item_title_sm_nl', '', '', '', '', '', 2147483647, 1, 0, 1, 45),
+('bx_channels_snippet_meta', 'bx_channels', 'subscribe', '_sys_menu_item_title_system_sm_subscribe', '_sys_menu_item_title_sm_subscribe', '', '', '', '', '', 2147483647, 1, 0, 1, 50),
+('bx_channels_snippet_meta', 'bx_channels', 'unsubscribe', '_sys_menu_item_title_system_sm_unsubscribe', '_sys_menu_item_title_sm_unsubscribe', '', '', '', '', '', 2147483647, 1, 0, 1, 60);
+
+-- MENU: manage tools submenu
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_channels_menu_manage_tools', '_bx_channels_menu_title_manage_tools', 'bx_channels_menu_manage_tools', 'bx_channels', 6, 0, 1, 'BxCnlMenuManageTools', 'modules/boonex/channels/classes/BxCnlMenuManageTools.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_channels_menu_manage_tools', 'bx_channels', '_bx_channels_menu_set_title_manage_tools', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_channels_menu_manage_tools', 'bx_channels', 'delete', '_bx_channels_menu_item_title_system_delete', '_bx_channels_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 1),
+('bx_channels_menu_manage_tools', 'bx_channels', 'delete-with-content', '_bx_channels_menu_item_title_system_delete_with_content', '_bx_channels_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 0, 0, 2);
+
+-- MENU: dashboard manage tools
+SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
+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_dashboard_manage_tools', 'bx_channels', 'channels-administration', '_bx_channels_menu_item_title_system_admt_channels', '_bx_channels_menu_item_title_admt_channels', 'page.php?i=channels-administration', '', '_self', 'hashtag', 'a:2:{s:6:"module";s:11:"bx_channels";s:6:"method";s:27:"get_menu_addon_manage_tools";}', '', 128, 1, 0, @iManageMenuOrder + 1);
+
+-- MENU: profile followings
+SET @iFollowingsMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_profile_followings' LIMIT 1);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `collapsed`, `active`, `copyable`, `order`) VALUES 
+('sys_profile_followings', 'bx_channels', 'channels', '_bx_channels_menu_item_title_system_followings', '_bx_channels_menu_item_title_followings', 'javascript:void(0)', '', '_self', 'hashtag', '', '', 2147483647, 0, 1, 0, @iFollowingsMenuOrder + 1);
+
+-- MENU: add menu item to profiles modules (trigger* menu sets are processed separately upon modules enable/disable)
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('trigger_profile_view_submenu', 'bx_channels', 'channels-author', '_bx_channels_menu_item_title_system_view_entries_author', '_bx_channels_menu_item_title_view_entries_author', 'page.php?i=channels-author&profile_id={profile_id}', '', '', 'hashtag', '', 2147483647, 1, 0, 0);
+
+
+-- ACL
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_channels', 'delete entry', NULL, '_bx_channels_acl_action_delete_profile', '', 1, 1);
+SET @iIdActionProfileDelete = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_channels', 'view entry', NULL, '_bx_channels_acl_action_view_profile', '', 1, 0);
+SET @iIdActionProfileView = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_channels', 'edit any entry', NULL, '_bx_channels_acl_action_edit_any_profile', '', 1, 3);
+SET @iIdActionProfileEditAny = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_channels', 'delete any entry', NULL, '_bx_channels_acl_action_delete_any_profile', '', 1, 3);
+SET @iIdActionProfileDeleteAny = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_channels', 'create channel auto', NULL, '_bx_channels_acl_action_create_channel_auto', '_bx_channels_acl_action_create_channel_auto_info', 1, 1);
+SET @iIdActionCreateChannelAuto = 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
+
+-- profile delete
+(@iAdministrator, @iIdActionProfileDelete),
+
+-- profile view
+(@iUnauthenticated, @iIdActionProfileView),
+(@iAccount, @iIdActionProfileView),
+(@iStandard, @iIdActionProfileView),
+(@iUnconfirmed, @iIdActionProfileView),
+(@iPending, @iIdActionProfileView),
+(@iModerator, @iIdActionProfileView),
+(@iAdministrator, @iIdActionProfileView),
+(@iPremium, @iIdActionProfileView),
+
+-- any profile edit
+(@iModerator, @iIdActionProfileEditAny),
+(@iAdministrator, @iIdActionProfileEditAny),
+
+-- any profile delete
+(@iAdministrator, @iIdActionProfileDeleteAny),
+
+-- create channel auto
+(@iAccount, @iIdActionCreateChannelAuto),
+(@iStandard, @iIdActionCreateChannelAuto),
+(@iUnconfirmed, @iIdActionCreateChannelAuto),
+(@iPending, @iIdActionCreateChannelAuto),
+(@iModerator, @iIdActionCreateChannelAuto),
+(@iAdministrator, @iIdActionCreateChannelAuto),
+(@iPremium, @iIdActionCreateChannelAuto);
+
+
+-- METATAGS
+INSERT INTO `sys_objects_metatags` (`object`, `module`, `table_keywords`, `table_locations`, `table_mentions`, `override_class_name`, `override_class_file`) VALUES
+('bx_channels', 'bx_channels', 'bx_cnl_meta_keywords', '', 'bx_cnl_meta_mentions', '', '');
+
+-- SEARCH
+SET @iSearchOrder = (SELECT IFNULL(MAX(`Order`), 0) FROM `sys_objects_search`);
+INSERT INTO `sys_objects_search` (`ObjectName`, `Title`, `Order`, `ClassName`, `ClassPath`) VALUES
+('bx_channels', '_bx_channels', @iSearchOrder + 1, 'BxCnlSearchResult', 'modules/boonex/channels/classes/BxCnlSearchResult.php');
+
+-- STATS
+SET @iMaxOrderStats = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_statistics`);
+INSERT INTO `sys_statistics` (`module`, `name`, `title`, `link`, `icon`, `query`, `order`) VALUES 
+('bx_channels', 'bx_channels', '_bx_channels', 'page.php?i=channels-home', 'hashtag', 'SELECT COUNT(*) FROM `bx_cnl_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_channels'' WHERE 1 AND `tp`.`status`=''active''', @iMaxOrderStats + 1);
+
+-- CHARTS
+SET @iMaxOrderCharts = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_objects_chart`);
+INSERT INTO `sys_objects_chart` (`object`, `title`, `table`, `field_date_ts`, `field_date_dt`, `field_status`, `query`, `active`, `order`, `class_name`, `class_file`) VALUES
+('bx_channels_growth', '_bx_channels_chart_growth', 'bx_cnl_data', 'added', '', '', 'SELECT {field_date_formatted} AS `period`, COUNT(*) AS {object} FROM {table} LEFT JOIN `sys_profiles` AS `tp` ON {table}.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_channels'' WHERE 1 AND `tp`.`status`=''active'' {where_inteval} GROUP BY `period` ORDER BY {table}.{field_date} ASC', 1, @iMaxOrderCharts + 1, 'BxDolChartGrowth', ''),
+('bx_channels_growth_speed', '_bx_channels_chart_growth_speed', 'bx_cnl_data', 'added', '', '', 'SELECT {field_date_formatted} AS `period`, COUNT(*) AS {object} FROM {table} LEFT JOIN `sys_profiles` AS `tp` ON {table}.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_channels'' WHERE 1 AND `tp`.`status`=''active'' {where_inteval} GROUP BY `period` ORDER BY {table}.{field_date} ASC', 1, @iMaxOrderCharts + 2, 'BxDolChartGrowthSpeed', '');
+
+-- GRIDS: administration
+
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_channels_administration', 'Sql', 'SELECT `td`.*, `td`.`channel_name` AS `name`, `ta`.`email` AS `account`, `td`.`added` AS `added_ts`, `tp`.`status` AS `status_profile` FROM `bx_cnl_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_channels'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_cnl_data', 'id', 'added', 'status_admin', '', 20, NULL, 'start', '', 'channel_name', '', 'like', 'reports', '', 192, 'BxCnlGridAdministration', 'modules/boonex/channels/classes/BxCnlGridAdministration.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_channels_administration', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_channels_administration', 'switcher', '_bx_channels_grid_column_title_adm_active', '8%', 0, '', '', 2),
+('bx_channels_administration', 'reports', '_sys_txt_reports_title', '5%', 0, '', '', 3),
+('bx_channels_administration', 'name', '_bx_channels_grid_column_title_adm_name', '45%', 0, '', '', 4),
+('bx_channels_administration', 'added_ts', '_bx_channels_grid_column_title_adm_added', '20%', 1, '25', '', 5),
+('bx_channels_administration', 'actions', '', '20%', 0, '', '', 6);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `active`, `order`) VALUES
+('bx_channels_administration', 'bulk', 'delete', '_bx_channels_grid_action_title_adm_delete', '', 0, 1, 1, 2),
+('bx_channels_administration', 'bulk', 'delete_with_content', '_bx_channels_grid_action_title_adm_delete_with_content', '', 0, 1, 0, 3),
+('bx_channels_administration', 'single', 'settings', '_bx_channels_grid_action_title_adm_more_actions', 'cog', 1, 0, 1, 2),
+('bx_channels_administration', 'single', 'audit_content', '_bx_channels_grid_action_title_adm_audit_content', 'search', 1, 0, 1, 3),
+('bx_channels_administration', 'single', 'audit_profile', '_bx_channels_grid_action_title_adm_audit_context', 'search-location', 1, 0, 1, 4);
+
+
+-- ALERTS
+
+INSERT INTO `sys_alerts_handlers` (`name`, `class`, `file`, `service_call`) VALUES 
+('bx_channels', 'BxCnlAlertsResponse', 'modules/boonex/channels/classes/BxCnlAlertsResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('system', 'save_setting', @iHandler),
+('bx_timeline', 'post_common', @iHandler),
+('bx_channels_pics', 'file_deleted', @iHandler),
+('profile', 'delete', @iHandler),
+('bx_channels', 'timeline_view', @iHandler),
+('bx_channels', 'timeline_post', @iHandler),
+('bx_channels', 'timeline_delete', @iHandler),
+('bx_channels', 'timeline_comment', @iHandler),
+('bx_channels', 'timeline_vote', @iHandler),
+('bx_channels', 'timeline_score', @iHandler),
+('bx_channels', 'timeline_report', @iHandler),
+('bx_channels', 'timeline_repost', @iHandler),
+('bx_channels', 'timeline_pin', @iHandler),
+('bx_channels', 'timeline_promote', @iHandler),
+('bx_persons', 'added', @iHandler),
+('bx_persons', 'edited', @iHandler),
+('bx_organizations', 'added', @iHandler),
+('bx_organizations', 'edited', @iHandler),
+('meta_keyword', 'added', @iHandler),
+('meta_keyword', 'url', @iHandler),
+('meta_keyword', 'deleted', @iHandler);
+
+
+-- PRIVACY 
+
+INSERT INTO `sys_objects_privacy` (`object`, `module`, `action`, `title`, `default_group`, `spaces`, `table`, `table_field_id`, `table_field_author`, `override_class_name`, `override_class_file`) VALUES
+('bx_channels_allow_view_to', 'bx_channels', 'view', '_bx_channels_form_profile_input_allow_view_to', '3', '', 'bx_cnl_data', 'id', 'author', 'BxCnlPrivacy', 'modules/boonex/channels/classes/BxCnlPrivacy.php'),
+('bx_channels_allow_view_notification_to', 'bx_channels', 'view_event', '_bx_channels_form_profile_input_allow_view_notification_to', '3', '', 'bx_notifications_events', 'id', 'object_owner_id', 'BxCnlPrivacyNotifications', 'modules/boonex/channels/classes/BxCnlPrivacyNotifications.php');
+
+
+-- UPLOADERS
+
+INSERT INTO `sys_objects_uploader` (`object`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_channels_cover_crop', 1, 'BxCnlUploaderCoverCrop', 'modules/boonex/channels/classes/BxCnlUploaderCoverCrop.php'),
+('bx_channels_picture_crop', 1, 'BxCnlUploaderPictureCrop', 'modules/boonex/channels/classes/BxCnlUploaderPictureCrop.php');

+ 58 - 0
modules/boonex/channels/updates/15.0.0_15.0.1/source/install/sql/uninstall.sql

@@ -0,0 +1,58 @@
+
+-- TABLES
+
+DROP TABLE IF EXISTS `bx_cnl_data`, `bx_cnl_content`, `bx_cnl_pics`, `bx_cnl_pics_resized`, `bx_cnl_cmts`, `bx_cnl_cmts_note`, `bx_cnl_views_track`, `bx_cnl_meta_keywords`, `bx_cnl_meta_mentions`, `bx_cnl_votes`, `bx_cnl_votes_track`, `bx_cnl_reactions`, `bx_cnl_reactions_track`, `bx_cnl_reports`, `bx_cnl_reports_track`, `bx_cnl_favorites_track`, `bx_cnl_scores`, `bx_cnl_scores_track`;
+
+-- PROFILES
+
+DELETE FROM sys_profiles WHERE `type` = 'bx_channels';
+
+-- STORAGES & TRANSCODERS
+
+DELETE FROM `sys_objects_storage` WHERE `object` IN('bx_channels_pics', 'bx_channels_pics_resized');
+DELETE FROM `sys_storage_tokens` WHERE `object` IN('bx_channels_pics', 'bx_channels_pics_resized');
+
+DELETE FROM `sys_objects_transcoder` WHERE `object` IN('bx_channels_icon', 'bx_channels_thumb', 'bx_channels_avatar', 'bx_channels_avatar_big', 'bx_channels_picture', 'bx_channels_cover', 'bx_channels_cover_thumb', 'bx_channels_gallery');
+DELETE FROM `sys_transcoder_filters` WHERE `transcoder_object` IN('bx_channels_icon', 'bx_channels_thumb', 'bx_channels_avatar', 'bx_channels_avatar_big', 'bx_channels_picture', 'bx_channels_cover', 'bx_channels_cover_thumb', 'bx_channels_gallery');
+DELETE FROM `sys_transcoder_images_files` WHERE `transcoder_object` IN('bx_channels_icon', 'bx_channels_thumb', 'bx_channels_avatar', 'bx_channels_avatar_big', 'bx_channels_picture', 'bx_channels_cover', 'bx_channels_cover_thumb', 'bx_channels_gallery');
+
+-- FORMS
+
+DELETE FROM `sys_objects_form` WHERE `module` = 'bx_channels';
+DELETE FROM `sys_form_displays` WHERE `module` = 'bx_channels';
+DELETE FROM `sys_form_inputs` WHERE `module` = 'bx_channels';
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN('bx_channel_add', 'bx_channel_delete', 'bx_channel_edit', 'bx_channel_edit_cover', 'bx_channel_view', 'bx_channel_view_full');
+
+-- COMMENTS
+DELETE FROM `sys_objects_cmts` WHERE `Name` IN ('bx_channels', 'bx_cnl_notes');
+
+-- VIEWS
+DELETE FROM `sys_objects_view` WHERE `name` = 'bx_channels';
+
+-- VOTES
+DELETE FROM `sys_objects_vote` WHERE `Name` IN ('bx_channels', 'bx_channels_reactions');
+
+-- SCORES
+DELETE FROM `sys_objects_score` WHERE `name` = 'bx_channels';
+
+-- REPORTS
+DELETE FROM `sys_objects_report` WHERE `name` = 'bx_channels';
+
+-- FAFORITES
+DELETE FROM `sys_objects_favorite` WHERE `name` = 'bx_channels';
+
+-- FEATURED
+DELETE FROM `sys_objects_feature` WHERE `name` = 'bx_channels';
+
+-- CONTENT INFO
+DELETE FROM `sys_objects_content_info` WHERE `name` IN ('bx_channels', 'bx_channels_cmts');
+
+DELETE FROM `sys_content_info_grids` WHERE `object` IN ('bx_channels');
+
+-- SEARCH EXTENDED
+DELETE FROM `sys_objects_search_extended` WHERE `module` = 'bx_channels';
+
+-- STUDIO PAGE & WIDGET
+DELETE FROM `tp`, `tw`, `twb`, `tpw` 
+USING `sys_std_pages` AS `tp` LEFT JOIN `sys_std_widgets` AS `tw` ON `tp`.`id` = `tw`.`page_id` LEFT JOIN `sys_std_widgets_bookmarks` AS `twb` ON `tw`.`id` = `twb`.`widget_id` LEFT JOIN `sys_std_pages_widgets` AS `tpw` ON `tw`.`id` = `tpw`.`widget_id`
+WHERE  `tp`.`name` = 'bx_channels';

+ 51 - 0
modules/boonex/convos/updates/15.0.1_15.0.2/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Conversations',
+    'version_from' => '15.0.1',
+    'version_to' => '15.0.2',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/convos/updates/update_15.0.1_15.0.2/',
+    'home_uri' => 'convos_update_1501_1502',
+
+    'module_dir' => 'boonex/convos/',
+    'module_uri' => 'convos',
+
+    'db_prefix' => 'bx_convos_',
+    'class_prefix' => 'BxCnv',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Conversations',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/convos/updates/15.0.1_15.0.2/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxCnvUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 274 - 0
modules/boonex/convos/updates/15.0.1_15.0.2/source/classes/BxCnvFormEntry.php

@@ -0,0 +1,274 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Convos Convos
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Create/Edit entry form
+ */
+class BxCnvFormEntry extends BxBaseModTextFormEntry
+{
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_convos';
+        parent::__construct($aInfo, $oTemplate);
+
+        $oTemplate = BxDolTemplate::getInstance();
+        $oTemplate->addJsTranslation(array(
+            '_bx_cnv_draft_saving_error',
+            '_bx_cnv_draft_saved_success',
+        ));
+    }
+
+    public function update ($iContentId, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
+    {
+        $CNF = $this->_oModule->_oConfig->CNF;
+        if ($bRet = parent::update ($iContentId, $aValsToAdd, $aTrackTextFieldsChanges)) {
+            $iFolder = $this->_oModule->_oDb->getConversationFolder($iContentId, bx_get_logged_profile_id());
+
+            $aContentInfo = $this->_oModule->_oDb->getContentInfoById($iContentId);
+            if ($aContentInfo && bx_get_logged_profile_id() == $aContentInfo[$CNF['FIELD_AUTHOR']]) // allow to edit participants for author only
+                $this->updateParticipants ($iContentId, $iFolder, $iFolder == BX_CNV_FOLDER_DRAFTS ? true : false);
+        }
+
+        return $bRet;
+    }
+
+    public function insert ($aValsToAdd = array(), $isIgnore = false)
+    {
+        $CNF = $this->_oModule->_oConfig->CNF;
+
+        $aValsToAdd['last_reply_timestamp'] = time();
+        $aValsToAdd['last_reply_profile_id'] = bx_get_logged_profile_id();
+
+        $bSaveToDrafts = bx_get('draft_save');
+        $iContentId = (int)bx_get('draft_id');
+        $bDraft = $iContentId ? BX_CNV_FOLDER_DRAFTS == $this->_oModule->_oDb->getConversationFolder($iContentId, bx_get_logged_profile_id()) : false;
+
+        // check for spam
+        $bSpam = false;
+        $sValue = $this->getCleanValue($CNF['FIELD_TEXT']);
+         /**
+         * @hooks
+         * @hookdef hook-system-check_spam 'system', 'check_spam' - hook on check spam in some content
+         * - $unit_name - equals `system`
+         * - $action - equals `check_spam` 
+         * - $object_id - not used  
+         * - $sender_id - account id for current user
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `is_spam` - [bool] by ref, сontain spam content or not, can be overridden in hook processing
+         *      - `content` - [string] by ref, content, can be overridden in hook processing
+         *      - `where` - [string] module name
+         * @hook @ref hook-system-check_spam
+         */
+        bx_alert('system', 'check_spam', 0, getLoggedId(), array(
+            'is_spam' => &$bSpam, 
+            'content' => &$sValue, 
+
+            'is_spam_ref' => &$bSpam, 
+            'content_ref' => &$sValue, 
+
+            'where' => $this->MODULE
+        ));
+        self::setSubmittedValue($CNF['FIELD_TEXT'], $sValue, $this->aFormAttrs['method']);
+
+        if($iContentId) {
+            if(!$bDraft)
+                return 0;
+
+            if(!parent::update($iContentId, $aValsToAdd))
+                return 0;
+
+        } 
+        else {
+            
+            $iContentId = parent::insert($aValsToAdd, $isIgnore);
+            if(!$iContentId)
+                return 0;
+        }
+
+        if($bSaveToDrafts) {
+            if(!$bDraft)
+                $this->_oModule->_oDb->conversationToFolder($iContentId, BX_CNV_FOLDER_DRAFTS, bx_get_logged_profile_id(), 0);
+
+            // process uploaded files
+            if(isset($CNF['FIELD_PHOTO']))
+                $this->processFiles ($CNF['FIELD_PHOTO'], $iContentId, true);
+
+            // draft is saved via ajax call only, upon successfull draft saving content id is returned
+            echo $iContentId . ',' . $this->getCsrfToken();
+            exit;
+        } 
+        else {
+            $iFolder = $bSpam ? BX_CNV_FOLDER_SPAM : BX_CNV_FOLDER_INBOX;
+            $this->updateParticipants ($iContentId, $iFolder, $bDraft);
+        }
+
+        return $iContentId;
+    }
+
+    public function updateParticipants ($iContentId, $iFolder, $bDraft, $aRecipientsAdd = array()) 
+    {
+        $iSender = bx_get_logged_profile_id();
+        $aRecipientsOld = $this->_oModule->_oDb->getCollaborators($iContentId);
+
+        // place conversation to "inbox" (or "spam" - in case of spam) folder
+        $aRecipientsFormForm = $this->getCleanValue('recipients') ? $this->getCleanValue('recipients') : array();
+        $aRecipients = array_unique(array_merge($aRecipientsFormForm, array($iSender), $aRecipientsAdd), SORT_NUMERIC);
+        foreach ($aRecipients as $iProfile) {
+            $oProfile = BxDolProfile::getInstance($iProfile);
+            if(!$oProfile)
+                continue;
+
+            if($this->_oModule->checkAllowedContact($iProfile) !== CHECK_ACTION_RESULT_ALLOWED)
+                continue;
+
+            $iRecipient = $oProfile->id();
+            if($bDraft && $iRecipient == $iSender)
+                $this->_oModule->_oDb->moveConvo($iContentId, $iRecipient, $iFolder);
+            else {
+                if($iRecipient != $iSender) {
+                    $bCanContact = true;
+                    /**
+                     * @hooks
+                     * @hookdef hook-profile-check_contact 'profile', 'check_contact' - hook on check some profile to allow contact him
+                     * - $unit_name - equals `profile`
+                     * - $action - equals `check_contact` 
+                     * - $object_id - not used  
+                     * - $sender_id - not used
+                     * - $extra_params - array of additional params with the following array keys:
+                     *      - `can_contact` - [bool] by ref, can contact or not, can be overridden in hook processing
+                     *      - `sender` - [int] profile_id for sender
+                     *      - `recipient` - [int] profile_id for recipient 
+                     *      - `where` - [string] module name
+                     * @hook @ref hook-profile-check_contact
+                     */
+                    bx_alert('profile', 'check_contact', 0, false, array(
+                        'can_contact' => &$bCanContact, 
+                        'can_contact_ref' => &$bCanContact, 
+                        'sender' => $iSender, 
+                        'recipient' => $iRecipient, 
+                        'where' => $this->MODULE
+                    ));
+                    if(!$bCanContact)
+                        $iFolder = BX_CNV_FOLDER_SPAM;
+                }
+
+                if ($aRecipientsAdd && in_array($iRecipient, $aRecipientsAdd))
+                    $this->_oModule->_oDb->removeCollaborator($iContentId, $iRecipient);
+
+                $this->_oModule->_oDb->conversationToFolder($iContentId, $iFolder, $iRecipient, $iRecipient == $iSender ? 0 : -1);
+            }
+        }
+
+        // remove participants
+        foreach ($aRecipientsOld as $iProfileId => $iCount) {
+            if (!in_array($iProfileId, $aRecipients))
+                $this->_oModule->_oDb->removeCollaborator($iContentId, $iProfileId);
+        }
+    }
+
+    protected function genCustomInputRecipients ($aInput)
+    {
+        $aInput['ajax_get_suggestions'] = BX_DOL_URL_ROOT . "modules/?r=convos/ajax_get_recipients";
+        return $this->genCustomInputUsernamesSuggestions($aInput);
+    }
+
+    protected function genCustomInputSubmitText ($aInput)
+    {
+        $aVars = array();
+        return $this->_oModule->_oTemplate->parseHtmlByName('form_submit_text.html', $aVars);
+    }
+
+    function initChecker ($aValues = array (), $aSpecificValues = array())
+    {
+        $CNF = $this->_oModule->_oConfig->CNF;
+        if ($aValues && bx_get_logged_profile_id() != $aValues[$CNF['FIELD_AUTHOR']]) { // unset some fields for non author
+            unset($this->aInputs[$CNF['FIELD_ALLOW_EDIT']]);
+            unset($this->aInputs['recipients']);
+        }
+
+        if ($iContentId = bx_get('draft_id')) { // if adding from draft, fill in existing fields info
+            $aContentInfo = $this->_oModule->_oDb->getContentInfoById($iContentId);
+            if ($aContentInfo)
+                $aValues = array_merge($aContentInfo, $aValues);
+        }
+
+        if ($sProfilesIds = bx_get('profiles')) { // if writing directly to some recipients, pre-fill them
+            $a = explode(',', $sProfilesIds);
+            $a = array_unique(BxDolFormCheckerHelper::passInt($a));
+            if ($a)
+                $aValues['recipients'] = array_merge(empty($aValues['recipients']) ? array() : $aValues['recipients'], $a);            
+        }
+
+        $sEt = bx_get('et');
+        if($sEt !== false) { // if 'attachable email template' was specified, process it
+            $aEt = json_decode(base64_decode(urldecode($sEt)), true);
+
+            if(!empty($aEt) && is_array($aEt) && !empty($aEt['name'])) {
+                $sName = bx_process_input($aEt['name']);
+                $aParams = isset($aEt['params']) ? bx_process_input($aEt['params']) : array();
+
+                $sAet = getParam('sys_email_attachable_email_templates');
+                if(strpos($sAet, $sName) !== false) {
+                    $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($sName, $aParams);
+                    if(!empty($aTemplate) && is_array($aTemplate)) {
+                        if(isset($this->aInputs[$CNF['FIELD_TITLE']]) && empty($this->aInputs[$CNF['FIELD_TITLE']]['value']))
+                            $this->aInputs[$CNF['FIELD_TITLE']]['value'] = $aTemplate['Subject'];
+
+                        if(isset($this->aInputs[$CNF['FIELD_TEXT']]) && empty($this->aInputs[$CNF['FIELD_TEXT']]['value']))
+                            $this->aInputs[$CNF['FIELD_TEXT']]['value'] = $aTemplate['Body'];
+                    }
+                }
+            }
+            
+        }
+
+        return parent::initChecker ($aValues, $aSpecificValues);
+    }
+
+    function isValid ()
+    {
+        if (bx_get('draft_save')) // form is always valid when saving to drafts
+            return true;
+        return parent::isValid ();
+    }
+
+    public function processFiles ($sFieldFile, $iContentId = 0, $isAssociateWithContent = false)
+    {
+        if (!$isAssociateWithContent && bx_get('draft_id')) // when draft is already saved then db update is called but we still need to do association since it's draft
+            $isAssociateWithContent = true; // TODO: if edit mode will be added, then this functionality maybe reconsidered
+         
+        return parent::processFiles ($sFieldFile, $iContentId, $isAssociateWithContent);
+    }
+}
+
+class BxCnvFormEntryCheckerHelper extends BxDolFormCheckerHelper
+{
+    static public function checkRecipients($aIds)
+    {
+        $bResult = self::checkAvail($aIds);
+        if(!$bResult)
+            return $bResult;
+
+        $oModule = BxDolModule::getInstance('bx_convos');
+
+        $aIdsAllowed = array();
+        foreach($aIds as $iId)
+            if($oModule->checkAllowedContact($iId) === CHECK_ACTION_RESULT_ALLOWED)
+                $aIdsAllowed[] = $iId;
+
+        if(empty($aIdsAllowed)) 
+            return false;
+
+        return true;
+    }
+}
+
+/** @} */

+ 104 - 0
modules/boonex/convos/updates/15.0.1_15.0.2/source/install/config.php

@@ -0,0 +1,104 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Convos Convos
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_convos',
+    'title' => 'Conversations',
+    'note' => 'Conversations module.',
+    'version' => '15.0.2',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/convos/',
+    'home_uri' => 'convos',
+
+    'db_prefix' => 'bx_convos_',
+    'class_prefix' => 'BxCnv',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Conversations',
+
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+        'trigger_profile_view_actions',
+        'trigger_group_view_actions',
+    ),
+
+	/**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+    	'bx_convos_files'
+    ),
+
+	/**
+     * Transcoders.
+     */
+    'transcoders' => array(
+		'bx_convos_preview'
+    ),
+
+    /**
+     * 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,
+    ),
+	'enable_success' => array(
+    	'process_menu_triggers' => 1,
+    	'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+    	'unregister_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Credits',
+    'version_from' => '15.0.0',
+    'version_to' => '15.0.1',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/credits/updates/update_15.0.0_15.0.1/',
+    'home_uri' => 'credits_update_1500_1501',
+
+    'module_dir' => 'boonex/credits/',
+    'module_uri' => 'credits',
+
+    'db_prefix' => 'bx_credits_',
+    'class_prefix' => 'BxCredits',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Credits',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxCreditsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 6 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/install/sql/enable.sql

@@ -0,0 +1,6 @@
+-- MENUS
+UPDATE `sys_menu_items` SET `icon`='copyright' WHERE `set_name`='sys_site' AND `name`='credits-home' AND `icon`='copyright col-green3';
+UPDATE `sys_menu_items` SET `icon`='copyright' WHERE `set_name`='sys_homepage' AND `name`='credits-home' AND `icon`='copyright col-green3';
+UPDATE `sys_menu_items` SET `icon`='copyright' WHERE `set_name`='sys_toolbar_member' AND `name`='credits-history' AND `icon`='copyright col-green3';
+UPDATE `sys_menu_items` SET `icon`='copyright' WHERE `set_name`='sys_account_notifications' AND `name`='notifications-credits' AND `icon`='copyright col-green3';
+UPDATE `sys_menu_items` SET `icon`='copyright' WHERE `set_name`='sys_account_dashboard' AND `name`='credits-manage' AND `icon`='copyright col-green3';

+ 338 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsConfig.php

@@ -0,0 +1,338 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxCreditsConfig extends BxBaseModGeneralConfig
+{
+    protected $_iAuthor;
+    protected $_aCurrency;
+
+    protected $_sCheckoutSessionKey;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'copyright',
+
+            // database tables
+            'TABLE_BUNDLES' => $aModule['db_prefix'] . 'bundles',
+            'TABLE_ORDERS' => $aModule['db_prefix'] . 'orders',
+            'TABLE_ORDERS_DELETED' => $aModule['db_prefix'] . 'orders_deleted',
+            'TABLE_PROFILES' => $aModule['db_prefix'] . 'profiles',
+            'TABLE_HISTORY' => $aModule['db_prefix'] . 'history',
+            'TABLE_WITHDRAWALS' => $aModule['db_prefix'] . 'withdrawals',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => '',
+            'FIELD_ADDED' => 'added',
+            'FIELD_CHANGED' => '',
+            'FIELD_NAME' => 'name',
+            'FIELD_TITLE' => 'title',
+            'FIELD_DESCRIPTION' => 'description',
+            'FIELD_AMOUNT' => 'amount',
+            'FIELD_BONUS' => 'bonus',
+            'FIELD_PRICE' => 'price',
+            'FIELD_STATUS' => 'active',
+
+            'FIELD_C_CLEARED' => 'cleared',
+            'FIELD_C_AMOUNT' => 'amount',
+            'FIELD_C_MESSAGE' => 'message',
+
+            'FIELD_H_ID' => 'id',
+            'FIELD_H_DIRECTION' => 'direction',
+            'FIELD_H_DATE' => 'date',
+            'FIELD_H_CLEARED' => 'cleared',
+
+            // page URIs
+            'URI_HOME' => 'credits-home',
+            'URL_HOME' => 'page.php?i=credits-home',
+            'URL_CHECKOUT' => 'page.php?i=credits-checkout',
+            'URL_ORDERS_COMMON' => 'page.php?i=credits-orders',
+            'URL_ORDERS_ADMINISTRATION' => 'page.php?i=credits-orders-administration',
+            'URL_HISTORY_COMMON' => 'page.php?i=credits-history-common',
+            'URL_HISTORY_ADMINISTRATION' => 'page.php?i=credits-history-administration',
+            'URL_WITHDRAWALS_COMMON' => 'page.php?i=credits-withdrawals-common',
+            'URL_WITHDRAWALS_ADMINISTRATION' => 'page.php?i=credits-withdrawals-administration',
+
+            // some params
+            'PARAM_WITHDRAW' => 'bx_credits_enable_withdraw',
+            'PARAM_WITHDRAW_EMAIL' => 'bx_credits_withdraw_email',
+            'PARAM_WITHDRAW_CLEARING' => 'bx_credits_withdraw_clearing',
+            'PARAM_WITHDRAW_MINIMUM' => 'bx_credits_withdraw_minimum',
+            'PARAM_WITHDRAW_REMAINING' => 'bx_credits_withdraw_remaining',
+            'PARAM_PRECISION' => 'bx_credits_precision',
+            'PARAM_CR_USE' => 'bx_credits_conversion_rate_use',
+            'PARAM_CR_WITHDRAW' => 'bx_credits_conversion_rate_withdraw',
+            'PARAM_PROVIDER_NAME' => 'credits',
+            'PARAM_PROVIDER_ENABLE' => 'bx_credits_enable_provider',
+            'PARAM_CODE' => 'bx_credits_code',
+            'PARAM_ICON' => 'bx_credits_icon',
+
+            // objects
+            'OBJECT_FORM_CREDIT' => 'bx_credits_credit',
+            'OBJECT_FORM_CREDIT_DISPLAY_GRANT' => 'bx_credits_credit_grant',
+            'OBJECT_FORM_CREDIT_DISPLAY_SEND' => 'bx_credits_credit_send',
+            'OBJECT_FORM_CREDIT_DISPLAY_WITHDRAW_REQUEST' => 'bx_credits_credit_withdraw_request',
+            'OBJECT_FORM_BUNDLE' => 'bx_credits_bundle',
+            'OBJECT_FORM_BUNDLE_DISPLAY_ADD' => 'bx_credits_bundle_add',
+            'OBJECT_FORM_BUNDLE_DISPLAY_EDIT' => 'bx_credits_bundle_edit',
+            'OBJECT_FORM_PROFILE' => 'bx_credits_profile',
+            'OBJECT_FORM_PROFILE_DISPLAY_EDIT' => 'bx_credits_profile_edit',
+            'OBJECT_MENU_SUBMENU' => 'bx_credits_submenu', 
+            'OBJECT_MENU_MANAGE_SUBMENU' => 'bx_credits_manage_submenu',
+            'OBJECT_GRID_BUNDLES' => 'bx_credits_bundles',
+            'OBJECT_GRID_ORDERS_ADMINISTRATION' => 'bx_credits_orders_administration',
+            'OBJECT_GRID_ORDERS_COMMON' => 'bx_credits_orders_common',
+            'OBJECT_GRID_HISTORY_ADMINISTRATION' => 'bx_credits_history_administration',
+            'OBJECT_GRID_HISTORY_COMMON' => 'bx_credits_history_common',
+            'OBJECT_GRID_WITHDRAWALS_ADMINISTRATION' => 'bx_credits_withdrawals_administration',
+            'OBJECT_GRID_WITHDRAWALS_COMMON' => 'bx_credits_withdrawals_common',
+            'OBJECT_GRID_PROFILES_ADMINISTRATION' => 'bx_credits_profiles_administration',
+
+            // email templates
+            'ETEMPLATE_GRANTED' => 'bx_credits_granted',
+            'ETEMPLATE_RECEIVED' => 'bx_credits_received',
+            'ETEMPLATE_PURCHASED' => 'bx_credits_purchased',
+            'ETEMPLATE_IN' => 'bx_credits_in',
+            'ETEMPLATE_OUT' => 'bx_credits_out',
+            'ETEMPLATE_WITHDRAW_REQUESTED' => 'bx_credits_withdraw_requested',
+            'ETEMPLATE_WITHDRAW_CANCELED' => 'bx_credits_withdraw_canceled',
+            'ETEMPLATE_WITHDRAW_SENT' => 'bx_credits_withdraw_sent',
+
+            // some language keys
+            'T' => array (
+                'txt_sample_single' => '_bx_credits_txt_sample_single',
+                'grant_popup' => '_bx_credits_grid_popup_title_htr_grant',
+                'send_popup' => '_bx_credits_grid_popup_title_htr_send',
+                'withdraw_request_popup' => '_bx_credits_grid_popup_title_wdw_withdraw_request',
+            ),
+        );
+
+        $this->_aJsClasses = array_merge($this->_aJsClasses, array(
+            'studio' => 'BxCreditsStudio',
+            'checkout' => 'BxCreditsCheckout',
+            'subscribe' => 'BxCreditsSubscribe',
+            'withdraw' => 'BxCreditsWithdraw',
+        ));
+
+        $this->_aJsObjects = array_merge($this->_aJsObjects, array(
+            'studio' => 'oBxCreditsStudio',
+            'checkout' => 'oBxCreditsCheckout',
+            'subscribe' => 'oBxCreditsSubscribe',
+            'withdraw' => 'oBxCreditsWithdraw',
+        ));
+
+        $sPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = array(
+            'add_bundle_popup' =>  $sPrefix . '-add-bundle-popup',
+            'edit_bundle_popup' =>  $sPrefix . '-edit-bundle-popup',
+
+            'send_popup' =>  $sPrefix . '-send-popup',
+            'grant_popup' =>  $sPrefix . '-grant-popup',
+            'withdraw_request_popup' =>  $sPrefix . '-withdraw-request-popup',
+            'withdraw_confirm_popup' =>  $sPrefix . '-withdraw-confirm-popup',
+            'withdraw_field_rate' => $sPrefix . '-wff-rate',
+            'withdraw_field_amount' => $sPrefix . '-wff-amount',
+            'withdraw_field_result' => $sPrefix . '-wff-result',
+
+            'edit_profile_popup' =>  $sPrefix . '-edit-profile-popup',
+
+            'subscribe_popup' =>  $sPrefix . '-subscribe-popup',
+        );
+
+        $this->_aPrefixes = array(
+            'style' => 'bx-credits',
+        );
+
+        $oPayments = BxDolPayments::getInstance();
+        $this->_iAuthor = (int)$oPayments->getOption('site_admin');
+        $this->_aCurrency = array(
+            'code' => $oPayments->getOption('default_currency_code'),
+            'sign' => $oPayments->getOption('default_currency_sign')
+        );
+        
+        $this->_sCheckoutSessionKey = $this->_sName . '_checkout';
+    }
+
+    public function getHtmlIds($sKey = '')
+    {
+        if(empty($sKey))
+            return $this->_aHtmlIds;
+
+        return isset($this->_aHtmlIds[$sKey]) ? $this->_aHtmlIds[$sKey] : '';
+    }
+
+    public function getAuthor()
+    {
+    	return $this->_iAuthor;
+    }
+
+    public function getCurrency()
+    {
+    	return $this->_aCurrency;
+    }
+
+    public function isWithdraw()
+    {
+        return getParam($this->CNF['PARAM_WITHDRAW']) == 'on';
+    }
+
+    public function getWithdrawEmail()
+    {
+        $sEmail = getParam($this->CNF['PARAM_WITHDRAW_EMAIL']);
+        if(empty($sEmail))
+            $sEmail = getParam('site_email');
+
+        return $sEmail;
+    }
+
+    public function getWithdrawClearing()
+    {
+        return (int)getParam($this->CNF['PARAM_WITHDRAW_CLEARING']);
+    }
+
+    public function getWithdrawMinimum()
+    {
+        return (int)getParam($this->CNF['PARAM_WITHDRAW_MINIMUM']);
+    }
+
+    public function getWithdrawRemaining()
+    {
+        return (int)getParam($this->CNF['PARAM_WITHDRAW_REMAINING']);
+    }
+
+    public function getTransferTypesForClearing()
+    {
+        return [
+            BX_CREDITS_TRANSFER_TYPE_PURCHASE,
+            BX_CREDITS_TRANSFER_TYPE_CHECKOUT,
+            BX_CREDITS_TRANSFER_TYPE_GRANT,
+            BX_CREDITS_TRANSFER_TYPE_SEND,
+            BX_CREDITS_TRANSFER_TYPE_SERVICE
+        ];
+    }
+
+    public function getTransferTypesForSpending()
+    {
+        return [
+            BX_CREDITS_TRANSFER_TYPE_CHECKOUT,
+            BX_CREDITS_TRANSFER_TYPE_SEND,
+            BX_CREDITS_TRANSFER_TYPE_WITHDRAW,
+            BX_CREDITS_TRANSFER_TYPE_SERVICE
+        ];
+    }
+
+    public function getPrecision()
+    {
+        return (int)getParam($this->CNF['PARAM_PRECISION']);
+    }
+
+    public function getConversionRateUse()
+    {
+        return $this->_getConversionRate('use');
+    }
+
+    public function getConversionRateWithdraw()
+    {
+        return $this->_getConversionRate('withdraw');
+    }
+
+    public function getCheckoutUrl()
+    {
+        return bx_absolute_url(BxDolPermalinks::getInstance()->permalink($this->CNF['URL_CHECKOUT']));
+    }
+
+    public function getBundleUrl($aBundle)
+    {
+        return bx_absolute_url(BxDolPermalinks::getInstance()->permalink($this->CNF['URL_HOME']));
+    }
+
+    public function getBundleName($sName)
+    {
+        return uriGenerate($sName, $this->CNF['TABLE_BUNDLES'], $this->CNF['FIELD_NAME'], ['empty' => 'cat', 'divider' => '_', 'lowercase' => false]);
+    }
+
+    public function getBundleDescription($aBundle)
+    {
+        if(empty($aBundle[$this->CNF['FIELD_BONUS']]))
+            return $aBundle[$this->CNF['FIELD_AMOUNT']];
+
+        return _t('_bx_credits_txt_n_plus_m_for_free', $aBundle[$this->CNF['FIELD_AMOUNT']], $aBundle[$this->CNF['FIELD_BONUS']]);
+    }
+
+    public function getCheckoutData()
+    {
+        return BxDolSession::getInstance()->getUnsetValue($this->_sCheckoutSessionKey);
+    }
+
+    public function setCheckoutData($aData)
+    {
+        return BxDolSession::getInstance()->setValue($this->_sCheckoutSessionKey, $aData);
+    }
+
+    public function getOrder($iLength = 16)
+    {
+        return strtoupper(genRndPwd($iLength, false));
+    }
+
+    public function constructCheckoutCustomData()
+    {
+        $aParams = func_get_args();
+        return urlencode(base64_encode(implode('|', $aParams)));
+    }
+
+    public function deconstructCheckoutCustomData($sData)
+    {
+        return explode('|', base64_decode(urldecode($sData)));
+    }
+
+    /**
+     * Convert Credits to Money
+     */
+    public function convertC2M($fCredits, $fRate = false, $iPrecision = false)
+    {
+        if($fRate === false)
+            $fRate = $this->getConversionRateUse();
+        if($iPrecision === false)
+            $iPrecision = $this->getPrecision();
+
+        return round((float)$fCredits * $fRate, $iPrecision);
+    }
+
+    /**
+     * Convert Money to Credits
+     */
+    public function convertM2C($fMoney, $fRate = false, $iPrecision = false)
+    {
+        if($fRate === false)
+            $fRate = $this->getConversionRateUse();
+        if($iPrecision === false)
+            $iPrecision = $this->getPrecision();
+
+        return round((float)$fMoney / $fRate, $iPrecision);
+    }
+
+    /*
+     * Internal methods
+     */
+    protected function _getConversionRate($sType)
+    {
+        $fRate = (float)getParam($this->CNF['PARAM_CR_' . strtoupper($sType)]);
+
+        return $fRate > 0 && $fRate <= 1 ? $fRate : 1;
+    }
+}
+
+/** @} */

+ 124 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGrid.php

@@ -0,0 +1,124 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxCreditsGrid extends BxTemplGrid
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_iUserId;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_credits';
+    	$this->_oModule = BxDolModule::getInstance($this->_sModule);
+    	if(!$oTemplate)
+            $oTemplate = $this->_oModule->_oTemplate;
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $iUserId = bx_get_logged_profile_id();
+        if($iUserId !== false) {
+            $this->_iUserId = (int)$iUserId;
+            $this->_aQueryAppend['user_id'] = $this->_iUserId;
+        }
+    }
+
+    public function getFormCallBackUrlAPI($sAction, $iId = 0)
+    {
+         return '/api.php?r=system/perfom_action_api/TemplServiceGrid/&params[]=&o=' . $this->_sObject . '&a=' . $sAction . '&id=' . $iId;
+    }
+
+    protected function _getProfile($mixedValue) 
+    {
+        if(is_numeric($mixedValue) && (int)$mixedValue == 0)
+            $mixedValue = $this->_oModule->_oConfig->getAuthor();
+
+        $oProfile = BxDolProfile::getInstanceMagic($mixedValue);
+        if(!$oProfile)
+            return $mixedValue;
+
+        return $oProfile->getUnit(0, ['template' => ['name' => 'unit', 'size' => 'icon']]);
+    }
+
+    protected function _performActionWithProfileAmount($sAction)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $oForm = $this->_getFormObject($sAction);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $iProfile = (int)$oForm->getCleanValue('profile');
+            $fAmount = (float)$oForm->getCleanValue('amount');
+            $sMessage = $oForm->getCleanValue('message');
+
+            $aResult = $this->_oModule->{'process' . bx_gen_method_name($sAction)}($this->_iUserId, $iProfile, $fAmount, $sMessage);
+            $aResult = $this->_onPerformAction($aResult);
+
+            return $this->_bIsApi ? $aResult : echoJson($aResult);
+        }
+        else {
+            if($this->_bIsApi)
+                return $this->getFormBlockAPI($oForm, $sAction);
+
+            $sContent = BxTemplFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds($sAction . '_popup'), _t($CNF['T'][$sAction . '_popup']), $this->_oModule->_oTemplate->parseHtmlByName('credit_form.html', [
+                'form_id' => $oForm->aFormAttrs['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            ]));
+
+            echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+        }
+    }
+    
+    protected function _onPerformAction($aResult)
+    {
+        $aRes = [];
+
+        if((int)$aResult['code'] == 0) {
+            if(!empty($aResult['id']))
+                $aRes = $this->_bIsApi ? [] : ['grid' => $this->getCode(false), 'blink' => $aResult['id']];
+            else
+                $aRes = ($sMsg = _t(!empty($aResult['msg']) ? $aResult['msg'] : '_bx_credits_msg_action_performed')) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : ['msg' => $sMsg];
+        }
+        else
+            $aRes = ($sMsg = _t(!empty($aResult['msg']) ? $aResult['msg'] : '_bx_credits_err_cannot_perform_action')) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : ['msg' => $sMsg];
+
+        return $aRes;
+    }
+
+    protected function _getFormObject($sAction)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_CREDIT'], $CNF['OBJECT_FORM_CREDIT_DISPLAY_' . strtoupper($sAction)]);
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction;
+        $oForm->setUserId($this->_iUserId);
+
+        return $oForm;
+    }
+    
+    protected function _getId()
+    {
+        $aIds = bx_get('ids');
+        if(!empty($aIds) && is_array($aIds))
+            return array_shift($aIds);
+
+        if(($iId = bx_get('id')) !== false)
+            return (int)$iId;
+
+        return false;
+    }
+}
+
+/** @} */

+ 105 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridHistoryAdministration.php

@@ -0,0 +1,105 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxCreditsGrid.php');
+
+class BxCreditsGridHistoryAdministration extends BxCreditsGrid
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sDefaultSortingOrder = 'DESC';
+    }
+
+    public function performActionGrant()
+    {
+        $sAction = 'grant';
+
+        return $this->_performActionWithProfileAmount($sAction);
+    }
+
+    protected function _getCellDirection($mixedValue, $sKey, $aField, $aRow)
+    {
+        if(in_array($mixedValue, [BX_CREDITS_DIRECTION_IN, BX_CREDITS_DIRECTION_OUT]))
+            $mixedValue = _t('_bx_credits_txt_direction_' . $mixedValue);
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellFirstPid($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_getProfile($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellSecondPid($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_getProfile($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAmount($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_oModule->convertC2S($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellOrder($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellDate($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue, BX_FORMAT_DATE, true), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellCleared($mixedValue, $sKey, $aField, $aRow)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if($aRow[$CNF['FIELD_H_DIRECTION']] == BX_CREDITS_DIRECTION_IN) {
+            if(empty($mixedValue)) {
+                $iPeriod = (int)$aRow['wdw_clearing'];
+                if($iPeriod == 0) {
+                    $iPeriod = $this->_oModule->_oConfig->getWithdrawClearing();
+                    if($iPeriod == 0)
+                        $iPeriod = 1;
+                }
+
+                $oDate = date_create('@' . $aRow[$CNF['FIELD_H_DATE']]);
+                date_add($oDate, new DateInterval('P' . $iPeriod . 'D'));
+                $mixedValue = date_format($oDate, 'U');
+            }
+
+            $mixedValue = bx_time_js($mixedValue, BX_FORMAT_DATE, true);
+        }
+        else 
+            $mixedValue = '';
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oModule->_oTemplate->addJs([
+            'jquery.form.min.js'
+        ]);
+
+        $this->_oModule->_oTemplate->addCss([
+            'main.css'
+        ]);
+
+        $oForm = new BxTemplFormView([]);
+        $oForm->addCssJs();
+    }
+}
+
+/** @} */

+ 39 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridHistoryCommon.php

@@ -0,0 +1,39 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxCreditsGridHistoryAdministration.php');
+
+class BxCreditsGridHistoryCommon extends BxCreditsGridHistoryAdministration
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    public function performActionSend()
+    {
+        $sAction = 'send';
+
+        return $this->_performActionWithProfileAmount($sAction);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(empty($this->_iUserId) || $this->_iUserId != bx_get_logged_profile_id())
+            return [];
+
+        $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `first_pid`=?", $this->_iUserId);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+}
+
+/** @} */

+ 68 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridOrdersAdministration.php

@@ -0,0 +1,68 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxCreditsGrid.php');
+
+class BxCreditsGridOrdersAdministration extends BxCreditsGrid
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sDefaultSortingOrder = 'DESC';
+        $this->_aQueryReset = array($this->_aOptions['order_get_field'], $this->_aOptions['order_get_dir'], $this->_aOptions['paginate_get_start'], $this->_aOptions['paginate_get_per_page']);
+    }
+
+    protected function _getCellProfileId($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_getProfile($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellBundle($mixedValue, $sKey, $aField, $aRow)
+    {
+        if(!$this->_bIsApi)
+            $mixedValue = $this->_oTemplate->parseHtmlByName('bundle_link.html', [
+                'href' => $this->_oModule->_oConfig->getBundleUrl(['id' => $aRow['bundle_id']]),
+                'title' => bx_html_attribute($mixedValue),
+                'content' => $mixedValue
+            ]);
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellType($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(_t('_bx_credits_grid_column_value_ord_type_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAdded($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue, BX_FORMAT_DATE, true), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellExpired($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$mixedValue = (int)$mixedValue != 0 ? bx_time_js($mixedValue, BX_FORMAT_DATE, true): _t('_bx_credits_txt_never');
+    		
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getProfile($mixedValue) 
+    {
+        $oProfile = BxDolProfile::getInstanceMagic($mixedValue);
+        if(!$oProfile)
+            return $mixedValue;
+
+        return $oProfile->getUnit(0, ['template' => ['name' => 'unit', 'size' => 'icon']]);
+    }
+}
+
+/** @} */

+ 109 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridProfilesAdministration.php

@@ -0,0 +1,109 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxCreditsGridProfilesAdministration extends BxTemplGrid
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->_sModule = 'bx_credits';
+    	$this->_oModule = BxDolModule::getInstance($this->_sModule);
+    	if(!$oTemplate)
+            $oTemplate = $this->_oModule->_oTemplate;
+
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    public function getFormCallBackUrlAPI($sAction, $iId = 0)
+    {
+         return '/api.php?r=system/perfom_action_api/TemplServiceGrid/&params[]=&o=' . $this->_sObject . '&a=' . $sAction . '&id=' . $iId;
+    }
+
+    public function performActionEdit()
+    {
+        $sAction = 'edit';
+
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId)
+                return false;
+
+            $aIds = [$iId];
+        }
+
+        $iId = $aIds[0];
+
+        $aProfile = $this->_oModule->_oDb->getProfile(['type' => 'id', 'id' => $iId]);
+        if(empty($aProfile) || !is_array($aProfile))
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $oForm = $this->_getFormObject($sAction, $aProfile);
+        $oForm->initChecker($aProfile);
+
+        if($oForm->isSubmittedAndValid()) {
+            if($oForm->update($iId) !== false)
+                $aRes = $this->_bIsApi ? [] : ['grid' => $this->getCode(false), 'blink' => $iId];
+            else
+                $aRes = ($sMsg = _t('_bx_credits_txt_err_cannot_perform_action')) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : ['msg' => $sMsg];
+
+            return $this->_bIsApi ? $aRes : echoJson($aRes);
+        }
+        else {
+            if($this->_bIsApi)
+                return $this->getFormBlockAPI($oForm, $sAction, $iId);
+
+            $sContent = BxTemplFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('edit_profile_popup'), _t('_bx_credits_grid_popup_title_pfl_edit', BxDolProfile::getInstanceMagic($aProfile['id'])->getDisplayName()), $this->_oModule->_oTemplate->parseHtmlByName('profile_form.html', [
+                'form_id' => $oForm->aFormAttrs['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            ]));
+
+            echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false, 'removeOnClose' => true]]]);
+        }
+    }
+
+    protected function _getFormObject($sAction, $aProfile = array())
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_PROFILE'], $CNF['OBJECT_FORM_PROFILE_DISPLAY_' . strtoupper($sAction)]);
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction;
+        if(!empty($aProfile['id']))
+            $oForm->aFormAttrs['action'] .= '&id=' . $aProfile['id'];
+
+        return $oForm;
+    }
+
+    protected function _getCellId($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_getProfile($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellBalanceCleared($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_oModule->getProfileBalanceCleared($aRow['id']), $sKey, $aField, $aRow);
+    }
+
+    protected function _getProfile($mixedValue) 
+    {
+        $oProfile = BxDolProfile::getInstanceMagic($mixedValue);
+        if(!$oProfile)
+            return $mixedValue;
+
+        return $oProfile->getUnit(0, ['template' => ['name' => 'unit', 'size' => 'icon']]);
+    }
+}
+
+/** @} */

+ 106 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridWithdrawalsAdministration.php

@@ -0,0 +1,106 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxCreditsGrid.php');
+
+class BxCreditsGridWithdrawalsAdministration extends BxCreditsGrid
+{
+    protected $_bWithdraw;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sDefaultSortingOrder = 'DESC';
+
+        $this->_bWithdraw = $this->_oModule->_oConfig->isWithdraw();
+    }
+
+
+    public function performActionWithdrawConfirm()
+    {
+        if(!$this->_bWithdraw)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $sAction = 'withdraw_confirm';
+
+        $iId = $this->_getId();
+        if(!$iId)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $aResult = $this->_oModule->processWithdrawConfirm($this->_iUserId, $iId);
+        $aResult = $this->_onPerformAction($aResult);
+
+        return $this->_bIsApi ? $aResult : echoJson($aResult);
+    }
+
+    protected function _getActionWithdrawConfirm($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(!$this->_bWithdraw || $aRow['status'] != BX_CREDITS_WITHDRAWAL_STATUS_REQUESTED)
+            return $this->_bIsApi ? [] : '';
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getCellProfileId($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_getProfile($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellPerformerId($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(!empty($mixedValue) ? $this->_getProfile($mixedValue) : '', $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAmount($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_oModule->convertC2S($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellOrder($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAdded($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue, BX_FORMAT_DATE, true), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellConfirmed($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(!empty($mixedValue) ? bx_time_js($mixedValue, BX_FORMAT_DATE, true) : '', $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellStatus($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(_t('_bx_credits_txt_withdrawal_status_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oModule->_oTemplate->addJs([
+            'jquery.form.min.js', 
+            'withdraw.js'
+        ]);
+
+        $this->_oModule->_oTemplate->addCss([
+            'main.css',
+            'withdraw.css'
+        ]);
+
+        $oForm = new BxTemplFormView([]);
+        $oForm->addCssJs();
+    }
+}
+
+/** @} */

+ 84 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsGridWithdrawalsCommon.php

@@ -0,0 +1,84 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxCreditsGridWithdrawalsAdministration.php');
+
+class BxCreditsGridWithdrawalsCommon extends BxCreditsGridWithdrawalsAdministration
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    public function performActionWithdrawRequest()
+    {
+        if(!$this->_bWithdraw)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $sAction = 'withdraw_request';
+
+        return $this->_performActionWithProfileAmount($sAction);
+    }
+
+    public function performActionWithdrawCancel()
+    {
+        if(!$this->_bWithdraw)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $sAction = 'withdraw_cancel';
+
+        $iId = $this->_getId();
+        if(!$iId)
+            return $this->_bIsApi ? [] : echoJson([]);
+
+        $aResult = $this->_oModule->processWithdrawCancel($iId);
+        $aResult = $this->_onPerformAction($aResult);
+
+        return $this->_bIsApi ? $aResult : echoJson($aResult);
+    }
+
+    public function getCode($isDisplayHeader = true)
+    {
+        $sResult = parent::getCode($isDisplayHeader);
+        if(!empty($sResult) && $isDisplayHeader)
+            $sResult = $this->_oModule->_oTemplate->getJsCode('withdraw') . $sResult;
+
+        return $sResult;
+    }
+
+    protected function _getActionWithdrawRequest($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(!$this->_bWithdraw)
+            return $this->_bIsApi ? [] : '';
+
+        return parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionWithdrawCancel($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(!$this->_bWithdraw || $aRow['status'] != BX_CREDITS_WITHDRAWAL_STATUS_REQUESTED)
+            return $this->_bIsApi ? [] : '';
+
+        return parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(empty($this->_iUserId) || $this->_iUserId != bx_get_logged_profile_id())
+            return [];
+
+        $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `profile_id`=?", $this->_iUserId);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+}
+
+/** @} */

+ 1528 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsModule.php

@@ -0,0 +1,1528 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define('BX_CREDITS_TRANSFER_TYPE_PURCHASE', 'purchase');
+define('BX_CREDITS_TRANSFER_TYPE_CHECKOUT', 'checkout');
+define('BX_CREDITS_TRANSFER_TYPE_CANCELLATION', 'cancellation');
+define('BX_CREDITS_TRANSFER_TYPE_GRANT', 'grant');
+define('BX_CREDITS_TRANSFER_TYPE_SEND', 'send');
+define('BX_CREDITS_TRANSFER_TYPE_WITHDRAW', 'withdraw');
+define('BX_CREDITS_TRANSFER_TYPE_SERVICE', 'service');
+
+define('BX_CREDITS_DIRECTION_IN', 'in');
+define('BX_CREDITS_DIRECTION_OUT', 'out');
+
+define('BX_CREDITS_ORDER_TYPE_SINGLE', 'single'); //--- one-time payment
+define('BX_CREDITS_ORDER_TYPE_RECURRING', 'recurring'); //--- recurring payment (subscription)
+
+define('BX_CREDITS_WITHDRAWAL_STATUS_REQUESTED', 'requested');
+define('BX_CREDITS_WITHDRAWAL_STATUS_CANCELED', 'canceled');
+define('BX_CREDITS_WITHDRAWAL_STATUS_CONFIRMED', 'confirmed');
+
+class BxCreditsModule extends BxBaseModGeneralModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    public function actionGetProfiles()
+    {
+        $sTerm = bx_get('term');
+
+        $a = BxDolService::call('system', 'profiles_search', array($sTerm), 'TemplServiceProfiles');
+
+        echoJson($a);
+    }
+
+    public function actionCheckBundleName()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$sName = bx_process_input(bx_get('name'));
+    	if(empty($sName))
+            return echoJson(array());
+
+        $sResult = '';
+
+        $iId = (int)bx_get('id');
+        if(!empty($iId)) {
+            $aBundle = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iId)); 
+            if(strcmp($sName, $aBundle[$CNF['FIELD_NAME']]) == 0) 
+                $sResult = $sName;
+        }
+
+    	echoJson(array(
+            'name' => !empty($sResult) ? $sResult : $this->_oConfig->getBundleName($sName)
+    	));
+    }
+
+    public function actionCheckout()
+    {
+        $iBuyerId = bx_get_logged_profile_id();
+        if(!$iBuyerId)
+            return echoJson(['code' => 1, 'msg' => _t('_bx_credits_err_unknown_buyer')]);
+
+        $sErrIncorrectData = _t('_bx_credits_err_incorrect_data');
+
+        $aData = $this->_oConfig->getCheckoutData();
+        if(empty($aData) || !is_array($aData))
+            return echoJson(['code' => 2, 'msg' => $sErrIncorrectData]);
+
+        $fConversion = $this->_oConfig->getConversionRateUse();
+        $iPrecision = $this->_oConfig->getPrecision();
+
+        $iSellerId = (int)$aData['seller'];
+        $fAmount = (float)$aData['amount'];
+        $aCustomData = $this->_oConfig->deconstructCheckoutCustomData($aData['custom']);
+
+        $oPayments = BxDolPayments::getInstance();
+        $aPpOrders = $oPayments->getPendingOrdersInfo(['id' => (int)$aCustomData[1]]);
+        if(!empty($aPpOrders) && is_array($aPpOrders)) {
+            $aPpOrder = reset($aPpOrders);
+            if(empty($aPpOrder) || !is_array($aPpOrder) || (int)$aPpOrder['seller_id'] != $iSellerId)
+                return echoJson(['code' => 3, 'msg' => $sErrIncorrectData]);
+
+            $fAmountM = (float)$aPpOrder['amount'];
+            $sCurrencyDefault = $oPayments->getCurrencyCode();
+            if(strcmp($aPpOrder['currency'], $sCurrencyDefault) != 0)
+                $fAmountM = $oPayments->convert ($fAmountM, $aPpOrder['currency'], $sCurrencyDefault);
+
+            if($this->_oConfig->convertM2C($fAmountM, $fConversion, $iPrecision) != $fAmount)
+                return echoJson(['code' => 3, 'msg' => $sErrIncorrectData]);
+        }
+
+        $fBalance = (float)$this->_oDb->getProfile(['type' => 'balance', 'id' => $iBuyerId]);
+        if($fAmount > $fBalance)
+            return echoJson(['code' => 4, 'msg' => _t('_bx_credits_err_low_balance')]);
+
+        $sOrder = $this->_oConfig->getOrder();
+        $sInfo = '_bx_credits_txt_history_info_checkout';
+        $sData = serialize([
+            'conversion' => $fConversion,
+            'precision' => $iPrecision
+        ]);
+
+        $this->updateProfileBalance($iBuyerId, $iSellerId, -$fAmount, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sOrder, $sInfo, $sData);
+        $this->updateProfileBalance($iSellerId, $iBuyerId, $fAmount, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sOrder, $sInfo, $sData);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-checkout 'bx_credits', 'checkout' - hook after checkout with creadits was performed
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `checkout`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `seller` - [int] seller profile id
+         *      - `buyer` - [int] buyer profile id
+         *      - `amount` - [float] amount was paid
+         *      - `order` - [string] order number for processed checkout action
+         * @hook @ref hook-bx_credits-checkout
+         */
+        bx_alert($this->getName(), 'checkout', 0, false, [
+            'seller' => $iSellerId,
+            'buyer' => $iBuyerId,
+            'amount' => $fAmount, 
+            'order' => $sOrder
+        ]);
+
+        return echoJson([
+            'code' => 0,
+            'redirect' => bx_append_url_params($aData['return_data_url'], ['o' => $sOrder, 'c' => $aData['custom']])
+        ]);
+    }
+
+    public function actionSubscribe()
+    {
+        $iBuyerId = bx_get_logged_profile_id();
+        if(!$iBuyerId)
+            return echoJson(array('code' => 1, 'msg' => _t('_bx_credits_err_unknown_buyer')));
+
+        $aData = $this->_oConfig->getCheckoutData();
+        if(empty($aData) || !is_array($aData))
+            return echoJson(array('code' => 2, 'msg' => _t('_bx_credits_err_incorrect_data')));
+
+        $iSellerId = (int)$aData['seller'];
+        $fAmount = (float)$aData['amount'];
+
+        $fBalance = (float)$this->_oDb->getProfile(array('type' => 'balance', 'id' => $iBuyerId));
+        if($fAmount > $fBalance)
+            return echoJson(array('code' => 3, 'msg' => _t('_bx_credits_err_low_balance')));
+
+        $sUnique = $this->_oConfig->getOrder(9);
+        $sCustomer = 'bx_cus_' . $sUnique;
+        $sSubscription = 'bx_sub_' . $sUnique;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-subscribe 'bx_credits', 'subscribe' - hook after subscription was registered
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `subscribe`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `seller` - [int] seller profile id
+         *      - `buyer` - [int] buyer profile id
+         *      - `amount` - [float] amount was paid
+         *      - `trial` - [boolean] if trial period is available or not
+         *      - `customer` - [string] unique customer id for newly registered subscription
+         *      - `subscription` - [string] unique subscription id
+         * @hook @ref hook-bx_credits-subscribe
+         */
+        bx_alert($this->getName(), 'subscribe', 0, false, [
+            'seller' => $iSellerId,
+            'buyer' => $iBuyerId,
+            'amount' => $fAmount, 
+            'trial' => $aData['trial'],
+            'customer' => $sCustomer,
+            'subscription' => $sSubscription
+        ]);
+
+        return echoJson([
+            'code' => 0,
+            'redirect' => bx_append_url_params($aData['return_data_url'], [
+                'cs' => $sCustomer, 
+                'sb' => $sSubscription,
+                'tr' => $aData['trial'],
+                'c' => $aData['custom']
+            ])
+        ]);
+    }
+
+    public function serviceGetSafeServices()
+    {
+        return [
+            'GetBlockBundles' => '',
+            'GetBlockOrders' => '',
+            'GetBlockHistory' => '',
+            'AddToCart' => ''
+        ];
+    }
+
+    public function serviceGetCheckoutUrl()
+    {
+        return $this->_oConfig->getCheckoutUrl();
+    }
+
+    public function serviceConvertMoneyToCredits($fMoney, $fRate = false, $iPrecision = false)
+    {
+        return $this->_oConfig->convertM2C($fMoney, $fRate, $iPrecision);
+    }
+
+    public function serviceConvertCreditsToMoney($fCredits, $fRate = false, $iPrecision = false)
+    {
+        return $this->_oConfig->convertC2M($fCredits, $fRate, $iPrecision);
+    }
+
+    public function serviceValidateCheckout($iSeller, $iBuyer, $fAmount, $sOrder)
+    {
+        $fAmount = (float)$fAmount;
+
+        $aOut = $this->_oDb->getHistory(array('type' => 'row_by', 'by' => array('first_pid' => $iBuyer, 'direction' => BX_CREDITS_DIRECTION_OUT, 'order' => $sOrder)));
+        if(empty($aOut) || !is_array($aOut) || (int)$aOut['second_pid'] != (int)$iSeller)
+            return false;
+        
+        $aDataOut = unserialize($aOut['data']);
+        if($this->_oConfig->convertM2C($fAmount, $aDataOut['conversion'], $aDataOut['precision']) != (float)$aOut['amount'])
+            return false;
+
+        $aIn = $this->_oDb->getHistory(array('type' => 'row_by', 'by' => array('first_pid' => $iSeller, 'direction' => BX_CREDITS_DIRECTION_IN, 'order' => $sOrder)));
+        if(empty($aIn) || !is_array($aIn) || (int)$aIn['second_pid'] != (int)$iBuyer)
+            return false;
+
+        $aDataIn = unserialize($aIn['data']);
+        if($this->_oConfig->convertM2C($fAmount, $aDataIn['conversion'], $aDataIn['precision']) != (float)$aIn['amount'])
+            return false;
+
+        return true;
+    }
+
+    public function serviceGetBlockCheckout()
+    {
+        $oBuyer = BxDolProfile::getInstance();
+        if(!$oBuyer)
+            return MsgBox(_t('_bx_credits_err_unknown_buyer'));
+        
+        $iSeller = (int)bx_get('seller');
+        $oSeller = BxDolProfile::getInstance($iSeller);
+        if(!$oSeller)
+            return MsgBox(_t('_bx_credits_err_unknown_seller'));
+
+        $fAmountM = $fAmountMInDef = (float)bx_get('amount');
+
+        $oPayments = BxDolPayments::getInstance();
+        $sCurrency = bx_process_input(bx_get('currency_code'));
+        $sCurrencyDefault = $oPayments->getCurrencyCode();
+        if(strcmp($sCurrency, $sCurrencyDefault) != 0)
+            $fAmountMInDef = $oPayments->convert($fAmountM, $sCurrency, $sCurrencyDefault);
+
+        $fAmountC = $this->_oConfig->convertM2C($fAmountMInDef);
+
+        $aData = [
+            'seller' => $iSeller,
+            'currency' => [
+                'code' => $sCurrency,
+                'sign' => bx_process_input(bx_get('currency_sign')),
+            ],
+            'amountm' => $fAmountM,
+            'amountc' => $fAmountC
+        ];
+
+        $iItemsCount = (int)bx_get('items_count');
+        for($i = 0; $i < $iItemsCount; $i++) {
+            $aData['items'][] = [
+                'title' => bx_process_input(bx_get('item_title_' . $i)),
+                'quantity' => (int)bx_get('item_quantity_' . $i)
+            ];
+        }
+
+        $this->_oConfig->setCheckoutData([
+            'seller' => $iSeller,
+            'amount' => $fAmountC,
+            'custom' => bx_process_input(bx_get('custom')),
+            'return_data_url' => bx_process_input(bx_get('return_data_url')),
+        ]);
+        return $this->_oTemplate->getBlockCheckout($oBuyer, $oSeller, $aData);
+    }
+
+    public function serviceGetPopupSubscribe($aData)
+    {
+        $oBuyer = BxDolProfile::getInstance();
+        if(!$oBuyer)
+            return ['msg' => _t('_bx_credits_err_unknown_buyer')];
+
+        $iSeller = (int)$aData['seller'];
+        $oSeller = BxDolProfile::getInstance($iSeller);
+        if(!$oSeller)
+            return ['msg' => _t('_bx_credits_err_unknown_seller')];
+
+        $fAmountM = $fAmountMInDef = (float)$aData['amount'];
+
+        $oPayments = BxDolPayments::getInstance();
+        $sCurrency = $aData['currency_code'];
+        $sCurrencyDefault = $oPayments->getCurrencyCode();
+        if(strcmp($sCurrency, $sCurrencyDefault) != 0)
+            $fAmountMInDef = $oPayments->convert($fAmountM, $sCurrency, $sCurrencyDefault);
+
+        $fAmountC = $this->_oConfig->convertM2C($fAmountMInDef);
+
+        $aData = array_merge($aData, [
+            'currency' => [
+                'code' => $sCurrency,
+                'sign' => $aData['currency_sign'],
+            ],
+            'amountm' => $fAmountM,
+            'amountc' => $fAmountC
+        ]);
+
+        $this->_oConfig->setCheckoutData([
+            'seller' => $iSeller,
+            'amount' => $fAmountC,
+            'trial' => (int)$aData['trial'] > 0,
+            'custom' => bx_process_input($aData['custom']),
+            'return_data_url' => bx_process_input($aData['return_data_url']),
+        ]);
+        return $this->_oTemplate->getPopupSubscribe($oBuyer, $oSeller, $aData);
+    }
+
+    public function serviceGetMenuItemAddonAmount()
+    {
+        $iProfileId = bx_get_logged_profile_id();
+        if(!$iProfileId)
+            return '';
+
+        return array(
+            'unit' => $this->_oTemplate->getUnit(), 
+            'value' => $this->convertC2S($this->_oDb->getProfile(array('type' => 'balance', 'id' => $iProfileId)), false)
+        );
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-page_blocks Page Blocks
+     * @subsubsection bx_credits-get_block_bundles get_block_bundles
+     * 
+     * @code bx_srv('bx_credits', 'get_block_bundles'); @endcode
+     * 
+     * Get page block with bundles
+     * 
+     * @see BxCreditsModule::serviceGetBlockBundles
+     */
+    /** 
+     * @ref bx_credits-get_block_bundles "get_block_bundles"
+     */
+    public function serviceGetBlockBundles()
+    {
+        $mixedResult = $this->_oTemplate->getBlockBundles();
+
+        if($this->_bIsApi)
+            return [
+                bx_api_get_block('bundles', $mixedResult)
+            ];
+
+        return $mixedResult;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-page_blocks Page Blocks
+     * @subsubsection bx_credits-get_block_orders_note get_block_orders_note
+     * 
+     * @code bx_srv('bx_credits', 'get_block_orders_note', [...]); @endcode
+     * 
+     * Get page block with a notice for orders usage.
+     *
+     * @return HTML string with block content to display on the site.
+     * 
+     * @see BxCreditsModule::serviceBlockLicensesNote
+     */
+    /** 
+     * @ref bx_credits-get_block_orders_note "get_block_orders_note"
+     */
+    public function serviceGetBlockOrdersNote()
+    {
+        return MsgBox(_t('_bx_credits_page_block_content_orders_common_note'));
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-page_blocks Page Blocks
+     * @subsubsection bx_credits-get_block_orders get_block_orders
+     * 
+     * @code bx_srv('bx_credits', 'get_block_orders', [...]); @endcode
+     * 
+     * Get page block with a list of orders purchased by currently logged member.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxCreditsModule::serviceGetBlockOrders
+     */
+    /** 
+     * @ref bx_credits-get_block_orders "get_block_orders"
+     */
+    public function serviceGetBlockOrders($sType = 'common') 
+    {
+        return $this->_getBlockOrders($sType);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-page_blocks Page Blocks
+     * @subsubsection bx_credits-get_block_history get_block_history
+     * 
+     * @code bx_srv('bx_credits', 'get_block_history', [...]); @endcode
+     * 
+     * Get page block with a list of all changes with credits.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxCreditsModule::serviceGetBlockHistory
+     */
+    /** 
+     * @ref bx_credits-get_block_history "get_block_history"
+     */
+    public function serviceGetBlockHistory($sType = 'common') 
+    {
+        return $this->_getBlockHistory($sType);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-page_blocks Page Blocks
+     * @subsubsection bx_credits-get_block_withdrawals get_block_withdrawals
+     * 
+     * @code bx_srv('bx_credits', 'get_block_withdrawals', [...]); @endcode
+     * 
+     * Get page block with a list of all credits withdrawals.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxCreditsModule::serviceGetBlockHistory
+     */
+    /** 
+     * @ref bx_credits-get_block_withdrawals "get_block_withdrawals"
+     */
+    public function serviceGetBlockWithdrawals($sType = 'common') 
+    {
+        if(!$this->_oConfig->isWithdraw())
+            return MsgBox (_t('_bx_credits_msg_action_withdrawals_disabled'));
+
+        return $this->_getBlockWithdrawals($sType);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-page_blocks Page Blocks
+     * @subsubsection bx_credits-get_block_profiles get_block_profiles
+     * 
+     * @code bx_srv('bx_credits', 'get_block_profiles', [...]); @endcode
+     * 
+     * Get page block with a list of profiles for managing their parameters.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxCreditsModule::serviceGetBlockProfiles
+     */
+    /** 
+     * @ref bx_credits-get_block_profiles "get_block_profiles"
+     */
+    public function serviceGetBlockProfiles($sType = 'common') 
+    {
+        return $this->_getBlockProfiles($sType);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-get_payment_data get_payment_data
+     * 
+     * @code bx_srv('bx_credits', 'get_payment_data', [...]); @endcode
+     * 
+     * Get an array with module's description. Is needed for payments processing module.
+     * 
+     * @return an array with module's description.
+     * 
+     * @see BxCreditsModule::serviceGetPaymentData
+     */
+    /** 
+     * @ref bx_credits-get_payment_data "get_payment_data"
+     */
+    public function serviceGetPaymentData()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oPermalink = BxDolPermalinks::getInstance();
+        return array_merge($this->_aModule, [
+            'url_browse_order_common' => bx_absolute_url(bx_append_url_params($oPermalink->permalink($CNF['URL_ORDERS_COMMON']), ['filter' => '{order}'], false)),
+            'url_browse_order_administration' => bx_absolute_url(bx_append_url_params($oPermalink->permalink($CNF['URL_ORDERS_ADMINISTRATION']), ['filter' => '{order}'], false))
+        ]);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-add_to_cart add_to_cart
+     * 
+     * @code bx_srv('bx_credits', 'add_to_cart', [...]); @endcode
+     * 
+     * Adds an item in Shopping Cart. Is needed for payments processing module.
+     * 
+     * @return an array with module's description.
+     * 
+     * @see BxCreditsModule::serviceAddToCart
+     */
+    /** 
+     * @ref bx_credits-add_to_cart "add_to_cart"
+     */
+    public function serviceAddToCart($iItemId)
+    {
+        $iVendor = $this->_oConfig->getAuthor();
+        $oPayments = BxDolPayments::getInstance();
+
+        $aResult = $oPayments->addToCart($iVendor, $this->_oConfig->getName(), $iItemId, 1, true);
+        if(isset($aResult['code']) && (int)$aResult['code'] != 0)
+            return $this->_bIsApi ? [bx_api_get_msg($aResult['message'])] : $aResult;
+
+        return $this->_bIsApi ? [
+            'url' => bx_api_get_relative_url($oPayments->getCartUrl($iVendor))
+        ] : $aResult;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-get_cart_item get_cart_item
+     * 
+     * @code bx_srv('bx_credits', 'get_cart_item', [...]); @endcode
+     * 
+     * Get an array with prodict's description. Is used in Shopping Cart in payments processing module.
+     * 
+     * @param $iItemId product's ID.
+     * @return an array with prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxCreditsModule::serviceGetCartItem
+     */
+    /** 
+     * @ref bx_credits-get_cart_item "get_cart_item"
+     */
+    public function serviceGetCartItem($iItemId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        if(!$iItemId)
+            return array();
+
+        $aItem = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iItemId));
+        if(empty($aItem) || !is_array($aItem))
+            return array();
+
+        return array (
+            'id' => $aItem[$CNF['FIELD_ID']],
+            'author_id' => $this->_oConfig->getAuthor(),
+            'name' => $aItem[$CNF['FIELD_NAME']],
+            'title' => _t($aItem[$CNF['FIELD_TITLE']]),
+            'description' => $this->_oConfig->getBundleDescription($aItem),
+            'url' => $this->_oConfig->getBundleUrl($aItem),
+            'price_single' => $aItem[$CNF['FIELD_PRICE']],
+            'price_recurring' => '',
+            'period_recurring' => 1,
+            'period_unit_recurring' => '',
+            'trial_recurring' => ''
+        );
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-get_cart_items get_cart_items
+     * 
+     * @code bx_srv('bx_credits', 'get_cart_items', [...]); @endcode
+     * 
+     * Get an array with prodicts' descriptions by seller. Is used in Manual Order Processing in payments processing module.
+     * 
+     * @param $iSellerId seller ID.
+     * @return an array with prodicts' descriptions. Empty array is returned if something is wrong or seller doesn't have any products.
+     * 
+     * @see BxCreditsModule::serviceGetCartItems
+     */
+    /** 
+     * @ref bx_credits-get_cart_items "get_cart_items"
+     */
+    public function serviceGetCartItems($iSellerId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        $iAuthorId = $this->_oConfig->getAuthor();
+        if($iSellerId != $iAuthorId && !BxDolAcl::getInstance()->isMemberLevelInSet([MEMBERSHIP_ID_ADMINISTRATOR], $iSellerId))
+            return array();
+
+        $aItems = $this->_oDb->getBundle(array('type' => 'all', 'active' => 1));
+
+        $aResult = array();
+        foreach($aItems as $aItem)
+            $aResult[] = array(
+                'id' => $aItem[$CNF['FIELD_ID']],
+                'author_id' => $iAuthorId,
+                'name' => $aItem[$CNF['FIELD_NAME']],
+                'title' => _t($aItem[$CNF['FIELD_TITLE']]),
+                'description' => $this->_oConfig->getBundleDescription($aItem),
+                'url' => $this->_oConfig->getBundleUrl($aItem),
+                'price_single' => $aItem[$CNF['FIELD_PRICE']],
+                'price_recurring' => '',
+                'period_recurring' => 1,
+                'period_unit_recurring' => '',
+                'trial_recurring' => ''
+            );
+
+        return $aResult;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-register_cart_item register_cart_item
+     * 
+     * @code bx_srv('bx_credits', 'register_cart_item', [...]); @endcode
+     * 
+     * Register a processed single time payment inside the Credits module. Is called with payment processing module after the payment was registered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxCreditsModule::serviceRegisterCartItem
+     */
+    /** 
+     * @ref bx_credits-register_cart_item "register_cart_item"
+     */
+    public function serviceRegisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_SINGLE);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-register_subscription_item register_subscription_item
+     * 
+     * @code bx_srv('bx_credits', 'register_subscription_item', [...]); @endcode
+     * 
+     * Register a processed subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was registered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxCreditsModule::serviceRegisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_credits-register_subscription_item "register_subscription_item"
+     */
+    public function serviceRegisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_RECURRING);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-reregister_cart_item reregister_cart_item
+     * 
+     * @code bx_srv('bx_credits', 'reregister_cart_item', [...]); @endcode
+     * 
+     * Reregister a single time payment inside the Credits module. Is called with payment processing module after the payment was reregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemIdOld old item ID.
+     * @param $iItemIdNew new item ID.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxCreditsModule::serviceReregisterCartItem
+     */
+    /** 
+     * @ref bx_credits-reregister_cart_item "reregister_cart_item"
+     */
+    public function serviceReregisterCartItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+        return array();
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-reregister_subscription_item reregister_subscription_item
+     * 
+     * @code bx_srv('bx_credits', 'reregister_subscription_item', [...]); @endcode
+     * 
+     * Reregister a subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was reregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemIdOld old item ID.
+     * @param $iItemIdNew new item ID.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxCreditsModule::serviceReregisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_credits-reregister_subscription_item "reregister_subscription_item"
+     */
+    public function serviceReregisterSubscriptionItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+        return array();
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-unregister_cart_item unregister_cart_item
+     * 
+     * @code bx_srv('bx_credits', 'unregister_cart_item', [...]); @endcode
+     * 
+     * Unregister an earlier processed single time payment inside the Credits module. Is called with payment processing module after the payment was unregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return boolean value determining where the payment was unregistered or not.
+     * 
+     * @see BxCreditsModule::serviceUnregisterCartItem
+     */
+    /** 
+     * @ref bx_credits-unregister_cart_item "unregister_cart_item"
+     */
+    public function serviceUnregisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_SINGLE);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-unregister_subscription_item unregister_subscription_item
+     * 
+     * @code bx_srv('bx_credits', 'unregister_subscription_item', [...]); @endcode
+     * 
+     * Unregister an earlier processed subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was unregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return boolean value determining where the subscription was unregistered or not.
+     * 
+     * @see BxCreditsModule::serviceUnregisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_credits-unregister_subscription_item "unregister_subscription_item"
+     */
+    public function serviceUnregisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+    	return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_CREDITS_ORDER_TYPE_RECURRING); 
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_credits Credits
+     * @subsection bx_credits-payments Payments
+     * @subsubsection bx_credits-cancel_subscription_item cancel_subscription_item
+     * 
+     * @code bx_srv('bx_credits', 'cancel_subscription_item', [...]); @endcode
+     * 
+     * Cancel an earlier processed subscription (recurring payment) inside the Credits module. Is called with payment processing module after the subscription was canceled there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return boolean value determining where the subscription was canceled or not.
+     * 
+     * @see BxCreditsModule::serviceCancelSubscriptionItem
+     */
+    /** 
+     * @ref bx_credits-cancel_subscription_item "cancel_subscription_item"
+     */
+    public function serviceCancelSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder)
+    {
+    	return true;
+    }
+
+    public function serviceGetProfileBalance($iProfileId = 0)
+    {
+        return $this->getProfileBalance($iProfileId);
+    }
+
+    public function serviceUpdateProfileBalance($iFirstPid, $fAmount, $iSecondPid = 0, $sOrder = '', $sInfo = '')
+    {
+        if(empty($sInfo))
+            $sInfo = '_bx_credits_txt_history_info_service';
+
+        return $this->updateProfileBalance($iFirstPid, $iSecondPid, $fAmount, BX_CREDITS_TRANSFER_TYPE_SERVICE, $sOrder, $sInfo);
+    }
+    
+    public function serviceMakePayment($iBuyerPid, $fAmount, $iSellerPid, $sOrder = '')
+    {
+        $sInfo = '_bx_credits_txt_history_info_checkout';
+
+        if(!$this->updateProfileBalance($iBuyerPid, $iSellerPid, -$fAmount, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sOrder, $sInfo))
+            return false;
+
+        if(!$this->updateProfileBalance($iSellerPid, $iBuyerPid, $fAmount, $sOrder, BX_CREDITS_TRANSFER_TYPE_CHECKOUT, $sInfo)) {
+            $this->updateProfileBalance($iBuyerPid, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_CANCELLATION, $sOrder, '_bx_credits_txt_history_info_cancellation');
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Delete all content by profile 
+     * @param $iProfileId profile id 
+     * @return number of deleted items
+     */
+    public function serviceDeleteEntitiesByAuthor ($iProfileId)
+    {
+        $this->_oDb->deleteProfile(array('id' => $iProfileId));
+        $this->_oDb->deleteHistory(array('first_pid' => $iProfileId));
+    }
+
+
+    /*
+     * Common methods
+     */
+    public function convertC2S($fCredits, $bWithUnit = true)
+    {
+        $sResult = '';
+        if($bWithUnit)
+            $sResult .= $this->_oTemplate->getUnit();
+
+        $iPrecision = $this->_oConfig->getPrecision();
+        $sResult .= sprintf("%01." . $iPrecision . "f", round((float)$fCredits, $iPrecision));
+
+        return $sResult;
+    }
+
+    public function getProfileLimits($iProfileId = 0)
+    {
+        if(empty($iProfileId))
+            $iProfileId = bx_get_logged_profile_id();
+
+        $aResults = [
+            'wdw_clearing' => $this->_oConfig->getWithdrawClearing(),
+            'wdw_minimum' => $this->_oConfig->getWithdrawMinimum(),
+            'wdw_remaining' => $this->_oConfig->getWithdrawRemaining(),
+        ];
+
+        if(empty($iProfileId))
+            return $aResults;
+
+        $aProfile = $this->_oDb->getProfile(['type' => 'id', 'id' => $iProfileId]);
+
+        foreach($aResults as $sKey => $mixedValue)
+            if(!empty($aProfile[$sKey]))
+                $aResults[$sKey] = $aProfile[$sKey];
+
+        return $aResults;
+    }
+
+    public function getProfileBalance($iProfileId = 0)
+    {
+        if(empty($iProfileId))
+            $iProfileId = bx_get_logged_profile_id();
+
+        if(empty($iProfileId))
+            return 0;
+
+        return (float)$this->_oDb->getProfile(array('type' => 'balance', 'id' => $iProfileId));
+    }
+
+    public function getProfileBalanceCleared($iProfileId = 0)
+    {
+        if(empty($iProfileId))
+            $iProfileId = bx_get_logged_profile_id();
+
+        if(empty($iProfileId))
+            return 0;
+
+        $fCleared = (float)$this->_oDb->getHistory(['type' => 'cleared', 'profile' => $iProfileId]);
+        $fSpent = (float)$this->_oDb->getHistory(['type' => 'spent', 'profile' => $iProfileId]);
+
+        return $fCleared > $fSpent ? round($fCleared - $fSpent, 2) : 0;
+    }
+
+    public function updateProfileBalance($iFirstPid, $iSecondPid, $fAmount, $sType, $sOrder = '', $sInfo = '', $sData = '')
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($sOrder))
+            $sOrder = $this->_oConfig->getOrder();
+
+        if(empty($sData))
+            $sData = serialize(array(
+                'conversion' => $this->_oConfig->getConversionRateUse(),
+                'precision' => $this->_oConfig->getPrecision()
+            ));
+
+        if(!$this->_oDb->updateProfileBalance($iFirstPid, $fAmount))
+            return false;
+
+        $fAmountAbs = abs($fAmount);
+        $sDirection = $fAmount > 0 ? BX_CREDITS_DIRECTION_IN : BX_CREDITS_DIRECTION_OUT;
+
+        $iHistoryId = $this->_oDb->insertHistory([
+            'first_pid' => $iFirstPid,
+            'second_pid' => $iSecondPid,
+            'amount' => $fAmountAbs,
+            'type' => $sType,
+            'direction' => $sDirection,
+            'order' => $sOrder,
+            'data' => $sData,
+            'info' => $sInfo,
+            'date' => time()
+        ]);
+
+        if(!$iHistoryId)
+            return false;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-update_balance 'bx_credits', 'update_balance' - hook after profile's credits balance was updated (credits were transferred from one profile to another)
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `update_balance`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `first_pid` - [int] first profile id
+         *      - `second_pid` - [int] second profile id
+         *      - `amount` - [float] amount was transferred
+         *      - `type` - [string] transfer type: purchase, checkout, cancellation, grant, send, withdraw, service
+         *      - `direction` - [string] transfer direction: in or out
+         *      - `order` - [string] unique order number associated with the transfer
+         * @hook @ref hook-bx_credits-update_balance
+         */
+        bx_alert($this->getName(), 'update_balance', 0, false, [
+            'first_pid' => $iFirstPid,
+            'second_pid' => $iSecondPid,
+            'amount' => $fAmountAbs,
+            'type' => $sType,
+            'direction' => $sDirection,
+            'order' => $sOrder,
+        ]);
+
+        $oSecondProfile = BxDolProfile::getInstanceMagic($iSecondPid);
+        sendMailTemplate($CNF['ETEMPLATE_' . strtoupper($sDirection)], 0, $iFirstPid, array(
+            'second_profile_name' => $oSecondProfile->getDisplayName(),
+            'amount' => $fAmountAbs,
+        ));
+
+        return $iHistoryId;
+    }
+
+    public function processGrant($iUserId, $iProfileId, $fAmount, $sMessage = '')
+    {
+        $CNF = &$this->_oConfig->CNF;
+        
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        if(!$oProfile)
+            return ['code' => 2, 'msg' => '_bx_credits_err_profile_not_found'];
+
+        $sInfo = '_bx_credits_txt_history_info_grant';
+        $iHistoryId = $this->updateProfileBalance($iProfileId, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_GRANT, '', $sInfo);
+        if(!$iHistoryId)
+            return ['code' => 3, 'msg' => '_bx_credits_err_cannot_update_balance'];
+
+        sendMailTemplate($CNF['ETEMPLATE_GRANTED'], 0, $iProfileId, [
+            'amount' => $fAmount,
+            'message' => $sMessage
+        ]);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-granted 'bx_credits', 'granted' - hook after some amount of credits was granted to a profile
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `granted`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile` - [int] recipient profile id
+         *      - `amount` - [float] amount was granted
+         * @hook @ref hook-bx_credits-granted
+         */
+        bx_alert($this->getName(), 'granted', 0, false, [
+            'profile' => $iProfileId,
+            'amount' => $fAmount,
+        ]);
+
+        return ['code' => 0, 'id' => $iHistoryId];
+    }
+
+    public function processSend($iUserId, $iProfileId, $fAmount, $sMessage = '')
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $fAmountAvail = $this->getProfileBalance($iUserId);
+        if($fAmount > $fAmountAvail)
+            return ['code' => 1, 'msg' => '_bx_credits_err_low_balance'];
+
+        $oProfile = null;
+        if(!$iProfileId || !($oProfile = BxDolProfile::getInstance($iProfileId)))
+            return ['code' => 2, 'msg' => '_bx_credits_err_profile_not_found'];
+
+        $sOrder = $this->_oConfig->getOrder();
+        $sInfo = !empty($sMessage) ? $sMessage : '_bx_credits_txt_history_info_send';
+
+        $iHistoryId = $this->updateProfileBalance($iUserId, $iProfileId, -$fAmount, BX_CREDITS_TRANSFER_TYPE_SEND, $sOrder, $sInfo);
+        if(!$iHistoryId)
+            return ['code' => 3, 'msg' => '_bx_credits_err_cannot_update_balance'];
+
+        if(!$this->updateProfileBalance($iProfileId, $iUserId, $fAmount, BX_CREDITS_TRANSFER_TYPE_SEND, $sOrder, $sInfo)) {
+            $sInfo = '_bx_credits_txt_history_info_cancellation';
+            $iHistoryId = $this->updateProfileBalance($iUserId, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_CANCELLATION, $sOrder, $sInfo);
+
+            return ['code' => 3, 'id' => $iHistoryId, 'msg' => '_bx_credits_err_cannot_update_balance'];
+        }
+
+        $oUser = BxDolProfile::getInstance($iUserId);
+        sendMailTemplate($CNF['ETEMPLATE_RECEIVED'], 0, $iProfileId, [
+            'performer_id' => $iUserId,
+            'performer_name' => $oUser->getDisplayName(),
+            'performer_link' => $oUser->getUrl(),
+            'amount' => $fAmount,
+            'order' => $sOrder,
+            'message' => $sMessage
+        ]);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-sent 'bx_credits', 'sent' - hook after some amount of credits was sent from one profile to another
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `sent`
+         * - $object_id - not used
+         * - $sender_id - sender profile id
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `performer` - [int] sender profile id
+         *      - `profile` - [int] recipient profile id
+         *      - `amount` - [float] amount was sent
+         *      - `order` - [string] unique order number associated with 'sent' action
+         * @hook @ref hook-bx_credits-sent
+         */
+        bx_alert($this->getName(), 'sent', 0, $iUserId, [
+            'performer' => $iUserId,
+            'profile' => $iProfileId,
+            'amount' => $fAmount,
+            'order' => $sOrder,
+        ]);
+
+        return ['code' => 0, 'id' => $iHistoryId];
+    }
+
+    public function processWithdrawRequest($iUserId, $iProfileId, $fAmount, $sMessage = '')
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($iProfileId))
+            $iProfileId = bx_get_logged_profile_id ();
+
+        if(empty($iProfileId))
+            return ['code' => 1, 'msg' => '_bx_credits_err_login_required'];
+
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        if(!$oProfile)
+            return ['code' => 1, 'msg' => '_bx_credits_err_login_required'];
+
+        $fBalanceCleared = $this->getProfileBalanceCleared($iProfileId);
+        if($fAmount > $fBalanceCleared)
+            return ['code' => 2, 'msg' => '_bx_credits_err_low_balance'];
+
+        $aProfileInfo = $this->_oDb->getProfile(['type' => 'id', 'id' => $iProfileId]);
+
+        $iWithdrawMinimum = !empty($aProfileInfo['wdw_minimum']) ? (int)$aProfileInfo['wdw_minimum'] : (int)getParam($CNF['PARAM_WITHDRAW_MINIMUM']);
+        if($fAmount < $iWithdrawMinimum)
+            return ['code' => 3, 'msg' => _t('_bx_credits_err_withdraw_minimum', $iWithdrawMinimum)];
+
+        $fBalance = $this->getProfileBalance($iProfileId);
+        $iWithdrawRemaining = !empty($aProfileInfo['wdw_remaining']) ? (int)$aProfileInfo['wdw_remaining'] : (int)getParam($CNF['PARAM_WITHDRAW_REMAINING']);
+        if(($fBalance - $fAmount) < $iWithdrawRemaining)
+            return ['code' => 4, 'msg' => _t('_bx_credits_err_withdraw_remaining', $iWithdrawRemaining)];
+
+        $aResult = ['code' => 5, 'msg' => '_bx_credits_err_cannot_send'];
+
+        $fRate = $this->_oConfig->getConversionRateWithdraw();
+
+        $iId = $this->_oDb->insertWithdrawal([
+            'profile_id' => $iProfileId, 
+            'amount' => $fAmount,
+            'rate' => $fRate,
+            'message' => $sMessage,
+            'added' => time()
+        ]);
+
+        if(!$iId)
+            return $aResult;
+
+        $aTemplateVars = [
+            'profile_link' => $oProfile->getUrl(),
+            'profile_name' => $oProfile->getDisplayName(),
+            'amount' => $fAmount,
+            'rate' => $fRate,
+            'message' => $sMessage,
+            'confirm_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_HISTORY_ADMINISTRATION']))
+        ];
+        $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($CNF['ETEMPLATE_WITHDRAW_REQUESTED'], $aTemplateVars);
+        if(!$aTemplate)
+            return $aResult;
+
+        $sEmail = $this->_oConfig->getWithdrawEmail();
+        if(!sendMail($sEmail, $aTemplate['Subject'], $aTemplate['Body'], 0, [], BX_EMAIL_SYSTEM))
+            return $aResult;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-withdraw_requested 'bx_credits', 'withdraw_requested' - hook after a profile requested to withdraw some amount of credits from his balance
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `withdraw_requested`
+         * - $object_id - not used
+         * - $sender_id - performer profile id
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile` - [int] performer profile id
+         *      - `amount` - [float] amount to withdraw
+         *      - `rate` - [float] withdraw conversion rate
+         * @hook @ref hook-bx_credits-withdraw_requested
+         */
+        bx_alert($this->getName(), 'withdraw_requested', 0, $iProfileId, [
+            'profile' => $iProfileId,
+            'amount' => $fAmount,
+            'rate' => $fRate
+        ]);
+
+        return ['code' => 0, 'id' => $iId];
+    }
+
+    public function processWithdrawCancel($iId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aWithdrawal = $this->_oDb->getWithdrawal(['type' => 'id', 'id' => $iId]);
+        if(empty($aWithdrawal) || !is_array($aWithdrawal))
+            return ['code' => 1, 'msg' => '_bx_credits_err_withdrawal_not_found'];
+
+        $iProfileId = (int)$aWithdrawal['profile_id'];
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        if(!$oProfile)
+            return ['code' => 2, 'msg' => '_bx_credits_err_profile_not_found'];
+
+        $aResult = ['code' => 3, 'msg' => '_bx_credits_err_cannot_cancel_withdrawal'];       
+
+        $aTemplateVars = [
+            'profile_link' => $oProfile->getUrl(),
+            'profile_name' => $oProfile->getDisplayName(),
+            'amount' => $aWithdrawal['amount'],
+            'manage_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_WITHDRAWALS_ADMINISTRATION']))
+        ];
+        $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($CNF['ETEMPLATE_WITHDRAW_CANCELED'], $aTemplateVars);
+        if(!$aTemplate)
+            return $aResult;
+
+        $sEmail = $this->_oConfig->getWithdrawEmail();
+        if(!sendMail($sEmail, $aTemplate['Subject'], $aTemplate['Body'], 0, [], BX_EMAIL_SYSTEM))
+            return $aResult;
+
+        if(!$this->_oDb->updateWithdrawal(['status' => BX_CREDITS_WITHDRAWAL_STATUS_CANCELED], ['id' => $iId]))
+            return $aResult;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-withdraw_canceled 'bx_credits', 'withdraw_canceled' - hook after a profile canceled his withdrawal request
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `withdraw_canceled`
+         * - $object_id - not used
+         * - $sender_id - performer profile id
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile` - [int] performer profile id
+         *      - `amount` - [float] amount to withdraw
+         *      - `rate` - [float] withdraw conversion rate
+         * @hook @ref hook-bx_credits-withdraw_canceled
+         */
+        bx_alert($this->getName(), 'withdraw_canceled', $iId, $iProfileId, [
+            'profile' => $iProfileId,
+            'amount' => $aWithdrawal['amount'],
+        ]);
+
+        return ['code' => 0, 'id' => $iId];
+    }
+
+    public function processWithdrawConfirm($iUserId, $iWithdrawalId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aWithdrawal = $this->_oDb->getWithdrawal(['type' => 'id', 'id' => $iWithdrawalId]);
+        if(empty($aWithdrawal) || !is_array($aWithdrawal))
+            return ['code' => 1, 'msg' => '_bx_credits_err_withdrawal_not_found'];
+
+        $iProfileId = (int)$aWithdrawal['profile_id'];
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        if(!$oProfile)
+            return ['code' => 2, 'msg' => '_bx_credits_err_profile_not_found'];
+
+        $fAmount = (float)$aWithdrawal['amount'];
+        $fBalance = $this->getProfileBalance($iProfileId);
+        if($fAmount > $fBalance)
+            return ['code' => 3, 'msg' => '_bx_credits_err_low_balance'];
+
+        $sInfo = '_bx_credits_txt_history_info_withdraw';
+        $iHistoryId = $this->updateProfileBalance($iProfileId, 0, -$fAmount, BX_CREDITS_TRANSFER_TYPE_WITHDRAW, '', $sInfo);
+        if(!$iHistoryId)
+            return ['code' => 4, 'msg' => '_bx_credits_err_cannot_update_balance'];
+
+        $aHistory = $this->_oDb->getHistory(['type' => 'id', 'id' => $iHistoryId]);
+        
+        if(!$this->_oDb->updateWithdrawal(['performer_id' => $iUserId, 'order' => $aHistory['order'], 'confirmed' => time(), 'status' => BX_CREDITS_WITHDRAWAL_STATUS_CONFIRMED], ['id' => $iWithdrawalId]))
+            return $aResult;
+
+        sendMailTemplate($CNF['ETEMPLATE_WITHDRAW_SENT'], 0, $iProfileId, [
+            'amount' => $fAmount
+        ], BX_EMAIL_SYSTEM);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-withdraw_sent 'bx_credits', 'withdraw_sent' - hook after the amount of credits requested to withdraw was sent
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `withdraw_sent`
+         * - $object_id - not used
+         * - $sender_id - performer profile id
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile` - [int] performer profile id
+         *      - `amount` - [float] amount to withdraw
+         *      - `rate` - [float] withdraw conversion rate
+         * @hook @ref hook-bx_credits-withdraw_sent
+         */
+        bx_alert($this->getName(), 'withdraw_sent', $iWithdrawalId, $iUserId, [
+            'performer' => $iUserId,
+            'profile' => $iProfileId,
+            'amount' => $fAmount,
+        ]);
+
+        return ['code' => 0, 'id' => $iWithdrawalId];
+    }
+
+    public function processClearing()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aItems = $this->_oDb->getHistory(['type' => 'clearing', 'clearing' => $this->_oConfig->getWithdrawClearing()]);
+        if(empty($aItems) || !is_array($aItems))
+            return;
+
+        $iNow = time();
+        foreach($aItems as $aItem) {
+            $this->_oDb->updateHistory([$CNF['FIELD_H_CLEARED'] => $iNow], [$CNF['FIELD_H_ID'] => $aItem['id']]);
+
+            //TODO: Some action(s) related to clearing can be performed here.
+        }
+    }
+
+    /*
+     * Internal methods
+     */
+    protected function _serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aItem = $this->serviceGetCartItem($iItemId);
+        if(empty($aItem) || !is_array($aItem))
+            return array();
+
+        $aBundle = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iItemId));
+        if(empty($aBundle) || !is_array($aBundle))
+            return array();
+
+        $iTrial = 0;
+        $sDuration = '';
+        $sAction = 'register';
+        if($sType == BX_CREDITS_ORDER_TYPE_RECURRING && isset($CNF['FIELD_DURATION_RECURRING'], $CNF['FIELD_TRIAL_RECURRING'])) {
+            $iTrial = $aBundle[$CNF['FIELD_TRIAL_RECURRING']];
+            $sDuration = $aBundle[$CNF['FIELD_DURATION_RECURRING']];
+
+            if($this->_oDb->isOrderByPbo($iClientId, $iItemId, $sOrder))
+                $sAction = 'prolong';
+        }
+
+        if(!$this->_oDb->{$sAction . 'Order'}($iClientId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType, $sDuration, $iTrial))
+            return array();
+
+        $sInfo = '_bx_credits_txt_history_info_purchase';
+        $fAmount = ((float)$aBundle[$CNF['FIELD_AMOUNT']] + (float)$aBundle[$CNF['FIELD_BONUS']]) * $iItemCount;
+        if(!$this->updateProfileBalance($iClientId, 0, $fAmount, BX_CREDITS_TRANSFER_TYPE_PURCHASE, '', $sInfo))
+            return array();
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-order_register 'bx_credits', 'order_register' - hook after the payment for a bundle of credits was processed with payment processing module
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `order_register`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile_id` - [int] client (buyer) profile id
+         *      - `bundle_id` - [int] bundle id
+         *      - `product_id` - [int] bundle id
+         *      - `count` - [int] number of items in order
+         *      - `order` - [string] order number provided with payment processing module
+         *      - `license` - [string] license number provided with payment processing module
+         *      - `type` - [string] payment type ('single' or 'recurring'), current version supports 'single' type payments for bundles only
+         *      - `duration` - [string] isn't supported
+         *      - `trial` - [string] isn't supported
+         * @hook @ref hook-bx_credits-order_register
+         */
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-order_prolong 'bx_credits', 'order_prolong' - hook isn't currently used because 'single' type payments for bundles are only supported
+         * It's equivalent to @ref hook-bx_credits-order_register
+         * @hook @ref hook-bx_credits-order_prolong
+         */
+        bx_alert($this->getName(), 'order_' . $sAction, 0, false, [
+            'profile_id' => $iClientId,
+            'bundle_id' => $iItemId,
+            'product_id' => $iItemId,   //--- Alias for 'bundle_id'
+            'count' => $iItemCount,
+            'order' => $sOrder,
+            'license' => $sLicense,
+            'type' => $sType,
+            'duration' => $sDuration,
+            'trial' => $iTrial
+        ]);
+
+        $oClient = BxDolProfile::getInstanceMagic($iClientId);
+        $oSeller = BxDolProfile::getInstanceMagic($iSellerId);
+        $sSellerUrl = $oSeller->getUrl();
+        $sSellerName = $oSeller->getDisplayName();
+
+        sendMailTemplate($CNF['ETEMPLATE_PURCHASED'], 0, $iClientId, array(
+            'client_name' => $oClient->getDisplayName(),
+            'bundle_name' => $aBundle[$CNF['FIELD_NAME']],
+            'bundle_title' => _t($aBundle[$CNF['FIELD_TITLE']]),
+            'bundle_url' => $this->_oConfig->getBundleUrl($aBundle),
+            'vendor_url' => $sSellerUrl,
+            'vendor_name' => $sSellerName,
+            'order' => $sOrder,
+            'license' => $sLicense,
+            'notes' => _t('_bx_credits_txt_purchased_note', $sSellerUrl, $sSellerName),
+        ));
+
+        return $aItem;
+    }
+
+    protected function _serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aBundle = $this->_oDb->getBundle(array('type' => 'id', 'id' => $iItemId));
+        if(empty($aBundle) || !is_array($aBundle))
+            return false;
+
+        if(!$this->_oDb->unregisterOrder($iClientId, $iItemId, $sOrder, $sLicense, $sType))
+            return false;
+
+        $fAmount = (float)$aBundle[$CNF['FIELD_AMOUNT']] + (float)$aBundle[$CNF['FIELD_BONUS']];
+        $aProfile = $this->_oDb->getProfile(array('type' => 'id', 'id' => $iClientId));
+        if(!empty($aProfile) && is_array($aProfile))
+            $this->_oDb->updateProfile(array('balance' => (float)$aProfile['balance'] - $fAmount), array('id' => $aProfile['id']));
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_credits-order_unregister 'bx_credits', 'order_unregister' - hook after the payment for a bundle of credits was refunded with payment processing module
+         * - $unit_name - equals `bx_credits`
+         * - $action - equals `order_unregister`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile_id` - [int] client (buyer) profile id
+         *      - `bundle_id` - [int] bundle id
+         *      - `product_id` - [int] bundle id
+         *      - `count` - [int] number of items in order
+         *      - `order` - [string] order number provided with payment processing module
+         *      - `license` - [string] license number provided with payment processing module
+         *      - `type` - [string] payment type ('single' or 'recurring'), current version supports 'single' type payments for bundles only
+         * @hook @ref hook-bx_credits-order_unregister
+         */
+        bx_alert($this->getName(), 'order_unregister', 0, false, [
+            'profile_id' => $iClientId,
+            'bundle_id' => $iItemId,
+            'product_id' => $iItemId,   //--- Alias for 'bundle_id'
+            'count' => $iItemCount,
+            'order' => $sOrder,
+            'license' => $sLicense,
+            'type' => $sType,
+        ]);
+
+        return true;
+    }
+
+    protected function _getBlockOrders($sType) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sGrid = $CNF['OBJECT_GRID_ORDERS_' . strtoupper($sType)];
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return $this->_bIsApi ? [] : '';
+
+        if($this->_bIsApi)
+            return [
+                bx_api_get_block('grid', $oGrid->getCodeAPI())
+            ];
+        
+        return [
+            'content' => $oGrid->getCode(),
+            'menu' => $CNF['OBJECT_MENU_MANAGE_SUBMENU']
+        ];
+    }
+
+    protected function _getBlockHistory($sType) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sGrid = $CNF['OBJECT_GRID_HISTORY_' . strtoupper($sType)];
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return $this->_bIsApi ? [] : '';
+
+        if($this->_bIsApi)
+            return [
+                bx_api_get_block('grid', $oGrid->getCodeAPI())
+            ];
+
+        return [
+            'content' => $oGrid->getCode(),
+            'menu' => $CNF['OBJECT_MENU_MANAGE_SUBMENU']
+        ];
+    }
+
+    protected function _getBlockWithdrawals($sType) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sGrid = $CNF['OBJECT_GRID_WITHDRAWALS_' . strtoupper($sType)];
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return $this->_bIsApi ? [] : '';
+
+        if($this->_bIsApi)
+            return [
+                bx_api_get_block('grid', $oGrid->getCodeAPI())
+            ];
+
+        return [
+            'content' => $oGrid->getCode(),
+            'menu' => $CNF['OBJECT_MENU_MANAGE_SUBMENU']
+        ];
+    }
+
+    protected function _getBlockProfiles($sType) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sKey = 'OBJECT_GRID_PROFILES_' . strtoupper($sType);
+        if(empty($CNF[$sKey]))
+            return $this->_bIsApi ? [] : '';
+
+        $sGrid = $CNF[$sKey];
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return $this->_bIsApi ? [] : '';
+
+        if($this->_bIsApi)
+            return [
+                bx_api_get_block('grid', $oGrid->getCodeAPI())
+            ];
+
+        return [
+            'content' => $oGrid->getCode(),
+            'menu' => $CNF['OBJECT_MENU_MANAGE_SUBMENU']
+        ];
+    }
+}
+
+/** @} */

+ 204 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/classes/BxCreditsTemplate.php

@@ -0,0 +1,204 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxCreditsTemplate extends BxBaseModGeneralTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        $this->MODULE = 'bx_credits';
+
+        parent::__construct($oConfig, $oDb);
+
+        $this->aMethodsToCallAddJsCss = array('getBlockBundles', 'getBlockCheckout');
+    }
+
+    public function getJsCode($sType, $aParams = array(), $bWrap = true)
+    {
+        $aParams = array_merge(array(
+            'aHtmlIds' => $this->_oConfig->getHtmlIds()
+        ), $aParams);
+
+        return parent::getJsCode($sType, $aParams, $bWrap);
+    }
+
+    public function getEmptyAuthor()
+    {
+    	return MsgBox(_t('_bx_credits_msg_empty_author'));
+    }
+
+    public function getUnit()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sUnit = '';
+        if(($sCode = getParam($CNF['PARAM_CODE'])) != '')
+            $sUnit = $sCode;
+        else if(($sIcon = getParam($CNF['PARAM_ICON'])) != '')
+            $sUnit = $this->parseIcon($sIcon, array('class' => $this->_oConfig->getPrefix('style') . '-prefix-icon bx-def-margin-thd-right'));
+
+        return $sUnit;
+    }
+
+    public function getBlockCheckout($oBuyer, $oSeller, $aData)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObject('checkout');
+
+        $sTxtQt = _t('_bx_credits_txt_checkout_qt');
+
+        $fRate = $this->_oConfig->getConversionRateUse();
+        $aCurrency = $this->_oConfig->getCurrency();
+
+        $sCurrencySign = $aCurrency['sign'];
+        if(!empty($aData['currency']['sign']))
+            $sCurrencySign = $aData['currency']['sign'];
+
+        $aTmplVarsItems = array();
+        foreach($aData['items'] as $iIndex => $aItem)
+            $aTmplVarsItems[] = array(
+                'sp' => $sStylePrefix,
+                'item_index' => $iIndex + 1,
+                'item_title' => $aItem['title'],
+                'item_quantity' => $aItem['quantity'] . $sTxtQt
+            );
+
+        $this->addJs(array('checkout.js'));
+        $this->addCss(array('checkout.css'));
+        return $this->parseHtmlByName('checkout.html', array(
+            'sp' => $sStylePrefix,
+            'jo' => $sJsObject,
+            'title' => _t('_bx_credits_txt_checkout_to', $oSeller->getDisplayName()),
+            'bx_repeat:items' => $aTmplVarsItems,
+            'amount' => $sCurrencySign . sprintf("%.2f", (float)($aData['amountm'])),
+            'bx_if:show_rate' => array(
+                'condition' => $fRate != 1,
+                'content' => array(
+                    'sp' => $sStylePrefix,
+                    'rate' => $fRate,
+                )
+            ),
+            'credits' => $this->getModule()->convertC2S($aData['amountc']),
+            'js_code' => $this->getJsCode('checkout')
+        ));
+    }
+
+    public function getBlockBundles()
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $sModule = $this->_oConfig->getName();
+        $iAuthor = $this->_oConfig->getAuthor();
+        $aCurrency = $this->_oConfig->getCurrency();
+
+        $aBundles = $this->_oDb->getBundle(['type' => 'all', 'active' => 1]);
+        if(empty($aBundles) || !is_array($aBundles))
+            return ($sMsg = _t('_Empty')) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : MsgBox($sMsg);
+
+        $oPayment = BxDolPayments::getInstance();
+
+        $aTmplVarsBundles = [];
+        foreach($aBundles as $aBundle) {
+            $iBundle = $aBundle[$CNF['FIELD_ID']];
+
+            $aJs = $oPayment->getAddToCartJs($iAuthor, $this->MODULE, $iBundle, 1, true);
+            if(empty($aJs) || !is_array($aJs))
+                continue;
+
+            list($sJsCode, $sJsMethod) = $aJs;
+
+            $aTmplVarsBundles[] = array_merge($aBundle, [
+                'title' => _t($aBundle[$CNF['FIELD_TITLE']]),
+                'description' => _t($aBundle[$CNF['FIELD_DESCRIPTION']]),
+                'currency_sign' => $aCurrency['sign'],
+                'currency_code' => $aCurrency['code'],
+            ], !$this->_bIsApi ? [
+                'sp' => $sStylePrefix,
+                'bx_if:show_bonus' => [
+                    'condition' => (int)$aBundle[$CNF['FIELD_BONUS']] > 0,
+                    'content' => [
+                        'sp' => $sStylePrefix,
+                        'bonus' => (int)$aBundle[$CNF['FIELD_BONUS']]
+                    ]
+                ],
+                'onclick' => $sJsMethod
+            ] : [
+                'buttons' => [[
+                    'title' => _t('_bx_credits_txt_purchase'),
+                    'request_url' => $sModule . '/add_to_cart/&params[]=' . $iBundle . '&params[]=',
+                ]]
+            ]);
+        }
+
+        return $this->_bIsApi ? $aTmplVarsBundles : $this->parseHtmlByName('bundles.html', [
+            'sp' => $sStylePrefix,
+            'bx_repeat:bundles' => $aTmplVarsBundles,
+            'js_code' => $oPayment->getCartJs()
+        ]);
+    }
+
+    public function getPopupSubscribe($oBuyer, $oSeller, $aData)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $bDynamic = bx_is_dynamic_request();
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObject('subscribe');
+
+        $fRate = $this->_oConfig->getConversionRateUse();
+        $aCurrency = $this->_oConfig->getCurrency();
+
+        $sCurrencySign = $aCurrency['sign'];
+        if(!empty($aData['currency']['sign']))
+            $sCurrencySign = $aData['currency']['sign'];
+
+        if(!empty($aData['period']) && !empty($aData['period_unit'])) {
+            $sPeriod = _t('_bx_credits_txt_subscribe_period_unit_' . $aData['period_unit']);
+            if((int)$aData['period'] > 1);
+                $sPeriod = _t('_bx_credits_txt_subscribe_period_mask', $aData['period'], $sPeriod);
+        }
+        else
+            $sPeriod = _t('_bx_credits_txt_subscribe_lifetime');
+
+        $sInclude = '';
+        $sInclude .= $this->addJs(array('subscribe.js'), $bDynamic);
+        $sInclude .= $this->addCss(array('main.css', 'subscribe.css'), $bDynamic);
+
+        $sContent = ($bDynamic ? $sInclude : '') . $this->parseHtmlByName('subscribe.html', array(
+            'sp' => $sStylePrefix,
+            'jo' => $sJsObject,
+            'title' => _t('_bx_credits_txt_subscribe_for', $oSeller->getDisplayName()),
+            'item_title' => $aData['title'],
+            'amount' => $sCurrencySign . sprintf("%.2f", (float)($aData['amountm'])),
+            'period' => $sPeriod,
+            'bx_if:show_rate' => array(
+                'condition' => $fRate != 1,
+                'content' => array(
+                    'sp' => $sStylePrefix,
+                    'rate' => $fRate,
+                )
+            ),
+            'bx_if:show_trial' => array(
+                'condition' => (int)$aData['trial'] > 0,
+                'content' => array(
+                    'sp' => $sStylePrefix,
+                    'trial' => _t('_bx_credits_txt_subscribe_trial_mask', $aData['trial'], _t('_bx_credits_txt_subscribe_period_unit_day'))
+                )
+            ),
+            'credits' => $this->getModule()->convertC2S($aData['amountc']),
+            'js_code' => $this->getJsCode('subscribe')
+        ));
+
+        return BxTemplFunctions::getInstance()->transBox($this->_oConfig->getHtmlIds('subscribe_popup'), $sContent);
+    }
+}
+
+/** @} */

+ 77 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/install/config.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_credits',
+    'title' => 'Credits',
+    'note' => 'Credits module.',
+    'version' => '15.0.1',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/credits/',
+    'home_uri' => 'credits',
+
+    'db_prefix' => 'bx_credits_',
+    'class_prefix' => 'BxCredits',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Credits',
+
+    /**
+     * 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(),
+
+    /**
+     * Relations Section
+     */
+    'relations' => array(),
+
+);
+
+/** @} */

+ 293 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/install/sql/enable.sql

@@ -0,0 +1,293 @@
+
+-- 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', 'bx_credits', '_bx_credits', 'bx_credits@modules/boonex/credits/|std-icon.svg', IF(ISNULL(@iTypeOrder), 1, @iTypeOrder + 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_credits', '_bx_credits', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
+('bx_credits_enable_withdraw', '', @iCategId, '_bx_credits_option_enable_withdraw', 'checkbox', '', '', '', 1),
+('bx_credits_withdraw_email', '', @iCategId, '_bx_credits_option_withdraw_email', 'digit', '', '', '', 2),
+('bx_credits_withdraw_clearing', '30', @iCategId, '_bx_credits_option_withdraw_clearing', 'digit', '', '', '', 3),
+('bx_credits_withdraw_minimum', '500', @iCategId, '_bx_credits_option_withdraw_minimum', 'digit', '', '', '', 4),
+('bx_credits_withdraw_remaining', '100', @iCategId, '_bx_credits_option_withdraw_remaining', 'digit', '', '', '', 5),
+
+('bx_credits_precision', '2', @iCategId, '_bx_credits_option_precision', 'digit', '', '', '', 10),
+('bx_credits_conversion_rate_use', '1.0', @iCategId, '_bx_credits_option_conversion_rate_use', 'digit', '', '', '', 11),
+('bx_credits_conversion_rate_withdraw', '1.0', @iCategId, '_bx_credits_option_conversion_rate_withdraw', 'digit', '', '', '', 12),
+
+('bx_credits_code', '', @iCategId, '_bx_credits_option_code', 'digit', '', '', '', 20),
+('bx_credits_icon', 'copyright', @iCategId, '_bx_credits_option_icon', 'digit', '', '', '', 21),
+
+('bx_credits_enable_provider', '', @iCategId, '_bx_credits_enable_provider', 'checkbox', '', '', '', 30);
+
+
+-- PAGE: module home
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_credits_home', 'credits-home', '_bx_credits_page_title_sys_home', '_bx_credits_page_title_home', 'bx_credits', 5, 2147483647, 1, 'page.php?i=credits-home', '', '', '', 0, 1, 0, 'BxCreditsPageBrowse', 'modules/boonex/credits/classes/BxCreditsPageBrowse.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_credits_home', 1, 'bx_credits', '_bx_credits_page_block_title_sys_bundles', '_bx_credits_page_block_title_bundles', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:17:"get_block_bundles";}', 0, 1, 1, 0);
+
+-- PAGE: checkout
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `type_id`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_credits_checkout', 'credits-checkout', '_bx_credits_page_title_sys_checkout', '_bx_credits_page_title_checkout', 'bx_credits', 2, 5, 2147483647, 1, 'page.php?i=credits-checkout', '', '', '', 0, 1, 0, 'BxCreditsPageBrowse', 'modules/boonex/credits/classes/BxCreditsPageBrowse.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_credits_checkout', 1, 'bx_credits', '_bx_credits_page_block_title_sys_checkout', '_bx_credits_page_block_title_checkout', 0, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:18:"get_block_checkout";}', 0, 0, 1, 0);
+
+-- PAGE: profile's orders
+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_credits_orders_common', '_bx_credits_page_title_sys_orders_common', '_bx_credits_page_title_orders_common', 'bx_credits', 5, 2147483647, 1, 'credits-orders-common', '', '', '', '', 0, 1, 0, 'BxCreditsPageOrders', 'modules/boonex/credits/classes/BxCreditsPageOrders.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_credits_orders_common', 1, 'bx_credits', '_bx_credits_page_block_title_sys_orders_common_note', '_bx_credits_page_block_title_orders_common_note', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:21:"get_block_orders_note";}', 0, 0, 1, 0),
+('bx_credits_orders_common', 1, 'bx_credits', '_bx_credits_page_block_title_sys_orders_common', '_bx_credits_page_block_title_orders_common', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:16:"get_block_orders";}', 0, 0, 1, 1);
+
+-- PAGE: orders administration
+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_credits_orders_administration', '_bx_credits_page_title_sys_orders_administration', '_bx_credits_page_title_orders_administration', 'bx_credits', 5, 192, 1, 'credits-orders-administration', '', '', '', '', 0, 1, 0, 'BxCreditsPageOrders', 'modules/boonex/credits/classes/BxCreditsPageOrders.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_credits_orders_administration', 1, 'bx_credits', '_bx_credits_page_block_title_sys_orders_administration', '_bx_credits_page_block_title_orders_administration', 11, 192, 'service', 'a:3:{s:6:"module";s:10:"bx_credits";s:6:"method";s:16:"get_block_orders";s:6:"params";a:1:{i:0;s:14:"administration";}}', 0, 0, 1, 0);
+
+-- PAGE: profile's history
+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_credits_history_common', '_bx_credits_page_title_sys_history_common', '_bx_credits_page_title_history_common', 'bx_credits', 5, 2147483647, 1, 'credits-history-common', 'page.php?i=credits-history-common', '', '', '', 0, 1, 0, 'BxCreditsPageHistory', 'modules/boonex/credits/classes/BxCreditsPageHistory.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_credits_history_common', 1, 'bx_credits', '_bx_credits_page_block_title_sys_history_common', '_bx_credits_page_block_title_history_common', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:17:"get_block_history";}}', 0, 1, 0);
+
+-- PAGE: history administration
+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_credits_history_administration', '_bx_credits_page_title_sys_history_administration', '_bx_credits_page_title_history_administration', 'bx_credits', 5, 192, 1, 'credits-history-administration', 'page.php?i=credits-history-administration', '', '', '', 0, 1, 0, 'BxCreditsPageHistory', 'modules/boonex/credits/classes/BxCreditsPageHistory.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_credits_history_administration', 1, 'bx_credits', '_bx_credits_page_block_title_sys_history_administration', '_bx_credits_page_block_title_history_administration', 11, 192, 'service', 'a:3:{s:6:"module";s:10:"bx_credits";s:6:"method";s:17:"get_block_history";s:6:"params";a:1:{i:0;s:14:"administration";}}', 0, 1, 0);
+
+-- PAGE: profile's withdrawals
+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_credits_withdrawals_common', '_bx_credits_page_title_sys_withdrawals_common', '_bx_credits_page_title_withdrawals_common', 'bx_credits', 5, 2147483647, 1, 'credits-withdrawals-common', 'page.php?i=credits-withdrawals-common', '', '', '', 0, 1, 0, 'BxCreditsPageWithdrawals', 'modules/boonex/credits/classes/BxCreditsPageWithdrawals.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_credits_withdrawals_common', 1, 'bx_credits', '_bx_credits_page_block_title_sys_withdrawals_common', '_bx_credits_page_block_title_withdrawals_common', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:21:"get_block_withdrawals";}}', 0, 1, 0);
+
+-- PAGE: withdrawals administration
+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_credits_withdrawals_administration', '_bx_credits_page_title_sys_withdrawals_administration', '_bx_credits_page_title_withdrawals_administration', 'bx_credits', 5, 192, 1, 'credits-withdrawals-administration', 'page.php?i=credits-withdrawals-administration', '', '', '', 0, 1, 0, 'BxCreditsPageWithdrawals', 'modules/boonex/credits/classes/BxCreditsPageWithdrawals.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_credits_withdrawals_administration', 1, 'bx_credits', '_bx_credits_page_block_title_sys_withdrawals_administration', '_bx_credits_page_block_title_withdrawals_administration', 11, 192, 'service', 'a:3:{s:6:"module";s:10:"bx_credits";s:6:"method";s:21:"get_block_withdrawals";s:6:"params";a:1:{i:0;s:14:"administration";}}', 0, 1, 0);
+
+-- PAGE: profiles administration
+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_credits_profiles_administration', '_bx_credits_page_title_sys_profiles_administration', '_bx_credits_page_title_profiles_administration', 'bx_credits', 5, 192, 1, 'credits-profiles-administration', 'page.php?i=credits-profiles-administration', '', '', '', 0, 1, 0, 'BxCreditsPageProfiles', 'modules/boonex/credits/classes/BxCreditsPageProfiles.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_credits_profiles_administration', 1, 'bx_credits', '_bx_credits_page_block_title_sys_profiles_administration', '_bx_credits_page_block_title_profiles_administration', 11, 192, 'service', 'a:3:{s:6:"module";s:10:"bx_credits";s:6:"method";s:18:"get_block_profiles";s:6:"params";a:1:{i:0;s:14:"administration";}}', 0, 1, 0);
+
+
+-- MENU: add to site menu
+SET @iSiteMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_site' AND `active` = 1 AND `order` < 9999 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_site', 'bx_credits', 'credits-home', '_bx_credits_menu_item_title_system_credits', '_bx_credits_menu_item_title_credits', 'page.php?i=credits-home', '', '', 'copyright', 'bx_credits_submenu', 2147483647, 1, 1, IFNULL(@iSiteMenuOrder, 0) + 1);
+
+-- MENU: add to homepage menu
+SET @iHomepageMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_homepage' AND `active` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_homepage', 'bx_credits', 'credits-home', '_bx_credits_menu_item_title_system_credits', '_bx_credits_menu_item_title_credits', 'page.php?i=credits-home', '', '', 'copyright', 'bx_credits_submenu', 2147483647, 1, 1, IFNULL(@iHomepageMenuOrder, 0) + 1);
+
+-- MENU: member toolbar
+SET @iMenuToolbarMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_toolbar_member' LIMIT 1);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `markers`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_toolbar_member', 'bx_credits', 'credits-history', '_bx_credits_menu_item_title_system_history', '_bx_credits_menu_item_title_history', 'page.php?i=credits-history-common', '', '', 'copyright', '', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:26:"get_menu_item_addon_amount";}', '', 0, 2147483646, 0, 1, 0);
+
+-- MENU: Notifications
+SET @iAccountNotificationsMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_account_notifications' AND `order` < 9999);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `markers`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('sys_account_notifications', 'bx_credits', 'notifications-credits', '_bx_credits_menu_item_title_system_notifications', '_bx_credits_menu_item_title_notifications', 'page.php?i=credits-history-common', '', '', 'copyright', '', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:26:"get_menu_item_addon_amount";}', '', '0', 2147483646, 1, 1, @iAccountNotificationsMenuOrder + 1);
+
+-- MENU: account dashboard
+SET @iAccountDashboardMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard' LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES
+('sys_account_dashboard', 'bx_credits', 'credits-manage', '_bx_credits_menu_item_title_system_manage', '_bx_credits_menu_item_title_manage', 'page.php?i=credits-history-common', '', '', 'copyright', '', '', 2147483646, 1, 0, 1, @iAccountDashboardMenuOrder + 1);
+
+-- MENU: module sub-menu
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_credits_submenu', '_bx_credits_menu_title_submenu', 'bx_credits_submenu', 'bx_credits', 8, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_credits_submenu', 'bx_credits', '_bx_credits_menu_set_title_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_credits_submenu', 'bx_credits', 'credits-home', '_bx_credits_menu_item_title_system_home', '_bx_credits_menu_item_title_home', 'page.php?i=credits-home', '', '', '', '', 2147483647, 0, 1, 1);
+
+-- MENU: manage submenu
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_credits_manage_submenu', '_bx_credits_menu_title_manage_submenu', 'bx_credits_manage_submenu', 'bx_credits', 6, 0, 1, 'BxCreditsMenuManage', 'modules/boonex/credits/classes/BxCreditsMenuManage.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_credits_manage_submenu', 'bx_credits', '_bx_credits_menu_set_title_manage_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES 
+('bx_credits_manage_submenu', 'bx_credits', 'credits-history-common', '_bx_credits_menu_item_title_system_history_common', '_bx_credits_menu_item_title_history_common', 'page.php?i=credits-history-common', '', '_self', '', '', '', 2147483646, 1, 0, 1, 1),
+('bx_credits_manage_submenu', 'bx_credits', 'credits-history-administration', '_bx_credits_menu_item_title_system_history_administration', '_bx_credits_menu_item_title_history_administration', 'page.php?i=credits-history-administration', '', '_self', '', '', '', 192, 1, 0, 1, 2),
+('bx_credits_manage_submenu', 'bx_credits', 'credits-orders-common', '_bx_credits_menu_item_title_system_orders_common', '_bx_credits_menu_item_title_orders_common', 'page.php?i=credits-orders-common', '', '_self', '', '', '', 2147483646, 1, 0, 1, 3),
+('bx_credits_manage_submenu', 'bx_credits', 'credits-orders-administration', '_bx_credits_menu_item_title_system_orders_administration', '_bx_credits_menu_item_title_orders_administration', 'page.php?i=credits-orders-administration', '', '_self', '', '', '', 192, 1, 0, 1, 4),
+('bx_credits_manage_submenu', 'bx_credits', 'credits-withdrawals-common', '_bx_credits_menu_item_title_system_withdrawals_common', '_bx_credits_menu_item_title_withdrawals_common', 'page.php?i=credits-withdrawals-common', '', '_self', '', '', '', 2147483646, 1, 0, 1, 5),
+('bx_credits_manage_submenu', 'bx_credits', 'credits-withdrawals-administration', '_bx_credits_menu_item_title_system_withdrawals_administration', '_bx_credits_menu_item_title_withdrawals_administration', 'page.php?i=credits-withdrawals-administration', '', '_self', '', '', '', 192, 1, 0, 1, 6),
+('bx_credits_manage_submenu', 'bx_credits', 'credits-profiles-administration', '_bx_credits_menu_item_title_system_profiles_administration', '_bx_credits_menu_item_title_profiles_administration', 'page.php?i=credits-profiles-administration', '', '_self', '', '', '', 192, 1, 0, 1, 7);
+
+-- MENU: dashboard manage tools
+SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
+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_dashboard_manage_tools', 'bx_credits', 'credits-history-administration', '_bx_credits_menu_item_title_system_admt_credits', '_bx_credits_menu_item_title_admt_credits', 'page.php?i=credits-history-administration', '', '_self', 'copyright', 'a:2:{s:6:"module";s:10:"bx_credits";s:6:"method";s:27:"get_menu_addon_manage_tools";}', '', 192, 1, 0, @iManageMenuOrder + 1);
+
+
+-- GRIDS: bundles
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_credits_bundles', 'Sql', 'SELECT * FROM `bx_credits_bundles` WHERE 1 ', 'bx_credits_bundles', 'id', 'order', 'active', '', 100, NULL, 'start', '', 'period,period_unit,price', '', 'like', '', '', 192, 'BxCreditsGridBundles', 'modules/boonex/credits/classes/BxCreditsGridBundles.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_credits_bundles', 'checkbox', '_sys_select', '1%', 0, '', '', 1),
+('bx_credits_bundles', 'order', '', '1%', 0, '', '', 2),
+('bx_credits_bundles', 'name', '_bx_credits_grid_column_title_name', '23%', 0, 32, '', 3),
+('bx_credits_bundles', 'title', '_bx_credits_grid_column_title_title', '30%', 1, 16, '', 4),
+('bx_credits_bundles', 'amount', '_bx_credits_grid_column_title_amount', '10%', 0, 16, '', 5),
+('bx_credits_bundles', 'bonus', '_bx_credits_grid_column_title_bonus', '5%', 0, 16, '', 6),
+('bx_credits_bundles', 'price', '_bx_credits_grid_column_title_price', '10%', 0, 16, '', 7),
+('bx_credits_bundles', 'actions', '', '20%', 0, '', '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_credits_bundles', 'independent', 'add', '_bx_credits_grid_action_title_add', '', 0, 0, 1),
+('bx_credits_bundles', 'single', 'edit', '_bx_credits_grid_action_title_edit', 'pencil-alt', 1, 0, 1),
+('bx_credits_bundles', 'single', 'delete', '_bx_credits_grid_action_title_delete', 'remove', 1, 1, 2),
+('bx_credits_bundles', 'bulk', 'delete', '_bx_credits_grid_action_title_delete', '', 0, 1, 1);
+
+-- GRIDS: orders
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_credits_orders_administration', 'Sql', 'SELECT `to`.`id` AS `id`, `to`.`profile_id` AS `profile_id`, `to`.`bundle_id` AS `bundle_id`, `tb`.`title` AS `bundle`, `to`.`order` AS `transaction`, `to`.`license` AS `license`, `to`.`type` AS `type`, `to`.`added` AS `added`, `to`.`expired` AS `expired` FROM `bx_credits_orders` AS `to` LEFT JOIN `bx_credits_bundles` AS `tb` ON `to`.`bundle_id`=`tb`.`id` WHERE 1 ', 'bx_credits_orders', 'id', 'added', '', '', 20, NULL, 'start', '', 'tb`.`title,to`.`order,to`.`license,to`.`type', '', 'like', '', '', 192, 'BxCreditsGridOrdersAdministration', 'modules/boonex/credits/classes/BxCreditsGridOrdersAdministration.php'),
+('bx_credits_orders_common', 'Sql', 'SELECT `to`.`id` AS `id`, `to`.`profile_id` AS `profile_id`, `to`.`bundle_id` AS `bundle_id`, `tb`.`title` AS `bundle`, `to`.`order` AS `transaction`, `to`.`license` AS `license`, `to`.`type` AS `type`, `to`.`added` AS `added`, `to`.`expired` AS `expired` FROM `bx_credits_orders` AS `to` LEFT JOIN `bx_credits_bundles` AS `tb` ON `to`.`bundle_id`=`tb`.`id` WHERE 1 ', 'bx_credits_orders', 'id', 'added', '', '', 20, NULL, 'start', '', 'tb`.`title,to`.`order,to`.`license,to`.`type', '', 'like', '', '', 2147483647, 'BxCreditsGridOrdersCommon', 'modules/boonex/credits/classes/BxCreditsGridOrdersCommon.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_credits_orders_administration', 'profile_id', '_bx_credits_grid_column_title_ord_profile_id', '10%', 0, '28', '', 1),
+('bx_credits_orders_administration', 'bundle', '_bx_credits_grid_column_title_ord_bundle', '20%', 1, '28', '', 2),
+('bx_credits_orders_administration', 'transaction', '_bx_credits_grid_column_title_ord_transaction', '10%', 0, '32', '', 3),
+('bx_credits_orders_administration', 'type', '_bx_credits_grid_column_title_ord_type', '5%', 1, '12', '', 4),
+('bx_credits_orders_administration', 'added', '_bx_credits_grid_column_title_ord_added', '10%', 1, '25', '', 5),
+('bx_credits_orders_administration', 'expired', '_bx_credits_grid_column_title_ord_expired', '10%', 1, '25', '', 6),
+
+('bx_credits_orders_common', 'bundle', '_bx_credits_grid_column_title_ord_bundle', '20%', 1, '28', '', 1),
+('bx_credits_orders_common', 'transaction', '_bx_credits_grid_column_title_ord_transaction', '10%', 0, '32', '', 2),
+('bx_credits_orders_common', 'type', '_bx_credits_grid_column_title_ord_type', '10%', 1, '12', '', 3),
+('bx_credits_orders_common', 'added', '_bx_credits_grid_column_title_ord_added', '10%', 1, '25', '', 4),
+('bx_credits_orders_common', 'expired', '_bx_credits_grid_column_title_ord_expired', '10%', 1, '25', '', 5);
+
+-- GRIDS: history
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_credits_history_administration', 'Sql', 'SELECT `th`.*, `tp`.`wdw_clearing` FROM `bx_credits_history` AS `th` LEFT JOIN `bx_credits_profiles` AS `tp` ON `th`.`first_pid`=`tp`.`id` WHERE 1 ', 'bx_credits_history', 'id', 'date', '', '', 20, NULL, 'start', '', 'th`.`direction,th`.`order,th`.`info', '', 'like', '', '', 192, 'BxCreditsGridHistoryAdministration', 'modules/boonex/credits/classes/BxCreditsGridHistoryAdministration.php'),
+('bx_credits_history_common', 'Sql', 'SELECT `th`.*, `tp`.`wdw_clearing` FROM `bx_credits_history` AS `th` LEFT JOIN `bx_credits_profiles` AS `tp` ON `th`.`first_pid`=`tp`.`id` WHERE 1 ', 'bx_credits_history', 'id', 'date', '', '', 20, NULL, 'start', '', 'th`.`direction,th`.`order,th`.`info', '', 'like', '', '', 2147483647, 'BxCreditsGridHistoryCommon', 'modules/boonex/credits/classes/BxCreditsGridHistoryCommon.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_credits_history_administration', 'first_pid', '_bx_credits_grid_column_title_htr_first_pid', '15%', 0, '', '', 1),
+('bx_credits_history_administration', 'direction', '_bx_credits_grid_column_title_htr_direction', '10%', 0, '', '', 2),
+('bx_credits_history_administration', 'second_pid', '_bx_credits_grid_column_title_htr_second_pid', '15%', 0, '', '', 3),
+('bx_credits_history_administration', 'amount', '_bx_credits_grid_column_title_htr_amount', '10%', 1, '', '', 4),
+('bx_credits_history_administration', 'order', '_bx_credits_grid_column_title_htr_order', '15%', 0, '16', '', 5),
+('bx_credits_history_administration', 'info', '_bx_credits_grid_column_title_htr_info', '15%', 1, '24', '', 6),
+('bx_credits_history_administration', 'date', '_bx_credits_grid_column_title_htr_date', '10%', 0, '', '', 7),
+('bx_credits_history_administration', 'cleared', '_bx_credits_grid_column_title_htr_cleared', '10%', 0, '', '', 8),
+
+('bx_credits_history_common', 'direction', '_bx_credits_grid_column_title_htr_direction', '10%', 0, '', '', 1),
+('bx_credits_history_common', 'second_pid', '_bx_credits_grid_column_title_htr_pid', '25%', 0, '', '', 2),
+('bx_credits_history_common', 'amount', '_bx_credits_grid_column_title_htr_amount', '15%', 1, '', '', 3),
+('bx_credits_history_common', 'order', '_bx_credits_grid_column_title_htr_order', '15%', 0, '16', '', 4),
+('bx_credits_history_common', 'info', '_bx_credits_grid_column_title_htr_info', '15%', 1, '32', '', 5),
+('bx_credits_history_common', 'date', '_bx_credits_grid_column_title_htr_date', '10%', 0, '', '', 6),
+('bx_credits_history_common', 'cleared', '_bx_credits_grid_column_title_htr_cleared', '10%', 0, '', '', 7);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_credits_history_administration', 'independent', 'grant', '_bx_credits_grid_action_title_htr_grant', '', 0, 0, 1),
+
+('bx_credits_history_common', 'independent', 'send', '_bx_credits_grid_action_title_htr_send', '', 0, 0, 1);
+
+-- GRIDS: withdrawals
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_credits_withdrawals_administration', 'Sql', 'SELECT * FROM `bx_credits_withdrawals` WHERE 1 ', 'bx_credits_withdrawals', 'id', 'added', '', '', 20, NULL, 'start', '', 'message,order', '', 'like', '', '', 192, 'BxCreditsGridWithdrawalsAdministration', 'modules/boonex/credits/classes/BxCreditsGridWithdrawalsAdministration.php'),
+('bx_credits_withdrawals_common', 'Sql', 'SELECT * FROM `bx_credits_withdrawals` WHERE 1 ', 'bx_credits_withdrawals', 'id', 'added', '', '', 20, NULL, 'start', '', 'message,order', '', 'like', '', '', 2147483647, 'BxCreditsGridWithdrawalsCommon', 'modules/boonex/credits/classes/BxCreditsGridWithdrawalsCommon.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_credits_withdrawals_administration', 'profile_id', '_bx_credits_grid_column_title_wdw_profile_id', '15%', 0, 0, '', 1),
+('bx_credits_withdrawals_administration', 'amount', '_bx_credits_grid_column_title_wdw_amount', '5%', 0, 0, '', 2),
+('bx_credits_withdrawals_administration', 'rate', '_bx_credits_grid_column_title_wdw_rate', '5%', 0, 0, '', 3),
+('bx_credits_withdrawals_administration', 'message', '_bx_credits_grid_column_title_wdw_message', '10%', 0, 16, '', 4),
+('bx_credits_withdrawals_administration', 'added', '_bx_credits_grid_column_title_wdw_added', '5%', 0, 0, '', 5),
+('bx_credits_withdrawals_administration', 'performer_id', '_bx_credits_grid_column_title_wdw_performer_id', '15%', 0, 0, '', 6),
+('bx_credits_withdrawals_administration', 'order', '_bx_credits_grid_column_title_wdw_order', '15%', 0, 16, '', 7),
+('bx_credits_withdrawals_administration', 'confirmed', '_bx_credits_grid_column_title_wdw_confirmed', '5%', 0, 0, '', 8),
+('bx_credits_withdrawals_administration', 'status', '_bx_credits_grid_column_title_wdw_status', '5%', 0, 0, '', 9),
+('bx_credits_withdrawals_administration', 'actions', '', '20%', 0, 0, '', 10),
+
+('bx_credits_withdrawals_common', 'amount', '_bx_credits_grid_column_title_wdw_amount', '10%', 0, 0, '', 1),
+('bx_credits_withdrawals_common', 'rate', '_bx_credits_grid_column_title_wdw_rate', '10%', 0, 0, '', 2),
+('bx_credits_withdrawals_common', 'message', '_bx_credits_grid_column_title_wdw_message', '15%', 0, 16, '', 3),
+('bx_credits_withdrawals_common', 'added', '_bx_credits_grid_column_title_wdw_added', '10%', 0, 0, '', 4),
+('bx_credits_withdrawals_common', 'order', '_bx_credits_grid_column_title_wdw_order', '15%', 0, 16, '', 5),
+('bx_credits_withdrawals_common', 'confirmed', '_bx_credits_grid_column_title_wdw_confirmed', '10%', 0, 0, '', 6),
+('bx_credits_withdrawals_common', 'status', '_bx_credits_grid_column_title_wdw_status', '10%', 0, 0, '', 7),
+('bx_credits_withdrawals_common', 'actions', '', '20%', 0, 0, '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_credits_withdrawals_administration', 'single', 'withdraw_confirm', '_bx_credits_grid_action_title_wdw_withdraw_confirm', 'check', 1, 1, 1),
+
+('bx_credits_withdrawals_common', 'independent', 'withdraw_request', '_bx_credits_grid_action_title_wdw_withdraw_request', '', 0, 0, 1),
+('bx_credits_withdrawals_common', 'single', 'withdraw_cancel', '_bx_credits_grid_action_title_wdw_withdraw_cancel', 'times', 1, 1, 1);
+
+
+-- GRIDS: profiles
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_credits_profiles_administration', 'Sql', 'SELECT * FROM `bx_credits_profiles` WHERE 1 ', 'bx_credits_profiles', 'id', '', '', '', 20, NULL, 'start', '', '', '', 'like', '', '', 192, 'BxCreditsGridProfilesAdministration', 'modules/boonex/credits/classes/BxCreditsGridProfilesAdministration.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_credits_profiles_administration', 'id', '_bx_credits_grid_column_title_pfl_id', '40%', 0, '', '', 1),
+('bx_credits_profiles_administration', 'wdw_clearing', '_bx_credits_grid_column_title_pfl_wdw_clearing', '10%', 0, '', '', 2),
+('bx_credits_profiles_administration', 'wdw_minimum', '_bx_credits_grid_column_title_pfl_wdw_minimum', '10%', 0, '', '', 3),
+('bx_credits_profiles_administration', 'wdw_remaining', '_bx_credits_grid_column_title_pfl_wdw_remaining', '10%', 0, '', '', 4),
+('bx_credits_profiles_administration', 'balance', '_bx_credits_grid_column_title_pfl_balance', '15%', 0, '', '', 5),
+('bx_credits_profiles_administration', 'balance_cleared', '_bx_credits_grid_column_title_pfl_balance_cleared', '15%', 0, '', '', 6),
+('bx_credits_profiles_administration', 'actions', '', '20%', 0, '', '', 7);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_credits_profiles_administration', 'single', 'edit', '_bx_credits_grid_action_title_pfl_edit', 'pencil-alt', 1, 0, 1);
+
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers` (`name`, `class`, `file`, `service_call`) VALUES 
+('bx_credits', 'BxCreditsAlertsResponse', 'modules/boonex/credits/classes/BxCreditsAlertsResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('system', 'save_setting', @iHandler),
+('profile', 'add', @iHandler),
+('profile', 'delete', @iHandler);
+
+
+-- EMAIL TEMPLATES
+INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES 
+('bx_credits', '_bx_credits_et_txt_name_granted', 'bx_credits_granted', '_bx_credits_et_txt_subject_granted', '_bx_credits_et_txt_body_granted'),
+('bx_credits', '_bx_credits_et_txt_name_received', 'bx_credits_received', '_bx_credits_et_txt_subject_received', '_bx_credits_et_txt_body_received'),
+('bx_credits', '_bx_credits_et_txt_name_purchased', 'bx_credits_purchased', '_bx_credits_et_txt_subject_purchased', '_bx_credits_et_txt_body_purchased'),
+('bx_credits', '_bx_credits_et_txt_name_in', 'bx_credits_in', '_bx_credits_et_txt_subject_in', '_bx_credits_et_txt_body_in'),
+('bx_credits', '_bx_credits_et_txt_name_out', 'bx_credits_out', '_bx_credits_et_txt_subject_out', '_bx_credits_et_txt_body_out'),
+('bx_credits', '_bx_credits_et_txt_name_withdraw_requested', 'bx_credits_withdraw_requested', '_bx_credits_et_txt_subject_withdraw_requested', '_bx_credits_et_txt_body_withdraw_requested'),
+('bx_credits', '_bx_credits_et_txt_name_withdraw_canceled', 'bx_credits_withdraw_canceled', '_bx_credits_et_txt_subject_withdraw_canceled', '_bx_credits_et_txt_body_withdraw_canceled'),
+('bx_credits', '_bx_credits_et_txt_name_withdraw_sent', 'bx_credits_withdraw_sent', '_bx_credits_et_txt_subject_withdraw_sent', '_bx_credits_et_txt_body_withdraw_sent');
+
+
+-- CRON
+INSERT INTO `sys_cron_jobs` (`name`, `time`, `class`, `file`, `service_call`) VALUES
+('bx_credits_clearing', '0 0 * * *', 'BxCreditsCronClearing', 'modules/boonex/credits/classes/BxCreditsCronClearing.php', '');

+ 55 - 0
modules/boonex/credits/updates/15.0.0_15.0.1/source/js/withdraw.js

@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxCreditsWithdraw(oOptions) {
+    this._sActionsUri = oOptions.sActionUri;
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxCreditsWithdraw' : oOptions.sObjName;
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+}
+
+BxCreditsWithdraw.prototype.getResult = function(oElement) {
+    var fRate = parseFloat($('#' + this._aHtmlIds['withdraw_field_rate']).val());
+    var fAmount = parseFloat($(oElement).val())
+
+    $('#' + this._aHtmlIds['withdraw_field_result']).val(this._round(fRate * fAmount, 2));
+};
+
+BxCreditsWithdraw.prototype.loadingInButton = function(e, bShow) {
+    if($(e).length)
+        bx_loading_btn($(e), bShow);
+    else
+        bx_loading($('body'), bShow);	
+};
+
+BxCreditsWithdraw.prototype.loadingInBox = function(e, bShow) {
+    var oParent = $(e).length ? $(e).parents('.bx-base-text-poll:first') : $('body'); 
+    bx_loading(oParent, bShow);
+};
+
+BxCreditsWithdraw.prototype.loadingInBlock = function(e, bShow) {
+    var oParent = $(e).length ? $(e).parents('.bx-db-container:first') : $('body'); 
+    bx_loading(oParent, bShow);
+};
+
+BxCreditsWithdraw.prototype._getDefaultData = function() {
+    var oDate = new Date();
+    return jQuery.extend({}, this._oRequestParams, {_t:oDate.getTime()});
+};
+
+BxCreditsWithdraw.prototype._round = function(fNum, iPrecision = 0) {
+    var iMux = Math.pow(10, iPrecision);
+    var iNum = (fNum * iMux) * (1 + Number.EPSILON);
+    return Math.round(iNum) / iMux;
+}
+
+/** @} */

+ 51 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Developer',
+    'version_from' => '15.0.1',
+    'version_to' => '15.0.2',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/developer/updates/update_15.0.1_15.0.2/',
+    'home_uri' => 'developer_update_1501_1502',
+
+    'module_dir' => 'boonex/developer/',
+    'module_uri' => 'developer',
+
+    'db_prefix' => 'bx_dev_',
+    'class_prefix' => 'BxDev',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'BoonEx Developer',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxDevUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 52 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/install/langs/en.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_add name="_bx_dev_frm_txt_field_title_caption_system"><![CDATA[System Name Key]]></string_add>
+	<string_add name="_bx_dev_frm_txt_field_title_caption"><![CDATA[Display Name Key]]></string_add>
+	<string_add name="_bx_dev_bp_txt_sys_block_description"><![CDATA[Description]]></string_add>
+	<string_add name="_bx_dev_bp_txt_block_description"><![CDATA[Description]]></string_add>
+	<string_add name="_bx_dev_bp_txt_sys_block_icon"><![CDATA[Icon]]></string_add>
+	<string_add name="_bx_dev_bp_txt_block_icon"><![CDATA[Icon]]></string_add>
+	<string_add name="_bx_dev_bp_txt_sys_block_content_empty"><![CDATA[Placeholder text for empty block]]></string_add>
+	<string_add name="_bx_dev_bp_txt_block_content_empty"><![CDATA[Placeholder text for empty block]]></string_add>
+	<string_add name="_bx_dev_bp_txt_sys_block_help"><![CDATA[Help]]></string_add>
+	<string_add name="_bx_dev_bp_txt_block_help"><![CDATA[Help]]></string_add>
+	<string_add name="_bx_dev_bp_txt_sys_block_cache_lifetime"><![CDATA[Block Cache Lifetime (in seconds)]]></string_add>
+	<string_add name="_bx_dev_bp_txt_block_cache_lifetime"><![CDATA[Block Cache Lifetime (in seconds)]]></string_add>
+
+	<string_upd name="_bx_dev_frm_txt_sys_forms_object"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_forms_object"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_sys_forms_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_forms_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_sys_displays_display_name"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_displays_display_name"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_sys_displays_object"><![CDATA[Form Identifier]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_displays_object"><![CDATA[Form Identifier]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_sys_displays_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_frm_txt_displays_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_menus_object"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_sys_menus_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_sys_menus_title_public"><![CDATA[Display Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_menus_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_menus_title_public"><![CDATA[Display Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_sys_sets_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_sets_title"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_sys_items_name"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_items_name"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_sys_items_title_system"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_items_title_system"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_sys_items_title"><![CDATA[Display Name Key]]></string_upd>
+	<string_upd name="_bx_dev_nav_txt_items_title"><![CDATA[Display Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_sys_page_object"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_page_object"><![CDATA[Identifier]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_sys_page_title_system"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_page_title_system"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_sys_page_title"><![CDATA[Display Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_page_title"><![CDATA[Display Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_sys_block_object"><![CDATA[Page Identifier]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_block_object"><![CDATA[Page Identifier]]></string_upd>
+	<string_upd name="_bx_dev_bp_err_block_object"><![CDATA[You need to enter a correct page identifier here.]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_sys_block_title_system"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_block_title_system"><![CDATA[System Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_sys_block_title"><![CDATA[Display Name Key]]></string_upd>
+	<string_upd name="_bx_dev_bp_txt_block_title"><![CDATA[Display Name Key]]></string_upd>
+</resources>

+ 19 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/install/sql/install.sql

@@ -0,0 +1,19 @@
+SET @sName = 'bx_developer';
+
+
+-- FORMS
+DELETE FROM `sys_form_inputs` WHERE `object`='bx_developer_bp_block' AND `name` IN ('description', 'icon', 'content_empty', 'help', 'cache_lifetime');
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_bp_block', @sName, 'description', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_description', '_bx_dev_bp_txt_block_description', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'icon', '', '', 0, 'textarea', '_bx_dev_bp_txt_sys_block_icon', '_bx_dev_bp_txt_block_icon', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'content_empty', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_content_empty', '_bx_dev_bp_txt_block_content_empty', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'help', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_help', '_bx_dev_bp_txt_block_help', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'cache_lifetime', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_cache_lifetime', '_bx_dev_bp_txt_block_cache_lifetime', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0);
+
+DELETE FROM `sys_form_display_inputs` WHERE `display_name`='bx_developer_bp_block_edit' AND `input_name` IN ('description', 'icon', 'content_empty', 'help', 'cache_lifetime');
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_bp_block_edit', 'description', 2147483647, 1, 5),
+('bx_developer_bp_block_edit', 'icon', 2147483647, 1, 5),
+('bx_developer_bp_block_edit', 'content_empty', 2147483647, 1, 13),
+('bx_developer_bp_block_edit', 'help', 2147483647, 1, 13),
+('bx_developer_bp_block_edit', 'cache_lifetime', 2147483647, 1, 13);

+ 541 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/source/classes/BxDevBuilderPage.php

@@ -0,0 +1,541 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Developer Developer
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxDevBuilderPage extends BxTemplStudioBuilderPage
+{
+    protected $sActionPageExport = 'page_export';
+
+    protected $oModule;
+    protected $aParams;
+
+    function __construct($aParams)
+    {
+        parent::__construct(isset($aParams['type']) ? $aParams['type'] : '',  isset($aParams['page']) ? $aParams['page'] : '');
+
+        $this->bPageMenuTitle = false;
+
+        $this->aParams = $aParams;
+        $this->sBaseUrl = $this->aParams['url'];
+        $this->sTypeUrl = $this->sBaseUrl . '&bp_type=%s';
+        $this->sPageUrl = $this->sTypeUrl . '&bp_page=%s';
+
+        $this->oModule = BxDolModule::getInstance('bx_developer');
+
+        $this->oModule->_oTemplate->addStudioCss(array('builder_page.css'));
+    }
+
+    function getPageCode($sPage = '', $bWrap = true)
+    {
+        return $this->oModule->_oTemplate->getJsCode('main') . parent::getPageCode($sPage, $bWrap);
+    }
+
+    function getBlockPanelTop($aBlock)
+    {
+         return BxBaseStudioWidget::getBlockPanelTop(
+            array('panel_top' => $this->oModule->_oTemplate->parseHtmlByName('bp_block_panel_top.html', $this->_getTmplVarsBlockPanelTop()))
+        );
+    }
+
+    protected function actionPageCreate()
+    {
+    	$sFormObject = $this->oModule->_oConfig->getObject('form_bp_page');
+        $sFormDisplay = $this->oModule->_oConfig->getObject('form_display_bp_page_add');
+
+        $oForm = BxDolForm::getObjectInstance($sFormObject, $sFormDisplay, $this->oModule->_oTemplate);
+        $oForm->aFormAttrs['action'] = sprintf($this->sPageUrl, $this->sType, $this->sPage) . '&bp_action=' . $this->sActionPageCreate;
+        $oForm->aInputs['module']['values'] = array_merge(array('' => _t('_bx_dev_bp_txt_select_module')), BxDolStudioUtils::getModules());
+
+        $oForm->aInputs['layout_id']['values'] = array(
+            array('key' => '', 'value' => _t('_bx_dev_bp_txt_page_layout_id_select'))
+        );
+
+        $aLayouts = array();
+        $this->oDb->getLayouts(array('type' => 'all'), $aLayouts, false);
+        foreach($aLayouts as $aLayout)
+            $oForm->aInputs['layout_id']['values'][] = array('key' => $aLayout['id'], 'value' => _t($aLayout['title']));
+
+        if(($sKey = 'config_api') && isset($oForm->aInputs[$sKey]))
+            $oForm->aInputs[$sKey] = array_merge($oForm->aInputs[$sKey], [
+                'code' => 1
+            ]);
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+            $sObject = $oForm->getCleanValue('object');
+            $sModule = $oForm->getCleanValue('module');
+
+            if(($iId = (int)$oForm->insert()) != 0)
+                return array('eval' => $this->getPageJsObject() . '.onCreatePage(\'' . $sModule . '\', \'' . $sObject . '\')');
+            else
+                return array('msg' => _t('_bx_dev_bp_err_page_create'));
+        }
+
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->aHtmlIds['add_popup_id'], _t('_bx_dev_bp_txt_page_create_popup'), $this->oModule->_oTemplate->parseHtmlByName('bp_add_page.html', array(
+            'form_id' => $oForm->aFormAttrs['id'],
+            'form' => $oForm->getCode(true)
+        )));
+
+        return array('popup' => $sContent);
+    }
+
+    protected function actionPageExport()
+    {
+        $sContentInsert = $sContentDelete = "";
+
+        $aPage = $this->oDb->getPages(array('type' => 'by_object', 'value' => $this->sPage));
+        if(empty($aPage) || !is_array($aPage))
+            return array();
+
+        $sContentInsert .= ($this->oModule->_oDb->getQueryInsert('sys_objects_page', array($aPage), "Dumping data for '" . $aPage['object'] . "' page"));
+        $sContentDelete .= ($this->oModule->_oDb->getQueryDelete('sys_objects_page', 'object', array($aPage), "Deleting data for '" . $aPage['object'] . "' page"));
+
+        $aBlocks = array();
+        $this->oDb->getBlocks(array('type' => 'by_object', 'value' => $aPage['object']), $aBlocks, false);
+        $sContentInsert .= $this->oModule->_oDb->getQueryInsert('sys_pages_blocks', $aBlocks, false, array('id', 'name'));
+        $sContentDelete .= $this->oModule->_oDb->getQueryDelete('sys_pages_blocks', 'object', array($aPage), false);
+
+        $aForm = array(
+            'form_attrs' => array(),
+            'inputs' => array (
+                'insert' => array(
+                    'type' => 'textarea',
+                    'name' => 'insert',
+                    'caption' => _t('_bx_dev_bp_txt_page_export_insert'),
+                    'value' => $sContentInsert,
+                ),
+                'delete' => array(
+                    'type' => 'textarea',
+                    'name' => 'delete',
+                    'caption' => _t('_bx_dev_bp_txt_page_export_delete'),
+                    'value' => $sContentDelete,
+                ),
+                'done' => array (
+                    'type' => 'button',
+                    'name' => 'done',
+                    'value' => _t('_bx_dev_bp_btn_page_done'),
+                    'attrs' => array(
+                        'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                    ),
+                )
+            )
+        );
+        $oForm = new BxTemplStudioFormView($aForm);
+
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox('bx-dev-bp-page-export-popup', _t('_bx_dev_bp_txt_page_export_popup', _t($aPage['title'])), $this->oModule->_oTemplate->parseHtmlByName('bp_export.html', array(
+            'content' => $oForm->getCode()
+        )));
+
+        return array('popup' => $sContent);
+    }
+
+    protected function actionBlockEdit()
+    {
+    	$sFormObject = $this->oModule->_oConfig->getObject('form_bp_block');
+        $sFormDisplay = $this->oModule->_oConfig->getObject('form_display_bp_block_edit');
+        
+        $iId = (int)bx_get('id');
+        if(!$iId)
+            return array();
+
+        $aBlock = array();
+        $this->oDb->getBlocks(array('type' => 'by_id', 'value' => $iId), $aBlock, false);
+        if(empty($aBlock) || !is_array($aBlock))
+            return array('msg' => _t('_bx_dev_bp_err_block_not_found'));
+
+        $oForm = BxDolForm::getObjectInstance($sFormObject, $sFormDisplay, $this->oModule->_oTemplate);
+
+        $this->onLoadBlock($oForm, $aBlock);
+
+        $oForm->initChecker($aBlock);
+        if($oForm->isSubmittedAndValid()) {
+            $this->onSaveBlock($oForm, $aBlock);
+
+            if($oForm->update($iId) !== false)
+                return array('eval' => $this->getPageJsObject() . '.onEditBlock(oData)');
+            else
+                return array('msg' => _t('_bx_dev_bp_err_block_edit'));
+        }
+
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->aHtmlIds['edit_block_popup_id'], _t('_bx_dev_bp_txt_block_edit_popup', _t($aBlock['title'])), $this->oModule->_oTemplate->parseHtmlByName('bp_add_block.html', array(
+            'form_id' => $oForm->aFormAttrs['id'],
+            'form' => $oForm->getCode(true)
+        )));
+
+        return array('popup' => $sContent);
+    }
+
+    protected function actionBlockDelete()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $iId = (int)bx_get('id');
+
+        $aBlock = array();
+        $this->oDb->getBlocks(array('type' => 'by_id', 'value' => $iId), $aBlock, false);
+        if(empty($aBlock) || !is_array($aBlock))
+            return array('msg' => _t('_bx_dev_bp_err_block_not_found'));
+
+        if(!$this->oDb->deleteBlocks(array('type' => 'by_id', 'value' => $iId)))
+            return array('msg' => _t('_bx_dev_bp_err_block_delete'));
+
+        $this->onBlockDelete($aBlock);
+        return array('eval' => $sJsObject . '.onDeleteBlock(' . $iId . ', oData)');
+    }
+
+    protected function getSettingsOptions($aPage = array(), $bCreate = true, $bInputsOnly = false)
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-settings-seo',
+            ),
+            'params' => array (
+                'remove_form' => '1',
+                'csrf' => array(
+                    'disable' => true
+                )
+            ),
+            'inputs' => array(
+                'object'  => array(
+                    'type' => 'text',
+                    'name' => 'object',
+                    'caption' => _t('_bx_dev_bp_txt_page_object'),
+                    'info' => '',
+                    'value' => $this->aPageRebuild['object'],
+                    'required' => '',
+                    'attrs' => array(
+                        'disabled' => 'disabled'
+                    ),
+                )
+            )
+        );
+
+        $aForm['inputs'] += parent::getSettingsOptions($aPage, $bCreate, true);
+
+        $aForm['inputs']['title_system']['type'] = 'text';
+        $aForm['inputs']['title_system']['caption'] = _t('_bx_dev_bp_txt_page_title_system');
+        $aForm['inputs']['title']['type'] = 'text';
+
+        $aUri = array(
+            'uri'  => array(
+                'type' => 'text',
+                'name' => 'uri',
+                'caption' => _t('_bx_dev_bp_txt_page_uri'),
+                'info' => '',
+                'value' => $this->aPageRebuild['uri'],
+                'required' => '',
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+            )
+        );
+        $aForm['inputs'] = bx_array_insert_before($aUri, $aForm['inputs'], 'url');
+
+        $aForm['inputs']['url']['caption'] = _t('_bx_dev_bp_txt_page_url');
+        $aForm['inputs']['url']['value'] = isset($aPage['url']) ? $aPage['url'] : '';
+        $aForm['inputs']['url']['db'] = array (
+            'pass' => 'Xss',
+        );
+        unset($aForm['inputs']['url']['attrs']['disabled']);
+
+        $aConfigApi = array(
+            'config_api'  => array(
+                'type' => 'textarea',
+                'name' => 'config_api',
+                'caption' => _t('_bx_dev_bp_txt_page_config_api'),
+                'info' => '',
+                'value' => $this->aPageRebuild['config_api'],
+                'required' => '',
+                'code' => '1',
+                'db' => array (
+                    'pass' => 'XssHtml',
+                ),
+            )
+        );
+        $aForm['inputs'] = bx_array_insert_before($aConfigApi, $aForm['inputs'], 'sticky_columns');
+
+        $aForm['inputs']['deletable'] = array(
+            'type' => 'switcher',
+            'name' => 'deletable',
+            'caption' => _t('_bx_dev_bp_txt_page_deletable'),
+            'info' => '',
+            'value' => '1',
+            'checked' => (int)$this->aPageRebuild['deletable'] == 1,
+            'required' => '',
+            'db' => array (
+                'pass' => 'Int',
+            )
+        );
+        $aForm['inputs']['override_class_name'] = array(
+            'type' => 'text',
+            'name' => 'override_class_name',
+            'caption' => _t('_bx_dev_bp_txt_page_override_class_name'),
+            'info' => '',
+            'value' => $this->aPageRebuild['override_class_name'],
+            'required' => '0',
+            'db' => array (
+                'pass' => 'Xss',
+            )
+        );
+        $aForm['inputs']['override_class_file'] = array(
+            'type' => 'text',
+            'name' => 'override_class_file',
+            'caption' => _t('_bx_dev_bp_txt_page_override_class_file'),
+            'info' => '',
+            'value' => $this->aPageRebuild['override_class_file'],
+            'required' => '0',
+            'db' => array (
+                'pass' => 'Xss',
+            )
+        );
+
+        if($bInputsOnly)
+            return $aForm['inputs'];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        return $oForm->getCode();
+    }
+
+    protected function getSettingsCache($aPage = array(), $bCreate = true, $bInputsOnly = false)
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-settings-cache',
+            ),
+            'params' => array (
+                'remove_form' => '1',
+                'csrf' => array(
+                    'disable' => true
+                )
+            ),
+            'inputs' => parent::getSettingsCache($aPage, $bCreate, true)
+        );
+
+        unset($aForm['inputs']['cache_lifetime']['attrs']);
+
+        $aForm['inputs']['cache_editable'] = array(
+            'type' => 'switcher',
+            'name' => 'cache_editable',
+            'caption' => _t('_bx_dev_bp_txt_page_cache_editable'),
+            'info' => '',
+            'value' => '1',
+            'checked' => (int)$this->aPageRebuild['cache_editable'] == 1,
+            'required' => '',
+            'db' => array (
+                'pass' => 'Int',
+            )
+        );
+
+        if($bInputsOnly)
+            return $aForm['inputs'];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        return $oForm->getCode();
+    }
+
+    protected function getBlockModule($aBlock)
+    {
+        return $aBlock['module'] != BX_DOL_STUDIO_BP_SKELETONS ? $aBlock['module'] : $this->aPageRebuild['module'];
+    }
+
+    protected function getBlockContent($aBlock)
+    {
+        $aFields = array();
+
+        switch($aBlock['type']) {
+            case BX_DOL_STUDIO_BP_BLOCK_SERVICE:
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'textarea',
+                        'name' => 'content',
+                        'caption' => _t('_bx_dev_bp_txt_block_content_service'),
+                        'info' => '',
+                        'value' => $aBlock['content'],
+                        'required' => '0',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    )
+                );
+                break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_LANG:
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'text',
+                        'name' => 'content',
+                        'caption' => _t('_bx_dev_bp_txt_block_content_lang'),
+                        'info' => _t('_bx_dev_bp_dsc_block_content_lang'),
+                        'value' => $aBlock['content'],
+                        'required' => '0',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    ),
+                );
+                break;
+
+            default:
+                $aFields = parent::getBlockContent($aBlock);
+                break;
+        }
+
+        return $aFields;
+    }
+
+    protected function getBlocks($sModule)
+    {
+        $aBlocks = parent::getBlocks($sModule);
+
+        if($sModule == BX_DOL_STUDIO_BP_SKELETONS) {
+            $aBlock = array();
+            $this->oDb->getBlocks(array('type' => 'skeleton_by_type', 'value' => 'service'), $aBlock, false);
+            if(!empty($aBlock) && is_array($aBlock))
+                $aBlocks[] = $aBlock;
+        }
+
+        return $aBlocks;
+    }
+
+    protected function onLoadBlock(&$oForm, &$aBlock)
+    {
+        $oForm->aFormAttrs['action'] = sprintf($this->sPageUrl, $this->sType, $this->sPage) . '&bp_action=' . $this->sActionBlockEdit;
+        $oForm->aInputs['module']['values'] = array_merge(array('' => _t('_bx_dev_bp_txt_select_module')), BxDolStudioUtils::getModules());
+
+        if(($sKey = 'icon') && isset($oForm->aInputs[$sKey]))
+            $oForm->aInputs[$sKey] = array_merge($oForm->aInputs[$sKey], [
+                'code' => 1
+            ]);
+
+        $aDBoxes = array();
+        $this->oDb->getDesignBoxes(array('type' => 'ordered'), $aDBoxes, false);
+        foreach($aDBoxes as $aDBox)
+            $oForm->aInputs['designbox_id']['values'][] = array('key' => $this->sSelectKeyPrefix . $aDBox['id'], 'value' => _t($aDBox['title']));
+
+        $oForm->aInputs['class']['value'] = $aBlock['class'];
+
+        $aContentPlaceholders = $this->oDb->getContentPlaceholders();
+        $oForm->aInputs['async']['values'] = array(
+            array('key' => 0, 'value' => _t('_sys_no_async'))
+        );
+        foreach ($aContentPlaceholders as $iKey => $sTitle)
+            $oForm->aInputs['async']['values'][] = array('key' => $iKey, 'value' => _t($sTitle));
+
+        $oForm->aInputs['hidden_on']['values'] = array(
+            BX_DB_HIDDEN_PHONE => _t('_bx_dev_bp_txt_block_hidden_on_phone'),
+            BX_DB_HIDDEN_TABLET => _t('_bx_dev_bp_txt_block_hidden_on_tablet'),
+            BX_DB_HIDDEN_DESKTOP => _t('_bx_dev_bp_txt_block_hidden_on_desktop'),
+            BX_DB_HIDDEN_MOBILE => _t('_bx_dev_bp_txt_block_hidden_on_mobile')
+        );
+
+        $aSubmenus = $this->oDb->getBlockSubmenus();
+        $oForm->aInputs['submenu']['values'] = array(
+            array('key' => '', 'value' => _t('_sys_no_menu'))
+        );
+        foreach ($aSubmenus as $sObject => $sTitle)
+            $oForm->aInputs['submenu']['values'][] = array('key' => $sObject, 'value' => _t($sTitle));
+
+        $oForm->aInputs['visible_for']['value'] = $aBlock['visible_for_levels'] == BX_DOL_INT_MAX ? BX_DOL_STUDIO_VISIBLE_ALL : BX_DOL_STUDIO_VISIBLE_SELECTED;
+        $oForm->aInputs['visible_for']['values'] = array(
+            array('key' => BX_DOL_STUDIO_VISIBLE_ALL, 'value' => _t('_bx_dev_bp_txt_block_visible_for_all')),
+            array('key' => BX_DOL_STUDIO_VISIBLE_SELECTED, 'value' => _t('_bx_dev_bp_txt_block_visible_for_selected')),
+        );
+        $oForm->aInputs['visible_for']['attrs']['onchange'] = $this->getPageJsObject() . '.onChangeVisibleFor(this)';
+
+        $oForm->aInputs['visible_for_levels']['tr_attrs']['style'] = $aBlock['visible_for_levels'] == BX_DOL_INT_MAX ? 'display:none' : '';
+        BxDolStudioUtils::getVisibilityValues($aBlock['visible_for_levels'], $oForm->aInputs['visible_for_levels']['values'], $oForm->aInputs['visible_for_levels']['value']);
+        $aBlock['visible_for_levels'] = $oForm->aInputs['visible_for_levels']['value'];
+
+        $oForm->aInputs = $this->addInArray($oForm->aInputs, 'visible_for_levels', $this->getBlockContent($aBlock));
+
+        if(($sKey = 'config_api') && isset($oForm->aInputs[$sKey]))
+            $oForm->aInputs[$sKey] = array_merge($oForm->aInputs[$sKey], [
+                'code' => 1
+            ]);
+
+        $oForm->aInputs['controls'][0]['value'] = _t('_bx_dev_bp_btn_block_save');
+        $oForm->aInputs['controls'][2]['attrs']['onclick'] = $this->getPageJsObject() . ".deleteBlock(" . $aBlock['id'] . ")";
+
+        if($aBlock['type'] == BX_DOL_STUDIO_BP_BLOCK_SERVICE)
+            $aBlock['content'] = BxDevFunctions::unserializeString($aBlock['content']);
+
+        $aBlock['designbox_id'] = $this->sSelectKeyPrefix . $aBlock['designbox_id'];
+    }
+
+    protected function onSavePage(&$oForm, &$aPage) 
+    {
+        if(($sConfigApi = $oForm->getCleanValue('config_api')) && !$this->oModule->isValidJson($sConfigApi))
+            BxDolForm::setSubmittedValue('config_api', '', $oForm->aFormAttrs['method']);
+    }
+
+    protected function onSaveBlock(&$oForm, &$aBlock)
+    {
+        parent::onSaveBlock($oForm, $aBlock);
+
+        if($aBlock['type'] == BX_DOL_STUDIO_BP_BLOCK_SERVICE && isset($oForm->aInputs['content']))
+            $this->onSaveBlockService($oForm, $aBlock);
+
+        if(($sConfigApi = $oForm->getCleanValue('config_api')) && !$this->oModule->isValidJson($sConfigApi))
+            BxDolForm::setSubmittedValue('config_api', '', $oForm->aFormAttrs['method']);
+    }
+
+    protected function onSaveBlockLang(&$oForm, &$aBlock)
+    {
+        $sValue = $oForm->getCleanValue('content');
+        BxDolForm::setSubmittedValue('content', $sValue, $oForm->aFormAttrs['method']);
+    }
+
+    protected function onSaveBlockService(&$oForm, &$aBlock)
+    {
+        $sValue = $oForm->getCleanValue('content');
+        $sValue = BxDevFunctions::serializeString($sValue);
+        BxDolForm::setSubmittedValue('content', $sValue, $oForm->aFormAttrs['method']);
+    }
+
+    protected function _getPageEditForm()
+    {
+        $oForm = parent::_getPageEditForm();
+        $oForm->addCssJsCodeMirror();
+
+        return $oForm;
+    }
+
+    protected function _getTmplVarsBlockPanelTop()
+    {
+        $aResult = parent::_getTmplVarsBlockPanelTop();
+
+        $aResult['bx_if:show_actions_global'] = [
+            'condition' => $this->sPage == '' || empty($this->aPageRebuild),
+            'content' => [
+                'js_object' => $this->oModule->_oConfig->getJsObject('main'),
+                'action_page_import' => 'import',
+                'action_page_export' => 'export'
+            ]
+        ];
+
+        return $aResult;
+    }
+
+    protected function _getTmplVarsBlockPanelTopActions()
+    {
+        $sJsObject = $this->getPageJsObject();
+
+        $oPermalinks = BxDolPermalinks::getInstance();
+
+        return array(
+            'js_object' => $sJsObject,
+            'url_view' => bx_absolute_url($oPermalinks->permalink($this->aPageRebuild['url'])),
+            'action_page_export' => $this->sActionPageExport,
+            'action_page_edit' => $this->sActionPageEdit,
+            'action_block_create' => $this->sActionBlockCreate,
+        );
+    }
+}
+
+/** @} */

+ 145 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/source/classes/BxDevFunctions.php

@@ -0,0 +1,145 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Developer Developer
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxDevFunctions
+{
+    function __construct() {}
+
+    /*
+     * Is needed to change fields in "Form's Field" creation form.
+     * When the form is inited.
+     *
+     * @see BxDevFormsField
+     */
+    public static function changeFormField($aParams, $aInputs, $oDb)
+    {
+        $aInputs['module']['type'] = 'select';
+        $aInputs['module']['values'] = array_merge(array('' => _t('_bx_dev_frm_txt_select_module')), BxDolStudioUtils::getModules());
+        $aInputs['module']['value'] = $aParams['module'];
+
+        $aInputs['object']['type'] = 'select';
+        $aInputs['object']['value'] = $aParams['object'];
+        $aInputs['object']['values'] = array();
+
+        foreach(['caption_system', 'caption'] as $sField)
+            if(isset($aInputs[$sField]))
+                $aInputs[$sField]['caption'] = _t('_bx_dev_frm_txt_field_title_' . $sField);
+
+    	$aForms = array();
+        $oDb->getForms(array('type' => 'by_module', 'value' => $aParams['module']), $aForms, false);
+        if(!empty($aForms) && is_array($aForms)) {
+	        foreach($aForms as $aForm)
+	            $aInputs['object']['values'][$aForm['object']] = _t($aForm['title']);
+
+	        asort($aInputs['object']['values']);
+        }
+        $aInputs['object']['values'] = array_merge(array('' => _t('_bx_dev_frm_txt_select_object')), $aInputs['object']['values']);
+
+        $aInputs['name']['type'] = 'text';
+
+        $sTrlTypePostfix = '_translatable';
+        $sTrlCheckFuncPostfix = 'Translatable';
+        foreach($aInputs as $sName => $aInput)
+            if(isset($aInput['type']) && stripos($aInput['type'], $sTrlTypePostfix) !== false) {
+                $aInputs[$sName]['type'] = 'text';
+                $aInputs[$sName]['html'] = 0;
+
+                if(isset($aInput['checker']['func']) && stripos($aInput['checker']['func'], $sTrlCheckFuncPostfix) !== false)
+                    $aInputs[$sName]['checker']['func'] = str_ireplace($sTrlCheckFuncPostfix, '', $aInput['checker']['func']);
+            }
+
+        unset($aInputs['required']['attrs']['onchange']);
+        unset($aInputs['checker_func']['attrs']['disabled'], $aInputs['checker_func']['tr_attrs']['style']);
+        unset($aInputs['checker_error']['attrs']['disabled'], $aInputs['checker_error']['tr_attrs']['style']);
+
+        $aFields = array(
+            'editable' => array(
+                'type' => 'switcher',
+                'name' => 'editable',
+                'caption' => _t('_bx_dev_frm_txt_field_title_editable'),
+                'info' => _t('_bx_dev_frm_txt_field_info_editable'),
+                'value' => '1',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            ),
+            'deletable' => array(
+                'type' => 'switcher',
+                'name' => 'deletable',
+                'caption' => _t('_bx_dev_frm_txt_field_title_deletable'),
+                'info' => _t('_bx_dev_frm_txt_field_info_deletable'),
+                'value' => '1',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            ),
+        );
+        $aInputs = BxDolStudioUtils::addInArray($aInputs, 'controls', $aFields, false);
+
+        return $aInputs;
+    }
+
+    /*
+     * Is needed to change form's array in "Form's Field" add/edit form. 
+     * It's called just before the form is displayed with "getCode" method.
+     *
+     * @see BxDevFormsField
+     */
+    public static function changeForm($sAction, $aForm, $oForm)
+    {
+        switch($sAction) {
+            case 'add':
+                break;
+
+            case 'edit':
+                if(isset($aForm['inputs']['checker_func'])) {
+                    if(isset($aForm['inputs']['checker_func']['attrs']['disabled']))
+                        unset($aForm['inputs']['checker_func']['attrs']['disabled']);
+
+                    $sCfValue = $aForm['inputs']['checker_func']['value'];
+                    if(!empty($sCfValue) && !in_array($sCfValue, $oForm->getCheckFunctions()))
+                        $aForm['inputs']['checker_func']['info'] = _t('_adm_form_txt_field_checker_func_info_custom');
+                }
+                break;
+        }
+
+        return $aForm;
+    }
+
+    public static function serializeString($sValue)
+    {
+        if(empty($sValue))
+            return '';
+
+        $aValue = array();
+        @eval("\$aValue = " . $sValue . ";");
+        if(empty($aValue) || !is_array($aValue))
+            return '';
+
+        return serialize($aValue);
+    }
+
+    public static function unserializeString($sValue)
+    {
+        if(empty($sValue))
+            return '';
+
+        $aValue = @unserialize($sValue);
+        if(empty($aValue) || !is_array($aValue))
+            return '';
+
+        return var_export($aValue, true);
+    }
+}
+
+/** @} */

+ 517 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/source/classes/BxDevModule.php

@@ -0,0 +1,517 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Developer Developer
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+require_once('BxDevFunctions.php');
+
+define('BX_DEV_TOOLS_SETTINGS', 'settings');
+define('BX_DEV_TOOLS_FORMS', 'forms');
+define('BX_DEV_TOOLS_PAGES', 'pages');
+define('BX_DEV_TOOLS_NAVIGATION', 'navigation');
+define('BX_DEV_TOOLS_POLYGLOT', 'polyglot');
+define('BX_DEV_TOOLS_PERMISSIONS', 'permissions');
+
+class BxDevModule extends BxDolModule
+{
+    public $aTools;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->aTools = array(
+            array('name' => BX_DEV_TOOLS_SETTINGS, 'title' => '_adm_page_cpt_settings', 'icon' => 'bx-dev-mi-settings.svg'),
+            array('name' => BX_DEV_TOOLS_POLYGLOT, 'title' => '_adm_page_cpt_polyglot', 'icon' => 'bx-dev-mi-polyglot.svg'),
+            array('name' => BX_DEV_TOOLS_FORMS, 'title' => '_adm_page_cpt_builder_forms', 'icon' => 'bx-dev-mi-forms.svg'),
+            array('name' => BX_DEV_TOOLS_NAVIGATION, 'title' => '_adm_page_cpt_builder_menus', 'icon' => 'bx-dev-mi-navigation.svg'),
+            array('name' => BX_DEV_TOOLS_PAGES, 'title' => '_adm_page_cpt_builder_pages', 'icon' => 'bx-dev-mi-pages.svg'),
+            //array('name' => BX_DEV_TOOLS_PERMISSIONS, 'title' => '_adm_page_cpt_builder_permissions', 'icon' => 'bx-dev-mi-permissions.png'),
+        );
+    }
+
+    function getToolsList()
+    {
+        return $this->aTools;
+    }
+
+    public function actionResetHash($sType, $sModule = '')
+    {
+    	$oDb = bx_instance('BxDolStudioInstallerQuery');
+
+    	$sResult = '';
+    	switch ($sType) {
+    		case 'system':
+                    $oHasher = bx_instance('BxDolInstallerHasher');
+
+                    $oDb->deleteModuleTrackFiles(BX_SYSTEM_MODULE_ID);
+
+                    $sResult = _t('_bx_dev_hash_' . ($oHasher->hashSystemFiles() ? 'msg' : 'err') . '_reset_hash_system');    			
+                    break;
+
+    		case 'modules':
+                    $oModuleQuery = BxDolModuleQuery::getInstance();
+                    if(!empty($sModule)) {
+                        $aNames = strpos($sModule, ',') !== false ? explode(',', $sModule) : array($sModule);
+
+                        $aModule = array();
+                        foreach($aNames as $aName)
+                            $aModules[] = $oModuleQuery->getModuleByName($aName);
+                    }
+                    else
+                        $aModules = $oModuleQuery->getModules();
+
+                    $aTmplVarsModules = array();
+                    if(!empty($aModules) && is_array($aModules))
+                        foreach($aModules as $aModule) {
+                            if($aModule['name'] == 'system')
+                                continue;
+
+                            $aConfig = BxDolInstallerUtils::getModuleConfig($aModule);
+                            $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $aModule['path'] . 'install/installer.php';
+                            if(empty($aConfig) || !file_exists($sPathInstaller))
+                                continue;
+
+                            require_once($sPathInstaller);
+
+                            $sClassName = $aConfig['class_prefix'] . 'Installer';
+                            $oInstaller = new $sClassName($aConfig);
+
+                            $oDb->deleteModuleTrackFiles($aModule['id']);
+
+                            $aFiles = array();
+                            $oInstaller->hashFiles(BX_DIRECTORY_PATH_ROOT . 'modules/' . $aModule['path'], $aFiles);
+                            foreach($aFiles as $aFile)
+                                $oDb->insertModuleTrack($aModule['id'], $aFile);
+
+                            $aTmplVarsModules[] = array(
+                                'module' => $aModule['title'],
+                                'files' => count($aFiles)
+                            );
+                        }
+
+                        $sResult = $this->_oTemplate->parseHtmlByName('hash_modules.html', array(
+                            'bx_repeat:modules' => $aTmplVarsModules
+                        ));
+                        break;
+    	}
+
+    	echo $sResult;
+    	exit;
+    }
+    
+    public function actionExport()
+    {
+        $sType = bx_process_input(bx_get('type'));
+        if(empty($sType))
+            return echoJson([]);
+
+        echoJson([
+            'url' => BX_DOL_URL_ROOT . bx_append_url_params($this->_oConfig->getBaseUri() . 'download', [
+                'type' => $sType,
+            ]),
+            'eval' => $this->_oConfig->getJsObject('main') . '.onExport(oData);'
+        ]);
+    }
+
+    public function actionDownload()
+    {
+        $sType = bx_process_input(bx_get('type'));
+        if(empty($sType))
+            return;
+
+        $sMethod = '_eiExport' . bx_gen_method_name($sType);
+        if(!method_exists($this, $sMethod))
+            return;
+
+        $mixedFull = ($mixedFull = bx_get('full')) !== false ? (int)$mixedFull != 0 : false;
+
+        list($aMeta, $aData) = $this->$sMethod($mixedFull);
+
+        $iNow = time();
+    	$sContent = json_encode([
+            'meta' => array_merge([
+                'version' => bx_get_ver(),
+                'type' => $sType,
+                'full' => $mixedFull,
+                'date' => date('D, d M Y H:i:s', $iNow),
+            ], $aMeta),
+            'data' => $aData
+    	]);
+
+    	header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+        header("Content-type: application/json");
+        header("Content-Length: " . strlen($sContent));
+        header("Content-Disposition: attachment; filename=\"" . $sType . ($mixedFull ? "_f" : "") . "_". date('d_m_Y', $iNow) . ".json\"");
+
+        echo $sContent;
+        exit;
+    }
+    
+    public function actionImport()
+    {
+        $sType = bx_process_input(bx_get('type'));
+        if(empty($sType))
+            return echoJson([]);
+
+        $aResult = $this->getPopupCodeImport($sType, [
+            'form_name' => 'bx-dev-' . $sType . '-import-full',
+            'form_action' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'import?type=' . $sType
+        ]);
+
+        if(!isset($aResult['code']) || (int)$aResult['code'] != 0)
+            return echoJson($aResult);
+        
+        $sMethod = '_eiImport' . bx_gen_method_name($sType);
+        if(!method_exists($this, $sMethod))
+            return echoJson([]);
+
+        echoJson($this->$sMethod($aResult['content'], $aResult['disable'] != 0));
+    }
+
+    public function getPopupCodeImport($sType, $aParams)
+    {
+    	$sJsObject = $this->_oConfig->getJsObject('main');
+
+    	$sForm = $aParams['form_name'];
+    	$aForm = [
+            'form_attrs' => [
+                'id' => $sForm,
+                'name' => $sForm,
+                'action' => $aParams['form_action'],
+                'method' => 'post',
+                'enctype' => 'multipart/form-data'
+            ],
+            'params' => [
+                'db' => [
+                    'submit_name' => 'save'
+                ],
+            ],
+            'inputs' => [
+            	'file' => [
+                    'type' => 'file',
+                    'name' => 'file',
+                    'caption' => '',
+                    'value' => '',
+                ],
+                'disable' => [
+                    'type' => 'switcher',
+                    'name' => 'disable',
+                    'caption' => _t('_bx_dev_txt_disable_before_import'),
+                    'info' => _t('_bx_dev_txt_disable_before_import_note'),
+                    'value' => '1',
+                    'checked' => 0,
+                    'db' => [
+                        'pass' => 'Int',
+                    ],
+                ],
+                'controls' => [
+                    'type' => 'input_set', [
+                        'type' => 'submit',
+                        'name' => 'save',
+                        'value' => _t('_bx_dev_btn_import_full'),
+                    ], [
+                        'type' => 'button',
+                        'name' => 'cancel',
+                        'value' => _t('_bx_dev_btn_cancel'),
+                        'attrs' => [
+                            'class' => 'bx-def-margin-sec-left-auto',
+                            'onclick' => '$(".bx-popup-applied:visible").dolPopupHide()'
+                        ]
+                    ]
+                ]
+            ]
+        ];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $sError = _t('_bx_dev_err_cannot_perform');
+            $sErrorWf = _t('_bx_dev_err_import_wrong_format');
+
+            if(empty($_FILES['file']) || empty($_FILES['file']['tmp_name']))
+                return ['code' => 1, 'msg' => $sError];
+
+            $aFile = $_FILES['file'];
+            $sFile = $aFile['tmp_name'];
+            $rHandle = @fopen($sFile, "r");
+            if(!$rHandle)
+                return ['code' => 2, 'message' => $sError];
+
+            $sContents = fread($rHandle, filesize($sFile));
+            fclose($rHandle);
+
+            $aContent = json_decode($sContents, true);
+            if(!is_array($aContent) || empty($aContent['meta']) || empty($aContent['data']))
+                return ['code' => 3, 'message' => $sErrorWf];
+
+            if(empty($aContent['meta']['type']) || strcmp($aContent['meta']['type'], $sType) != 0)
+                return ['code' => 4, 'message' => $sErrorWf];
+
+            return ['code' => 0, 'content' => $aContent, 'disable' => $oForm->getCleanValue('disable')];
+        }
+
+        return [
+            'popup' => BxTemplStudioFunctions::getInstance()->popupBox('bx-dev-import-popup', _t('_bx_dev_txt_import_full_popup'), $this->_oTemplate->parseHtmlByName('import_popup.html', [
+                'js_object' => $sJsObject,
+                'form_id' => $sForm,
+                'form' => $oForm->getCode(true),
+            ]))
+        ];
+    }
+
+    public function isValidJson($s)
+    {
+        //--- Validation is disabled for now.         
+        //--- return json_decode($s, true) !== null;
+
+        return true;
+    }
+
+    /**
+     * 
+     * Methods for Export/Import feature.
+     * 
+     */
+    protected function _eiExportForms($bModeFull = false)
+    {
+        $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_forms'));
+        if(!$oGrid)
+            return [];
+
+        $aForms = [];
+        $oGrid->oDb->getForms(['type' => 'export'], $aForms, false);
+        if(empty($aForms) || !is_array($aForms))
+            return [];
+
+        $aMeta = [
+            'masks' => [
+                'form' => ['object', 'active'],
+                'display' => ['display_name'],
+                'input' => ['object', 'name', 'privacy', 'checker_func', 'checker_params', 'checker_error', 'db_pass', 'db_params'],
+                'display_input' => ['display_name', 'input_name', 'visible_for_levels', 'active', 'order']
+            ]
+        ];
+
+        if($bModeFull) {
+            $aMeta['masks'] = [
+                'form' => ['object', 'module', 'title', 'action', 'form_attrs', 'submit_name', 'table', 'key', 'uri', 'uri_title', 'params', 'deletable', 'active', 'override_class_name', 'override_class_file'],
+                'display' => ['display_name', 'module', 'object', 'title', 'view_mode'],
+                'input' => ['object', 'module', 'name', 'value', 'values', 'checked', 'type', 'caption_system', 'caption', 'info', 'help', 'icon', 'required', 'unique', 'collapsed', 'html', 'privacy', 'rateable', 'attrs', 'attrs_tr', 'attrs_wrapper', 'checker_func', 'checker_params', 'checker_error', 'db_pass', 'db_params', 'editable', 'deletable'],
+                'display_input' => ['display_name', 'input_name', 'visible_for_levels', 'active', 'order']
+            ];
+        }
+
+        $aMfForm = $aMfDisplay = $aMfInput = $aMfDisplayInput = false;
+        foreach($aMeta['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $aData = [];
+        foreach($aForms as $aForm) {
+            $sObject = $aForm['object'];
+
+            $aDisplays = [];
+            $oGrid->oDb->getDisplays(['type' => 'export_by_object', 'value' => $sObject], $aDisplays, false);
+            
+            $aListDisplays = [];
+            $aDataDisplays = [];
+            if(!empty($aDisplays) && is_array($aDisplays))
+                foreach($aDisplays as $aDisplay) {
+                    $aListDisplays[] = $aDisplay['display_name'];
+                    $aDataDisplays[] = $aMfDisplay !== false ? array_intersect_key($aDisplay, $aMfDisplay) : $aDisplay;
+                }
+
+            $aInputs = [];
+            $oGrid->oDb->getInputs(['type' => 'dump_inputs', 'value' => $sObject], $aInputs, false);
+
+            $aDataInputs = [];
+            if(!empty($aInputs) && is_array($aInputs))
+                foreach($aInputs as $aInput)
+                    $aDataInputs[] = $aMfInput !== false ? array_intersect_key($aInput, $aMfInput) : $aInput;
+
+            $aDisplayInputs = [];
+            $oGrid->oDb->getInputs(['type' => 'dump_display_inputs', 'displays' => $aListDisplays], $aDisplayInputs, false);
+
+            $aDataDisplayInputs = [];
+            if(!empty($aDisplayInputs) && is_array($aDisplayInputs))
+                foreach($aDisplayInputs as $aDisplayInput)
+                    $aDataDisplayInputs[] = $aMfDisplayInput !== false ? array_intersect_key($aDisplayInput, $aMfDisplayInput) : $aDisplayInput;
+
+            $aData[] = [
+                'form' => $aMfForm !== false ? array_intersect_key($aForm, $aMfForm) : $aForm,
+                'displays' => $aDataDisplays,
+                'inputs' => $aDataInputs,
+                'display_inputs' => $aDataDisplayInputs
+            ];
+        }
+
+        return [$aMeta, $aData];
+    }
+
+    protected function _eiExportMenus($bModeFull = false)
+    {
+        $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_nav_menus'));
+        if(!$oGrid)
+            return [];
+
+        $aMenus = [];
+        $oGrid->oDb->getMenus(['type' => 'export'], $aMenus, false);
+        if(empty($aMenus) || !is_array($aMenus))
+            return [];
+
+        $aMeta = [
+            'masks' => [
+                'menu' => ['object', 'active'],
+                'set' => ['set_name'],
+                'item' => ['set_name', 'name', 'visible_for_levels', 'visibility_custom', 'hidden_on', 'hidden_on_cxt', 'hidden_on_pt', 'hidden_on_col', 'active', 'active_api', 'order']
+            ]
+        ];
+
+        if($bModeFull) {
+            $aMeta['masks'] = [
+                'menu' => ['object', 'title', 'set_name', 'module', 'template_id', 'deletable', 'active', 'override_class_name', 'override_class_file'],
+                'set' => ['set_name', 'module', 'title', 'deletable'],
+                'item' => ['parent_id', 'set_name', 'module', 'name', 'title_system', 'title', 'link', 'onclick', 'target', 'icon', 'addon', 'addon_cache', 'markers', 'submenu_object', 'submenu_popup', 'visible_for_levels', 'visibility_custom', 'hidden_on', 'hidden_on_cxt', 'hidden_on_pt', 'hidden_on_col', 'primary', 'collapsed', 'active', 'active_api', 'copyable', 'editable', 'order']
+            ];
+        }
+
+        $aMfMenu = $aMfSet = $aMfItem = false;
+        foreach($aMeta['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $aData = [];
+        foreach($aMenus as $aMenu) {
+            $aSet = [];
+            $oGrid->oDb->getSets(['type' => 'export_by_name', 'value' => $aMenu['set_name']], $aSet, false);
+
+            $aDataItems = [];
+            if(!empty($aSet) && is_array($aSet)) {
+                $aItems = [];
+                $oGrid->oDb->getItems(['type' => 'export_by_set_name', 'value' => $aSet['set_name']], $aItems, false);
+
+                if(!empty($aItems) && is_array($aItems))
+                    foreach($aItems as $aItem)
+                        $aDataItems[] = $aMfItem !== false ? array_intersect_key($aItem, $aMfItem) : $aItem;
+            }
+
+            $aData[] = [
+                'menu' => $aMfMenu !== false ? array_intersect_key($aMenu, $aMfMenu) : $aMenu,
+                'set' => !empty($aSet) && $aMfSet !== false ? array_intersect_key($aSet, $aMfSet) : $aSet,
+                'items' => $aDataItems,
+            ];
+        }
+
+        return [$aMeta, $aData];
+    }
+
+    protected function _eiExportPages($bModeFull = false)
+    {
+        bx_import('BuilderPage', $this->_aModule);
+        $oBuilderPage = new BxDevBuilderPage(['type' => '', 'page' => '', 'url' => '']);
+        if(!$oBuilderPage)
+            return [];
+
+        $oBpDb = $oBuilderPage->getDb();
+
+        $aPages = $oBpDb->getPages(['type' => 'export']);
+        if(empty($aPages) || !is_array($aPages))
+            return [];
+
+        $aMeta = [
+            'masks' => [
+                'page' => ['object', 'visible_for_levels'],
+                'block' => ['object', 'cell_id', 'module', 'title_system', 'title', 'visible_for_levels', 'hidden_on', 'active', 'active_api', 'order']
+            ]
+        ];
+
+        if($bModeFull) {
+            $aMeta['masks'] = [
+                'page' => ['author', 'added', 'object', 'uri', 'title_system', 'title', 'module', 'cover', 'cover_image', 'cover_title', 'type_id', 'layout_id', 'sticky_columns', 'submenu', 'visible_for_levels', 'visible_for_levels_editable', 'url', 'content_info', 'meta_description', 'meta_keywords', 'meta_robots', 'cache_lifetime', 'cache_editable', 'inj_head', 'inj_footer', 'deletable', 'override_class_name', 'override_class_file'],
+                'block' => ['object', 'cell_id', 'module', 'title_system', 'title', 'designbox_id', 'class', 'submenu', 'tabs', 'async', 'visible_for_levels', 'hidden_on', 'type', 'content', 'content_empty', 'text', 'text_updated', 'help', 'cache_lifetime', 'deletable', 'copyable', 'active', 'active_api', 'order']
+            ];
+        }
+
+        $aMfPage = $aMfBlock = false;
+        foreach($aMeta['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        $aData = [];
+        foreach($aPages as $aPage) {
+            $aBlocks = [];
+            $oBpDb->getBlocks(['type' => 'export_by_object', 'value' => $aPage['object']], $aBlocks, false);
+
+            $aDataBlocks = [];
+            if(!empty($aBlocks) && is_array($aBlocks))
+                foreach($aBlocks as $aBlock)
+                    $aDataBlocks[] = $aMfBlock !== false ? array_intersect_key($aBlock, $aMfBlock) : $aBlock;
+
+            $aData[] = [
+                'page' => $aMfPage !== false ? array_intersect_key($aPage, $aMfPage) : $aPage,
+                'blocks' => $aDataBlocks,
+            ];
+        }
+
+        return [$aMeta, $aData];
+    }
+
+    protected function _eiImportPages($aContent, $bDisable = false)
+    {
+        bx_import('BuilderPage', $this->_aModule);
+        $oBuilderPage = new BxDevBuilderPage(['type' => '', 'page' => '', 'url' => '']);
+        if(!$oBuilderPage)
+            return [];
+
+        $oBpDb = $oBuilderPage->getDb();
+
+        $bModeFull = isset($aContent['meta']['full']) && (bool)$aContent['meta']['full'] === true;
+
+        $aMfMenu = $aMfSet = $aMfItem = false;
+        foreach($aContent['meta']['masks'] as $sMask => $aMask)
+            ${'aMf' . bx_gen_method_name($sMask)} = array_flip($aMask);
+
+        if($bDisable)
+            $oBpDb->updateBlocks(['active' => 0, 'active_api' => 0]);
+
+        $iData = 0;
+        foreach($aContent['data'] as $aData) {
+            $iData += 1;
+
+            $sObject = $aData['page']['object'];
+            if($bModeFull && !$oBpDb->isPageExists($sObject)) {
+                if(!$oBpDb->addPage($aData['page']))
+                    continue;
+            }
+            else
+                $oBpDb->updatePageByObject($sObject, $aData['page']);
+
+            foreach($aData['blocks'] as $aBlock) {
+                $aFields = [
+                    'object' => $aBlock['object'],
+                    'module' => $aBlock['module'], 
+                    'title_system' => $aBlock['title_system'], 
+                    'title' => $aBlock['title']
+                ];
+
+                if($bModeFull && !$oBpDb->isBlockExists($aFields))
+                    $oBpDb->insertBlock($aBlock);
+                else
+                    $oBpDb->updateBlockByFields($aFields, $aBlock);
+            }
+        }
+
+        BxDolCacheUtilities::getInstance()->clear('db');
+
+        return [
+            'msg' => _t('_bx_dev_msg_imported', $iData), 
+            'eval' => $this->_oConfig->getJsObject('main') . '.onImport(oData);'
+        ];
+    }
+}
+/** @} */

+ 69 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/source/install/config.php

@@ -0,0 +1,69 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Developer Developer
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_developer',
+    'title' => 'Developer',
+    'note' => 'Developer tools...',
+    'version' => '15.0.2',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/developer/',
+    'home_uri' => 'developer',
+
+    'db_prefix' => 'bx_dev_',
+    'class_prefix' => 'BxDev',
+
+	/**
+     * Category for language keys.
+     */
+    'language_category' => 'BoonEx Developer',
+
+    /**
+     * 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,
+    ),
+    'disable' => array(
+        'execute_sql' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+);
+
+/** @} */

+ 565 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/source/install/langs/en.xml

@@ -0,0 +1,565 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_developer"><![CDATA[Developer]]></string>
+
+    <string name="_bx_dev_adm_wgt_cpt"><![CDATA[Developer]]></string>
+    <string name="_bx_dev_adm_page_cpt"><![CDATA[Developer]]></string>
+    
+    <!-- Forms Builder -->
+    <string name="_bx_dev_frm_txt_forms_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_forms_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_forms_gl_displays"><![CDATA[Displays]]></string>
+    <string name="_bx_dev_frm_btn_forms_gl_export"><![CDATA[Export Form]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_form"><![CDATA[Form]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_fields"><![CDATA[Fields]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_caption_system"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_visible"><![CDATA[Visible For]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_values"><![CDATA[Values]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_use_for_sets"><![CDATA[Use For Sets]]></string>
+    <string name="_bx_dev_frm_txt_pre_values_gl_lkey"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_gl_fields"><![CDATA[Fields]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_search_type"><![CDATA[Search Type]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_search_operator"><![CDATA[Search Operator]]></string>
+    <string name="_bx_dev_frm_btn_forms_gl_create"><![CDATA[Add New Form]]></string>
+    <string name="_bx_dev_frm_btn_displays_gl_create"><![CDATA[Add New Display]]></string>
+    <string name="_bx_dev_frm_btn_fields_create"><![CDATA[Add New Field]]></string>
+    <string name="_bx_dev_frm_btn_pre_lists_create"><![CDATA[Add New List]]></string>
+    <string name="_bx_dev_frm_btn_pre_values_create"><![CDATA[Add New Item]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_create"><![CDATA[Add New Search]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_edit"><![CDATA[Edit search form]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_fields_edit"><![CDATA[Edit search field]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_fields_reset"><![CDATA[Reset]]></string>
+    <string name="_bx_dev_frm_txt_select_module"><![CDATA[Select Module...]]></string>
+    <string name="_bx_dev_frm_txt_select_object"><![CDATA[Select Object...]]></string>
+    <string name="_bx_dev_frm_txt_select_object_content_info"><![CDATA[Select Content Info Object...]]></string>
+    <string name="_bx_dev_frm_txt_forms_create_popup"><![CDATA[Add New Form]]></string>
+    <string name="_bx_dev_frm_txt_forms_edit_popup"><![CDATA[Edit "{0}" Form]]></string>
+    <string name="_bx_dev_frm_txt_forms_export_popup"><![CDATA[Export "{0}" Form]]></string>
+    <string name="_bx_dev_frm_err_forms_create"><![CDATA[Cannot add form]]></string>
+    <string name="_bx_dev_frm_err_forms_edit"><![CDATA[Cannot update form]]></string>
+    <string name="_bx_dev_frm_txt_forms_form"><![CDATA[Forms Builder Form]]></string>
+    <string name="_bx_dev_frm_txt_forms_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_forms_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_object"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_frm_txt_forms_object"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_frm_err_forms_object"><![CDATA[You need to enter a unique value here]]></string>    
+    <string name="_bx_dev_frm_txt_sys_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_forms_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_frm_txt_forms_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_frm_err_forms_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_action"><![CDATA[Action]]></string>
+    <string name="_bx_dev_frm_txt_forms_action"><![CDATA[Action]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_form_attrs"><![CDATA[Form Attrs]]></string>
+    <string name="_bx_dev_frm_txt_forms_form_attrs"><![CDATA[Form Attrs]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_submit_name"><![CDATA[Submit Name]]></string>
+    <string name="_bx_dev_frm_txt_forms_submit_name"><![CDATA[Submit Name]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_table"><![CDATA[Table]]></string>
+    <string name="_bx_dev_frm_txt_forms_table"><![CDATA[Table]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_key"><![CDATA[Key Field]]></string>
+    <string name="_bx_dev_frm_txt_forms_key"><![CDATA[Key Field]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_uri"><![CDATA[URI Field]]></string>
+    <string name="_bx_dev_frm_txt_forms_uri"><![CDATA[URI Field]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_uri_title"><![CDATA[URI Source Field]]></string>
+    <string name="_bx_dev_frm_txt_forms_uri_title"><![CDATA[URI Source Field]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_params"><![CDATA[Params]]></string>
+    <string name="_bx_dev_frm_txt_forms_params"><![CDATA[Params]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_frm_txt_forms_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_forms_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_txt_forms_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_txt_forms_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_frm_txt_forms_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_frm_btn_sys_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_forms_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_forms_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_frm_txt_displays_create_popup"><![CDATA[Add New Display]]></string>
+    <string name="_bx_dev_frm_txt_displays_edit_popup"><![CDATA[Edit "{0}" Display]]></string>
+    <string name="_bx_dev_frm_err_displays_create"><![CDATA[Cannot add display]]></string>
+    <string name="_bx_dev_frm_err_displays_edit"><![CDATA[Cannot update display]]></string>
+    <string name="_bx_dev_frm_txt_displays_diplay"><![CDATA[Forms Builder Display]]></string>
+    <string name="_bx_dev_frm_txt_displays_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_displays_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_display_name"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_frm_txt_displays_display_name"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_frm_err_displays_display_name"><![CDATA[You need to enter a unique (in the form) value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_displays_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_displays_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_object"><![CDATA[Form Identifier]]></string>
+    <string name="_bx_dev_frm_txt_displays_object"><![CDATA[Form Identifier]]></string>
+    <string name="_bx_dev_frm_err_displays_object"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_frm_txt_displays_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_frm_err_displays_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_view_mode"><![CDATA[View Mode]]></string>
+    <string name="_bx_dev_frm_txt_displays_view_mode"><![CDATA[View Mode]]></string>
+    <string name="_bx_dev_frm_btn_sys_displays_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_displays_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_displays_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_displays_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_displays_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_txt_field_title_caption_system"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_frm_txt_field_title_caption"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_frm_txt_field_title_editable"><![CDATA[Editable]]></string>
+    <string name="_bx_dev_frm_txt_field_info_editable"><![CDATA[Make the field Editable in Studio Forms Builder]]></string>
+    <string name="_bx_dev_frm_txt_field_title_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_frm_txt_field_info_deletable"><![CDATA[Make the field Deletable in Studio Forms Builder]]></string>
+    <string name="_bx_dev_frm_txt_prelists_create_popup"><![CDATA[Add Data List]]></string>
+    <string name="_bx_dev_frm_err_prelists_create"><![CDATA[Cannot add data list]]></string>
+    <string name="_bx_dev_frm_txt_prelists_edit_popup"><![CDATA[Edit "{0}" Data List]]></string>
+    <string name="_bx_dev_frm_err_prelists_edit"><![CDATA[Cannot update data list]]></string>
+    <string name="_bx_dev_frm_txt_prelists_export_popup"><![CDATA[Export "{0}" Data List]]></string>
+    <string name="_bx_dev_frm_txt_prelists_prelist"><![CDATA[Forms Builder Data List]]></string>
+    <string name="_bx_dev_frm_txt_prelists_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_prelists_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_id"><![CDATA[id]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_key"><![CDATA[Key]]></string>
+    <string name="_bx_dev_frm_txt_prelists_key"><![CDATA[Key]]></string>
+    <string name="_bx_dev_frm_err_prelists_key"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_prelists_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_prelists_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_prelists_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_prelists_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_use_for_sets"><![CDATA[Use For Sets]]></string>
+    <string name="_bx_dev_frm_txt_prelists_use_for_sets"><![CDATA[Use For Sets]]></string>
+    <string name="_bx_dev_frm_btn_sys_prelists_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prelists_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prelists_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_prelists_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_prelists_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_prelists_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_frm_txt_prelists_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_frm_txt_prelists_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_create_popup"><![CDATA[Add Data Value]]></string>
+    <string name="_bx_dev_frm_err_prevalues_create"><![CDATA[Cannot add data value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_edit_popup"><![CDATA[Edit "{0}" Data Value]]></string>
+    <string name="_bx_dev_frm_err_prevalues_edit"><![CDATA[Cannot update data value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_prevalue"><![CDATA[Forms Builder Data Value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_display_edit"><![CDATA[Edd]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_key"><![CDATA[List]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_key"><![CDATA[List]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_key_select"><![CDATA[Select List...]]></string>
+    <string name="_bx_dev_frm_err_prevalues_key"><![CDATA[You need to select a list]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_value"><![CDATA[Value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_value"><![CDATA[Value]]></string>
+    <string name="_bx_dev_frm_err_prevalues_value"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_lkey"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_lkey"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_prevalues_lkey"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_lkey2"><![CDATA[Title Additional]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_lkey2"><![CDATA[Title Additional]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_data"><![CDATA[Data]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_data"><![CDATA[Data]]></string>
+    <string name="_bx_dev_frm_btn_sys_prevalues_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prevalues_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prevalues_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_prevalues_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_prevalues_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_err_prevalues_create_forbidden"><![CDATA[This list is already used. It's impossible to add non-numeric, or less than 1, or more than {0} values in it.]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_create_popup"><![CDATA[Add New Search Form]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_edit_popup"><![CDATA[Edit "{0}" Search Form]]></string>
+    <string name="_bx_dev_frm_err_search_forms_create"><![CDATA[Cannot add search form]]></string>
+    <string name="_bx_dev_frm_err_search_forms_edit"><![CDATA[Cannot update search form]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_form"><![CDATA[Forms Builder Search Form]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_err_forms_object"><![CDATA[You need to enter a unique value here]]></string>    
+    <string name="_bx_dev_frm_txt_sys_search_forms_object_content_info"><![CDATA[Content Info object]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_object_content_info"><![CDATA[Content Info object]]></string>
+    <string name="_bx_dev_frm_err_forms_object_content_info"><![CDATA[You need to select the name of Content Info object here]]></string>    
+    <string name="_bx_dev_frm_txt_sys_search_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_forms_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_forms_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_edit_popup"><![CDATA[Edit "{0}" Search Field]]></string>
+    <string name="_bx_dev_frm_err_search_fields_edit"><![CDATA[Cannot update search field]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_form"><![CDATA[Forms Builder Search Field]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_object"><![CDATA[Search Form]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_object"><![CDATA[Search Form]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_err_search_fields_name"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_err_search_fields_type"><![CDATA[You need to select a type]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_dev_frm_err_search_fields_caption"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_values"><![CDATA[Values]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_values"><![CDATA[Values]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_search_type"><![CDATA[Search Type]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_search_type"><![CDATA[Search Type]]></string>
+    <string name="_bx_dev_frm_err_search_fields_search_type"><![CDATA[You need to select a search type]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_search_value"><![CDATA[Default Value]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_search_value"><![CDATA[Default Value]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_search_operator"><![CDATA[Operator]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_search_operator"><![CDATA[Operator]]></string>
+    <string name="_bx_dev_frm_err_search_fields_search_operator"><![CDATA[You need to select an operator]]></string>
+    <string name="_bx_dev_frm_btn_search_fields_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_fields_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_search_fields_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_fields_cancel"><![CDATA[Cancel]]></string>
+    
+    <!-- Navigation Builder -->
+    <string name="_bx_dev_nav_txt_menus_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_set_title"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_items"><![CDATA[Items]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_template_title"><![CDATA[Style]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_sets_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_nav_txt_sets_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_sets_gl_items"><![CDATA[Items]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_title_system"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_link"><![CDATA[URL]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_visible"><![CDATA[Visibile For]]></string>
+    <string name="_bx_dev_nav_btn_menus_gl_create"><![CDATA[Add New Menu]]></string>
+    <string name="_bx_dev_nav_btn_menus_gl_export"><![CDATA[Export Menu]]></string>
+    <string name="_bx_dev_nav_btn_sets_gl_create"><![CDATA[Add New Set]]></string>
+    <string name="_bx_dev_nav_btn_items_gl_create"><![CDATA[Add New Item]]></string>
+    <string name="_bx_dev_nav_btn_items_gl_import"><![CDATA[Import Items]]></string>
+    <string name="_bx_dev_nav_btn_items_gl_visible"><![CDATA[Visibile For]]></string>
+    <string name="_bx_dev_nav_txt_select_module"><![CDATA[Select Module...]]></string>
+    <string name="_bx_dev_nav_txt_menus_create_popup"><![CDATA[Add New Menu]]></string>
+    <string name="_bx_dev_nav_err_menus_create"><![CDATA[Cannot add menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_edit_popup"><![CDATA[Edit "{0}" Menu]]></string>
+    <string name="_bx_dev_nav_err_menus_edit"><![CDATA[Cannot update menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_export_popup"><![CDATA[Export "{0}" Menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_menu"><![CDATA[Navigation Builder Menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_txt_menus_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_nav_txt_menus_object"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_nav_err_menus_object"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_menus_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_err_menus_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_title_public"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_nav_txt_menus_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_nav_txt_menus_title_public"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_nav_err_menus_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_menus_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_menus_set_name_select"><![CDATA[Select Set...]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_template_id"><![CDATA[Style]]></string>
+    <string name="_bx_dev_nav_txt_menus_template_id"><![CDATA[Style]]></string>
+    <string name="_bx_dev_nav_txt_menus_template_id_select"><![CDATA[Select Style...]]></string>
+    <string name="_bx_dev_nav_err_menus_template_id"><![CDATA[You need to select a style]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_config_api"><![CDATA[Config (for App)]]></string>
+    <string name="_bx_dev_nav_txt_menus_config_api"><![CDATA[Config (for App in JSON format)]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_txt_menus_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_nav_txt_menus_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_nav_txt_menus_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_nav_txt_menus_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_nav_txt_menus_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_nav_btn_sys_menus_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_menus_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_menus_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_nav_btn_sys_menus_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_menus_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_menus_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_nav_txt_sets_create_popup"><![CDATA[Add New Set]]></string>
+    <string name="_bx_dev_nav_err_sets_create"><![CDATA[Cannot add set]]></string>
+    <string name="_bx_dev_nav_txt_sets_edit_popup"><![CDATA[Edit "{0}" Set]]></string>
+    <string name="_bx_dev_nav_err_sets_edit"><![CDATA[Cannot update set]]></string>
+    <string name="_bx_dev_nav_txt_sets_set"><![CDATA[Navigation Builder Sets]]></string>
+    <string name="_bx_dev_nav_txt_sets_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_txt_sets_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_sets_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_err_sets_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_sets_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_err_sets_set_name"><![CDATA[You need to enter a set name]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_nav_txt_sets_title"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_nav_err_sets_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_txt_sets_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_btn_sys_sets_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_sets_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_sys_sets_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_sets_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_txt_items_create_popup"><![CDATA[Add New Item]]></string>
+    <string name="_bx_dev_nav_err_items_create"><![CDATA[Cannot add item]]></string>
+    <string name="_bx_dev_nav_txt_items_edit_popup"><![CDATA[Edit "{0}" Item]]></string>
+    <string name="_bx_dev_nav_err_items_edit"><![CDATA[Cannot update item]]></string>
+    <string name="_bx_dev_nav_txt_items_item"><![CDATA[Navigation Builder Items]]></string>
+    <string name="_bx_dev_nav_txt_items_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_txt_items_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_items_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_err_items_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_items_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_err_items_set_name"><![CDATA[You need to select a set]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_name"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_nav_txt_items_name"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_nav_err_items_name"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_title_system"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_nav_txt_items_title_system"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_title"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_nav_txt_items_title"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_nav_err_items_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_link"><![CDATA[Link]]></string>
+    <string name="_bx_dev_nav_txt_items_link"><![CDATA[Link]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_onclick"><![CDATA[OnClick]]></string>
+    <string name="_bx_dev_nav_txt_items_onclick"><![CDATA[OnClick]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_target"><![CDATA[Target]]></string>
+    <string name="_bx_dev_nav_txt_items_target"><![CDATA[Target]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_nav_txt_items_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_addon"><![CDATA[Addon]]></string>
+    <string name="_bx_dev_nav_txt_items_addon"><![CDATA[Addon]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_submenu_object"><![CDATA[Submenu Object]]></string>
+    <string name="_bx_dev_nav_txt_items_submenu_object"><![CDATA[Submenu Object]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_submenu_popup"><![CDATA[Submenu Popup]]></string>
+    <string name="_bx_dev_nav_txt_items_submenu_popup"><![CDATA[Submenu Popup]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_visibility_custom"><![CDATA[Custom Visibility]]></string>
+    <string name="_bx_dev_nav_txt_items_visibility_custom"><![CDATA[Custom Visibility]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_phone"><![CDATA[phone]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_tablet"><![CDATA[tablet]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_desktop"><![CDATA[desktop]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_mobile"><![CDATA[mobile app]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_pt"><![CDATA[Hidden for page type]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_pt"><![CDATA[Hidden for page type]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_col"><![CDATA[Hidden for column]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col"><![CDATA[Hidden for column]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_thin"><![CDATA[Thin]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_half"><![CDATA[Half]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_wide"><![CDATA[Wide]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_full"><![CDATA[Full]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_cxt"><![CDATA[Hidden in contexts]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_cxt"><![CDATA[Hidden in contexts]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_config_api"><![CDATA[Config (for App)]]></string>
+    <string name="_bx_dev_nav_txt_items_config_api"><![CDATA[Config (for App in JSON format)]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_primary"><![CDATA[Primary]]></string>
+    <string name="_bx_dev_nav_txt_items_primary"><![CDATA[Primary]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_collapsed"><![CDATA[Collapsed]]></string>
+    <string name="_bx_dev_nav_txt_items_collapsed"><![CDATA[Collapsed]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_nav_txt_items_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_nav_txt_items_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_nav_btn_sys_items_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_items_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_items_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_nav_btn_sys_items_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_items_cancel"><![CDATA[Cancel]]></string>
+
+    <!-- Page Builder -->
+    <string name="_bx_dev_bp_txt_select_module"><![CDATA[Select Module...]]></string>
+    <string name="_bx_dev_bp_txt_page_create_popup"><![CDATA[Add New Page]]></string>
+    <string name="_bx_dev_bp_err_page_create"><![CDATA[Cannot add page]]></string>
+    <string name="_bx_dev_bp_txt_page_form"><![CDATA[Page in Page Builder]]></string>
+    <string name="_bx_dev_bp_txt_page_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_txt_page_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_err_page_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_object"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_bp_txt_page_object"><![CDATA[Identifier]]></string>
+    <string name="_bx_dev_bp_err_page_object"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_title_system"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_bp_txt_page_title_system"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_title"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_bp_txt_page_title"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_bp_err_page_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_uri"><![CDATA[URI]]></string>
+    <string name="_bx_dev_bp_txt_page_uri"><![CDATA[URI]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_url"><![CDATA[URL]]></string>
+    <string name="_bx_dev_bp_txt_page_url"><![CDATA[URL]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_layout_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_txt_page_layout_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_txt_page_layout_id_select"><![CDATA[Select Layout...]]></string>
+    <string name="_bx_dev_bp_err_page_layout_id"><![CDATA[You need to select a layout]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_config_api"><![CDATA[Config (for App)]]></string>
+    <string name="_bx_dev_bp_txt_page_config_api"><![CDATA[Config (for App in JSON format)]]></string>
+    <string name="_bx_dev_bp_txt_page_cache_editable"><![CDATA[Editable]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_page_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_bp_txt_page_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_bp_txt_page_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_bp_btn_sys_page_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_page_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_sys_page_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_page_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_page_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_bp_btn_page_export"><![CDATA[Export Page]]></string>
+    <string name="_bx_dev_bp_txt_page_export_popup"><![CDATA[Export "{0}" Page]]></string>
+    <string name="_bx_dev_bp_txt_page_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_bp_txt_page_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_bp_txt_block_edit_popup"><![CDATA[Edit "{0}" Block]]></string>
+    <string name="_bx_dev_bp_err_block_edit"><![CDATA[Cannot update page]]></string>
+    <string name="_bx_dev_bp_err_block_not_found"><![CDATA[Block was not found]]></string>
+    <string name="_bx_dev_bp_err_block_delete"><![CDATA[Cannot delete block]]></string>
+    <string name="_bx_dev_bp_txt_block_form"><![CDATA[Block in Page Builder]]></string>
+    <string name="_bx_dev_bp_txt_block_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_object"><![CDATA[Page Identifier]]></string>
+    <string name="_bx_dev_bp_txt_block_object"><![CDATA[Page Identifier]]></string>
+    <string name="_bx_dev_bp_err_block_object"><![CDATA[You need to enter a correct page identifier here.]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_txt_block_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_err_block_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_title_system"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_bp_txt_block_title_system"><![CDATA[System Name Key]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_title"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_bp_txt_block_title"><![CDATA[Display Name Key]]></string>
+    <string name="_bx_dev_bp_err_block_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_description"><![CDATA[Description]]></string>
+    <string name="_bx_dev_bp_txt_block_description"><![CDATA[Description]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_bp_txt_block_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_designbox_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_txt_block_designbox_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_err_block_designbox_id"><![CDATA[You need to select a layout]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_content_empty"><![CDATA[Placeholder text for empty block]]></string>
+    <string name="_bx_dev_bp_txt_block_content_empty"><![CDATA[Placeholder text for empty block]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_help"><![CDATA[Help]]></string>
+    <string name="_bx_dev_bp_txt_block_help"><![CDATA[Help]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_class"><![CDATA[CSS class]]></string>
+    <string name="_bx_dev_bp_txt_block_class"><![CDATA[CSS class]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_cache_lifetime"><![CDATA[Block Cache Lifetime (in seconds)]]></string>
+    <string name="_bx_dev_bp_txt_block_cache_lifetime"><![CDATA[Block Cache Lifetime (in seconds)]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_config_api"><![CDATA[Config (for App)]]></string>
+    <string name="_bx_dev_bp_txt_block_config_api"><![CDATA[Config (for App in JSON format)]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_async"><![CDATA[Asynchronous Mode]]></string>
+    <string name="_bx_dev_bp_txt_block_async"><![CDATA[Asynchronous Mode]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_submenu"><![CDATA[Block submenu]]></string>
+    <string name="_bx_dev_bp_txt_block_submenu"><![CDATA[Block submenu]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_tabs"><![CDATA[Use 'tabs']]></string>
+    <string name="_bx_dev_bp_txt_block_tabs"><![CDATA[Use 'tabs' based block submenu]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_phone"><![CDATA[phone]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_tablet"><![CDATA[tablet]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_desktop"><![CDATA[desktop]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_mobile"><![CDATA[mobile]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_visible_for"><![CDATA[Visible For]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for"><![CDATA[Visible For]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for_all"><![CDATA[Anyone]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for_selected"><![CDATA[Selected levels]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_visible_for_levels"><![CDATA[Select Levels]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for_levels"><![CDATA[Select Levels]]></string>
+    <string name="_bx_dev_bp_txt_block_content_service"><![CDATA[Code]]></string>
+    <string name="_bx_dev_bp_txt_block_content_lang"><![CDATA[Content]]></string>
+    <string name="_bx_dev_bp_dsc_block_content_lang"><![CDATA[Enter language key]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_block_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_bp_txt_block_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_active"><![CDATA[Active]]></string>
+    <string name="_bx_dev_bp_txt_block_active"><![CDATA[Active]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_bp_txt_block_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_bp_btn_sys_block_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_block_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_block_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_bp_btn_sys_block_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_block_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_sys_block_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_dev_bp_btn_block_delete"><![CDATA[Delete]]></string>
+
+    <!-- Polyglot -->
+    <string name="_bx_dev_pgt_txt_manage_gl_title"><![CDATA[Module]]></string>
+    <string name="_bx_dev_pgt_btn_manage_gl_add_keys"><![CDATA[Add Keys]]></string>
+    <string name="_bx_dev_pgt_btn_manage_gl_recompile"><![CDATA[Recompile All]]></string>
+    <string name="_bx_dev_pgt_btn_manage_gl_restore"><![CDATA[Restore All]]></string>
+    <string name="_bx_dev_pgt_txt_manage_recompile_title_sing"><![CDATA[Recompile %s language for %s module.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_restore_title_sing"><![CDATA[Restore %s language for %s module.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_recompile_title_plur"><![CDATA[Recompile %s language for all modules.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_restore_title_plur"><![CDATA[Restore %s language for all modules.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_modules_all"><![CDATA[All]]></string>
+    <string name="_bx_dev_pgt_txt_keys_add_popup"><![CDATA[Add Keys]]></string>
+
+    <string name="_bx_dev_pgt_txt_keys_form"><![CDATA[Keys]]></string>
+    <string name="_bx_dev_pgt_txt_keys_display_add"><![CDATA[Add Keys]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_language"><![CDATA[Language]]></string>
+    <string name="_bx_dev_pgt_txt_keys_language"><![CDATA[Language]]></string>
+    <string name="_bx_dev_pgt_inf_keys_language"><![CDATA[Leave empty to add in all available languages.]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_category"><![CDATA[Category]]></string>
+    <string name="_bx_dev_pgt_txt_keys_category"><![CDATA[Category]]></string>
+    <string name="_bx_dev_pgt_err_keys_category"><![CDATA[You need to select a category]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_content"><![CDATA[Keys]]></string>
+    <string name="_bx_dev_pgt_txt_keys_content"><![CDATA[Keys]]></string>
+    <string name="_bx_dev_pgt_err_keys_content"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_pgt_inf_keys_content"><![CDATA[Use XML format, like the following:<br />&lt;string name="_key_first"&gt;&lt;![CDATA[First Value]]&gt;&lt;/string&gt;<br />&lt;string name="_key_second"&gt;&lt;![CDATA[Second Value]]&gt;&lt;/string&gt;]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_replace"><![CDATA[Replace]]></string>
+    <string name="_bx_dev_pgt_txt_keys_replace"><![CDATA[Force replace if exists]]></string>
+    <string name="_bx_dev_pgt_btn_sys_keys_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_dev_pgt_btn_keys_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_dev_pgt_btn_sys_keys_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_pgt_btn_keys_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_pgt_msg_keys_added"><![CDATA[{0} keys were added]]></string>
+
+    <!-- Hash -->
+    <string name="_bx_dev_hash_msg_reset_hash_system"><![CDATA[System hash was successfully reset.]]></string>
+    <string name="_bx_dev_hash_err_reset_hash_system"><![CDATA[Cannot reset system hash.]]></string>
+    <string name="_bx_dev_hash_msg_reset_hash_modules"><![CDATA[Hash was successfully reset for the following modules:]]></string>
+    <string name="_bx_dev_hash_txt_reset_hash_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_hash_txt_reset_hash_files"><![CDATA[Files Hashed]]></string>
+
+    <!-- Export/Import -->
+    <string name="_bx_dev_btn_basic"><![CDATA[Basic]]></string>
+    <string name="_bx_dev_btn_full"><![CDATA[Full]]></string>
+    <string name="_bx_dev_btn_import_full"><![CDATA[Import]]></string>
+    <string name="_bx_dev_btn_export_full"><![CDATA[Export]]></string>
+    <string name="_bx_dev_btn_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_txt_disable_before_import"><![CDATA[Deactivate before import]]></string>
+    <string name="_bx_dev_txt_disable_before_import_note"><![CDATA[Existed menu items, blocks, etc will be deactivated before importing new ones. This allows custom (different) elements to be hidden.]]></string>
+    <string name="_bx_dev_txt_import_full_popup"><![CDATA[Import]]></string>
+    <string name="_bx_dev_txt_export_type"><![CDATA[What type of export do you need? <br />(<b>Basic</b> - visibility related data only, <b>Full</b> - all data)]]></string>
+    <string name="_bx_dev_err_cannot_perform"><![CDATA[Cannot perform the action.]]></string>
+    <string name="_bx_dev_msg_imported"><![CDATA[Import finished. {0} items were processed.]]></string>
+    <string name="_bx_dev_err_import_wrong_format"><![CDATA[Wrong file format.]]></string>
+</resources>

+ 754 - 0
modules/boonex/developer/updates/15.0.1_15.0.2/source/install/sql/install.sql

@@ -0,0 +1,754 @@
+SET @sName = 'bx_developer';
+
+
+--
+-- Studio page and widget.
+--
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_dev_adm_page_cpt', '_bx_dev_adm_page_cpt', 'bx_developer@modules/boonex/developer/|std-icon.svg');
+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`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, @sName, 'extensions', CONCAT('{url_studio}module.php?name=', @sName), '', 'bx_developer@modules/boonex/developer/|std-icon.svg', '_bx_dev_adm_wgt_cpt', '', '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(NOT ISNULL(@iParentPageOrder), @iParentPageOrder + 1, 1));
+
+
+--
+-- Forms Builder -> Grid descriptors
+--
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_forms', 'Sql', 'SELECT * FROM `sys_objects_form` WHERE 1 ', 'sys_objects_form', 'id', 'module,title', 'active', '', 100, NULL, 'start', '', 'module', 'title', 'like', 'module', 'title', 2147483647, 'BxDevFormsForms', 'modules/boonex/developer/classes/BxDevFormsForms.php'),
+('bx_developer_forms_displays', 'Sql', 'SELECT `td`.`id` AS `id`, `td`.`object` AS `object`, `td`.`display_name` AS `display_name`, `td`.`title` AS `display_title`, `td`.`module` AS `module`, `tf`.`title` AS `form_title` FROM `sys_form_displays` AS `td` LEFT JOIN `sys_objects_form` AS `tf` ON `td`.`object`=`tf`.`object` WHERE 1 ', 'sys_form_displays', 'id', 'module,object,display_title', '', '', 100, NULL, 'start', '', 'td`.`module', 'td`.`title', 'like', 'module', 'display_title,form_title', 2147483647, 'BxDevFormsDisplays', 'modules/boonex/developer/classes/BxDevFormsDisplays.php'),
+('bx_developer_forms_fields', 'Sql', 'SELECT `tdi`.`id` AS `id`, `ti`.`caption_system` AS `caption_system`, `ti`.`caption` AS `caption`, `ti`.`type` AS `type`, `ti`.`module` AS `module`, `tdi`.`visible_for_levels` AS `visible_for_levels`, `tdi`.`active` AS `active`, `ti`.`editable` AS `editable`, `ti`.`deletable` AS `deletable`, `tdi`.`order` AS `order` FROM `sys_form_display_inputs` AS `tdi` LEFT JOIN `sys_form_inputs` AS `ti` ON `tdi`.`input_name`=`ti`.`name` AND `ti`.`object`=? WHERE 1 AND `tdi`.`display_name`=?', 'sys_form_display_inputs', 'id', 'order', 'active', '', 100, NULL, 'start', '', 'ti`.`type', 'ti`.`caption_system', 'like', '', '', 2147483647, 'BxDevFormsFields', 'modules/boonex/developer/classes/BxDevFormsFields.php'),
+
+('bx_developer_forms_pre_lists', 'Sql', 'SELECT * FROM `sys_form_pre_lists` WHERE 1 ', 'sys_form_pre_lists', 'id', '', '', '', 100, NULL, 'start', '', 'module,key', 'title', 'auto', 'module', 'title', 2147483647, 'BxDevFormsPreLists', 'modules/boonex/developer/classes/BxDevFormsPreLists.php'),
+('bx_developer_forms_pre_values', 'Sql', 'SELECT * FROM `sys_form_pre_values` WHERE 1 ', 'sys_form_pre_values', 'id', 'Order', '', '', 1000, NULL, 'start', '', 'Key,Value', 'LKey,LKey2', 'auto', '', '', 2147483647, 'BxDevFormsPreValues', 'modules/boonex/developer/classes/BxDevFormsPreValues.php'),
+
+('bx_developer_search_forms', 'Sql', 'SELECT * FROM `sys_objects_search_extended` WHERE 1 ', 'sys_objects_search_extended', 'id', 'module,title', 'active', '', 100, NULL, 'start', '', 'module', 'title', 'like', 'module', 'title', 2147483647, 'BxDevFormsSearchForms', 'modules/boonex/developer/classes/BxDevFormsSearchForms.php'),
+('bx_developer_search_forms_fields', 'Sql', 'SELECT * FROM `sys_search_extended_fields` WHERE 1 AND `object`=?', 'sys_search_extended_fields', 'id', 'order', 'active', '', 100, NULL, 'start', '', 'type', 'caption', 'like', '', '', 2147483647, 'BxDevFormsSearchFields', 'modules/boonex/developer/classes/BxDevFormsSearchFields.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_developer_forms', 'switcher', '', '10%', 0, '', '', 1),
+('bx_developer_forms', 'title', '_bx_dev_frm_txt_forms_gl_title', '40%', 1, '38', '', 2),
+('bx_developer_forms', 'module', '_bx_dev_frm_txt_forms_gl_module', '15%', 0, '13', '', 3),
+('bx_developer_forms', 'displays', '_bx_dev_frm_txt_forms_gl_displays', '15%', 0, '13', '', 4),
+('bx_developer_forms', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_forms_displays', 'display_title', '_bx_dev_frm_txt_displays_gl_title', '30%', 1, '48', '', 1),
+('bx_developer_forms_displays', 'module', '_bx_dev_frm_txt_displays_gl_module', '13%', 0, '11', '', 2),
+('bx_developer_forms_displays', 'form_title', '_bx_dev_frm_txt_displays_gl_form', '24%', 1, '22', '', 3),
+('bx_developer_forms_displays', 'fields', '_bx_dev_frm_txt_displays_gl_fields', '13%', 0, '11', '', 4),
+('bx_developer_forms_displays', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_forms_fields', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_forms_fields', 'switcher', '', '9%', 0, '', '', 2),
+('bx_developer_forms_fields', 'type', '_bx_dev_frm_txt_fields_gl_type', '5%', 0, '', '', 3),
+('bx_developer_forms_fields', 'caption_system', '_bx_dev_frm_txt_fields_gl_caption_system', '40%', 1, '38', '', 4),
+('bx_developer_forms_fields', 'module', '_bx_dev_frm_txt_fields_gl_module', '15%', 0, '13', '', 5),
+('bx_developer_forms_fields', 'visible_for_levels', '_bx_dev_frm_txt_fields_gl_visible', '10%', 0, '10', '', 6),
+('bx_developer_forms_fields', 'actions', '', '20%', 0, '', '', 7),
+
+('bx_developer_forms_pre_lists', 'title', '_bx_dev_frm_txt_pre_lists_gl_title', '45%', 1, '50', '', 1),
+('bx_developer_forms_pre_lists', 'values', '_bx_dev_frm_txt_pre_lists_gl_values', '12%', 0, '10', '', 2),
+('bx_developer_forms_pre_lists', 'module', '_bx_dev_frm_txt_pre_lists_gl_module', '13%', 0, '11', '', 3),
+('bx_developer_forms_pre_lists', 'use_for_sets', '_bx_dev_frm_txt_pre_lists_gl_use_for_sets', '10%', 0, '8', '', 4),
+('bx_developer_forms_pre_lists', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_forms_pre_values', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_forms_pre_values', 'LKey', '_bx_dev_frm_txt_pre_values_gl_lkey', '79%', 1, '75', '', 2),
+('bx_developer_forms_pre_values', 'actions', '', '20%', 0, '', '', 3),
+
+('bx_developer_search_forms', 'switcher', '', '10%', 0, '', '', 1),
+('bx_developer_search_forms', 'title', '_bx_dev_frm_txt_search_forms_gl_title', '40%', 1, '38', '', 2),
+('bx_developer_search_forms', 'module', '_bx_dev_frm_txt_search_forms_gl_module', '15%', 0, '13', '', 3),
+('bx_developer_search_forms', 'fields', '_bx_dev_frm_txt_search_forms_gl_fields', '15%', 0, '13', '', 4),
+('bx_developer_search_forms', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_search_forms_fields', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_search_forms_fields', 'switcher', '', '9%', 0, '', '', 2),
+('bx_developer_search_forms_fields', 'type', '_bx_dev_frm_txt_search_forms_fields_gl_type', '10%', 0, '', '', 3),
+('bx_developer_search_forms_fields', 'caption', '_bx_dev_frm_txt_search_forms_fields_gl_caption', '40%', 1, '38', '', 4),
+('bx_developer_search_forms_fields', 'search_type', '_bx_dev_frm_txt_search_forms_fields_gl_search_type', '10%', 0, '', '', 5),
+('bx_developer_search_forms_fields', 'search_operator', '_bx_dev_frm_txt_search_forms_fields_gl_search_operator', '10%', 0, '', '', 6),
+('bx_developer_search_forms_fields', 'actions', '', '20%', 0, '', '', 7);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_developer_forms', 'independent', 'add', '_bx_dev_frm_btn_forms_gl_create', '', 0, 1),
+('bx_developer_forms', 'independent', 'import_full', '_bx_dev_btn_import_full', '', 0, 2),
+('bx_developer_forms', 'independent', 'export_full', '_bx_dev_btn_export_full', '', 0, 3),
+('bx_developer_forms', 'single', 'export', '', 'download', 0, 1),
+('bx_developer_forms', 'single', 'edit', '', 'pencil-alt', 0, 2),
+
+('bx_developer_forms_displays', 'independent', 'add', '_bx_dev_frm_btn_displays_gl_create', '', 0, 1),
+('bx_developer_forms_displays', 'single', 'edit', '', 'pencil-alt', 0, 1),
+
+('bx_developer_forms_fields', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_forms_fields', 'single', 'delete', '', 'remove', 1, 2),
+('bx_developer_forms_fields', 'single', 'show_to', '_bx_dev_frm_btn_fields_visible', '', 0, 3),
+('bx_developer_forms_fields', 'independent', 'add', '_bx_dev_frm_btn_fields_create', '', 0, 1),
+
+('bx_developer_forms_pre_lists', 'independent', 'add', '_bx_dev_frm_btn_pre_lists_create', '', 0, 1),
+('bx_developer_forms_pre_lists', 'single', 'export', '', 'download', 0, 1),
+('bx_developer_forms_pre_lists', 'single', 'edit', '', 'pencil-alt', 0, 2),
+('bx_developer_forms_pre_lists', 'single', 'delete', '', 'remove', 1, 3),
+
+('bx_developer_forms_pre_values', 'independent', 'add', '_bx_dev_frm_btn_pre_values_create', '', 0, 1),
+('bx_developer_forms_pre_values', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_forms_pre_values', 'single', 'delete', '', 'remove', 1, 2),
+
+('bx_developer_search_forms', 'independent', 'add', '_bx_dev_frm_btn_search_forms_create', '', 0, 1),
+('bx_developer_search_forms', 'single', 'edit', '', 'pencil-alt', 0, 1),
+
+('bx_developer_search_forms_fields', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_search_forms_fields', 'independent', 'reset', '_bx_dev_frm_btn_search_forms_fields_reset', '', 0, 1);
+
+
+--
+-- Forms Builder -> Forms.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_forms_form', @sName, '_bx_dev_frm_txt_forms_form', '', '', 'do_submit', 'sys_objects_form', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_form_add', @sName, 'bx_developer_forms_form', '_bx_dev_frm_txt_forms_display_add', 0),
+('bx_developer_forms_form_edit', @sName, 'bx_developer_forms_form', '_bx_dev_frm_txt_forms_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_forms_form', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_forms_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_form', @sName, 'object', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_object', '_bx_dev_frm_txt_forms_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_object', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_forms_module', '_bx_dev_frm_txt_forms_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_forms_module', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_title', '_bx_dev_frm_txt_forms_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_title', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'action', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_action', '_bx_dev_frm_txt_forms_action', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'form_attrs', '', '', 0, 'textarea', '_bx_dev_frm_txt_sys_forms_form_attrs', '_bx_dev_frm_txt_forms_form_attrs', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'submit_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_submit_name', '_bx_dev_frm_txt_forms_submit_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'table', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_table', '_bx_dev_frm_txt_forms_table', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'key', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_key', '_bx_dev_frm_txt_forms_key', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'uri', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_uri', '_bx_dev_frm_txt_forms_uri', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'uri_title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_uri_title', '_bx_dev_frm_txt_forms_uri_title', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'params', '', '', 0, 'textarea', '_bx_dev_frm_txt_sys_forms_params', '_bx_dev_frm_txt_forms_params', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_frm_txt_sys_forms_deletable', '_bx_dev_frm_txt_forms_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_form', @sName, 'override_class_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_override_class_name', '_bx_dev_frm_txt_forms_override_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'override_class_file', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_override_class_file', '_bx_dev_frm_txt_forms_override_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_form', @sName, 'do_submit', '_bx_dev_frm_btn_forms_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_forms_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_form', @sName, 'cancel', '_bx_dev_frm_btn_forms_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_forms_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_form_add', 'object', 2147483647, 1, 1),
+('bx_developer_forms_form_add', 'module', 2147483647, 1, 2),
+('bx_developer_forms_form_add', 'title', 2147483647, 1, 3),
+('bx_developer_forms_form_add', 'action', 2147483647, 1, 4),
+('bx_developer_forms_form_add', 'form_attrs', 2147483647, 1, 5),
+('bx_developer_forms_form_add', 'submit_name', 2147483647, 1, 6),
+('bx_developer_forms_form_add', 'table', 2147483647, 1, 7),
+('bx_developer_forms_form_add', 'key', 2147483647, 1, 8),
+('bx_developer_forms_form_add', 'uri', 2147483647, 1, 9),
+('bx_developer_forms_form_add', 'uri_title', 2147483647, 1, 10),
+('bx_developer_forms_form_add', 'params', 2147483647, 1, 11),
+('bx_developer_forms_form_add', 'deletable', 2147483647, 1, 12),
+('bx_developer_forms_form_add', 'override_class_name', 2147483647, 1, 13),
+('bx_developer_forms_form_add', 'override_class_file', 2147483647, 1, 14),
+('bx_developer_forms_form_add', 'controls', 2147483647, 1, 15),
+('bx_developer_forms_form_add', 'do_submit', 2147483647, 1, 16),
+('bx_developer_forms_form_add', 'cancel', 2147483647, 1, 17),
+('bx_developer_forms_form_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_form_edit', 'object', 2147483647, 1, 2),
+('bx_developer_forms_form_edit', 'module', 2147483647, 1, 3),
+('bx_developer_forms_form_edit', 'title', 2147483647, 1, 4),
+('bx_developer_forms_form_edit', 'action', 2147483647, 1, 5),
+('bx_developer_forms_form_edit', 'form_attrs', 2147483647, 1, 6),
+('bx_developer_forms_form_edit', 'submit_name', 2147483647, 1, 7),
+('bx_developer_forms_form_edit', 'table', 2147483647, 1, 8),
+('bx_developer_forms_form_edit', 'key', 2147483647, 1, 9),
+('bx_developer_forms_form_edit', 'uri', 2147483647, 1, 10),
+('bx_developer_forms_form_edit', 'uri_title', 2147483647, 1, 11),
+('bx_developer_forms_form_edit', 'params', 2147483647, 1, 12),
+('bx_developer_forms_form_edit', 'deletable', 2147483647, 1, 13),
+('bx_developer_forms_form_edit', 'override_class_name', 2147483647, 1, 14),
+('bx_developer_forms_form_edit', 'override_class_file', 2147483647, 1, 15),
+('bx_developer_forms_form_edit', 'controls', 2147483647, 1, 16),
+('bx_developer_forms_form_edit', 'do_submit', 2147483647, 1, 17),
+('bx_developer_forms_form_edit', 'cancel', 2147483647, 1, 18);
+
+--
+-- Forms Builder -> Displays.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_forms_display', @sName, '_bx_dev_frm_txt_displays_diplay', '', '', 'do_submit', 'sys_form_displays', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_display_add', @sName, 'bx_developer_forms_display', '_bx_dev_frm_txt_displays_display_add', 0),
+('bx_developer_forms_display_edit', @sName, 'bx_developer_forms_display', '_bx_dev_frm_txt_displays_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_forms_display', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_displays_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_display', @sName, 'display_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_displays_display_name', '_bx_dev_frm_txt_displays_display_name', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_displays_display_name', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_displays_module', '_bx_dev_frm_txt_displays_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_displays_module', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'object', '', '', 0, 'select', '_bx_dev_frm_txt_sys_displays_object', '_bx_dev_frm_txt_displays_object', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_displays_object', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_displays_title', '_bx_dev_frm_txt_displays_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_displays_title', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'view_mode', '1', '', 0, 'switcher', '_bx_dev_frm_txt_sys_displays_view_mode', '_bx_dev_frm_txt_displays_view_mode', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_display', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_display', @sName, 'do_submit', '_bx_dev_frm_btn_displays_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_displays_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_display', @sName, 'cancel', '_bx_dev_frm_btn_displays_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_displays_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_display_add', 'display_name', 2147483647, 1, 1),
+('bx_developer_forms_display_add', 'module', 2147483647, 1, 2),
+('bx_developer_forms_display_add', 'object', 2147483647, 1, 3),
+('bx_developer_forms_display_add', 'title', 2147483647, 1, 4),
+('bx_developer_forms_display_add', 'view_mode', 2147483647, 1, 5),
+('bx_developer_forms_display_add', 'controls', 2147483647, 1, 6),
+('bx_developer_forms_display_add', 'do_submit', 2147483647, 1, 7),
+('bx_developer_forms_display_add', 'cancel', 2147483647, 1, 8),
+('bx_developer_forms_display_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_display_edit', 'display_name', 2147483647, 1, 1),
+('bx_developer_forms_display_edit', 'module', 2147483647, 1, 2),
+('bx_developer_forms_display_edit', 'object', 2147483647, 1, 3),
+('bx_developer_forms_display_edit', 'title', 2147483647, 1, 4),
+('bx_developer_forms_display_edit', 'view_mode', 2147483647, 1, 5),
+('bx_developer_forms_display_edit', 'controls', 2147483647, 1, 6),
+('bx_developer_forms_display_edit', 'do_submit', 2147483647, 1, 7),
+('bx_developer_forms_display_edit', 'cancel', 2147483647, 1, 8);
+
+--
+-- Forms Builder -> Pre Lists.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_forms_prelist', @sName, '_bx_dev_frm_txt_prelists_prelist', '', '', 'do_submit', 'sys_form_pre_lists', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_prelist_add', @sName, 'bx_developer_forms_prelist', '_bx_dev_frm_txt_prelists_display_add', 0),
+('bx_developer_forms_prelist_edit', @sName, 'bx_developer_forms_prelist', '_bx_dev_frm_txt_prelists_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_forms_prelist', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_prelists_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'key', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prelists_key', '_bx_dev_frm_txt_prelists_key', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_prelists_key', 'Xss', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_prelists_module', '_bx_dev_frm_txt_prelists_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_prelists_module', 'Xss', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prelists_title', '_bx_dev_frm_txt_prelists_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_prelists_title', 'Xss', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'use_for_sets', '1', '', 1, 'switcher', '_bx_dev_frm_txt_sys_prelists_use_for_sets', '_bx_dev_frm_txt_prelists_use_for_sets', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'do_submit', '_bx_dev_frm_btn_prelists_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_prelists_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'cancel', '_bx_dev_frm_btn_prelists_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_prelists_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_prelist_add', 'key', 2147483647, 1, 1),
+('bx_developer_forms_prelist_add', 'module', 2147483647, 1, 2),
+('bx_developer_forms_prelist_add', 'title', 2147483647, 1, 3),
+('bx_developer_forms_prelist_add', 'use_for_sets', 2147483647, 1, 4),
+('bx_developer_forms_prelist_add', 'controls', 2147483647, 1, 5),
+('bx_developer_forms_prelist_add', 'do_submit', 2147483647, 1, 6),
+('bx_developer_forms_prelist_add', 'cancel', 2147483647, 1, 7),
+('bx_developer_forms_prelist_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_prelist_edit', 'module', 2147483647, 1, 2),
+('bx_developer_forms_prelist_edit', 'title', 2147483647, 1, 3),
+('bx_developer_forms_prelist_edit', 'use_for_sets', 2147483647, 1, 4),
+('bx_developer_forms_prelist_edit', 'controls', 2147483647, 1, 5),
+('bx_developer_forms_prelist_edit', 'do_submit', 2147483647, 1, 6),
+('bx_developer_forms_prelist_edit', 'cancel', 2147483647, 1, 7);
+
+--
+-- Forms Builder -> Pre Values.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_forms_prevalue', @sName, '_bx_dev_frm_txt_prevalues_prevalue', '', '', 'do_submit', 'sys_form_pre_values', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_prevalue_add', @sName, 'bx_developer_forms_prevalue', '_bx_dev_frm_txt_prevalues_display_add', 0),
+('bx_developer_forms_prevalue_edit', @sName, 'bx_developer_forms_prevalue', '_bx_dev_frm_txt_prevalues_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_forms_prevalue', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_prevalues_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'Key', '', '', 0, 'select', '_bx_dev_frm_txt_sys_prevalues_key', '_bx_dev_frm_txt_prevalues_key', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_prevalues_key', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'Value', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prevalues_value', '_bx_dev_frm_txt_prevalues_value', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:1;s:3:"max";i:255;}', '_bx_dev_frm_err_prevalues_value', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'LKey', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prevalues_lkey', '_bx_dev_frm_txt_prevalues_lkey', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:1;s:3:"max";i:100;}', '_bx_dev_frm_err_prevalues_lkey', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'LKey2', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prevalues_lkey2', '_bx_dev_frm_txt_prevalues_lkey2', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'Data', '', '', 0, 'textarea', '_bx_dev_frm_txt_sys_prevalues_data', '_bx_dev_frm_txt_prevalues_data', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'do_submit', '_bx_dev_frm_btn_prevalues_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_prevalues_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'cancel', '_bx_dev_frm_btn_prevalues_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_prevalues_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_prevalue_add', 'Key', 2147483647, 1, 1),
+('bx_developer_forms_prevalue_add', 'Value', 2147483647, 1, 2),
+('bx_developer_forms_prevalue_add', 'LKey', 2147483647, 1, 3),
+('bx_developer_forms_prevalue_add', 'LKey2', 2147483647, 1, 4),
+('bx_developer_forms_prevalue_add', 'Data', 2147483647, 1, 5),
+('bx_developer_forms_prevalue_add', 'controls', 2147483647, 1, 6),
+('bx_developer_forms_prevalue_add', 'do_submit', 2147483647, 1, 7),
+('bx_developer_forms_prevalue_add', 'cancel', 2147483647, 1, 8),
+
+('bx_developer_forms_prevalue_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_prevalue_edit', 'Key', 2147483647, 1, 2),
+('bx_developer_forms_prevalue_edit', 'Value', 2147483647, 1, 3),
+('bx_developer_forms_prevalue_edit', 'LKey', 2147483647, 1, 4),
+('bx_developer_forms_prevalue_edit', 'LKey2', 2147483647, 1, 5),
+('bx_developer_forms_prevalue_edit', 'Data', 2147483647, 1, 6),
+('bx_developer_forms_prevalue_edit', 'controls', 2147483647, 1, 7),
+('bx_developer_forms_prevalue_edit', 'do_submit', 2147483647, 1, 8),
+('bx_developer_forms_prevalue_edit', 'cancel', 2147483647, 1, 9);
+
+--
+-- Forms Builder -> Search Forms.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_forms_search_form', @sName, '_bx_dev_frm_txt_search_forms_form', '', '', 'do_submit', 'sys_objects_search_extended', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_search_form_add', @sName, 'bx_developer_forms_search_form', '_bx_dev_frm_txt_search_forms_display_add', 0),
+('bx_developer_forms_search_form_edit', @sName, 'bx_developer_forms_search_form', '_bx_dev_frm_txt_search_forms_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_forms_search_form', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_search_forms_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'object', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_object', '_bx_dev_frm_txt_search_forms_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_object', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'object_content_info', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_forms_object_content_info', '_bx_dev_frm_txt_search_forms_object_content_info', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_forms_object_content_info', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_forms_module', '_bx_dev_frm_txt_search_forms_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_forms_module', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_title', '_bx_dev_frm_txt_search_forms_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_title', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'class_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_class_name', '_bx_dev_frm_txt_search_forms_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'class_file', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_class_file', '_bx_dev_frm_txt_search_forms_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'do_submit', '_bx_dev_frm_btn_search_forms_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_search_forms_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'cancel', '_bx_dev_frm_btn_search_forms_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_search_forms_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_search_form_add', 'object', 2147483647, 1, 1),
+('bx_developer_forms_search_form_add', 'object_content_info', 2147483647, 1, 2),
+('bx_developer_forms_search_form_add', 'module', 2147483647, 1, 3),
+('bx_developer_forms_search_form_add', 'title', 2147483647, 1, 4),
+('bx_developer_forms_search_form_add', 'class_name', 2147483647, 1, 5),
+('bx_developer_forms_search_form_add', 'class_file', 2147483647, 1, 6),
+('bx_developer_forms_search_form_add', 'controls', 2147483647, 1, 7),
+('bx_developer_forms_search_form_add', 'do_submit', 2147483647, 1, 8),
+('bx_developer_forms_search_form_add', 'cancel', 2147483647, 1, 9),
+
+('bx_developer_forms_search_form_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_search_form_edit', 'object', 2147483647, 1, 2),
+('bx_developer_forms_search_form_edit', 'object_content_info', 2147483647, 1, 3),
+('bx_developer_forms_search_form_edit', 'module', 2147483647, 1, 4),
+('bx_developer_forms_search_form_edit', 'title', 2147483647, 1, 5),
+('bx_developer_forms_search_form_edit', 'class_name', 2147483647, 1, 6),
+('bx_developer_forms_search_form_edit', 'class_file', 2147483647, 1, 7),
+('bx_developer_forms_search_form_edit', 'controls', 2147483647, 1, 8),
+('bx_developer_forms_search_form_edit', 'do_submit', 2147483647, 1, 9),
+('bx_developer_forms_search_form_edit', 'cancel', 2147483647, 1, 10);
+
+--
+-- Forms Builder -> Search Fields.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_forms_search_fields', @sName, '_bx_dev_frm_txt_search_fields_form', '', '', 'do_submit', 'sys_search_extended_fields', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_search_fields_edit', @sName, 'bx_developer_forms_search_fields', '_bx_dev_frm_txt_search_fields_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_forms_search_fields', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_search_fields_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'object', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_object', '_bx_dev_frm_txt_search_fields_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_object', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_name', '_bx_dev_frm_txt_search_fields_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_name', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'type', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_type', '_bx_dev_frm_txt_search_fields_type', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_type', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'caption', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_caption', '_bx_dev_frm_txt_search_fields_caption', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_search_fields_caption', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'values', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_values', '_bx_dev_frm_txt_search_fields_values', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'search_type', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_search_type', '_bx_dev_frm_txt_search_fields_search_type', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_search_type', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'search_value', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_search_value', '_bx_dev_frm_txt_search_fields_search_value', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'search_operator', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_search_operator', '_bx_dev_frm_txt_search_fields_search_operator', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_search_operator', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'do_submit', '_bx_dev_frm_btn_search_fields_save', '', 0, 'submit', '_bx_dev_frm_btn_sys_search_fields_save', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'cancel', '_bx_dev_frm_btn_search_fields_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_search_fields_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_search_fields_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_search_fields_edit', 'object', 2147483647, 1, 2),
+('bx_developer_forms_search_fields_edit', 'name', 2147483647, 1, 3),
+('bx_developer_forms_search_fields_edit', 'type', 2147483647, 1, 4),
+('bx_developer_forms_search_fields_edit', 'caption', 2147483647, 1, 5),
+('bx_developer_forms_search_fields_edit', 'values', 2147483647, 1, 6),
+('bx_developer_forms_search_fields_edit', 'search_type', 2147483647, 1, 7),
+('bx_developer_forms_search_fields_edit', 'search_value', 2147483647, 1, 8),
+('bx_developer_forms_search_fields_edit', 'search_operator', 2147483647, 1, 9),
+('bx_developer_forms_search_fields_edit', 'controls', 2147483647, 1, 10),
+('bx_developer_forms_search_fields_edit', 'do_submit', 2147483647, 1, 11),
+('bx_developer_forms_search_fields_edit', 'cancel', 2147483647, 1, 12);
+
+--
+-- Navigation Builder -> Grid descriptors
+--
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_nav_menus', 'Sql', 'SELECT `tm`.*, `tms`.`title` AS `set_title`, `tmt`.`title` AS `template_title` FROM `sys_objects_menu` AS `tm` LEFT JOIN `sys_menu_sets` AS `tms` ON `tm`.`set_name`=`tms`.`set_name` LEFT JOIN `sys_menu_templates` AS `tmt` ON `tm`.`template_id`=`tmt`.`id` WHERE 1 ', 'sys_objects_menu', 'id', '', 'active', '', 100, NULL, 'start', '', '', 'tm`.`title,tms`.`title,tmt`.`title', 'auto', '', '', 2147483647, 'BxDevNavigationMenus', 'modules/boonex/developer/classes/BxDevNavigationMenus.php'),
+('bx_developer_nav_sets', 'Sql', 'SELECT * FROM `sys_menu_sets` WHERE 1 ', 'sys_menu_sets', 'set_name', '', '', '', 100, NULL, 'start', '', '', 'title', 'auto', '', '', 2147483647, 'BxDevNavigationSets', 'modules/boonex/developer/classes/BxDevNavigationSets.php'),
+('bx_developer_nav_items', 'Sql', 'SELECT * FROM `sys_menu_items` WHERE 1 ', 'sys_menu_items', 'id', 'order', 'active', '', 100, NULL, 'start_it', '', 'link', 'title_system', 'like', '', '', 2147483647, 'BxDevNavigationItems', 'modules/boonex/developer/classes/BxDevNavigationItems.php');
+
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_developer_nav_menus', 'switcher', '', '10%', 0, '', '', 1),
+('bx_developer_nav_menus', 'title', '_bx_dev_nav_txt_menus_gl_title', '17%', 0, '15', '', 2),
+('bx_developer_nav_menus', 'set_title', '_bx_dev_nav_txt_menus_gl_set_title', '17%', 1, '15', '', 3),
+('bx_developer_nav_menus', 'items', '_bx_dev_nav_txt_menus_gl_items', '10%', 0, '8', '', 4),
+('bx_developer_nav_menus', 'template_title', '_bx_dev_nav_txt_menus_gl_template_title', '17%', 1, '15', '', 5),
+('bx_developer_nav_menus', 'module', '_bx_dev_nav_txt_menus_gl_module', '12%', 0, '10', '', 6),
+('bx_developer_nav_menus', 'actions', '', '17%', 0, '', '', 7),
+('bx_developer_nav_sets', 'title', '_bx_dev_nav_txt_sets_gl_title', '50%', 1, '48', '', 1),
+('bx_developer_nav_sets', 'module', '_bx_dev_nav_txt_sets_gl_module', '15%', 0, '13', '', 2),
+('bx_developer_nav_sets', 'items', '_bx_dev_nav_txt_sets_gl_items', '15%', 0, '13', '', 3),
+('bx_developer_nav_sets', 'actions', '', '20%', 0, '', '', 4),
+('bx_developer_nav_items', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_nav_items', 'switcher', '', '9%', 0, '', '', 2),
+('bx_developer_nav_items', 'icon', '_bx_dev_nav_txt_items_gl_icon', '5%', 0, '', '', 3),
+('bx_developer_nav_items', 'title_system', '_bx_dev_nav_txt_items_gl_title_system', '23%', 1, '23', '', 4),
+('bx_developer_nav_items', 'link', '_bx_dev_nav_txt_items_gl_link', '23%', 0, '23', '', 5),
+('bx_developer_nav_items', 'module', '_bx_dev_nav_txt_items_gl_module', '12%', 0, '12', '', 6),
+('bx_developer_nav_items', 'visible_for_levels', '_bx_dev_nav_txt_items_gl_visible', '10%', 0, '10', '', 7),
+('bx_developer_nav_items', 'actions', '', '17%', 0, '', '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_developer_nav_menus', 'independent', 'add', '_bx_dev_nav_btn_menus_gl_create', '', 0, 1),
+('bx_developer_nav_menus', 'independent', 'import_full', '_bx_dev_btn_import_full', '', 0, 2),
+('bx_developer_nav_menus', 'independent', 'export_full', '_bx_dev_btn_export_full', '', 0, 3),
+('bx_developer_nav_menus', 'single', 'export', '', 'download', 0, 1),
+('bx_developer_nav_menus', 'single', 'edit', '', 'pencil-alt', 0, 2),
+('bx_developer_nav_menus', 'single', 'delete', '', 'remove', 1, 3),
+
+('bx_developer_nav_sets', 'independent', 'add', '_bx_dev_nav_btn_sets_gl_create', '', 0, 1),
+('bx_developer_nav_sets', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_nav_sets', 'single', 'delete', '', 'remove', 1, 2),
+
+('bx_developer_nav_items', 'independent', 'import', '_bx_dev_nav_btn_items_gl_import', '', 0, 1),
+('bx_developer_nav_items', 'independent', 'add', '_bx_dev_nav_btn_items_gl_create', '', 0, 2),
+('bx_developer_nav_items', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_nav_items', 'single', 'delete', '', 'remove', 1, 2),
+('bx_developer_nav_items', 'single', 'show_to', '_bx_dev_nav_btn_items_gl_visible', '', 0, 3);
+
+--
+-- Navigation Builder -> Menus.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_nav_menu', @sName, '_bx_dev_nav_txt_menus_menu', '', '', 'do_submit', 'sys_objects_menu', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_nav_menu_add', @sName, 'bx_developer_nav_menu', '_bx_dev_nav_txt_menus_display_add', 0),
+('bx_developer_nav_menu_edit', @sName, 'bx_developer_nav_menu', '_bx_dev_nav_txt_menus_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_nav_menu', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_nav_txt_sys_menus_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'object', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_object', '_bx_dev_nav_txt_menus_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_menus_object', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'module', '', '', 0, 'select', '_bx_dev_nav_txt_sys_menus_module', '_bx_dev_nav_txt_menus_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_menus_module', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'title', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_title', '_bx_dev_nav_txt_menus_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_menus_title', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'title_public', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_title_public', '_bx_dev_nav_txt_menus_title_public', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'set_name', '', '', 0, 'select', '_bx_dev_nav_txt_sys_menus_set_name', '_bx_dev_nav_txt_menus_set_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'template_id', '', '', 0, 'select', '_bx_dev_nav_txt_sys_menus_template_id', '_bx_dev_nav_txt_menus_template_id', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_menus_template_id', 'Int', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'config_api', '', '', 0, 'textarea', '_bx_dev_nav_txt_sys_menus_config_api', '_bx_dev_nav_txt_menus_config_api', '', 0, 0, 0, '', '', '', '', '', '', 'XssHtml', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_menus_deletable', '_bx_dev_nav_txt_menus_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'override_class_name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_override_class_name', '_bx_dev_nav_txt_menus_override_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'override_class_file', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_override_class_file', '_bx_dev_nav_txt_menus_override_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'do_submit', '_bx_dev_nav_btn_menus_add', '', 0, 'submit', '_bx_dev_nav_btn_sys_menus_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'cancel', '_bx_dev_nav_btn_menus_cancel', '', 0, 'button', '_bx_dev_nav_btn_sys_menus_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_menu_add', 'object', 2147483647, 1, 1),
+('bx_developer_nav_menu_add', 'module', 2147483647, 1, 2),
+('bx_developer_nav_menu_add', 'title', 2147483647, 1, 3),
+('bx_developer_nav_menu_add', 'title_public', 2147483647, 1, 4),
+('bx_developer_nav_menu_add', 'set_name', 2147483647, 1, 5),
+('bx_developer_nav_menu_add', 'template_id', 2147483647, 1, 6),
+('bx_developer_nav_menu_add', 'config_api', 2147483647, 1, 7),
+('bx_developer_nav_menu_add', 'deletable', 2147483647, 1, 8),
+('bx_developer_nav_menu_add', 'override_class_name', 2147483647, 1, 9),
+('bx_developer_nav_menu_add', 'override_class_file', 2147483647, 1, 10),
+('bx_developer_nav_menu_add', 'controls', 2147483647, 1, 11),
+('bx_developer_nav_menu_add', 'do_submit', 2147483647, 1, 12),
+('bx_developer_nav_menu_add', 'cancel', 2147483647, 1, 13),
+
+('bx_developer_nav_menu_edit', 'id', 2147483647, 1, 1),
+('bx_developer_nav_menu_edit', 'object', 2147483647, 1, 2),
+('bx_developer_nav_menu_edit', 'module', 2147483647, 1, 3),
+('bx_developer_nav_menu_edit', 'title', 2147483647, 1, 4),
+('bx_developer_nav_menu_edit', 'title_public', 2147483647, 1, 5),
+('bx_developer_nav_menu_edit', 'set_name', 2147483647, 1, 6),
+('bx_developer_nav_menu_edit', 'template_id', 2147483647, 1, 7),
+('bx_developer_nav_menu_edit', 'config_api', 2147483647, 1, 8),
+('bx_developer_nav_menu_edit', 'deletable', 2147483647, 1, 9),
+('bx_developer_nav_menu_edit', 'override_class_name', 2147483647, 1, 10),
+('bx_developer_nav_menu_edit', 'override_class_file', 2147483647, 1, 11),
+('bx_developer_nav_menu_edit', 'controls', 2147483647, 1, 12),
+('bx_developer_nav_menu_edit', 'do_submit', 2147483647, 1, 13),
+('bx_developer_nav_menu_edit', 'cancel', 2147483647, 1, 14);
+
+--
+-- Navigation Builder -> Sets.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_nav_set', @sName, '_bx_dev_nav_txt_sets_set', '', '', 'do_submit', 'sys_menu_sets', 'set_name', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_nav_set_add', @sName, 'bx_developer_nav_set', '_bx_dev_nav_txt_sets_display_add', 0),
+('bx_developer_nav_set_edit', @sName, 'bx_developer_nav_set', '_bx_dev_nav_txt_sets_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_nav_set', @sName, 'module', '', '', 0, 'select', '_bx_dev_nav_txt_sys_sets_module', '_bx_dev_nav_txt_sets_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_sets_module', 'Xss', '', 0, 0),
+('bx_developer_nav_set', @sName, 'set_name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_sets_set_name', '_bx_dev_nav_txt_sets_set_name', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_sets_set_name', 'Xss', '', 0, 0),
+('bx_developer_nav_set', @sName, 'title', '', '', 0, 'text', '_bx_dev_nav_txt_sys_sets_title', '_bx_dev_nav_txt_sets_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_sets_title', 'Xss', '', 0, 0),
+('bx_developer_nav_set', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_sets_deletable', '_bx_dev_nav_txt_sets_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_set', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_set', @sName, 'do_submit', '_bx_dev_nav_btn_sets_add', '', 0, 'submit', '_bx_dev_nav_btn_sys_sets_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_set', @sName, 'cancel', '_bx_dev_nav_btn_sets_cancel', '', 0, 'button', '_bx_dev_nav_btn_sys_sets_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_set_add', 'module', 2147483647, 1, 1),
+('bx_developer_nav_set_add', 'set_name', 2147483647, 1, 2),
+('bx_developer_nav_set_add', 'title', 2147483647, 1, 3),
+('bx_developer_nav_set_add', 'deletable', 2147483647, 1, 4),
+('bx_developer_nav_set_add', 'controls', 2147483647, 1, 5),
+('bx_developer_nav_set_add', 'do_submit', 2147483647, 1, 6),
+('bx_developer_nav_set_add', 'cancel', 2147483647, 1, 7),
+('bx_developer_nav_set_edit', 'module', 2147483647, 1, 1),
+('bx_developer_nav_set_edit', 'set_name', 2147483647, 1, 2),
+('bx_developer_nav_set_edit', 'title', 2147483647, 1, 3),
+('bx_developer_nav_set_edit', 'deletable', 2147483647, 1, 4),
+('bx_developer_nav_set_edit', 'controls', 2147483647, 1, 5),
+('bx_developer_nav_set_edit', 'do_submit', 2147483647, 1, 6),
+('bx_developer_nav_set_edit', 'cancel', 2147483647, 1, 7);
+
+--
+-- Navigation Builder -> Items.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_nav_item', @sName, '_bx_dev_nav_txt_items_item', '', '', 'do_submit', 'sys_menu_items', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_nav_item_add', @sName, 'bx_developer_nav_item', '_bx_dev_nav_txt_items_display_add', 0),
+('bx_developer_nav_item_edit', @sName, 'bx_developer_nav_item', '_bx_dev_nav_txt_items_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_nav_item', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_nav_txt_sys_items_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'module', '', '', 0, 'select', '_bx_dev_nav_txt_sys_items_module', '_bx_dev_nav_txt_items_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_items_module', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'set_name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_set_name', '_bx_dev_nav_txt_items_set_name', '', 0, 0, 0, 'a:1:{s:8:"readonly";s:8:"readonly";}', '', '', 'Avail', '', '_bx_dev_nav_err_items_set_name', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_name', '_bx_dev_nav_txt_items_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_items_name', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'title_system', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_title_system', '_bx_dev_nav_txt_items_title_system', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'title', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_title', '_bx_dev_nav_txt_items_title', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_items_title', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'link', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_link', '_bx_dev_nav_txt_items_link', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'onclick', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_onclick', '_bx_dev_nav_txt_items_onclick', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'target', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_target', '_bx_dev_nav_txt_items_target', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'icon', '', '', 0, 'textarea', '_bx_dev_nav_txt_sys_items_icon', '_bx_dev_nav_txt_items_icon', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'addon', '', '', 0, 'textarea', '_bx_dev_nav_txt_sys_items_addon', '_bx_dev_nav_txt_items_addon', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'submenu_object', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_submenu_object', '_bx_dev_nav_txt_items_submenu_object', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'submenu_popup', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_submenu_popup', '_bx_dev_nav_txt_items_submenu_popup', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'visibility_custom', '', '', 0, 'textarea', '_bx_dev_nav_txt_sys_items_visibility_custom', '_bx_dev_nav_txt_items_visibility_custom', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on', '', '', 0, 'select_multiple', '_bx_dev_nav_txt_sys_items_hidden_on', '_bx_dev_nav_txt_items_hidden_on', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on_pt', '', '', 0, 'select_multiple', '_bx_dev_nav_txt_sys_items_hidden_on_pt', '_bx_dev_nav_txt_items_hidden_on_pt', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on_col', '', '', 0, 'select_multiple', '_bx_dev_nav_txt_sys_items_hidden_on_col', '_bx_dev_nav_txt_items_hidden_on_col', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on_cxt', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_hidden_on_cxt', '_bx_dev_nav_txt_items_hidden_on_cxt', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'config_api', '', '', 0, 'textarea', '_bx_dev_nav_txt_sys_items_config_api', '_bx_dev_nav_txt_items_config_api', '', 0, 0, 0, '', '', '', '', '', '', 'XssHtml', '', 0, 0),
+('bx_developer_nav_item', @sName, 'primary', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_primary', '_bx_dev_nav_txt_items_primary', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'collapsed', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_collapsed', '_bx_dev_nav_txt_items_collapsed', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'copyable', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_copyable', '_bx_dev_nav_txt_items_copyable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'active_api', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_active_api', '_bx_dev_nav_txt_items_active_api', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_item', @sName, 'do_submit', '_bx_dev_nav_btn_items_add', '', 0, 'submit', '_bx_dev_nav_btn_sys_items_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_item', @sName, 'cancel', '_bx_dev_nav_btn_items_cancel', '', 0, 'button', '_bx_dev_nav_btn_sys_items_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_item_add', 'module', 2147483647, 1, 1),
+('bx_developer_nav_item_add', 'set_name', 2147483647, 1, 2),
+('bx_developer_nav_item_add', 'name', 2147483647, 1, 3),
+('bx_developer_nav_item_add', 'title_system', 2147483647, 1, 4),
+('bx_developer_nav_item_add', 'title', 2147483647, 1, 5),
+('bx_developer_nav_item_add', 'link', 2147483647, 1, 6),
+('bx_developer_nav_item_add', 'onclick', 2147483647, 1, 7),
+('bx_developer_nav_item_add', 'target', 2147483647, 1, 8),
+('bx_developer_nav_item_add', 'icon', 2147483647, 1, 9),
+('bx_developer_nav_item_add', 'addon', 2147483647, 1, 10),
+('bx_developer_nav_item_add', 'submenu_object', 2147483647, 1, 11),
+('bx_developer_nav_item_add', 'submenu_popup', 2147483647, 1, 12),
+('bx_developer_nav_item_add', 'visibility_custom', 2147483647, 1, 13),
+('bx_developer_nav_item_add', 'hidden_on', 2147483647, 1, 14),
+('bx_developer_nav_item_add', 'hidden_on_pt', 2147483647, 1, 15),
+('bx_developer_nav_item_add', 'hidden_on_col', 2147483647, 1, 16),
+('bx_developer_nav_item_add', 'hidden_on_cxt', 2147483647, 1, 17),
+('bx_developer_nav_item_add', 'config_api', 2147483647, 1, 18),
+('bx_developer_nav_item_add', 'primary', 2147483647, 1, 19),
+('bx_developer_nav_item_add', 'collapsed', 2147483647, 1, 20),
+('bx_developer_nav_item_add', 'copyable', 2147483647, 1, 21),
+('bx_developer_nav_item_add', 'active_api', 2147483647, 1, 22),
+('bx_developer_nav_item_add', 'controls', 2147483647, 1, 23),
+('bx_developer_nav_item_add', 'do_submit', 2147483647, 1, 24),
+('bx_developer_nav_item_add', 'cancel', 2147483647, 1, 25),
+
+('bx_developer_nav_item_edit', 'id', 2147483647, 1, 1),
+('bx_developer_nav_item_edit', 'module', 2147483647, 1, 2),
+('bx_developer_nav_item_edit', 'set_name', 2147483647, 1, 3),
+('bx_developer_nav_item_edit', 'name', 2147483647, 1, 4),
+('bx_developer_nav_item_edit', 'title_system', 2147483647, 1, 5),
+('bx_developer_nav_item_edit', 'title', 2147483647, 1, 6),
+('bx_developer_nav_item_edit', 'link', 2147483647, 1, 7),
+('bx_developer_nav_item_edit', 'onclick', 2147483647, 1, 8),
+('bx_developer_nav_item_edit', 'target', 2147483647, 1, 9),
+('bx_developer_nav_item_edit', 'icon', 2147483647, 1, 10),
+('bx_developer_nav_item_edit', 'addon', 2147483647, 1, 11),
+('bx_developer_nav_item_edit', 'submenu_object', 2147483647, 1, 12),
+('bx_developer_nav_item_edit', 'submenu_popup', 2147483647, 1, 13),
+('bx_developer_nav_item_edit', 'visibility_custom', 2147483647, 1, 14),
+('bx_developer_nav_item_edit', 'hidden_on', 2147483647, 1, 15),
+('bx_developer_nav_item_edit', 'hidden_on_pt', 2147483647, 1, 16),
+('bx_developer_nav_item_edit', 'hidden_on_col', 2147483647, 1, 17),
+('bx_developer_nav_item_edit', 'hidden_on_cxt', 2147483647, 1, 18),
+('bx_developer_nav_item_edit', 'config_api', 2147483647, 1, 19),
+('bx_developer_nav_item_edit', 'primary', 2147483647, 1, 20),
+('bx_developer_nav_item_edit', 'collapsed', 2147483647, 1, 21),
+('bx_developer_nav_item_edit', 'copyable', 2147483647, 1, 22),
+('bx_developer_nav_item_edit', 'active_api', 2147483647, 1, 23),
+('bx_developer_nav_item_edit', 'controls', 2147483647, 1, 24),
+('bx_developer_nav_item_edit', 'do_submit', 2147483647, 1, 25),
+('bx_developer_nav_item_edit', 'cancel', 2147483647, 1, 26);
+
+--
+-- Page Builder -> Page.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_bp_page', @sName, '_bx_dev_bp_txt_page_form', '', '', 'do_submit', 'sys_objects_page', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_bp_page_add', @sName, 'bx_developer_bp_page', '_bx_dev_bp_txt_page_display_add', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_bp_page', @sName, 'object', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_object', '_bx_dev_bp_txt_page_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_bp_err_page_object', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'module', '', '', 0, 'select', '_bx_dev_bp_txt_sys_page_module', '_bx_dev_bp_txt_page_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_page_module', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'title_system', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_title_system', '_bx_dev_bp_txt_page_title_system', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'title', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_title', '_bx_dev_bp_txt_page_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_bp_err_page_title', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'uri', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_uri', '_bx_dev_bp_txt_page_uri', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'url', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_url', '_bx_dev_bp_txt_page_url', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'layout_id', '', '', 0, 'select', '_bx_dev_bp_txt_sys_page_layout_id', '_bx_dev_bp_txt_page_layout_id', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_page_layout_id', 'Int', '', 0, 0),
+('bx_developer_bp_page', @sName, 'config_api', '', '', 0, 'textarea', '_bx_dev_bp_txt_sys_page_config_api', '_bx_dev_bp_txt_page_config_api', '', 0, 0, 0, '', '', '', '', '', '', 'XssHtml', '', 0, 0),
+('bx_developer_bp_page', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_page_deletable', '_bx_dev_bp_txt_page_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_page', @sName, 'override_class_name', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_override_class_name', '_bx_dev_bp_txt_page_override_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'override_class_file', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_override_class_file', '_bx_dev_bp_txt_page_override_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_page', @sName, 'do_submit', '_bx_dev_bp_btn_page_add', '', 0, 'submit', '_bx_dev_bp_btn_sys_page_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_page', @sName, 'cancel', '_bx_dev_bp_btn_page_cancel', '', 0, 'button', '_bx_dev_bp_btn_sys_page_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_bp_page_add', 'object', 2147483647, 1, 1),
+('bx_developer_bp_page_add', 'module', 2147483647, 1, 2),
+('bx_developer_bp_page_add', 'title_system', 2147483647, 1, 3),
+('bx_developer_bp_page_add', 'title', 2147483647, 1, 4),
+('bx_developer_bp_page_add', 'uri', 2147483647, 1, 5),
+('bx_developer_bp_page_add', 'url', 2147483647, 1, 6),
+('bx_developer_bp_page_add', 'layout_id', 2147483647, 1, 7),
+('bx_developer_bp_page_add', 'config_api', 2147483647, 1, 8),
+('bx_developer_bp_page_add', 'deletable', 2147483647, 1, 9),
+('bx_developer_bp_page_add', 'override_class_name', 2147483647, 1, 10),
+('bx_developer_bp_page_add', 'override_class_file', 2147483647, 1, 11),
+('bx_developer_bp_page_add', 'controls', 2147483647, 1, 12),
+('bx_developer_bp_page_add', 'do_submit', 2147483647, 1, 13),
+('bx_developer_bp_page_add', 'cancel', 2147483647, 1, 14);
+
+--
+-- Page Builder -> Block.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_bp_block', @sName, '_bx_dev_bp_txt_block_form', '', '', 'do_submit', 'sys_pages_blocks', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_bp_block_edit', @sName, 'bx_developer_bp_block', '_bx_dev_bp_txt_block_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_bp_block', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_bp_txt_sys_block_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'object', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_object', '_bx_dev_bp_txt_block_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:1;s:3:"max";i:64;}', '_bx_dev_bp_err_block_object', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'module', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_module', '_bx_dev_bp_txt_block_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_block_module', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'title_system', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_title_system', '_bx_dev_bp_txt_block_title_system', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'title', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_title', '_bx_dev_bp_txt_block_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_bp_err_block_title', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'description', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_description', '_bx_dev_bp_txt_block_description', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'icon', '', '', 0, 'textarea', '_bx_dev_bp_txt_sys_block_icon', '_bx_dev_bp_txt_block_icon', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'designbox_id', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_designbox_id', '_bx_dev_bp_txt_block_designbox_id', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_block_designbox_id', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'content_empty', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_content_empty', '_bx_dev_bp_txt_block_content_empty', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'help', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_help', '_bx_dev_bp_txt_block_help', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'class', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_class', '_bx_dev_bp_txt_block_class', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'cache_lifetime', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_cache_lifetime', '_bx_dev_bp_txt_block_cache_lifetime', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'config_api', '', '', 0, 'textarea', '_bx_dev_bp_txt_sys_block_config_api', '_bx_dev_bp_txt_block_config_api', '', 0, 0, 0, '', '', '', '', '', '', 'XssHtml', '', 0, 0),
+
+('bx_developer_bp_block', @sName, 'async', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_async', '_bx_dev_bp_txt_block_async', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'submenu', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_submenu', '_bx_dev_bp_txt_block_submenu', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'tabs', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_tabs', '_bx_dev_bp_txt_block_tabs', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'hidden_on', '', '', 0, 'select_multiple', '_bx_dev_bp_txt_sys_block_hidden_on', '_bx_dev_bp_txt_block_hidden_on', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+
+('bx_developer_bp_block', @sName, 'visible_for', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_visible_for', '_bx_dev_bp_txt_block_visible_for', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'visible_for_levels', '', '', 0, 'checkbox_set', '_bx_dev_bp_txt_sys_block_visible_for_levels', '_bx_dev_bp_txt_block_visible_for_levels', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_deletable', '_bx_dev_bp_txt_block_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'copyable', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_copyable', '_bx_dev_bp_txt_block_copyable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'active', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_active', '_bx_dev_bp_txt_block_active', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'active_api', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_active_api', '_bx_dev_bp_txt_block_active_api', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'controls', '', 'do_submit,cancel,delete', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_block', @sName, 'do_submit', '_bx_dev_bp_btn_block_add', '', 0, 'submit', '_bx_dev_bp_btn_sys_block_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_block', @sName, 'cancel', '_bx_dev_bp_btn_block_cancel', '', 0, 'button', '_bx_dev_bp_btn_sys_block_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_block', @sName, 'delete', '_bx_dev_bp_btn_block_delete', '', 0, 'button', '_bx_dev_bp_btn_sys_block_delete', '', '', 0, 0, 0, 'a:1:{s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_bp_block_edit', 'id', 2147483647, 1, 1),
+('bx_developer_bp_block_edit', 'object', 2147483647, 1, 2),
+('bx_developer_bp_block_edit', 'module', 2147483647, 1, 3),
+('bx_developer_bp_block_edit', 'title_system', 2147483647, 1, 4),
+('bx_developer_bp_block_edit', 'title', 2147483647, 1, 5),
+('bx_developer_bp_block_edit', 'description', 2147483647, 1, 6),
+('bx_developer_bp_block_edit', 'icon', 2147483647, 1, 7),
+('bx_developer_bp_block_edit', 'designbox_id', 2147483647, 1, 8),
+('bx_developer_bp_block_edit', 'async', 2147483647, 1, 9),
+('bx_developer_bp_block_edit', 'submenu', 2147483647, 1, 10),
+('bx_developer_bp_block_edit', 'tabs', 2147483647, 1, 11),
+('bx_developer_bp_block_edit', 'hidden_on', 2147483647, 1, 12),
+('bx_developer_bp_block_edit', 'visible_for', 2147483647, 1, 13),
+('bx_developer_bp_block_edit', 'visible_for_levels', 2147483647, 1, 14),
+('bx_developer_bp_block_edit', 'content_empty', 2147483647, 1, 15),
+('bx_developer_bp_block_edit', 'help', 2147483647, 1, 16),
+('bx_developer_bp_block_edit', 'class', 2147483647, 1, 17),
+('bx_developer_bp_block_edit', 'cache_lifetime', 2147483647, 1, 18),
+('bx_developer_bp_block_edit', 'config_api', 2147483647, 1, 19),
+('bx_developer_bp_block_edit', 'deletable', 2147483647, 1, 20),
+('bx_developer_bp_block_edit', 'copyable', 2147483647, 1, 21),
+('bx_developer_bp_block_edit', 'active', 2147483647, 1, 22),
+('bx_developer_bp_block_edit', 'active_api', 2147483647, 1, 23),
+('bx_developer_bp_block_edit', 'controls', 2147483647, 1, 24),
+('bx_developer_bp_block_edit', 'do_submit', 2147483647, 1, 25),
+('bx_developer_bp_block_edit', 'cancel', 2147483647, 1, 26),
+('bx_developer_bp_block_edit', 'delete', 2147483647, 1, 27);
+
+--
+-- Polyglot -> Grid descriptors
+--
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_pgt_manage', 'Sql', 'SELECT * FROM `sys_modules` WHERE 1 ', 'sys_modules', 'id', 'name', '', '', 100, NULL, 'start', '', '', '', '', '', '', 2147483647, 'BxDevPolyglotManage', 'modules/boonex/developer/classes/BxDevPolyglotManage.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_developer_pgt_manage', 'title', '_bx_dev_pgt_txt_manage_gl_title', '100%', 0, '', '', 1);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_developer_pgt_manage', 'independent', 'add_keys', '_bx_dev_pgt_btn_manage_gl_add_keys', '', 0, 0),
+('bx_developer_pgt_manage', 'independent', 'recompile', '_bx_dev_pgt_btn_manage_gl_recompile', '', 0, 1),
+('bx_developer_pgt_manage', 'independent', 'restore', '_bx_dev_pgt_btn_manage_gl_restore', '', 0, 2);
+
+--
+-- Polyglot -> Manage.
+--
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_developer_pgt_keys', @sName, '_bx_dev_pgt_txt_keys_form', '', '', 'do_submit', 'sys_localization_keys', 'ID', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_pgt_keys_add', @sName, 'bx_developer_pgt_keys', '_bx_dev_pgt_txt_keys_display_add', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_developer_pgt_keys', @sName, 'language', '', '', 0, 'select', '_bx_dev_pgt_txt_sys_keys_language', '_bx_dev_pgt_txt_keys_language', '_bx_dev_pgt_inf_keys_language', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'category', '', '', 0, 'select', '_bx_dev_pgt_txt_sys_keys_category', '_bx_dev_pgt_txt_keys_category', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_pgt_err_keys_category', 'Int', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'content', '', '', 0, 'textarea', '_bx_dev_pgt_txt_sys_keys_content', '_bx_dev_pgt_txt_keys_content', '_bx_dev_pgt_inf_keys_content', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_pgt_err_keys_content', 'Xss', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'replace', '1', '', 0, 'switcher', '_bx_dev_pgt_txt_sys_keys_replace', '_bx_dev_pgt_txt_keys_replace', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'do_submit', '_bx_dev_pgt_btn_keys_submit', '', 0, 'submit', '_bx_dev_pgt_btn_sys_keys_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'do_cancel', '_bx_dev_pgt_btn_keys_cancel', '', 0, 'button', '_bx_dev_pgt_btn_sys_keys_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_pgt_keys_add', 'language', 2147483647, 1, 1),
+('bx_developer_pgt_keys_add', 'category', 2147483647, 1, 2),
+('bx_developer_pgt_keys_add', 'content', 2147483647, 1, 3),
+('bx_developer_pgt_keys_add', 'replace', 2147483647, 1, 4),
+('bx_developer_pgt_keys_add', 'controls', 2147483647, 1, 5),
+('bx_developer_pgt_keys_add', 'do_submit', 2147483647, 1, 6),
+('bx_developer_pgt_keys_add', 'do_cancel', 2147483647, 1, 7);

+ 51 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Donations',
+    'version_from' => '15.0.0',
+    'version_to' => '15.0.1',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/donations/updates/update_15.0.0_15.0.1/',
+    'home_uri' => 'donations_update_1500_1501',
+
+    'module_dir' => 'boonex/donations/',
+    'module_uri' => 'donations',
+
+    'db_prefix' => 'bx_donations_',
+    'class_prefix' => 'BxDonations',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Donations',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxDonationsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 4 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/install/sql/enable.sql

@@ -0,0 +1,4 @@
+-- MENUS
+UPDATE `sys_menu_items` SET `icon`='donate' WHERE `set_name`='sys_site' AND `name`='donations-make' AND `icon`='donate col-blue3';
+UPDATE `sys_menu_items` SET `icon`='donate' WHERE `set_name`='sys_homepage' AND `name`='donations-make' AND `icon`='donate col-blue3';
+UPDATE `sys_menu_items` SET `icon`='donate' WHERE `set_name`='sys_account_dashboard' AND `name`='dashboard-donations-list' AND `icon`='donate col-blue3';

+ 154 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/source/classes/BxDonationsConfig.php

@@ -0,0 +1,154 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Donations Donations
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxDonationsConfig extends BxBaseModGeneralConfig
+{
+    protected $_oDb;
+    protected $_aHtmlIds;
+
+    protected $_iOwner;
+    protected $_bShowTitle;
+    protected $_bEnableOther;
+    protected $_aBillingTypes;
+    protected $_aPeriodUnits;
+
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+            // module icon
+            'ICON' => 'donate',
+
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'entries',
+            'TABLE_ENTRIES_DELETED' => $aModule['db_prefix'] . 'entries_deleted',
+            'TABLE_TYPES' => $aModule['db_prefix'] . 'types',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_NAME' => 'name',
+            'FIELD_TITLE' => 'title',
+            'FIELD_PERIOD' => 'period',
+            'FIELD_PERIOD_UNIT' => 'period_unit',
+            'FIELD_AMOUNT' => 'amount',
+            'FIELD_CUSTOM' => 'custom',
+
+            // page URIs
+            'URL_MAKE' => 'page.php?i=donations-make',
+            'URL_LIST' => 'page.php?i=donations-list',
+            'URL_LIST_ALL' => 'page.php?i=donations-list-all',
+
+            // some params
+            'PARAM_SHOW_TITLE' => 'bx_donations_show_title',
+            'PARAM_ENABLE_OTHER' => 'bx_donations_enable_other',
+            'PARAM_AMOUNT_PRECISION' => 'bx_donations_amount_precision',
+
+            'PARAM_OTHER_NAME' => 'other',
+            'PARAM_OTHER_PERIOD' => 1,
+            'PARAM_OTHER_PERIOD_UNIT' => 'month',
+            'PARAM_OTHER_PRICE_MIN' => 5,
+
+            // objects 
+            'OBJECT_GRID_LIST' => 'bx_donations_list',
+            'OBJECT_GRID_LIST_ALL' => 'bx_donations_list_all',
+            'OBJECT_GRID_TYPES' => 'bx_donations_types',
+            'OBJECT_FORM_TYPE' => 'bx_donations_type',
+            'OBJECT_FORM_TYPE_DISPLAY_ADD' => 'bx_donations_type_add',
+            'OBJECT_FORM_TYPE_DISPLAY_EDIT' => 'bx_donations_type_edit',
+            'OBJECT_FORM_PRELISTS_PERIOD_UNITS' => 'bx_donations_period_units',
+            'OBJECT_MENU_LIST_SUBMENU' => 'bx_donations_list_submenu',
+
+            // email templates
+            'ETEMPLATE_DONATED' => 'bx_donations_donated',
+        );
+
+        $this->_aJsClasses = array(
+            'main' => 'BxDonationsMain',
+            'form' => 'BxDonationsForm',
+        );
+
+        $this->_aJsObjects = array(
+            'main' => 'oBxDonationsMain',
+            'form' => 'oBxDonationsForm',
+        );
+
+        $sHtmlPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = array(
+            'popup_type' => $sHtmlPrefix . '-popup-type',
+            'menu_billing_types' => $sHtmlPrefix . '-menu-billing_types',
+            'link_billing_type' => $sHtmlPrefix . '-link-billing-',
+        );
+
+        $oPayments = BxDolPayments::getInstance();
+        $this->_iOwner = (int)$oPayments->getOption('site_admin');
+
+        $this->_aBillingTypes = array(
+            BX_DONATIONS_BTYPE_SINGLE,
+            BX_DONATIONS_BTYPE_RECURRING
+        );
+
+        $this->_aPeriodUnits = BxDolForm::getDataItems($this->CNF['OBJECT_FORM_PRELISTS_PERIOD_UNITS']);
+    }
+
+    public function init(&$oDb)
+    {
+        $this->_oDb = &$oDb;
+
+        $this->_bShowTitle = $this->_oDb->getParam($this->CNF['PARAM_SHOW_TITLE']) == 'on';
+        $this->_bEnableOther = $this->_oDb->getParam($this->CNF['PARAM_ENABLE_OTHER']) == 'on';
+    }
+
+    public function getHtmlIds($sKey = '')
+    {
+        if(empty($sKey))
+            return $this->_aHtmlIds;
+
+        return isset($this->_aHtmlIds[$sKey]) ? $this->_aHtmlIds[$sKey] : '';
+    }
+    
+    public function getOwner()
+    {
+    	return $this->_iOwner;
+    }
+
+    public function isShowTitle()
+    {
+        return $this->_bShowTitle;
+    }
+
+    public function isEnableOther()
+    {
+        return $this->_bEnableOther;
+    }
+
+    public function getBillingTypes()
+    {
+        return $this->_aBillingTypes;
+    }
+
+    public function getPeriodUnits()
+    {
+        return $this->_aPeriodUnits;
+    }
+
+    public function getTypeName($sName)
+    {
+        return uriGenerate($sName, $this->CNF['TABLE_TYPES'], $this->CNF['FIELD_NAME'], ['lowercase' => false]);
+    }
+
+    public function getTypeNameCustom()
+    {
+        return $this->getTypeName($this->CNF['PARAM_OTHER_NAME'] . '_' . bx_get_logged_profile_id());
+    }
+}
+
+/** @} */

+ 318 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/source/classes/BxDonationsModule.php

@@ -0,0 +1,318 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Donations Donations
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define('BX_DONATIONS_BTYPE_SINGLE', 'single');
+define('BX_DONATIONS_BTYPE_RECURRING', 'recurring');
+
+class BxDonationsModule extends BxBaseModGeneralModule
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_oConfig->init($this->_oDb);
+    }
+
+    /**
+     * ACTION METHODS
+     */
+    public function actionCheckName()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$sName = bx_process_input(bx_get('name'));
+    	if(empty($sName))
+            return echoJson(array());
+
+        $sResult = '';
+
+        $iId = (int)bx_get('id');
+        if(!empty($iId)) {
+            $aPrice = $this->_oDb->getTypes(array('type' => 'by_id', 'value' => $iId)); 
+            if(strcmp($sName, $aPrice[$CNF['FIELD_NAME']]) == 0) 
+                $sResult = $sName;
+        }
+
+    	echoJson(array(
+            'name' => !empty($sResult) ? $sResult : $this->_oConfig->getTypeName($sName)
+    	));
+    }
+
+    public function actionMakeOther()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sBillingType = bx_process_input(bx_get('btype'));
+        $bBillingTypeRecurring = $sBillingType == BX_DONATIONS_BTYPE_RECURRING;
+
+        $fAmount = bx_process_input(bx_get('amount'), BX_DATA_FLOAT);
+    	if(empty($sBillingType) || empty($fAmount))
+            return echoJson(array());
+
+        if($fAmount < $CNF['PARAM_OTHER_PRICE_MIN'])
+            return echoJson(array('msg' => _t('_bx_donations_err_min_value', _t_format_currency($CNF['PARAM_OTHER_PRICE_MIN'], getParam($CNF['PARAM_AMOUNT_PRECISION'])))));
+
+        $iOwner = $this->_oConfig->getOwner();
+        $sModule = $this->getName();
+        $iTypeId = $this->_oDb->insertType(array(
+            $CNF['FIELD_NAME'] => $this->_oConfig->getTypeNameCustom(),
+            $CNF['FIELD_PERIOD'] => $bBillingTypeRecurring ? $CNF['PARAM_OTHER_PERIOD'] : 0,
+            $CNF['FIELD_PERIOD_UNIT'] => $bBillingTypeRecurring ? $CNF['PARAM_OTHER_PERIOD_UNIT'] : '',
+            $CNF['FIELD_AMOUNT'] => $fAmount,
+            $CNF['FIELD_CUSTOM'] => 1
+        ));
+
+        $oPayments = BxDolPayments::getInstance();
+
+        $aResult = array();
+        switch($sBillingType) {
+            case BX_DONATIONS_BTYPE_SINGLE:
+                $aResultSrv = $oPayments->addToCart($iOwner, $sModule, $iTypeId, 1);
+                if(!empty($aResultSrv['code']))
+                    $aResult = array('msg' => isset($aResultSrv['message']) ? $aResultSrv['message'] : _t('_bx_donations_err_cannot_perform'));
+                else
+                    $aResult = array('redirect' => $oPayments->getCartUrl($iOwner));
+                break;
+
+            case BX_DONATIONS_BTYPE_RECURRING:
+                $aResultSrv = $oPayments->subscribeWithAddons($iOwner, '', $sModule, $iTypeId, 1);
+                if(!empty($aResultSrv['code']))
+                    $aResult = array('msg' => isset($aResultSrv['message']) ? $aResultSrv['message'] : _t('_bx_donations_err_cannot_perform'));
+                else 
+                    $aResult = isset($aResultSrv['popup']) || isset($aResultSrv['redirect']) ? $aResultSrv : array('redirect' => $oPayments->getSubscriptionsUrl());
+                break;
+        }
+
+        return echoJson($aResult);
+    }
+
+    /**
+     * SERVICE METHODS
+     */
+    public function serviceGetTypesBy($aParams)
+    {
+    	return $this->_oDb->getTypes($aParams);
+    }
+
+    public function serviceIncludeCssJs()
+    {
+        return $this->_oTemplate->getIncludeCssJs();
+    }
+
+    public function serviceGetBlockMake()
+    {
+        return $this->_oTemplate->getBlockMake();
+    }
+
+    public function serviceGetBlockList()
+    {
+        return $this->_getBlockList();
+    }
+
+    public function serviceGetBlockListAll()
+    {
+        return $this->_getBlockList('all');
+    }
+
+    public function serviceGetPaymentData()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oPermalink = BxDolPermalinks::getInstance();
+        return array_merge($this->_aModule, [
+            'url_browse_order_common' => bx_absolute_url(bx_append_url_params($oPermalink->permalink($CNF['URL_LIST']), ['filter' => '{order}'], false)),
+            'url_browse_order_administration' => bx_absolute_url(bx_append_url_params($oPermalink->permalink($CNF['URL_LIST_ALL']), ['filter' => '{order}'], false))
+        ]);
+    }
+
+    public function serviceGetCartItem($mixedItemId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        if(!$mixedItemId)
+            return array();
+
+        $aItem = $this->_oDb->getTypes(array(
+            'type' => 'by_' . (is_numeric($mixedItemId) ? 'id' : 'name'), 
+            'value' => $mixedItemId
+        ));
+
+        if(empty($aItem) || !is_array($aItem))
+            return array();
+
+        return array (
+            'id' => $aItem[$CNF['FIELD_ID']],
+            'author_id' => $this->_oConfig->getOwner(),
+            'name' => $aItem[$CNF['FIELD_NAME']],
+            'title' => _t($this->_oConfig->isShowTitle() && !empty($aItem[$CNF['FIELD_TITLE']]) ? $aItem[$CNF['FIELD_TITLE']] : '_bx_donations_txt_cart_item_title'),
+            'description' => _t('_bx_donations_txt_cart_item_description', getParam('site_title')),
+            'url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_MAKE'])),
+            'price_single' => $aItem[$CNF['FIELD_AMOUNT']],
+            'price_recurring' => $aItem[$CNF['FIELD_AMOUNT']],
+            'period_recurring' => $aItem[$CNF['FIELD_PERIOD']],
+            'period_unit_recurring' => $aItem[$CNF['FIELD_PERIOD_UNIT']],
+            'trial_recurring' => ''
+        );
+    }
+
+    public function serviceGetCartItems($iSellerId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        $iSellerId = (int)$iSellerId;
+        if(empty($iSellerId))
+            return array();
+
+        $aItems = $this->_oDb->getTypes(array('type' => 'all'));
+        if(empty($aItems) || !is_array($aItems))
+            return array();
+
+        $bShowTitle = $this->_oConfig->isShowTitle();
+
+        $aResult = array();
+        foreach($aItems as $aItem)
+            $aResult[] = array(
+                'id' => $aItem[$CNF['FIELD_ID']],
+                'author_id' => $this->_oConfig->getOwner(),
+                'name' => $aItem[$CNF['FIELD_NAME']],
+                'title' => _t($bShowTitle && !empty($aItem[$CNF['FIELD_TITLE']]) ? $aItem[$CNF['FIELD_TITLE']] : '_bx_donations_txt_cart_item_title'),
+                'description' => _t('_bx_donations_txt_cart_item_description', getParam('site_title')),
+                'url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_MAKE'])),
+                'price_single' => $aItem[$CNF['FIELD_AMOUNT']],
+                'price_recurring' => $aItem[$CNF['FIELD_AMOUNT']],
+                'period_recurring' => $aItem[$CNF['FIELD_PERIOD']],
+                'period_unit_recurring' => $aItem[$CNF['FIELD_PERIOD_UNIT']],
+                'trial_recurring' => ''
+            );
+
+        return $aResult;
+    }
+
+    public function serviceRegisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_DONATIONS_BTYPE_SINGLE);
+    }
+
+    public function serviceRegisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_DONATIONS_BTYPE_RECURRING);
+    }
+
+    public function serviceReregisterCartItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+        return array();
+    }
+
+    public function serviceReregisterSubscriptionItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+        return array();
+    }
+
+    public function serviceUnregisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_DONATIONS_BTYPE_SINGLE);
+    }
+
+    public function serviceUnregisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_DONATIONS_BTYPE_RECURRING); 
+    }
+
+    public function serviceCancelSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder)
+    {
+        return true;
+    }
+
+    protected function _serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aItem = $this->serviceGetCartItem($iItemId);
+        if(empty($aItem) || !is_array($aItem))
+            return array();
+
+        if(!$this->_oDb->registerEntry($iClientId, $iItemId, $iItemCount, $sOrder, $sLicense))
+            return array();
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_donations-donation_register 'bx_donations', 'donation_register' - hook after the donation payment was processed with payment processing module
+         * - $unit_name - equals `bx_donations`
+         * - $action - equals `donation_register`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `donation_id` - [int] donation id
+         *      - `profile_id` - [int] donator profile id
+         *      - `order` - [string] order number provided with payment processing module
+         *      - `type` - [string] payment type ('single' or 'recurring')
+         *      - `amount` - [float] donated amount
+         *      - `count` - [int] number of items in order
+         * @hook @ref hook-bx_donations-donation_register
+         */
+        bx_alert($this->getName(), 'donation_register', 0, false, [
+            'donation_id' => $iItemId,
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'type' => $sType,
+            'amount' => (float)$aItem['price_' . $sType],
+            'count' => $iItemCount
+        ]);
+
+        $oClient = BxDolProfile::getInstanceMagic($iClientId);
+        sendMailTemplate($CNF['ETEMPLATE_DONATED'], 0, $iClientId, array(
+            'client_name' => $oClient->getDisplayName(),
+        ));
+
+        return $aItem;
+    }
+
+    protected function _serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+    	if(!$this->_oDb->unregisterEntry($iClientId, $iItemId, $sOrder, $sLicense))
+            return false;
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_donations-donation_unregister 'bx_donations', 'donation_unregister' - hook after the donation payment was refunded with payment processing module
+         * It's equivalent to @ref hook-bx_donations-donation_register
+         * except `amount` parameter in $extra_params is missing
+         * @hook @ref hook-bx_donations-donation_unregister
+         */
+        bx_alert($this->getName(), 'donation_unregister', 0, false, [
+            'donation_id' => $iItemId,
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'type' => $sType,
+            'count' => $iItemCount
+        ]);
+
+    	return true;
+    }
+
+
+    /*
+     * INTERNAL METHODS
+     */
+    protected function _getBlockList($sType = '') 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sGrid = $CNF['OBJECT_GRID_LIST' . (!empty($sType) ? '_' . strtoupper($sType) : '')];
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return '';
+
+        return $oGrid->getCode();
+    }
+}
+
+/** @} */

+ 192 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/source/classes/BxDonationsTemplate.php

@@ -0,0 +1,192 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Donations Donations
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxDonationsTemplate extends BxBaseModGeneralTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+
+        $this->aMethodsToCallAddJsCss[] = 'getBlockMake';
+    }
+
+    public function displayEmptyOwner()
+    {
+    	return MsgBox(_t('_bx_donations_msg_empty_owner'));
+    }
+
+    public function addCssJs()
+    {
+        parent::addCssJs();
+
+        $this->addJs(array('main.js'));
+    }
+
+    public function getIncludeCssJs()
+    {
+        return '';
+    }
+
+    public function getBlockMake($sSelected = 'single')
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oPayment = BxDolPayments::getInstance();
+        $iPaymentSeller = $this->_oConfig->getOwner();
+        $sPaymentModule = $this->_oConfig->getName();
+
+        $aJsCodes = [];
+        $sJsObject = $this->_oConfig->getJsObject('main');
+        $sHtmlIdBTypeLink = $this->_oConfig->getHtmlIds('link_billing_type');
+
+        $bShowTitle = $this->_oConfig->isShowTitle();
+        $bEnableOther = $this->_oConfig->isEnableOther();
+        $aPeriodUnits = $this->_oConfig->getPeriodUnits();
+
+        $aMenuBillingTypes = array();
+        $aTmplVarsBillingTypes = array();
+        
+        $aBillingTypes = $this->_oConfig->getBillingTypes();
+        foreach($aBillingTypes as $sBillingType) {
+            $aTypes = $this->_oDb->getTypes(array('type' => 'by_btype_' . $sBillingType, 'active' => 1));
+            if(empty($aTypes) || !is_array($aTypes))
+                continue;
+
+            $aTmplVarsTypes = array();
+            foreach($aTypes as $aType) {
+                $sDuration = '';
+
+                switch($sBillingType) {
+                    case BX_DONATIONS_BTYPE_SINGLE:
+                        $aJs = $oPayment->getAddToCartJs($iPaymentSeller, $sPaymentModule, $aType[$CNF['FIELD_ID']], 1, true);
+                        if(empty($aJs) || !is_array($aJs))
+                            continue 2;
+                        break;
+
+                    case BX_DONATIONS_BTYPE_RECURRING:
+                        $aJs = $oPayment->getSubscribeJs($iPaymentSeller, '', $sPaymentModule, $aType[$CNF['FIELD_ID']], 1);
+                        if(empty($aJs) || !is_array($aJs))
+                            continue 2;
+
+                        if((int)$aType[$CNF['FIELD_PERIOD']] > 1)
+                            $sDuration .= $aType[$CNF['FIELD_PERIOD']] . ' ';
+                        $sDuration .= _t($aPeriodUnits[$aType[$CNF['FIELD_PERIOD_UNIT']]]);
+                        break;
+                }
+
+                list($sJsCode, $sJsMethod) = $aJs;
+
+                $sJsCodeHash = md5($sJsCode);
+                if(!isset($aJsCodes[$sJsCodeHash]))
+                    $aJsCodes[$sJsCodeHash] = $sJsCode;
+
+                $sAmount = _t_format_currency($aType[$CNF['FIELD_AMOUNT']], getParam($CNF['PARAM_AMOUNT_PRECISION']));
+                $sAmount = _t('_bx_donations_txt_amount_' . $sBillingType, $sAmount, $sDuration);
+
+                $aTmplVarsTypes[] = array(
+                    'onclick' => $sJsMethod,
+                    'bx_if:show_title' => array(
+                        'condition' => $bShowTitle,
+                        'content' => array(
+                            'title' => _t($aType[$CNF['FIELD_TITLE']])
+                        )
+                    ),
+                    'amount' => $sAmount
+                );
+            }
+
+            if($bEnableOther)
+                $aTmplVarsTypes[] = array(
+                    'onclick' => $sJsObject . ".other(this, '" . $sBillingType . "')",
+                    'bx_if:show_title' => array(
+                        'condition' => $bShowTitle,
+                        'content' => array(
+                            'title' => _t('_bx_donations_txt_other_title')
+                        )
+                    ),
+                    'amount' => _t('_bx_donations_txt_other_value')
+                );
+
+            $aTmplVarsBillingTypes[$sBillingType] = array(
+                'class' => $sBillingType,
+                'bx_repeat:types' => $aTmplVarsTypes
+            );
+
+            $aMenuBillingTypes[] = array('name' => $sHtmlIdBTypeLink . $sBillingType, 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => 'javascript:' . $sJsObject . ".changeType(this, '" . $sBillingType . "')", 'target' => '_self', 'title' => _t('_bx_donations_txt_do_' . $sBillingType));
+        }
+
+        $iTmplVarsBillingTypes = count($aTmplVarsBillingTypes);
+        if($iTmplVarsBillingTypes > 1 && isset($aTmplVarsBillingTypes[$sSelected]))
+            $aTmplVarsBillingTypes[$sSelected]['class'] .= ' active';
+        else if($iTmplVarsBillingTypes > 0)
+            $aTmplVarsBillingTypes[key($aTmplVarsBillingTypes)]['class'] .= ' active';
+
+        $aTmplVarsMenu = array();
+        if(count($aMenuBillingTypes) > 1) {
+            $oMenuBillingTypes = new BxTemplMenu(array('template' => 'menu_buttons_hor.html', 'menu_id'=> $this->_oConfig->getHtmlIds('menu_billing_types'), 'menu_items' => $aMenuBillingTypes));
+            $oMenuBillingTypes->setSelected('', $sHtmlIdBTypeLink . $sSelected);
+
+            $aTmplVarsMenu = array(
+                'menu_billing_type' => $oMenuBillingTypes->getCode(),
+            );
+        }       
+
+        $sTmplName = 'block_make.html';
+        $aTmplVars = array(
+            'bx_if:show_menu' => array(
+                'condition' => !empty($aTmplVarsMenu),
+                'content' => $aTmplVarsMenu
+            ),
+            'bx_repeat:billing_types' => array_values($aTmplVarsBillingTypes),
+            'js_code' => implode('', $aJsCodes) . $this->getJsCode('main')
+        );
+
+        $sResult = null;
+        
+        /**
+         * @hooks
+         * @hookdef hook-bx_donations-donate 'bx_donations', 'donate' - hook to override the content of donate block
+         * - $unit_name - equals `bx_donations`
+         * - $action - equals `donate`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `tmpl_name` - [string] by ref, template name, can be overridden in hook processing
+         *      - `tmpl_vars` - [array] by ref, template parsable variables as key&value pairs, can be overridden in hook processing
+         *      - `override_result` - [string] by ref, string with HTML code to be output, if null the default template parsing function will be used, can be overridden in hook processing
+         * @hook @ref hook-bx_donations-donate
+         */
+        bx_alert($this->_oConfig->getName(), 'donate', 0, 0, [
+            'tmpl_name' => &$sTmplName,
+            'tmpl_vars' => &$aTmplVars,
+
+            'tmpl_name_ref' => &$sTmplName,
+            'tmpl_vars_ref' => &$aTmplVars,
+
+            'override_result' => &$sResult
+        ]);
+        if($sResult !== null)
+            return $sResult;
+
+        return $this->parseHtmlByName($sTmplName, $aTmplVars);
+    }
+
+    public function getJsCode($sType, $aParams = array(), $bWrap = true)
+    {
+        $aParams = array_merge(array(
+            'aHtmlIds' => $this->_oConfig->getHtmlIds()
+        ), $aParams);
+
+        return parent::getJsCode($sType, $aParams, $bWrap);
+    }
+}
+
+/** @} */

+ 71 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/source/install/config.php

@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Donations Donations
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_donations',
+    'title' => 'Donations',
+    'note' => 'Donations custom module.',
+    'version' => '15.0.1',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/donations/',
+    'home_uri' => 'donations',
+
+    'db_prefix' => 'bx_donations_',
+    'class_prefix' => 'BxDonations',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Donations',
+
+    /**
+     * 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(),
+
+);
+
+/** @} */

+ 114 - 0
modules/boonex/donations/updates/15.0.0_15.0.1/source/install/sql/enable.sql

@@ -0,0 +1,114 @@
+SET @sName = 'bx_donations';
+
+
+-- 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_donations', 'bx_donations@modules/boonex/donations/|std-icon.svg', 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_donations', 10);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
+('bx_donations_show_title', '', @iCategId, '_bx_donations_option_show_title', 'checkbox', '', '', '', 1),
+('bx_donations_enable_other', '', @iCategId, '_bx_donations_option_enable_other', 'checkbox', '', '', '', 2),
+('bx_donations_amount_precision', '2', @iCategId, '_bx_donations_option_amount_precision', 'digit', '', '', '', 10);
+
+
+-- PAGE: make
+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_donations_make', '_bx_donations_page_title_sys_make', '_bx_donations_page_title_make', @sName, 5, 2147483647, 1, 'donations-make', 'page.php?i=donations-make', '', '', '', 0, 1, 0, '', '');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_donations_make', 1, @sName, '_bx_donations_page_block_title_system_make', '_bx_donations_page_block_title_make', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:12:"bx_donations";s:6:"method";s:14:"get_block_make";}', 0, 1, 1);
+
+-- PAGE: list own
+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_donations_list', '_bx_donations_page_title_sys_list', '_bx_donations_page_title_list', @sName, 5, 2147483647, 1, 'donations-list', '', '', '', '', 0, 1, 0, 'BxDonationsPageList', 'modules/boonex/donations/classes/BxDonationsPageList.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `submenu`, `tabs`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_donations_list', 1, @sName, '', '_bx_donations_page_block_title_list', 11, 'bx_donations_list_submenu', 0, 2147483647, 'service', 'a:2:{s:6:"module";s:12:"bx_donations";s:6:"method";s:14:"get_block_list";}', 0, 0, 1, 1);
+
+-- PAGE: list all
+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_donations_list_all', '_bx_donations_page_title_sys_list_all', '_bx_donations_page_title_list_all', @sName, 5, 192, 1, 'donations-list-all', '', '', '', '', 0, 1, 0, 'BxDonationsPageList', 'modules/boonex/donations/classes/BxDonationsPageList.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `submenu`, `tabs`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_donations_list_all', 1, @sName, '', '_bx_donations_page_block_title_list_all', 11, 'bx_donations_list_submenu', 0, 192, 'service', 'a:2:{s:6:"module";s:12:"bx_donations";s:6:"method";s:18:"get_block_list_all";}', 0, 0, 1, 0);
+
+
+-- MENU: licenses submenu
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_donations_list_submenu', '_bx_donations_menu_title_list_submenu', 'bx_donations_list_submenu', @sName, 26, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_donations_list_submenu', @sName, '_bx_donations_menu_set_title_list_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES 
+('bx_donations_list_submenu', @sName, 'donations-list-all', '_bx_donations_menu_item_title_system_list_submenu_list_all', '_bx_donations_menu_item_title_list_submenu_list_all', 'page.php?i=donations-list-all', '', '_self', '', '', '', 192, 1, 0, 1, 1),
+('bx_donations_list_submenu', @sName, 'donations-list', '_bx_donations_menu_item_title_system_list_submenu_list', '_bx_donations_menu_item_title_list_submenu_list', 'page.php?i=donations-list', '', '_self', '', '', '', 2147483646, 1, 0, 1, 2);
+
+-- MENU: add to site menu
+SET @iSiteMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_site' AND `active` = 1 AND `order` < 9999 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_site', @sName, 'donations-make', '_bx_donations_menu_item_title_system_make', '_bx_donations_menu_item_title_make', 'page.php?i=donations-make', '', '', 'donate', '', 2147483647, 1, 1, IFNULL(@iSiteMenuOrder, 0) + 1);
+
+-- MENU: add to homepage menu
+SET @iHomepageMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_homepage' AND `active` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_homepage', @sName, 'donations-make', '_bx_donations_menu_item_title_system_make', '_bx_donations_menu_item_title_make', 'page.php?i=donations-make', '', '', 'donate', '', 2147483647, 1, 1, IFNULL(@iHomepageMenuOrder, 0) + 1);
+
+-- MENU: account dashboard
+SET @iDashboardMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard' LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES
+('sys_account_dashboard', @sName, 'dashboard-donations-list', '_bx_donations_menu_item_title_system_list', '_bx_donations_menu_item_title_list', 'page.php?i=donations-list', '', '', 'donate', '', '', 2147483646, 1, 0, 1, @iDashboardMenuOrder + 1);
+
+
+-- GRIDS: types
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_donations_types', 'Sql', 'SELECT * FROM `bx_donations_types` WHERE 1 AND `custom`=''0'' ', 'bx_donations_types', 'id', 'order', 'active', '', 100, NULL, 'start', '', 'name,period,period_unit,amount', 'title', 'like', '', '', 192, 'BxDonationsGridTypes', 'modules/boonex/donations/classes/BxDonationsGridTypes.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_donations_types', 'checkbox', '_sys_select', '1%', 0, 0, '', 1),
+('bx_donations_types', 'order', '', '1%', 0, 0, '', 2),
+('bx_donations_types', 'switcher', '_bx_donations_grid_column_active', '8%', 0, '', '', 3),
+('bx_donations_types', 'name', '_bx_donations_grid_column_name', '20%', 0, 32, '', 4),
+('bx_donations_types', 'title', '_bx_donations_grid_column_title', '20%', 1, 32, '', 5),
+('bx_donations_types', 'amount', '_bx_donations_grid_column_amount', '15%', 0, 8, '', 6),
+('bx_donations_types', 'period', '_bx_donations_grid_column_period', '15%', 0, 8, '', 7),
+('bx_donations_types', 'actions', '', '20%', 0, '', '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_donations_types', 'independent', 'add', '_bx_donations_grid_action_add', '', 0, 0, 1),
+('bx_donations_types', 'single', 'edit', '_bx_donations_grid_action_edit', 'pencil-alt', 1, 0, 1),
+('bx_donations_types', 'single', 'delete', '_bx_donations_grid_action_delete', 'remove', 1, 1, 2),
+('bx_donations_types', 'bulk', 'delete', '_bx_donations_grid_action_delete', '', 0, 1, 1);
+
+-- GRIDS: entries list
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `show_total_count`, `override_class_name`, `override_class_file`) VALUES
+('bx_donations_list_all', 'Sql', 'SELECT `te`.`id` AS `id`, `te`.`profile_id` AS `profile_id`, `te`.`type_id` AS `type_id`, `tt`.`title` AS `type_title`, `tt`.`amount` AS `type_amount`, `tt`.`period` AS `type_period`, `tt`.`period_unit` AS `type_period_unit`, `te`.`order` AS `transaction`, `te`.`added` AS `added` FROM `bx_donations_entries` AS `te` LEFT JOIN `bx_donations_types` AS `tt` ON `te`.`type_id`=`tt`.`id` LEFT JOIN `sys_profiles` AS `tup` ON `te`.`profile_id`=`tup`.`id` LEFT JOIN `sys_accounts` AS `tua` ON `tup`.`account_id`=`tua`.`id` WHERE 1 ', 'bx_donations_entries', 'id', 'added', '', '', 20, NULL, 'start', '', 'tt`.`name,tt`.`period,tt`.`period_unit,tt`.`amount,te`.`order,tua`.`name,tua`.`email', 'tt`.`title', 'like', '', '', 192, 0, 'BxDonationsGridListAll', 'modules/boonex/donations/classes/BxDonationsGridListAll.php'),
+('bx_donations_list', 'Sql', 'SELECT `te`.`id` AS `id`, `te`.`profile_id` AS `profile_id`, `te`.`type_id` AS `type_id`, `tt`.`title` AS `type_title`, `tt`.`amount` AS `type_amount`, `tt`.`period` AS `type_period`, `tt`.`period_unit` AS `type_period_unit`, `te`.`order` AS `transaction`, `te`.`added` AS `added` FROM `bx_donations_entries` AS `te` LEFT JOIN `bx_donations_types` AS `tt` ON `te`.`type_id`=`tt`.`id` WHERE 1 ', 'bx_donations_entries', 'id', 'added', '', '', 20, NULL, 'start', '', 'tt`.`name,tt`.`period,tt`.`period_unit,tt`.`amount,te`.`order', 'tt`.`title', 'like', '', '', 2147483647, 0, 'BxDonationsGridList', 'modules/boonex/donations/classes/BxDonationsGridList.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_donations_list_all', 'profile_id', '_bx_donations_grid_column_title_lst_profile_id', '20%', 0, 0, '', 1),
+('bx_donations_list_all', 'entry', '_bx_donations_grid_column_title_lst_entry', '30%', 0, 0, '', 2),
+('bx_donations_list_all', 'billing_type', '_bx_donations_grid_column_title_lst_billing_type', '10%', 0, 0, '', 3),
+('bx_donations_list_all', 'transaction', '_bx_donations_grid_column_title_lst_transaction', '30%', 0, 32, '', 4),
+('bx_donations_list_all', 'added', '_bx_donations_grid_column_title_lst_added', '10%', 0, 0, '', 5),
+
+('bx_donations_list', 'entry', '_bx_donations_grid_column_title_lst_entry', '45%', 0, 0, '', 1),
+('bx_donations_list', 'billing_type', '_bx_donations_grid_column_title_lst_billing_type', '10%', 0, 0, '', 2),
+('bx_donations_list', 'transaction', '_bx_donations_grid_column_title_lst_transaction', '35%', 0, 32, '', 3),
+('bx_donations_list', 'added', '_bx_donations_grid_column_title_lst_added', '10%', 0, 0, '', 4);
+
+
+-- INJECTIONS
+INSERT INTO `sys_injections` (`name`, `page_index`, `key`, `type`, `data`, `replace`, `active`) VALUES
+(@sName, 0, 'injection_head', 'service', 'a:2:{s:6:"module";s:12:"bx_donations";s:6:"method";s:14:"include_css_js";}', 0, 1);
+
+
+-- EMAIL TEMPLATES
+INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES 
+(@sName, '_bx_donations_et_txt_name_donated', 'bx_donations_donated', '_bx_donations_et_txt_subject_donated', '_bx_donations_et_txt_body_donated');

+ 51 - 0
modules/boonex/elasticsearch/updates/15.0.1_15.0.2/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'ElasticSearch',
+    'version_from' => '15.0.1',
+    'version_to' => '15.0.2',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/elasticsearch/updates/update_15.0.1_15.0.2/',
+    'home_uri' => 'elasticsearch_update_1501_1502',
+
+    'module_dir' => 'boonex/elasticsearch/',
+    'module_uri' => 'elasticsearch',
+
+    'db_prefix' => 'bx_els_',
+    'class_prefix' => 'BxEls',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'ElasticSearch',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/elasticsearch/updates/15.0.1_15.0.2/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxElsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 249 - 0
modules/boonex/elasticsearch/updates/15.0.1_15.0.2/source/classes/BxElsApi.php

@@ -0,0 +1,249 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    ElasticSearch ElasticSearch
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define('BX_ELASTICSEARCH_LOG', 1);
+
+/**
+ * ElasticSearch API
+ */
+class BxElsApi extends BxDol
+{
+    static $TYPE = '_doc';
+    
+	protected $_oModule;
+
+    protected $_sError = false;
+    protected $_iTimeout = 30;
+    protected $_sApiUrl = 'http://localhost:9200';
+
+    protected $_aOprs = array('AND' => array('AG' => 'bool', 'FN' => 'filter'), 'OR' => array('AG' => 'dis_max', 'FN' => 'queries'));
+
+    public function __construct(&$oModule)
+    {
+        $this->_oModule = $oModule;
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_sApiUrl = getParam($CNF['PARAM_API_URL']);
+    }
+
+    public function searchSimple($sIndex, $sType, $sTerm) 
+    {
+        if ($sType) {
+            $sQuery = "/{$sIndex}@{$sType}/_search";
+            return $this->api($sQuery, array('query' => array('simple_query_string' => array('query' => $sTerm))), 'post');
+        } 
+        else {
+            $this->log('ERROR: searchSimple - search without specifying particular module(type) isn\'t supported', '', BX_LOG_ERR);
+        }
+    }
+
+    public function searchExtended($sIndex, $sType, $aCondition, $aSelection = array()) 
+    {
+        if ($sType) {
+            $sQuery = "/{$sIndex}@{$sType}/_search";
+
+            $aQuery = array('query' => $this->_prepareConditionsGroup($aCondition));
+            if(!empty($aSelection))
+                $aQuery = array_merge($aQuery, $aSelection);
+
+            return $this->api($sQuery, $aQuery, 'post');
+        }
+        else {
+            $this->log('ERROR: searchExtended - search without specifying particular module(type) isn\'t supported', BX_LOG_ERR);
+        }
+    }
+
+    protected function _prepareConditionsGroup($aGrp)
+    {
+        if(!isset($aGrp['grp']) || !(bool)$aGrp['grp'])
+			return $this->_prepareConditionsSingle($aGrp);
+
+        $aResult = array();
+    	if(!isset($aGrp['opr'], $aGrp['cnds']) || empty($aGrp['cnds']) || !is_array($aGrp['cnds']))
+    		return $aResult;
+
+        $aOpr = $this->_aOprs[$aGrp['opr']];
+        $aResult[$aOpr['AG']] = array($aOpr['FN'] => array());
+
+        foreach($aGrp['cnds'] as $aCnd) {
+    		$sMethod = '_prepareConditions' . (isset($aCnd['grp']) && (bool)$aCnd['grp'] ? 'Group' : 'Single');
+    		$aResultCnd = $this->$sMethod($aCnd);
+    		if(!empty($aResultCnd))
+    		    $aResult[$aOpr['AG']][$aOpr['FN']][] = $aResultCnd;
+    	}
+
+        return $aResult;
+    }
+
+    protected function _prepareConditionsSingle($aCnd)
+    {
+        if(!isset($aCnd['val']) && !(isset($aCnd['val1']) || isset($aCnd['val2'])))
+            return array();
+
+        $bTp = !empty($aCnd['tp']);
+        $bFld = !empty($aCnd['fld']);
+        $bOprIn = !empty($aCnd['opr']) && $aCnd['opr'] == 'IN';
+
+        if($bTp)
+            $sType = $aCnd['tp'];
+        else if($bFld) {
+            if($bOprIn)
+                $sType = 'terms';
+            else 
+                $sType = 'term';
+        }
+        else 
+            $sType = 'simple_query_string';
+
+        $aQuery = array();
+        switch ($sType) {
+            case 'term':
+            case 'terms':
+                $aQuery = array($aCnd['fld'] => $aCnd['val']);
+                break;
+
+            case 'range':
+                $aOpr = array();
+                if(!empty($aCnd['opr1']) && isset($aCnd['val1']))
+                    $aOpr[$aCnd['opr1']] = $aCnd['val1'];
+                if(!empty($aCnd['opr2']) && isset($aCnd['val2']))
+                    $aOpr[$aCnd['opr2']] = $aCnd['val2'];
+
+                $aQuery = array($aCnd['fld'] => $aOpr);
+                break;
+
+            case 'simple_query_string':
+                $aQuery = array('query' => $aCnd['val']);
+                break;
+        }
+
+        return array($sType => $aQuery);
+    }
+
+    public function getData($sIndex, $sType, $iContentId) 
+    {
+        return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId");
+    }
+
+    public function indexData($sIndex, $sType, $iContentId, $aData) 
+    {
+        return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId", $aData, 'put');
+    }
+
+    public function updateData($sIndex, $sType, $iContentId, $aData)
+    {
+        return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId", $aData, 'post');
+    }
+
+    public function deleteData($sIndex, $sType, $iContentId)
+    {
+        return $this->api("/{$sIndex}@{$sType}/" . self::$TYPE . "/$iContentId", array(), 'delete');
+    }
+
+    public function getErrorMsg() 
+    {
+        return $this->_sError;
+    }
+    
+    public function api($sAction, $aData = array(), $sMetod = 'get', $bJsonResponse = true) 
+    {
+        if (!$this->_sApiUrl) {
+            $this->_sError = 'Host isn\'t defined';
+            return null;
+        }
+
+        if (!$sAction) {
+            $this->_sError = 'Action isn\'t defined';
+            return null;
+        }        
+
+        $sUrl = bx_append_url_params($this->_sApiUrl . $sAction, array(
+            'format' => 'json',
+            'human' => false,
+        ));
+
+        $sHttpCode = 200;
+        $s = $this->curl($sUrl, $aData, $sMetod, $sHttpCode);
+
+        $this->log($s, '', BX_LOG_DEBUG);
+
+        if ($sHttpCode >= 300 || $sHttpCode < 200) {
+            $this->_sError = 'Action('.$sAction.') returned status - ' . $sHttpCode;
+            return null;
+        }
+
+        // return JSON response
+        if ($s && $bJsonResponse) {
+            if (null === ($a = json_decode($s, true))) {
+                $this->_sError = 'Can not decode response';
+                return null;
+            }
+            return $a;
+        }
+    
+        return $s;
+    }
+
+    protected function curl ($sUrl, $aData = array(), $sMethod = 'get', &$sHttpCode = null, $aBasicAuth = array()) 
+    {
+        $rConnect = curl_init();
+
+        curl_setopt($rConnect, CURLOPT_USERAGENT, 'UNA ' . BX_DOL_VERSION);
+        curl_setopt($rConnect, CURLOPT_URL, $sUrl);
+        curl_setopt($rConnect, CURLOPT_HEADER, NULL === $sHttpCode ? false : true);
+        curl_setopt($rConnect, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($rConnect, CURLOPT_VERBOSE, 0);
+        curl_setopt($rConnect, CURLOPT_HEADER, 0);
+
+        curl_setopt($rConnect, CURLOPT_CONNECTTIMEOUT, $this->_iTimeout);
+        curl_setopt($rConnect, CURLOPT_TIMEOUT, $this->_iTimeout);
+
+        if (!ini_get('open_basedir'))
+            curl_setopt($rConnect, CURLOPT_FOLLOWLOCATION, 1);
+
+        if ($aBasicAuth)
+            curl_setopt($rConnect, CURLOPT_USERPWD, $aBasicAuth['user'] . ':' . $aBasicAuth['password']);
+
+        if ('get' != $sMethod)
+            curl_setopt($rConnect, CURLOPT_CUSTOMREQUEST, strtoupper($sMethod));
+
+        if ('post' == $sMethod)
+            curl_setopt($rConnect, CURLOPT_POST, true);
+
+        if ($aData) {
+            curl_setopt($rConnect, CURLOPT_POSTFIELDS, json_encode($aData));
+            curl_setopt($rConnect, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
+        }
+
+        $sResult = curl_exec($rConnect);
+
+        if (NULL !== $sHttpCode)
+            $sHttpCode = curl_getinfo($rConnect, CURLINFO_HTTP_CODE);
+
+        return $sResult;
+    }
+
+    protected function log ($mixed, $sProvider = '', $iLevel = BX_LOG_ERR)
+    {
+        if (!defined('BX_ELASTICSEARCH_LOG') || !constant('BX_ELASTICSEARCH_LOG'))
+            return;
+        
+		if (is_array($mixed))
+			$mixed = var_export($mixed, true);	
+		else if (is_object($mixed))
+			$mixed = json_encode($mixed);
+		
+		bx_log('bx_elasticsearch', $mixed, $iLevel);
+    }
+}
+
+/** @} */

+ 71 - 0
modules/boonex/elasticsearch/updates/15.0.1_15.0.2/source/install/config.php

@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    ElasticSearch ElasticSearch
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_elasticsearch',
+    'title' => 'ElasticSearch',
+    'note' => 'Allows to translate content of the site on different languages',
+    'version' => '15.0.2',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/elasticsearch/',
+    'home_uri' => 'elasticsearch',
+
+    'db_prefix' => 'bx_els_',
+    'class_prefix' => 'BxEls',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'ElasticSearch',
+
+    /**
+     * 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,
+    	'process_content_alerts' => 1,    //Note. It's essential to make this action AFTER 'execute_sql' one.
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/events/updates/15.0.1_15.0.2/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Events',
+    'version_from' => '15.0.1',
+    'version_to' => '15.0.2',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '15.0.0-B2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/events/updates/update_15.0.1_15.0.2/',
+    'home_uri' => 'events_update_1501_1502',
+
+    'module_dir' => 'boonex/events/',
+    'module_uri' => 'events',
+
+    'db_prefix' => 'bx_events_',
+    'class_prefix' => 'BxEvents',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Events',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/events/updates/15.0.1_15.0.2/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxEventsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 4 - 0
modules/boonex/events/updates/15.0.1_15.0.2/install/langs/en.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_upd name="_bx_events_form_profile_input_event_name_err"><![CDATA[Name is essential min 3 max 160.]]></string_upd>
+</resources>

+ 5 - 0
modules/boonex/events/updates/15.0.1_15.0.2/install/sql/install.sql

@@ -0,0 +1,5 @@
+SET @sName = 'bx_events';
+
+
+-- FORMS
+UPDATE `sys_form_inputs` SET `checker_func`='Length', `checker_params`='a:2:{s:3:"min";i:3;s:3:"max";i:160;}' WHERE `object`='bx_event' AND `name`='event_name';

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor