瀏覽代碼

Modules updates.

AntonLV 10 年之前
父節點
當前提交
dad2c87988
共有 100 個文件被更改,包括 7764 次插入 和 0 次删除
  1. 56 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/install/config.php
  2. 13 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/install/installer.php
  3. 9 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/install/sql/enable.sql
  4. 6 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/install/sql/install.sql
  5. 83 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntConfig.php
  6. 25 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntDb.php
  7. 168 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntGridAdministration.php
  8. 59 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntMenuManageTools.php
  9. 69 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntModule.php
  10. 70 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntSearchResult.php
  11. 27 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntStudioPage.php
  12. 79 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntTemplate.php
  13. 72 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/config.php
  14. 20 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/installer.php
  15. 74 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/sql/enable.sql
  16. 14 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/sql/install.sql
  17. 93 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/js/manage_tools.js
  18. 18 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/request.php
  19. 14 0
      modules/boonex/accounts/updates/9.0.1_9.0.2/source/template/images/icons/std-icon.svg
  20. 56 0
      modules/boonex/acl/updates/9.0.1_9.0.2/install/config.php
  21. 13 0
      modules/boonex/acl/updates/9.0.1_9.0.2/install/installer.php
  22. 4 0
      modules/boonex/acl/updates/9.0.1_9.0.2/install/langs/en.xml
  23. 5 0
      modules/boonex/acl/updates/9.0.1_9.0.2/install/sql/enable.sql
  24. 6 0
      modules/boonex/acl/updates/9.0.1_9.0.2/install/sql/install.sql
  25. 124 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclConfig.php
  26. 181 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclDb.php
  27. 248 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclGridAdministration.php
  28. 79 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclGridView.php
  29. 176 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclModule.php
  30. 43 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclResponse.php
  31. 49 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclStudioPage.php
  32. 42 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclTemplate.php
  33. 70 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/install/config.php
  34. 37 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/install/installer.php
  35. 73 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/install/langs/en.xml
  36. 38 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/install/sql/enable.sql
  37. 107 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/install/sql/install.sql
  38. 43 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/js/administration.js
  39. 36 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/js/main.js
  40. 18 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/request.php
  41. 125 0
      modules/boonex/acl/updates/9.0.1_9.0.2/source/template/images/icons/std-icon.svg
  42. 56 0
      modules/boonex/albums/updates/9.0.1_9.0.2/install/config.php
  43. 26 0
      modules/boonex/albums/updates/9.0.1_9.0.2/install/installer.php
  44. 7 0
      modules/boonex/albums/updates/9.0.1_9.0.2/install/langs/en.xml
  45. 9 0
      modules/boonex/albums/updates/9.0.1_9.0.2/install/sql/enable.sql
  46. 36 0
      modules/boonex/albums/updates/9.0.1_9.0.2/install/sql/install.sql
  47. 29 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsAlertsResponse.php
  48. 24 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsCmtsSearchResult.php
  49. 29 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsCmtsSearchResultMedia.php
  50. 156 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsConfig.php
  51. 78 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsDb.php
  52. 93 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsFormEntry.php
  53. 23 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsFormsEntryHelper.php
  54. 21 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsGridAdministration.php
  55. 21 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsGridCommon.php
  56. 24 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsMenu.php
  57. 25 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsMenuManageTools.php
  58. 32 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsMenuView.php
  59. 226 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsModule.php
  60. 24 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageAuthor.php
  61. 24 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageBrowse.php
  62. 24 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageBrowseMedia.php
  63. 36 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageEntry.php
  64. 137 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageMedia.php
  65. 145 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsSearchResult.php
  66. 190 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsSearchResultMedia.php
  67. 25 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsSearchResultMediaCamera.php
  68. 395 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsTemplate.php
  69. 33 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsUploaderCrop.php
  70. 33 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsUploaderHTML5.php
  71. 33 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsUploaderSimple.php
  72. 130 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/install/config.php
  73. 20 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/install/installer.php
  74. 192 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/install/langs/en.xml
  75. 395 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/install/sql/enable.sql
  76. 379 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/install/sql/install.sql
  77. 41 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/install/sql/uninstall.sql
  78. 36 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/js/main.js
  79. 51 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/js/manage_tools.js
  80. 239 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/js/media_view.js
  81. 18 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/request.php
  82. 49 0
      modules/boonex/albums/updates/9.0.1_9.0.2/source/template/images/icons/std-icon.svg
  83. 56 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/install/config.php
  84. 13 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/install/installer.php
  85. 23 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/install/langs/en.xml
  86. 6 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/install/sql/enable.sql
  87. 3 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/install/sql/install.sql
  88. 54 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamAkismet.php
  89. 42 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamAlertsResponse.php
  90. 71 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamConfig.php
  91. 20 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamCronPruning.php
  92. 194 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamDNSBlacklists.php
  93. 181 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamDNSURIBlacklists.php
  94. 20 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamDb.php
  95. 44 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamGridBlockLog.php
  96. 316 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamGridDNSBL.php
  97. 151 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamGridIpTable.php
  98. 157 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamIP.php
  99. 283 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamModule.php
  100. 24 0
      modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamPage.php

+ 56 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/install/config.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Accounts Manager',
+    'version_from' => '9.0.1',
+	'version_to' => '9.0.2',
+    'vendor' => 'BoonEx',
+
+	'compatible_with' => array(
+        '9.0.0-B3'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/accounts/updates/update_9.0.1_9.0.2/',
+	'home_uri' => 'accounts_update_901_902',
+
+	'module_dir' => 'boonex/accounts/',
+	'module_uri' => 'accounts',
+
+    'db_prefix' => 'bx_accnt_',
+    'class_prefix' => 'BxAccnt',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+		'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 0,
+		'clear_db_cache' => 1,
+    ),
+
+	/**
+     * Category for language keys.
+     */
+    'language_category' => 'Accounts',
+
+	/**
+     * Files Section
+     */
+    'delete_files' => array(
+		'template/images/icons/std-mi.png',
+		'template/images/icons/std-pi.png',
+		'template/images/icons/std-si.png',
+		'template/images/icons/std-wi.png'
+	),
+);

+ 13 - 0
modules/boonex/accounts/updates/9.0.1_9.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 BxAccntUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+	{
+        parent::__construct($aConfig);
+    }
+}

+ 9 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/install/sql/enable.sql

@@ -0,0 +1,9 @@
+SET @sName = 'bx_accounts';
+
+
+-- MENUS
+UPDATE `sys_menu_items` SET `onclick`='javascript:{js_object}.onClickResendCemail({content_id}, this);' WHERE `set_name`='bx_accounts_menu_manage_tools' AND `name`='resend-cemail';
+UPDATE `sys_menu_items` SET `onclick`='javascript:{js_object}.onClickDelete({content_id}, this);' WHERE `set_name`='bx_accounts_menu_manage_tools' AND `name`='delete';
+UPDATE `sys_menu_items` SET `onclick`='javascript:{js_object}.onClickDeleteWithContent({content_id}, this);' WHERE `set_name`='bx_accounts_menu_manage_tools' AND `name`='delete-with-content';
+UPDATE `sys_menu_items` SET `onclick`='javascript:{js_object}.onClickMakeOperator({content_id}, this);' WHERE `set_name`='bx_accounts_menu_manage_tools' AND `name`='make-operator';
+UPDATE `sys_menu_items` SET `onclick`='javascript:{js_object}.onClickUnmakeOperator({content_id}, this);' WHERE `set_name`='bx_accounts_menu_manage_tools' AND `name`='unmake-operator';

+ 6 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/install/sql/install.sql

@@ -0,0 +1,6 @@
+SET @sName = 'bx_accounts';
+
+
+-- STUDIO PAGE & WIDGET
+UPDATE `sys_std_pages` SET `icon`='bx_accounts@modules/boonex/accounts/|std-icon.svg' WHERE `name`=@sName;
+UPDATE `sys_std_widgets` SET `icon`='bx_accounts@modules/boonex/accounts/|std-icon.svg' WHERE `module`=@sName AND `caption`='_bx_accnt';

+ 83 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntConfig.php

@@ -0,0 +1,83 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntConfig extends BxBaseModGeneralConfig
+{
+    protected $_oDb;
+    protected $_aHtmlIds;
+
+    /**
+     * Constructor
+     */
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+        	// page URIs
+        	'URL_MANAGE_ADMINISTRATION' => 'page.php?i=accounts-administration',
+
+	        // objects
+	        'OBJECT_MENU_MANAGE_TOOLS' => 'bx_accounts_menu_manage_tools', //manage menu in content administration tools
+        	'OBJECT_GRID_ADMINISTRATION' => 'bx_accounts_administration',
+        	'OBJECT_GRID_MODERATION' => 'bx_accounts_moderation',
+
+        	// some language keys
+            'T' => array (
+        		'grid_action_err_delete' => '_bx_accnt_grid_action_err_delete',
+        		'grid_action_err_perform' => '_bx_accnt_grid_action_err_perform',
+            	'filter_item_active' => '_bx_accnt_grid_filter_item_title_adm_active',
+            	'filter_item_pending' => '_bx_accnt_grid_filter_item_title_adm_pending',
+            	'filter_item_suspended' => '_bx_accnt_grid_filter_item_title_adm_suspended',
+            	'filter_item_select_one_filter1' => '_bx_accnt_grid_filter_item_title_adm_select_one_filter1',
+        	)
+        );
+
+        $this->_aObjects = array(
+        	'alert' => $this->_sName,
+        );
+
+        $this->_aJsClasses = array(
+        	'manage_tools' => 'BxAccntManageTools'
+        );
+
+        $this->_aJsObjects = array(
+        	'manage_tools' => 'oBxAccntManageTools'
+        );
+
+        $this->_aGridObjects = array(
+        	'moderation' => $this->CNF['OBJECT_GRID_MODERATION'],
+        	'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION'],
+        	
+        );
+
+        $sHtmlPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = array(
+        	'profile' => $sHtmlPrefix . '-profile-',
+        	'profile_more_popup' => $sHtmlPrefix . '-profile-more-popup-',
+        );
+    }
+
+    public function init(&$oDb)
+    {
+        $this->_oDb = &$oDb;
+    }
+
+	public function getHtmlIds($sKey = '')
+    {
+        if(empty($sKey))
+            return $this->_aHtmlIds;
+
+        return isset($this->_aHtmlIds[$sKey]) ? $this->_aHtmlIds[$sKey] : '';
+    }
+}
+
+/** @} */

+ 25 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntDb.php

@@ -0,0 +1,25 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntDb extends BxBaseModGeneralDb
+{
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+
+    public function updateAccount($aSet, $aWhere)
+    {
+    	return (int)$this->query("UPDATE `sys_accounts` SET " . $this->arrayToSQL($aSet) . " WHERE " . $this->arrayToSQL($aWhere, ' AND ')) > 0;
+    }
+}
+
+/** @} */

+ 168 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntGridAdministration.php

@@ -0,0 +1,168 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+
+class BxAccntGridAdministration extends BxBaseModProfileGridAdministration
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_accounts';
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    public function performActionActivate()
+    {
+    	$this->_performActionEnable(true);
+    }
+
+	public function performActionSuspend()
+    {
+    	$this->_performActionEnable(false);
+    }
+
+    public function performActionResendCemail()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            echoJson(array());
+            return;
+        }
+
+        $oAccount = BxDolAccount::getInstance();
+
+        $iAffected = 0;
+        $aIdsAffected = array();
+        foreach($aIds as $iId)
+			if($oAccount->sendConfirmationEmail($iId)) {
+				$aIdsAffected[] = $iId;
+        		$iAffected++;
+			}
+
+		echoJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t($CNF['T']['grid_action_err_perform'])));
+    }
+
+	public function performActionMakeOperator()
+    {
+    	$this->_performActionChangeRole(3);
+    }
+
+	public function performActionUnmakeOperator()
+    {
+    	$this->_performActionChangeRole(1);
+    }
+
+    public function resetQueryParams()
+    {
+        $aKeys = array('order_field', 'order_dir', $this->_aOptions['paginate_get_start'], $this->_aOptions['paginate_get_per_page']);
+        foreach ($aKeys as $sKey) {
+            unset($_GET[$sKey]);
+            unset($_POST[$sKey]);
+        }
+    }
+
+	protected function _performActionChangeRole($iRole)
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            echoJson(array());
+            return;
+        }
+
+        $iAffected = 0;
+        $aIdsAffected = array();
+        foreach($aIds as $iId)
+			if($this->_oModule->_oDb->updateAccount(array('role' => $iRole), array('id' => $iId))) {
+				$aIdsAffected[] = $iId;
+        		$iAffected++;
+			}
+
+		echoJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t($CNF['T']['grid_action_err_perform'])));
+    }
+
+	protected function _performActionEnable($isChecked)
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            echoJson(array());
+            return;
+        }
+
+        $iAffected = 0;
+        $aIdsAffected = array();
+        foreach($aIds as $iId)
+        	if($this->_enable($iId, $isChecked)) {
+        		$aIdsAffected[] = $iId;
+        		$iAffected++;
+        	}
+
+        echoJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t($CNF['T']['grid_action_err_perform'])));
+    }
+
+    protected function _enable($mixedId, $isChecked)
+    {
+    	$oProfile = BxDolProfile::getInstanceAccountProfile($mixedId);
+    	if(!$oProfile)
+    	    return false;
+
+        $iAction = BX_PROFILE_ACTION_MANUAL;
+        $sMethod = $isChecked ? 'activate' : 'suspend';
+        if(!$oProfile->$sMethod($iAction))
+            return false;
+
+        $aProfiles = $oProfile->getAccountObject()->getProfiles();
+        foreach($aProfiles as $aProfile)
+            BxDolProfile::getInstance($aProfile['id'])->$sMethod($iAction);
+
+    	return true;
+    }
+
+    //--- Layout methods ---//
+    protected function _getCellEmailConfirmed($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$mixedValue = (int)$mixedValue == 1 ? '_Yes' : '_No';
+        return parent::_getCellDefault(_t($mixedValue), $sKey, $aField, $aRow);
+    }
+
+	protected function _getCellProfiles($mixedValue, $sKey, $aField, $aRow)
+    {
+        $s = $this->_oModule->_oTemplate->getProfilesByAccount($aRow);
+
+        return parent::_getCellDefault ($s, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellLogged($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _isCheckboxDisabled($aRow)
+    {
+        return false;
+    }
+
+	protected function _getContentInfo($iId)
+    {
+    	return BxDolAccountQuery::getInstance()->getInfoById($iId);
+    }
+
+	protected function _doDelete($iId, $aParams = array())
+    {
+    	return BxDolAccount::getInstance($iId)->delete(isset($aParams['with_content']) && $aParams['with_content'] === true);
+    }
+}
+
+/** @} */

+ 59 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntMenuManageTools.php

@@ -0,0 +1,59 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * 'Manage tools' menu.
+ */
+class BxAccntMenuManageTools extends BxBaseModGeneralMenuManageTools
+{
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_accounts';
+        parent::__construct($aObject, $oTemplate);
+    }
+
+    /**
+     * Check if menu items is visible.
+     * @param $a menu item array
+     * @return boolean
+     */
+    protected function _isVisible ($a)
+    {
+        if(!parent::_isVisible($a))
+            return false;
+
+        $aDataEntry = BxDolAccountQuery::getInstance()->getInfoById($this->_iContentId);
+        if(empty($aDataEntry) || !is_array($aDataEntry))
+        	return false;
+
+        $sCheckFuncName = '';
+        switch ($a['name']) {
+            case 'delete':
+            case 'delete-with-content':
+                $sCheckFuncName = 'checkAllowedDelete';
+                break;
+            case 'make-operator':
+            	$sCheckFuncName = 'checkAllowedMakeOperator';
+            	break;
+            case 'unmake-operator':
+            	$sCheckFuncName = 'checkAllowedUnmakeOperator';
+            	break;
+        }
+
+        if(!$sCheckFuncName || !method_exists($this->_oModule, $sCheckFuncName))
+            return true;
+
+        return $this->_oModule->{$sCheckFuncName}($aDataEntry) === CHECK_ACTION_RESULT_ALLOWED;
+    }
+}
+
+/** @} */

+ 69 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntModule.php

@@ -0,0 +1,69 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntModule extends BxBaseModGeneralModule
+{
+    /**
+     * Constructor
+     */
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_oConfig->init($this->_oDb);
+    }
+
+	public function serviceGetMenuAddonManageTools()
+	{
+		bx_import('SearchResult', $this->_aModule);
+        $sClass = $this->_aModule['class_prefix'] . 'SearchResult';
+        $o = new $sClass();
+        $o->fillFilters(array(
+			'unconfirmed' => 1
+        ));
+        $o->unsetPaginate();
+
+        return $o->getNum();
+	}
+
+    public function checkAllowedDelete(&$aDataEntry, $isPerformAction = false)
+    {
+    	if(isAdmin() && (int)$aDataEntry['id'] == getLoggedId())
+    		return _t('_sys_txt_access_denied');
+
+        // check ACL
+        $aCheck = checkActionModule($this->_iProfileId, 'delete account', 'system', $isPerformAction);
+        if($aCheck[CHECK_ACTION_RESULT] != CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkAllowedMakeOperator(&$aDataEntry, $isPerformAction = false)
+    {
+    	$bAdmin = isAdmin();
+    	if(!$bAdmin || (int)$aDataEntry['id'] == getLoggedId() || (int)$aDataEntry['role'] == 3)
+    		return _t('_sys_txt_access_denied');
+
+    	return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+	public function checkAllowedUnmakeOperator(&$aDataEntry, $isPerformAction = false)
+    {
+    	$bAdmin = isAdmin();
+    	if(!$bAdmin || (int)$aDataEntry['id'] == getLoggedId() || (int)$aDataEntry['role'] != 3)
+    		return _t('_sys_txt_access_denied');
+
+    	return CHECK_ACTION_RESULT_ALLOWED;
+    }
+}
+
+/** @} */

+ 70 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntSearchResult.php

@@ -0,0 +1,70 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntSearchResult extends BxBaseModGeneralSearchResult
+{
+    function __construct($sMode = '', $aParams = false)
+    {
+        parent::__construct($sMode, $aParams);
+
+        $this->aCurrent =  array(
+            'name' => 'bx_accounts',
+            'module_name' => 'bx_accounts',
+            'object_metatags' => '',
+            'title' => _t('_bx_accnt_page_title_browse'),
+            'table' => 'sys_accounts',
+            'tableSearch' => 'sys_accounts',
+            'ownFields' => array(),
+            'searchFields' => array('name', 'email'),
+            'restriction' => array(
+                'confirmed' => array('value' => '', 'field' => 'email_confirmed', 'operator' => '='),
+        		'unconfirmed' => array('value' => '', 'field' => 'email_confirmed', 'operator' => '<>'),
+            ),
+            'join' => array (),
+            'paginate' => array('perPage' => 20, 'start' => 0),
+            'sorting' => 'none',
+            'rss' => array(
+                'title' => '',
+                'link' => '',
+                'image' => '',
+                'profile' => 0,
+                'fields' => array (
+                    'Guid' => 'link',
+                    'Link' => 'link',
+                    'Title' => 'fullname',
+                    'DateTimeUTS' => 'added',
+                    'Desc' => 'fullname',
+                    'Picture' => 'picture',
+                ),
+            ),
+            'ident' => 'id'
+        );
+
+        $this->sFilterName = 'bx_accounts_filter';
+        $this->oModule = $this->getMain();
+
+        switch ($sMode) {
+            case '': // search results
+                $this->sBrowseUrl = BX_DOL_SEARCH_KEYWORD_PAGE;
+                $this->aCurrent['title'] = _t('_bx_accnt');
+                $this->aCurrent['paginate']['perPage'] = 5;
+                unset($this->aCurrent['rss']);
+                break;
+
+            default:
+                $this->isError = true;
+        }
+
+        parent::__construct();
+    }
+}
+
+/** @} */

+ 27 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntStudioPage.php

@@ -0,0 +1,27 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntStudioPage extends BxTemplStudioModule
+{
+    function __construct($sModule = "", $sPage = "")
+    {
+        parent::__construct($sModule, $sPage);
+
+        $oPermalink = BxDolPermalinks::getInstance();
+
+        $this->aMenuItems = array(
+            array('name' => 'settings', 'icon' => 'cogs', 'title' => '_adm_lmi_cpt_settings'),
+            array('name' => 'manage', 'icon' => 'wrench', 'title' => '_bx_accnt_menu_item_title_manage', 'link' => BX_DOL_URL_ROOT . $oPermalink->permalink('page.php?i=accounts-administration')),
+        );
+    }
+}
+
+/** @} */

+ 79 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/classes/BxAccntTemplate.php

@@ -0,0 +1,79 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntTemplate extends BxBaseModGeneralTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+    	$this->MODULE = 'bx_accounts';
+        parent::__construct($oConfig, $oDb);
+    }
+
+    public function getProfilesByAccount($aContentInfo, $iMaxVisible = 2)
+    {
+        $aProfiles = BxDolAccount::getInstance($aContentInfo['id'])->getProfiles();
+        $iProfiles = count($aProfiles);
+
+        $aTmplVars = array (
+        	'class_cnt' => '',
+            'bx_repeat:profiles' => array(),
+            'bx_if:profiles_more' => array(
+                'condition' => $iProfiles > $iMaxVisible,
+                'content' => array(
+        			'html_id' => $this->_oConfig->getHtmlIds('profile_more_popup') . $aContentInfo['id'],
+                    'more' => _t('_bx_accnt_txt_more', $iProfiles - $iMaxVisible),
+        			'more_attr' => bx_html_attribute(_t('_bx_accnt_txt_see_more')),
+                    'popup' => '',
+                ),
+            ),
+        );
+
+        $aTmplVarsPopup = array (
+        	'class_cnt' => ' bx-def-padding',
+            'bx_repeat:profiles' => array(),
+            'bx_if:profiles_more' => array(
+                'condition' => false,
+                'content' => array(),
+            ),
+        );
+
+        $i = 0;
+        foreach ($aProfiles as $iProfileId => $aProfile) {
+            $oProfile = BxDolProfile::getInstance($iProfileId);
+            if(!$oProfile)
+				continue;
+
+			$sName = $oProfile->getDisplayName();
+            $aTmplVarsProfile = array (
+            	'html_id' => $this->_oConfig->getHtmlIds('profile') . $aProfile['id'],
+                'id' => $oProfile->id(),
+                'url' => $oProfile->getUrl(),
+                'name' => $sName,
+                'name_attr' =>  bx_html_attribute($sName)
+            );
+
+            if($i < $iMaxVisible)
+				$aTmplVars['bx_repeat:profiles'][] = $aTmplVarsProfile;
+            if($i >= $iMaxVisible)
+                $aTmplVarsPopup['bx_repeat:profiles'][] = $aTmplVarsProfile;
+
+            ++$i;
+        }
+
+        if($aTmplVarsPopup['bx_repeat:profiles']) {
+            $aTmplVars['bx_if:profiles_more']['content']['popup'] = BxTemplFunctions::getInstance()->transBox('', $this->parseHtmlByName('profiles.html', $aTmplVarsPopup));
+        }
+
+        return $this->parseHtmlByName('profiles.html', $aTmplVars);
+    }
+}
+
+/** @} */

+ 72 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/config.php

@@ -0,0 +1,72 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_accounts',
+    'title' => 'Accounts Manager',
+    'note' => 'Accounts Manager module.',
+    'version' => '9.0.2',
+    'vendor' => 'BoonEx',
+	'help_url' => 'http://feed.boonex.com/?section={module_name}',
+    
+
+    'compatible_with' => array(
+        '9.0.0-B3'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/accounts/',
+    'home_uri' => 'accounts',
+
+    'db_prefix' => 'bx_accnt_',
+    'class_prefix' => 'BxAccnt',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Accounts',
+
+    /**
+     * 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(),
+
+);
+
+/** @} */

+ 20 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/installer.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntInstaller extends BxDolStudioInstaller
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}
+
+/** @} */

+ 74 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/sql/enable.sql

@@ -0,0 +1,74 @@
+SET @sName = 'bx_accounts';
+
+
+-- PAGE: module 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_accounts_administration', '_bx_accnt_page_title_sys_manage_administration', '_bx_accnt_page_title_manage', @sName, 5, 192, 1, 'accounts-administration', 'page.php?i=accounts-administration', '', '', '', 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_accounts_administration', 1, @sName, '_bx_accnt_page_block_title_system_manage_administration', '_bx_accnt_page_block_title_manage', 11, 192, 'service', 'a:3:{s:6:\"module\";s:11:\"bx_accounts\";s:6:\"method\";s:12:\"manage_tools\";s:6:\"params\";a:1:{i:0;s:14:\"administration\";}}', 0, 1, 0);
+
+
+-- 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_accounts_menu_manage_tools', '_bx_accnt_menu_title_manage_tools', 'bx_accounts_menu_manage_tools', @sName, 6, 0, 1, 'BxAccntMenuManageTools', 'modules/boonex/accounts/classes/BxAccntMenuManageTools.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_accounts_menu_manage_tools', @sName, '_bx_accnt_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_accounts_menu_manage_tools', @sName, 'resend-cemail', '_bx_accnt_menu_item_title_system_resend_cemail', '_bx_accnt_menu_item_title_resend_cemail', 'javascript:void(0)', 'javascript:{js_object}.onClickResendCemail({content_id}, this);', '_self', 'envelope-o', '', 192, 1, 0, 1),
+('bx_accounts_menu_manage_tools', @sName, 'delete', '_bx_accnt_menu_item_title_system_delete', '_bx_accnt_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id}, this);', '_self', 'trash-o', '', 192, 1, 0, 2),
+('bx_accounts_menu_manage_tools', @sName, 'delete-with-content', '_bx_accnt_menu_item_title_system_delete_with_content', '_bx_accnt_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id}, this);', '_self', 'trash-o', '', 192, 1, 0, 3),
+('bx_accounts_menu_manage_tools', @sName, 'make-operator', '_bx_accnt_menu_item_title_system_make_operator', '_bx_accnt_menu_item_title_make_operator', 'javascript:void(0)', 'javascript:{js_object}.onClickMakeOperator({content_id}, this);', '_self', 'wrench', '', 192, 1, 0, 4),
+('bx_accounts_menu_manage_tools', @sName, 'unmake-operator', '_bx_accnt_menu_item_title_system_unmake_operator', '_bx_accnt_menu_item_title_unmake_operator', 'javascript:void(0)', 'javascript:{js_object}.onClickUnmakeOperator({content_id}, this);', '_self', 'wrench', '', 192, 1, 0, 5);
+
+-- 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', @sName, 'accounts-administration', '_bx_accnt_menu_item_title_system_admt_accounts', '_bx_accnt_menu_item_title_admt_accounts', 'page.php?i=accounts-administration', '', '_self', '', 'a:2:{s:6:"module";s:11:"bx_accounts";s:6:"method";s:27:"get_menu_addon_manage_tools";}', '', 192, 1, 0, @iManageMenuOrder + 1);
+
+
+-- ACL
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+(@sName, 'edit any entry', NULL, '_bx_accnt_acl_action_edit_any_account', '', 1, 3);
+SET @iIdActionProfileEditAny = 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
+
+-- edit any entry 
+(@iModerator, @iIdActionProfileEditAny),
+(@iAdministrator, @iIdActionProfileEditAny);
+
+
+-- 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_accounts_administration', 'Sql', 'SELECT `ta`.*, `tp`.`status` AS `status` FROM `sys_accounts` AS `ta` LEFT JOIN `sys_profiles` AS `tp` ON `ta`.`id`=`tp`.`account_id` AND `tp`.`type`=''system'' WHERE 1 ', 'sys_accounts', 'id', 'logged', 'status', '', 20, NULL, 'start', '', 'name,email', '', 'like', '', '', 192, 'BxAccntGridAdministration', 'modules/boonex/accounts/classes/BxAccntGridAdministration.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_accounts_administration', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_accounts_administration', 'switcher', '_bx_accnt_grid_column_title_adm_active', '6%', 0, '', '', 2),
+('bx_accounts_administration', 'name', '_bx_accnt_grid_column_title_adm_name', '22%', 0, '22', '', 3),
+('bx_accounts_administration', 'email', '_bx_accnt_grid_column_title_adm_email', '22%', 0, '22', '', 4),
+('bx_accounts_administration', 'email_confirmed', '_bx_accnt_grid_column_title_adm_email_confirmed', '5%', 0, '', '', 5),
+('bx_accounts_administration', 'profiles', '_bx_accnt_grid_column_title_adm_profiles', '22%', 0, '22', '', 5),
+('bx_accounts_administration', 'logged', '_bx_accnt_grid_column_title_adm_logged', '15%', 0, '15', '', 7),
+('bx_accounts_administration', 'actions', '', '6%', 0, '', '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_accounts_administration', 'bulk', 'activate', '_bx_accnt_grid_action_title_adm_activate', '', 0, 0, 1),
+('bx_accounts_administration', 'bulk', 'suspend', '_bx_accnt_grid_action_title_adm_suspend', '', 0, 0, 2),
+('bx_accounts_administration', 'bulk', 'resend_cemail', '_bx_accnt_grid_action_title_adm_resend_cemail', '', 0, 0, 3),
+('bx_accounts_administration', 'bulk', 'delete', '_bx_accnt_grid_action_title_adm_delete', '', 0, 1, 4),
+('bx_accounts_administration', 'bulk', 'delete_with_content', '_bx_accnt_grid_action_title_adm_delete_with_content', '', 0, 1, 5),
+('bx_accounts_administration', 'single', 'settings', '_bx_accnt_grid_action_title_adm_more_actions', 'cog', 1, 0, 1);

+ 14 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/install/sql/install.sql

@@ -0,0 +1,14 @@
+SET @sName = 'bx_accounts';
+
+
+-- STUDIO PAGE & WIDGET
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_accnt', '_bx_accnt', 'bx_accounts@modules/boonex/accounts/|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`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, @sName, '{url_studio}module.php?name=bx_accounts', '', 'bx_accounts@modules/boonex/accounts/|std-icon.svg', '_bx_accnt', '', '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));

+ 93 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/js/manage_tools.js

@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxAccntManageTools(oOptions) {
+	this._sActionsUrl = oOptions.sActionUrl;
+	this._sObjNameGrid = oOptions.sObjNameGrid;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxAccntManageTools' : oOptions.sObjName;
+
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._sParamsDivider = oOptions.sParamsDivider == undefined ? '#-#' : oOptions.sParamsDivider;
+
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxAccntManageTools.prototype.onChangeFilter = function(oFilter) {
+	var oFilter1 = $('#bx-grid-filter1-' + this._sObjNameGrid);
+	var sValueFilter1 = oFilter1.length > 0 ? oFilter1.val() : '';
+
+	var oSearch = $('#bx-grid-search-' + this._sObjNameGrid);
+	var sValueSearch = oSearch.length > 0 ? oSearch.val() : '';
+	if(sValueSearch == _t('_sys_grid_search'))
+		sValueSearch = '';
+
+	glGrids[this._sObjNameGrid].setFilter(sValueFilter1 + this._sParamsDivider + sValueSearch, true);
+};
+
+BxAccntManageTools.prototype.onClickSettings = function(sMenuObject, oButton) {
+	if($(oButton).hasClass('bx-btn-disabled'))
+		return false;
+
+	bx_menu_popup(sMenuObject, oButton, {}, {
+		content_id: $(oButton).attr('bx_grid_action_data')
+	});
+};
+
+BxAccntManageTools.prototype.onClickResendCemail = function(iContentId, oLink) {
+	$('.bx-popup-applied:visible').dolPopupHide();
+
+	var oGrid = glGrids[this._sObjNameGrid];
+	var oData = oGrid._checkAppend(oLink, oGrid._getActionDataForReload());
+	oGrid.actionWithId(iContentId, 'resend_cemail', oData, '', false, 0);
+};
+
+BxAccntManageTools.prototype.onClickDelete = function(iContentId, oLink) {
+	$('.bx-popup-applied:visible').dolPopupHide();
+
+	var oGrid = glGrids[this._sObjNameGrid];
+	var oData = oGrid._checkAppend(oLink, {});
+	oGrid.actionWithId(iContentId, 'delete', oData, '', false, 1);
+};
+
+BxAccntManageTools.prototype.onClickDeleteWithContent = function(iContentId, oLink) {
+	$('.bx-popup-applied:visible').dolPopupHide();
+
+	var oGrid = glGrids[this._sObjNameGrid];
+	var oData = oGrid._checkAppend(oLink, {});
+	oGrid.actionWithId(iContentId, 'delete_with_content', oData, '', false, 1);
+};
+
+BxAccntManageTools.prototype.onClickMakeOperator = function(iContentId, oLink) {
+	$('.bx-popup-applied:visible').dolPopupHide({
+		onHide: function(oPopup) {
+			$(oPopup).remove();
+		} 
+	});
+
+	var oGrid = glGrids[this._sObjNameGrid];
+	var oData = oGrid._checkAppend(oLink, oGrid._getActionDataForReload());
+    oGrid.actionWithId(iContentId, 'make_operator', oData, '', false, 0);
+};
+
+BxAccntManageTools.prototype.onClickUnmakeOperator = function(iContentId, oLink) {
+	$('.bx-popup-applied:visible').dolPopupHide({
+		onHide: function(oPopup) {
+			$(oPopup).remove();
+		} 
+	});
+
+	var oGrid = glGrids[this._sObjNameGrid];
+	var oData = oGrid._checkAppend(oLink, oGrid._getActionDataForReload());
+	oGrid.actionWithId(iContentId, 'unmake_operator', oData, '', false, 0);
+};
+
+/** @} */

+ 18 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/request.php

@@ -0,0 +1,18 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+bx_import('BxDolLanguages');
+
+BxDolRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

文件差異過大導致無法顯示
+ 14 - 0
modules/boonex/accounts/updates/9.0.1_9.0.2/source/template/images/icons/std-icon.svg


+ 56 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/install/config.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'ACL',
+    'version_from' => '9.0.1',
+	'version_to' => '9.0.2',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '9.0.0-B3'
+    ),
+
+    /**
+     * '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_9.0.1_9.0.2/',
+	'home_uri' => 'acl_update_901_902',
+
+	'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' => 'Acl',
+
+	/**
+     * Files Section
+     */
+    'delete_files' => array(
+		'template/images/icons/std-mi.png',
+		'template/images/icons/std-pi.png',
+		'template/images/icons/std-si.png',
+		'template/images/icons/std-wi.png'
+	),
+);

+ 13 - 0
modules/boonex/acl/updates/9.0.1_9.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);
+    }
+}

+ 4 - 0
modules/boonex/acl/updates/9.0.1_9.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_acl"><![CDATA[Paid Levels]]></string_upd>
+</resources>

+ 5 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/install/sql/enable.sql

@@ -0,0 +1,5 @@
+SET @sName = 'bx_acl';
+
+
+-- SETTINGS
+UPDATE `sys_options_types` SET `icon`='bx_acl@modules/boonex/acl/|std-icon.svg' WHERE `name`=@sName;

+ 6 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/install/sql/install.sql

@@ -0,0 +1,6 @@
+SET @sName = 'bx_acl';
+
+
+-- STUDIO PAGE & WIDGET
+UPDATE `sys_std_pages` SET `icon`='bx_acl@modules/boonex/acl/|std-icon.svg' WHERE `name`=@sName;
+UPDATE `sys_std_widgets` SET `icon`='bx_acl@modules/boonex/acl/|std-icon.svg' WHERE `module`=@sName AND `caption`='_bx_acl';

+ 124 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclConfig.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    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAclConfig extends BxBaseModGeneralConfig
+{
+    protected $_oDb;
+    protected $_aHtmlIds;
+    protected $_aCurrency;
+    protected $_iOwner;
+
+    protected $_iExpireNotificationDays;
+    protected $_bExpireNotifyOnce;
+    protected $_iRemoveExpiredFor;
+
+    /**
+     * Constructor
+     */
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+        	'TABLE_PRICES' => $aModule['db_prefix'] . 'level_prices',
+
+        	'URL_ADMINISTRATION' => 'page.php?i=acl-administration',
+        	'URL_VIEW' => 'page.php?i=acl-view',
+
+        	'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',
+        );
+
+		$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',
+        	'administration' => 'BxAclAdministration',
+        );
+        $this->_aJsObjects = array(
+            'main' => 'oAclMain',
+	        'administration' => 'oAclAdministration',
+        );
+
+        $sHtmlPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = array(
+        	'popup_price' => $sHtmlPrefix . '-popup-price'
+        );
+
+		$oPayments = BxDolPayments::getInstance();
+		$this->_iOwner = (int)$oPayments->getOption('site_admin');
+        $this->_aCurrency = array(
+        	'code' => $oPayments->getOption('default_currency_code'),
+        	'sign' => $oPayments->getOption('default_currency_sign')
+        );
+
+		$this->_bExpireNotifyOnce = true;
+        $this->_iExpireNotificationDays = 1;
+        $this->_iRemoveExpiredFor = 30;
+    }
+
+    public function init(&$oDb)
+    {
+        $this->_oDb = &$oDb;
+
+        $sOptionPrefix = $this->getPrefix('option');
+
+        $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 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()
+    {
+    	return $this->_iOwner;
+    }
+
+    public function getCurrency()
+    {
+    	return $this->_aCurrency;
+    }
+}
+
+/** @} */

+ 181 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclDb.php

@@ -0,0 +1,181 @@
+<?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;
+
+    /*
+     * Constructor.
+     */
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+
+        $this->_oConfig = $oConfig;
+    }
+
+	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`.`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`.`period` AS `period`,
+			`tap`.`period_unit` AS `period_unit`,
+			`tap`.`price` AS `price`,
+			`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_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)
+    {
+    	$sSql = "DELETE FROM `" . $this->_oConfig->CNF['TABLE_PRICES'] . "` WHERE " . $this->arrayToSQL($aWhere, " AND ");
+        return (int)$this->query($sSql) > 0;
+    }
+}
+
+/** @} */

+ 248 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclGridAdministration.php

@@ -0,0 +1,248 @@
+<?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 BxAclGridAdministration extends BxTemplGrid
+{
+	protected $MODULE;
+	protected $_oModule;
+
+	protected $_sParamsDivider = '#-#';
+	protected $_iLevelId = 0;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_acl';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $iLevel = (int)bx_get('level');
+        if($iLevel > 0) {
+            $this->_iLevelId = $iLevel;
+            $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);
+
+        if((int)$this->_iLevelId == 0)
+            $this->_iLevelId = (int)bx_get('level_id');
+
+    	$oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_PRICE'], $CNF['OBJECT_FORM_PRICE_DISPLAY_ADD']);
+    	$oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction . '&level=' . $this->_iLevelId;
+    	$oForm->aInputs['level_id']['value'] = $this->_iLevelId;
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+        	$iLevel = $oForm->getCleanValue('level_id');
+        	$iPeriod = $oForm->getCleanValue('period');
+        	$sPeriodUnit = $oForm->getCleanValue('period_unit');
+
+            $aPrice = $this->_oModule->_oDb->getPrices(array('type' => 'by_level_id_duration', 'level_id' => $iLevel, 'period' => $iPeriod, 'period_unit' => $sPeriodUnit));
+            if(!empty($aPrice) && is_array($aPrice)) {
+                echoJson(array('msg' => _t('_bx_acl_err_price_duplicate')));
+                return;
+            }
+
+            $aLevel = $this->_oModule->_oDb->getLevels(array('type' => 'by_id', 'value' => $iLevel));
+            $sName = uriGenerate(strtolower(_t($aLevel['name'])) . ' ' . $iPeriod . ' ' . $sPeriodUnit, $CNF['TABLE_PRICES'], 'name');
+
+            $iId = (int)$oForm->insert(array('name' => $sName, 'order' => $this->_oModule->_oDb->getPriceOrderMax($this->_iLevelId) + 1));
+            if($iId != 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(array('Purchasable' => 'yes'), array('ID' => $this->_iLevelId));
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
+            } 
+            else
+                $aRes = array('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', array(
+			'form_id' => $oForm->aFormAttrs['id'],
+			'form' => $oForm->getCode(true),
+			'object' => $this->_sObject,
+			'action' => $sAction
+		)));
+
+		echoJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))));
+    }
+
+	public function performActionEdit()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sAction = 'edit';
+
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId) {
+	            echoJson(array());
+	            exit;
+	        }
+
+            $aIds = array($iId);
+        }
+
+        $iId = $aIds[0];
+
+        $aItem = $this->_oModule->_oDb->getPrices(array('type' => 'by_id', 'value' => $iId));
+        if(!is_array($aItem) || empty($aItem)) {
+        	echoJson(array());
+			exit;
+        }
+
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_PRICE'], $CNF['OBJECT_FORM_PRICE_DISPLAY_EDIT']);
+    	$oForm->aFormAttrs['action'] = 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 = array('grid' => $this->getCode(false), 'blink' => $aItem['id']);
+            else
+                $aRes = array('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', array(
+			'form_id' => $oForm->aFormAttrs['id'],
+			'form' => $oForm->getCode(true),
+			'object' => $this->_sObject,
+			'action' => $sAction
+		)));
+
+		$aRes = array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false)));
+
+		echoJson($aRes);
+    }
+
+	public function performActionDelete()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            echoJson(array());
+            return;
+        }
+
+        $iAffected = 0;
+        $aIdsAffected = array();
+        foreach($aIds as $iId)
+			if($this->_oModule->_oDb->deletePrices(array('id' => $iId))) {
+				$aIdsAffected[] = $iId;
+        		$iAffected++;
+			}
+
+		echoJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('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 _getCellPeriodUnit($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$mixedValue = _t('_bx_acl_pre_values_' . $mixedValue);
+    	return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+	protected function _getCellPrice($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$aCurrency = $this->_oModule->_oConfig->getCurrency();
+
+        return parent::_getCellDefault($aCurrency['sign'] . $mixedValue, $sKey, $aField, $aRow);
+    }
+
+	protected function _getActionAdd ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(empty($this->_iLevelId))
+            $isDisabled = true;
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+	protected function _getFilterControls()
+    {
+        parent::_getFilterControls();
+
+        $sContent = '';
+        $oForm = new BxTemplFormView(array());
+
+        $aInputLevels = array(
+            'type' => 'select',
+            'name' => 'level',
+            'attrs' => array(
+                'id' => 'bx-grid-level-' . $this->_sObject,
+                'onChange' => 'javascript:' . $this->_oModule->_oConfig->getJsObject('administration') . '.onChangeLevel()'
+            ),
+            'value' => $this->_iLevelId,
+            'values' => array(0 => _t('_bx_acl_txt_select_level'))
+        );
+
+        $aLevels = $this->_oModule->_oDb->getLevels(array('type' => 'for_selector'));
+        foreach($aLevels as $iId => $sTitle)
+        	$aInputLevels['values'][$iId] = _t($sTitle);
+
+        $sContent .=  $oForm->genRow($aInputLevels);
+
+        $aInputSearch = array(
+            'type' => 'text',
+            'name' => 'keyword',
+            'attrs' => array(
+                'id' => 'bx-grid-search-' . $this->_sObject,
+            ),
+            'tr_attrs' => array(
+                'style' => 'display:none;'
+            )
+        );
+        $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))
+            return array();
+
+        $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString("AND `level_id`=? ", $this->_iLevelId);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);;
+    }
+}
+
+/** @} */

+ 79 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclGridView.php

@@ -0,0 +1,79 @@
+<?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 BxAclGridView extends BxTemplGrid
+{
+	protected $MODULE;
+	protected $_oModule;
+
+	protected $_iOwner;
+	protected $_oPayment;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_acl';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+        parent::__construct ($aOptions, $this->_oModule->_oTemplate);
+
+        $this->_iOwner = $this->_oModule->_oConfig->getOwner();
+        $this->_oPayment = BxDolPayments::getInstance();
+    }
+
+    public function getCode($isDisplayHeader = true)
+    {
+    	return $this->_oPayment->getCartJs() . parent::getCode($isDisplayHeader);
+    }
+
+	protected function _getCellPeriodUnit($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$mixedValue = _t('_bx_acl_pre_values_' . $mixedValue);
+    	return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+	protected function _getCellPrice($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$aCurrency = $this->_oModule->_oConfig->getCurrency();
+
+        return parent::_getCellDefault($aCurrency['sign'] . $mixedValue, $sKey, $aField, $aRow);
+    }
+
+	protected function _getActionBuy ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+    	$aJs = $this->_oPayment->getAddToCartJs($this->_iOwner, $this->MODULE, $aRow['id'], 1, true);
+    	if(!empty($aJs) && is_array($aJs)) {
+    		list($sJsCode, $sJsMethod) = $aJs;
+
+    		$a['attr'] = array(
+    			'onclick' => $sJsMethod
+    		);
+    	}
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionSubscribe ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+    	$aJs = $this->_oPayment->getSubscribeJs($this->_iOwner, '', $this->MODULE, $aRow['id'], 1);
+		if(!empty($aJs) && is_array($aJs)) {
+			list($sJsCode, $sJsMethod) = $aJs;
+
+    		$a['attr'] = array(
+    			'onclick' => $sJsMethod
+    		);
+		}
+
+    	return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+}
+
+/** @} */

+ 176 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclModule.php

@@ -0,0 +1,176 @@
+<?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
+ *
+ * @{
+ */
+
+bx_import('BxDolAcl');
+
+define('BX_ACL_LICENSE_TYPE_SINGLE', 'single'); //--- one-time payment license
+define('BX_ACL_LICENSE_TYPE_RECURRING', 'recurring'); //--- recurring payment license
+
+class BxAclModule extends BxDolModule
+{
+    /**
+     * Constructor
+     */
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_oConfig->init($this->_oDb);
+    }
+
+	/**
+     * ACTION METHODS
+     */
+
+    /**
+     * SERVICE METHODS
+     */
+	public function serviceGetBlockView()
+	{
+		$sGrid = $this->_oConfig->getGridObject('view');
+		$oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return '';
+
+		return array(
+            'content' => $oGrid->getCode()
+        );
+	}
+
+	public function serviceGetMembershipActions($iProfileId)
+	{
+		if($iProfileId != $this->getUserId())
+			return '';
+
+		return $this->_oTemplate->displayMembershipActions($iProfileId);
+	}
+
+	/**
+     * Integration with Payment based modules.  
+     */
+	public function serviceGetPaymentData()
+    {
+        return $this->_aModule;
+    }
+
+    public function serviceGetCartItem($iItemId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        if(!$iItemId)
+			return array();
+
+		$aItem = $this->_oDb->getPrices(array('type' => 'by_id_full', 'value' => $iItemId));
+        if(empty($aItem) || !is_array($aItem))
+			return array();
+
+		return array (
+			'id' => $aItem['id'],
+			'author_id' => $this->_oConfig->getOwner(),
+			'name' => $aItem['name'],
+			'title' => _t('_bx_acl_txt_cart_item_title', _t($aItem['level_name']), $aItem['period'], $aItem['period_unit']),
+			'description' => _t($aItem['level_description']),
+			'url' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($CNF['URL_VIEW']),
+			'price_single' => $aItem['price'],
+			'price_recurring' => $aItem['price'],
+        );
+    }
+
+    public function serviceGetCartItems($iSellerId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$iSellerId = $this->_oConfig->getOwner();
+        $aItems = $this->_oDb->getPrices(array('type' => 'all_full'));
+        $sUrl = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($CNF['URL_VIEW']);
+
+        $aResult = array();
+        foreach($aItems as $aItem)
+            $aResult[] = array(
+				'id' => $aItem['id'],
+				'author_id' => $iSellerId,
+            	'name' => $aItem['name'],
+				'title' => _t('_bx_acl_txt_cart_item_title', _t($aItem['level_name']), $aItem['period'], $aItem['period_unit']),
+				'description' => _t($aItem['level_description']),
+				'url' => $sUrl,
+				'price_single' => $aItem['price'],
+            	'price_recurring' => $aItem['price'],
+           );
+
+        return $aResult;
+    }
+
+    public function serviceRegisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_ACL_LICENSE_TYPE_SINGLE);
+    }
+
+    public function serviceRegisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+		return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_ACL_LICENSE_TYPE_RECURRING);
+    }
+
+    public function serviceUnregisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_ACL_LICENSE_TYPE_SINGLE);
+    }
+
+    public function serviceUnregisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+    	return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_ACL_LICENSE_TYPE_RECURRING); 
+    }
+
+    protected function _serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+    	$aItem = $this->serviceGetCartItem($iItemId);
+        if(empty($aItem) || !is_array($aItem))
+			return array();
+
+		$aItemInfo = $this->_oDb->getPrices(array('type' => 'by_id', 'value' => $iItemId));
+        if(!BxDolAcl::getInstance()->setMembership($iClientId, $aItemInfo['level_id'], array('period' => $aItemInfo['period'], 'period_unit' => $aItemInfo['period_unit']), false, $sLicense))
+            return array();
+
+        return $aItem;
+    }
+
+    protected function _serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+    	$aItemInfo = $this->_oDb->getPrices(array('type' => 'by_id', 'value' => $iItemId));
+    	if(empty($aItemInfo) || !is_array($aItemInfo))
+			return false;
+
+    	return BxDolAcl::getInstance()->unsetMembership($iClientId, $aItemInfo['level_id'], $sLicense);
+    }
+
+    /**
+     * COMMON METHODS
+     */
+	public function getUserId()
+    {
+        return isLogged() ? bx_get_logged_profile_id() : 0;
+    }
+
+    public function getUserInfo($iUserId = 0)
+    {
+        $oProfile = BxDolProfile::getInstance($iUserId);
+        if (!$oProfile)
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+        return array(
+            $oProfile->getDisplayName(),
+            $oProfile->getUrl(),
+            $oProfile->getThumb(),
+            $oProfile->getUnit()
+        );
+    }
+}
+
+/** @} */

+ 43 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclResponse.php

@@ -0,0 +1,43 @@
+<?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)
+    {
+    	if($oAlert->sUnit != 'system' || !in_array($oAlert->sAction, array('page_output_block_acl_level')))
+    		return;
+
+		switch($oAlert->sAction) {
+			case 'page_output_block_acl_level':
+				$oAlert->aExtras['block_code'] .= $this->_oModule->serviceGetMembershipActions((int)$oAlert->aExtras['block_owner']);
+				break;
+		}
+    }
+}
+
+/** @} */

+ 49 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclStudioPage.php

@@ -0,0 +1,49 @@
+<?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 BxAclStudioPage extends BxTemplStudioModule
+{
+	protected $MODULE;
+	protected $_oModule;
+
+    function __construct($sModule = "", $sPage = "")
+    {
+    	$this->MODULE = 'bx_acl';
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+        parent::__construct($sModule, $sPage);
+    }
+
+    protected function getSettings()
+    {
+	    if (!isAdmin()) {
+            $this->_oTemplate->displayAccessDenied();
+            return;
+        }
+
+    	$sContent = '';
+
+		$sGrid = $this->_oModule->_oConfig->getGridObject('administration');
+		$oGrid = BxDolGrid::getObjectInstance($sGrid, BxDolStudioTemplate::getInstance());
+        if(!$oGrid)
+            return $sContent;
+
+		if($this->_oModule->_oConfig->getOwner() == 0)
+			$sContent .= $this->_oModule->_oTemplate->displayEmptyOwner();
+
+		$sContent .= $this->_oModule->_oTemplate->getJsCode('administration', array('sObjNameGrid' => $sGrid));
+		$sContent .= $oGrid->getCode();
+
+		return $sContent;
+    }
+}
+
+/** @} */

+ 42 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/classes/BxAclTemplate.php

@@ -0,0 +1,42 @@
+<?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 BxAclTemplate extends BxBaseModGeneralTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+    }
+
+	public function displayEmptyOwner()
+    {
+    	return MsgBox(_t('_bx_acl_msg_empty_owner'));
+    }
+
+    public function displayMembershipActions($iProfileId)
+    {
+		$this->addCss(array('pm_actions.css'));
+    	return $this->parseHtmlByName('pm_actions.html', array(
+    		'url_upgrade' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($this->_oConfig->CNF['URL_VIEW'])
+    	));
+    }
+
+	public function getJsCode($sType, $aParams = array(), $bWrap = true)
+    {
+        $aParams = array_merge(array(
+            'aHtmlIds' => $this->_oConfig->getHtmlIds()
+        ), $aParams);
+
+        return parent::getJsCode($sType, $aParams, $bWrap);
+    }
+}
+
+/** @} */

+ 70 - 0
modules/boonex/acl/updates/9.0.1_9.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' => '9.0.2',
+    'vendor' => 'BoonEx',
+	'help_url' => 'http://feed.boonex.com/?section={module_name}',
+
+    'compatible_with' => array(
+        '9.0.0-B3'
+    ),
+
+    /**
+     * '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(),
+);
+
+/** @} */

+ 37 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/install/installer.php

@@ -0,0 +1,37 @@
+<?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 BxAclInstaller extends BxDolStudioInstaller
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+
+	function enable($aParams)
+    {
+        $aResult = parent::enable($aParams);
+
+		if($aResult['result'])
+			BxDolPayments::getInstance()->updateDependentModules($this->_aConfig['name'], true);
+
+        return $aResult;
+    }
+
+    function disable($aParams)
+    {
+    	BxDolPayments::getInstance()->updateDependentModules($this->_aConfig['name'], false);
+
+        return parent::disable($aParams);
+    }
+}
+
+/** @} */

+ 73 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/install/langs/en.xml

@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+
+	<string name="_bx_acl"><![CDATA[Paid Levels]]></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>
+
+
+	<!-- 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 ID]]></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_err_period"><![CDATA[You need to enter a number of periods here.]]></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_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_err_price"><![CDATA[You need to enter a price here.]]></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(s)]]></string>
+	<string name="_bx_acl_pre_values_week"><![CDATA[Week(s)]]></string>
+	<string name="_bx_acl_pre_values_month"><![CDATA[Month(s)]]></string>
+	<string name="_bx_acl_pre_values_year"><![CDATA[Year(s)]]></string>
+
+
+	<!-- Grids -->
+	<string name="_bx_acl_grid_column_level_name"><![CDATA[Level]]></string>
+	<string name="_bx_acl_grid_column_period"><![CDATA[Period]]></string>
+	<string name="_bx_acl_grid_column_period_unit"><![CDATA[Period Unit]]></string>
+	<string name="_bx_acl_grid_column_price"><![CDATA[Price]]></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[One-time payment]]></string>
+	<string name="_bx_acl_grid_action_subscribe"><![CDATA[Subscribe on recurring payments]]></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>
+
+
+	<!-- Common Texts -->
+	<string name="_bx_acl_msg_empty_owner"><![CDATA['Site administrator' is empty. You need to mark some profile as 'Site administrator' in Settings of currently active Payment module.]]></string>
+	<string name="_bx_acl_err_price_duplicate"><![CDATA[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_select_level"><![CDATA[Select level...]]></string>
+	<string name="_bx_acl_txt_cart_item_title"><![CDATA[{0} ({1} {2})]]></string>
+	<string name="_bx_acl_txt_upgrade"><![CDATA[Upgrade]]></string>
+	<string name="_bx_acl_txt_manage"><![CDATA[Manage]]></string>
+</resources>

+ 38 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/install/sql/enable.sql

@@ -0,0 +1,38 @@
+SET @sName = 'bx_acl';
+
+
+-- PAGE: view levels
+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_acl_view', '_bx_acl_page_title_sys_view', '_bx_acl_page_title_view', @sName, 5, 2147483647, 1, 'acl-view', 'page.php?i=acl-view', '', '', '', 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_acl_view', 1, @sName, '_bx_acl_page_block_title_system_view', '_bx_acl_page_block_title_view', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:6:\"bx_acl\";s:6:\"method\";s:14:\"get_block_view\";}', 0, 1, 0);
+
+
+-- MENU: Account links
+SET @iMIOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_account_notifications');
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('sys_account_notifications', 'bx_acl', 'acl-view', '_bx_acl_menu_item_title_system_acl_view', '_bx_acl_menu_item_title_acl_view', 'page.php?i=acl-view', '', '', 'shield col-red2', '', '', 2147483646, 1, 0, @iMIOrder + 1);
+
+
+-- 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_acl', 'bx_acl@modules/boonex/acl/|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_acl', 1);
+-- SET @iCategId = LAST_INSERT_ID();
+
+-- INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
+-- ('bx_acl_remove_expired_for', '30', @iCategId, '_bx_acl_option_remove_expired_for', 'digit', '', '', '', 3);
+
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers`(`name`, `class`, `file`, `service_call`) VALUES 
+(@sName, 'BxAclResponse', 'modules/boonex/acl/classes/BxAclResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('system', 'page_output_block_acl_level', @iHandler);

+ 107 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/install/sql/install.sql

@@ -0,0 +1,107 @@
+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 '',
+  `period` int(11) unsigned NOT NULL default '1',
+  `period_unit` varchar(32) NOT NULL default '',
+  `price` float unsigned NOT NULL default '1',
+  `order` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`),
+  UNIQUE KEY `type` (`level_id`,`period`, `period_unit`)
+);
+
+
+-- 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, '', '');
+
+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, 'hidden', '_bx_acl_form_price_input_sys_level_id', '', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 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, '', '', '', 'Avail', '', '_bx_acl_form_price_input_err_period', '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', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_acl_form_price_input_err_period_unit', 'Xss', '', 1, 0),
+('bx_acl_price', @sName, 'price', '', '', 0, 'text', '_bx_acl_form_price_input_sys_price', '_bx_acl_form_price_input_price', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_acl_form_price_input_err_price', 'Float', '', 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', 'period', 2147483647, 1, 3),
+('bx_acl_price_add', 'period_unit', 2147483647, 1, 4),
+('bx_acl_price_add', 'price', 2147483647, 1, 5),
+('bx_acl_price_add', 'controls', 2147483647, 1, 6),
+('bx_acl_price_add', 'do_submit', 2147483647, 1, 7),
+('bx_acl_price_add', 'do_cancel', 2147483647, 1, 8),
+
+('bx_acl_price_edit', 'id', 2147483647, 1, 1),
+('bx_acl_price_edit', 'level_id', 2147483647, 1, 2),
+('bx_acl_price_edit', 'period', 2147483647, 1, 3),
+('bx_acl_price_edit', 'price', 2147483647, 1, 4),
+('bx_acl_price_edit', 'controls', 2147483647, 1, 5),
+('bx_acl_price_edit', 'do_submit', 2147483647, 1, 6),
+('bx_acl_price_edit', 'do_cancel', 2147483647, 1, 7);
+
+
+-- 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', '', '', 100, NULL, 'start', '', 'period,period_unit,price', '', 'like', '', '', 192, 'BxAclGridAdministration', 'modules/boonex/acl/classes/BxAclGridAdministration.php'),
+('bx_acl_view', 'Sql', 'SELECT `tlp`.*, `tl`.`Name` AS `level_name` FROM `bx_acl_level_prices` AS `tlp` LEFT JOIN `sys_acl_levels` AS `tl` ON `tlp`.`level_id`=`tl`.`ID` WHERE `tl`.`Active`=''yes'' AND `tl`.`Purchasable`=''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', '1%', 0, '', '', 1),
+('bx_acl_administration', 'order', '', '1%', 0, '', '', 2),
+('bx_acl_administration', 'period', '_bx_acl_grid_column_period', '26%', 0, 16, '', 3),
+('bx_acl_administration', 'period_unit', '_bx_acl_grid_column_period_unit', '26%', 0, 16, '', 4),
+('bx_acl_administration', 'price', '_bx_acl_grid_column_price', '26%', 0, 16, '', 5),
+('bx_acl_administration', 'actions', '', '20%', 0, '', '', 6),
+
+('bx_acl_view', 'level_name', '_bx_acl_grid_column_level_name', '35%', 1, 36, '', 1),
+('bx_acl_view', 'period', '_bx_acl_grid_column_period', '15%', 0, 16, '', 2),
+('bx_acl_view', 'period_unit', '_bx_acl_grid_column_period_unit', '15%', 0, 16, '', 3),
+('bx_acl_view', 'price', '_bx_acl_grid_column_price', '15%', 0, 16, '', 4),
+('bx_acl_view', 'actions', '', '20%', 0, '', '', 5);
+
+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', 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', 1, 0, 1),
+('bx_acl_view', 'single', 'subscribe', '_bx_acl_grid_action_subscribe', 'thumb-tack', 1, 0, 2);
+
+
+-- 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`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, @sName, '{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));

+ 43 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/js/administration.js

@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxAclAdministration(oOptions) {
+	this._sActionsUri = oOptions.sActionUri;
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjNameGrid = oOptions.sObjNameGrid;
+    this._sObjName = oOptions.sObjName == undefined ? 'oAclAdministration' : oOptions.sObjName;
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxAclAdministration.prototype.onChangeLevel = function() {
+	this.reloadGrid($('#bx-grid-level-' + this._sObjNameGrid).val());
+};
+
+BxAclAdministration.prototype.reloadGrid = function(iLevel) {
+	var oSearch = $('#bx-form-element-keyword');
+	var oActions = $("[bx_grid_action_independent]");
+	if(parseInt(iLevel) > 0) {
+		oSearch.show();
+		oActions.removeClass('bx-btn-disabled');
+	}
+	else {
+		oSearch.hide();
+		oActions.addClass('bx-btn-disabled');
+	}
+
+	if(glGrids[this._sObjNameGrid]._oQueryAppend['level'] == iLevel)
+		return;
+
+	glGrids[this._sObjNameGrid]._oQueryAppend['level'] = iLevel;
+	glGrids[this._sObjNameGrid].reload(0);
+};
+
+/** @} */

+ 36 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/js/main.js

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    PaidLevels Paid Levels
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxAclMain(oOptions) {
+	this._sActionsUri = oOptions.sActionUri;
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oAclMain' : oOptions.sObjName;
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxAclMain.prototype.loadingInButton = function(e, bShow) {
+	if($(e).length)
+		bx_loading_btn($(e), bShow);
+	else
+		bx_loading($('body'), bShow);	
+};
+
+BxAclMain.prototype._loading = function(e, bShow) {
+	var oParent = $(e).length ? $(e) : $('body'); 
+	bx_loading(oParent, bShow);
+};
+
+BxAclMain.prototype._getDefaultData = function () {
+	var oDate = new Date();
+    return jQuery.extend({}, this._oRequestParams, {_t:oDate.getTime()});
+};
+
+/** @} */

+ 18 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/request.php

@@ -0,0 +1,18 @@
+<?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(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+bx_import('BxDolLanguages');
+
+BxDolRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

+ 125 - 0
modules/boonex/acl/updates/9.0.1_9.0.2/source/template/images/icons/std-icon.svg

@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="600px" height="600px" viewBox="0 0 600 600" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
+    <title>Group 2 Copy 9</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
+            <stop stop-color="#FFFFFF" stop-opacity="0.2" offset="0%"></stop>
+            <stop stop-color="#000000" stop-opacity="0.138870018" offset="100%"></stop>
+        </linearGradient>
+        <rect id="path-2" x="0" y="54.2857143" width="230.714286" height="27.1428571" rx="8"></rect>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-3">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.2 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <rect id="path-4" x="312.142857" y="54.2857143" width="67.8571429" height="27.1428571" rx="8"></rect>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-5">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.2 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-6">
+            <stop stop-color="#FFFFFF" offset="0%"></stop>
+            <stop stop-color="#F6F6F6" offset="35.2427758%"></stop>
+            <stop stop-color="#E5E5E5" offset="100%"></stop>
+        </linearGradient>
+        <path d="M271.428571,135.714286 C256.43808,135.714286 244.285714,123.37192 244.285714,108.147321 L244.285714,27.5669643 C244.285714,12.3423661 256.43808,0 271.428571,0 C286.419062,0 298.571429,12.3423661 298.571429,27.5669643 L298.571429,108.147321 C298.571429,123.37192 286.419062,135.714286 271.428571,135.714286 Z" id="path-7"></path>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-8">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.08 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <rect id="path-9" x="0" y="54.2857143" width="67.8571429" height="27.1428571" rx="8"></rect>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-10">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.2 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <rect id="path-11" x="149.285714" y="54.2857143" width="230.714286" height="27.1428571" rx="8"></rect>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-12">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.2 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <path d="M135.714286,108.147321 C135.714286,123.37192 123.56192,135.714286 108.571429,135.714286 L108.571429,135.714286 C93.5809375,135.714286 81.4285714,123.37192 81.4285714,108.147321 L81.4285714,27.5669643 C81.4285714,12.3423661 93.5809375,0 108.571429,0 L108.571429,0 C123.56192,0 135.714286,12.3423661 135.714286,27.5669643 L135.714286,108.147321 L135.714286,108.147321 Z" id="path-13"></path>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-14">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.08 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <rect id="path-15" x="0" y="54.2857143" width="230.714286" height="27.1428571" rx="8"></rect>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-16">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.2 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <rect id="path-17" x="312.142857" y="54.2857143" width="67.8571429" height="27.1428571" rx="8"></rect>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-18">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.2 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <path d="M271.428571,135.714286 C256.43808,135.714286 244.285714,123.37192 244.285714,108.147321 L244.285714,27.5669643 C244.285714,12.3423661 256.43808,0 271.428571,0 C286.419062,0 298.571429,12.3423661 298.571429,27.5669643 L298.571429,108.147321 C298.571429,123.37192 286.419062,135.714286 271.428571,135.714286 Z" id="path-19"></path>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-20">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.08 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group-2-Copy-9">
+            <path d="M552.268958,552.268958 C600,504.537813 600,431.458333 600,300 C600,168.541667 600,95.4621875 552.268958,47.7310417 C504.537813,0 431.458333,0 300,0 C168.541667,0 95.4621875,0 47.7310417,47.7310417 C0,95.4621875 0,168.541667 0,300 C0,431.458333 0,504.537813 47.7310417,552.268958 C95.4621875,600 168.541667,600 300,600 C431.458333,600 504.537813,600 552.268958,552.268958 L552.268958,552.268958 Z" id="path-copy-12" fill="#FF4D63"></path>
+            <path d="M552.268958,552.268958 C600,504.537813 600,431.458333 600,300 C600,168.541667 600,95.4621875 552.268958,47.7310417 C504.537813,0 431.458333,0 300,0 C168.541667,0 95.4621875,0 47.7310417,47.7310417 C0,95.4621875 0,168.541667 0,300 C0,431.458333 0,504.537813 47.7310417,552.268958 C95.4621875,600 168.541667,600 300,600 C431.458333,600 504.537813,600 552.268958,552.268958 L552.268958,552.268958 Z" id="path-copy" fill="url(#linearGradient-1)"></path>
+            <g id="android-options" transform="translate(110.000000, 110.000000)">
+                <g id="Layer_1">
+                    <g id="Group">
+                        <g transform="translate(0.000000, 244.285714)">
+                            <g id="Rectangle-path" opacity="0.496773098">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                                <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-2"></use>
+                            </g>
+                            <g id="Rectangle-path" opacity="0.496773098">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-5)" xlink:href="#path-4"></use>
+                                <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-4"></use>
+                            </g>
+                            <g id="Shape">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-8)" xlink:href="#path-7"></use>
+                                <use fill="url(#linearGradient-6)" fill-rule="evenodd" xlink:href="#path-7"></use>
+                            </g>
+                        </g>
+                        <g transform="translate(0.000000, 122.142857)">
+                            <g id="Rectangle-path" opacity="0.496773098">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-10)" xlink:href="#path-9"></use>
+                                <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-9"></use>
+                            </g>
+                            <g id="Rectangle-path" opacity="0.496773098">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-12)" xlink:href="#path-11"></use>
+                                <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-11"></use>
+                            </g>
+                            <g id="Shape">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-14)" xlink:href="#path-13"></use>
+                                <use fill="url(#linearGradient-6)" fill-rule="evenodd" xlink:href="#path-13"></use>
+                            </g>
+                        </g>
+                        <g>
+                            <g id="Rectangle-path" opacity="0.496773098">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-16)" xlink:href="#path-15"></use>
+                                <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-15"></use>
+                            </g>
+                            <g id="Rectangle-path" opacity="0.496773098">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-18)" xlink:href="#path-17"></use>
+                                <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-17"></use>
+                            </g>
+                            <g id="Shape">
+                                <use fill="black" fill-opacity="1" filter="url(#filter-20)" xlink:href="#path-19"></use>
+                                <use fill="url(#linearGradient-6)" fill-rule="evenodd" xlink:href="#path-19"></use>
+                            </g>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 56 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/install/config.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Albums',
+    'version_from' => '9.0.1',
+	'version_to' => '9.0.2',
+    'vendor' => 'BoonEx',
+
+	'compatible_with' => array(
+        '9.0.0-B3'
+    ),
+
+    /**
+     * '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_9.0.1_9.0.2/',
+	'home_uri' => 'albums_update_901_902',
+
+	'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(
+		'template/images/icons/std-mi.png',
+		'template/images/icons/std-pi.png',
+		'template/images/icons/std-si.png',
+		'template/images/icons/std-wi.png'
+	),
+);

+ 26 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/install/installer.php

@@ -0,0 +1,26 @@
+<?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);
+    }
+
+	public function actionExecuteSql($sOperation)
+    {
+    	if($sOperation == 'install') {
+    		if(!$this->oDb->isFieldExists('bx_albums_albums', 'favorites'))
+        		$this->oDb->query("ALTER TABLE `bx_albums_albums` ADD `favorites` int(11) NOT NULL default '0' AFTER `votes`");
+
+			if(!$this->oDb->isFieldExists('bx_albums_files2albums', 'favorites'))
+	        		$this->oDb->query("ALTER TABLE `bx_albums_files2albums` ADD `favorites` int(11) NOT NULL default '0' AFTER `votes`");
+		}
+
+    	return parent::actionExecuteSql($sOperation);
+    }
+}

+ 7 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/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_albums_page_block_title_favorites_of_author"><![CDATA[Favorite albums by <a href="{profile_link}">{display_name}</a>]]></string_add>
+    <string_add name="_bx_albums_page_block_title_sys_favorites_of_author"><![CDATA[Favorite albums of author]]></string_add>
+    <string_add name="_bx_albums_page_block_title_favorites_of_author_media"><![CDATA[Favorite media by <a href="{profile_link}">{display_name}</a>]]></string_add>
+    <string_add name="_bx_albums_page_block_title_sys_favorites_of_author_media"><![CDATA[Favorite media of author]]></string_add>
+</resources>

+ 9 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/install/sql/enable.sql

@@ -0,0 +1,9 @@
+-- SETTINGS
+UPDATE `sys_options_types` SET `icon`='bx_albums@modules/boonex/albums/|std-icon.svg' WHERE `name`='bx_albums';
+
+
+-- PAGES
+DELETE FROM `sys_pages_blocks` WHERE `object`='bx_albums_author' AND `title` IN ('_bx_albums_page_block_title_favorites_of_author', '_bx_albums_page_block_title_favorites_of_author_media');
+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_albums_author', 1, 'bx_albums', '_bx_albums_page_block_title_sys_favorites_of_author', '_bx_albums_page_block_title_favorites_of_author', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:15:\"browse_favorite\";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 1, 0, 2),
+('bx_albums_author', 1, 'bx_albums', '_bx_albums_page_block_title_sys_favorites_of_author_media', '_bx_albums_page_block_title_favorites_of_author_media', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:21:\"browse_favorite_media\";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 1, 1, 3);

+ 36 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/install/sql/install.sql

@@ -0,0 +1,36 @@
+-- TABLES
+ALTER TABLE `bx_albums_files2albums` DROP INDEX `search_fields`;
+ALTER TABLE `bx_albums_files2albums` ADD FULLTEXT `search_fields` (`title`);
+
+ALTER TABLE `bx_albums_cmts` DROP INDEX `search_fields`;
+ALTER TABLE `bx_albums_cmts` ADD FULLTEXT `search_fields` (`cmt_text`);
+
+ALTER TABLE `bx_albums_cmts_media` DROP INDEX `search_fields`;
+ALTER TABLE `bx_albums_cmts_media` ADD FULLTEXT `search_fields` (`cmt_text`);
+
+-- TABLE: favorites
+CREATE TABLE IF NOT EXISTS `bx_albums_favorites_track` (
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  KEY `id` (`object_id`,`author_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_albums_favorites_media_track` (
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  KEY `id` (`object_id`,`author_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+
+-- FAFORITES
+DELETE FROM `sys_objects_favorite` WHERE `name` IN ('bx_albums', 'bx_albums_media');
+INSERT INTO `sys_objects_favorite` (`name`, `table_track`, `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', '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', '', 'favorites', '', '');
+
+
+-- STUDIO PAGE & WIDGET
+UPDATE `sys_std_pages` SET `icon`='bx_albums@modules/boonex/albums/|std-icon.svg' WHERE `name`='bx_albums';
+UPDATE `sys_std_widgets` SET `icon`='bx_albums@modules/boonex/albums/|std-icon.svg' WHERE `module`='bx_albums' AND `caption`='_bx_albums';

+ 29 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsAlertsResponse.php

@@ -0,0 +1,29 @@
+<?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 BxAlbumsAlertsResponse extends BxBaseModTextAlertsResponse
+{
+    public function __construct()
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct();
+    }
+
+    public function response($oAlert)
+    {
+        if ('bx_albums_files' == $oAlert->sUnit && 'file_deleted' == $oAlert->sAction)
+            BxDolService::call($this->MODULE, 'delete_file_associations', array($oAlert->iObject));
+
+        parent::response($oAlert);
+    }
+}
+
+/** @} */

+ 24 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsCmtsSearchResult.php

@@ -0,0 +1,24 @@
+<?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 BxAlbumsCmtsSearchResult extends BxBaseModGeneralCmtsSearchResult
+{
+    function __construct($sMode = '', $aParams = array())
+    {
+    	$this->sModule = 'bx_albums';
+
+        parent::__construct($sMode, $aParams);
+
+        $this->aCurrent['title'] = _t('_bx_albums_page_block_title_browse_cmts');
+    }
+}
+
+/** @} */

+ 29 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsCmtsSearchResultMedia.php

@@ -0,0 +1,29 @@
+<?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 BxAlbumsCmtsSearchResultMedia extends BxBaseModGeneralCmtsSearchResult
+{
+    function __construct($sMode = '', $aParams = array())
+    {
+    	$this->sModule = 'bx_albums';
+
+        parent::__construct($sMode, $aParams);
+
+        $this->aCurrent['title'] = _t('_bx_albums_page_block_title_browse_cmts_media');
+
+        $this->sModuleObjectComments = $this->oModule->_oConfig->CNF['OBJECT_COMMENTS_MEDIA'];
+
+        $this->aCurrent['name'] = $this->oModule->_oConfig->getName() . '_cmts_media';
+        $this->aCurrent['table'] = $this->oModule->_oConfig->getDbPrefix() . 'cmts_media';
+    }
+}
+
+/** @} */

+ 156 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsConfig.php

@@ -0,0 +1,156 @@
+<?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 BxAlbumsConfig extends BxBaseModTextConfig
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $aMenuItems2Methods = array (
+            'edit-album' => 'checkAllowedEdit',
+            'delete-album' => 'checkAllowedDelete',
+        );
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'picture-o col-blue1',
+
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'albums',
+        	'TABLE_ENTRIES_FULLTEXT' => 'title_text',
+            'TABLE_FILES2ENTRIES' => $aModule['db_prefix'] . 'files2albums',
+            'TABLE_FILES' => $aModule['db_prefix'] . 'files',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => 'author',
+            'FIELD_ADDED' => 'added',
+            'FIELD_CHANGED' => 'changed',
+            'FIELD_TITLE' => 'title',
+            'FIELD_TEXT' => 'text',
+            'FIELD_TEXT_ID' => 'album-desc',
+            'FIELD_ALLOW_VIEW_TO' => 'allow_view_to',
+            'FIELD_PHOTO' => 'pictures',
+            'FIELD_THUMB' => 'thumb',
+            'FIELD_COMMENTS' => 'comments',
+        	'FIELD_STATUS' => 'status',
+        	'FIELD_STATUS_ADMIN' => 'status_admin',
+            'FIELD_LOCATION_PREFIX' => 'location',
+            '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-album',
+            'URI_VIEW_MEDIA' => 'view-album-media',
+            'URI_AUTHOR_ENTRIES' => 'albums-author',
+            'URI_ADD_ENTRY' => 'create-album',
+        	'URI_EDIT_ENTRY' => 'edit-album',
+        	'URI_MANAGE_COMMON' => 'albums-manage',
+
+            'URL_HOME' => 'page.php?i=albums-home',
+            'URL_POPULAR' => 'page.php?i=albums-popular',
+            'URL_UPDATED' => 'page.php?i=albums-updated',
+            'URL_POPULAR_MEDIA' => 'page.php?i=albums-popular-media',
+            'URL_RECENT_MEDIA' => 'page.php?i=albums-recent-media', // TODO: add page
+        	'URL_MANAGE_COMMON' => 'page.php?i=albums-manage',
+        	'URL_MANAGE_ADMINISTRATION' => 'page.php?i=albums-administration',
+
+            // some params
+            'PARAM_CHARS_SUMMARY' => 'bx_albums_summary_chars',
+            'PARAM_CHARS_SUMMARY_PLAIN' => 'bx_albums_plain_summary_chars',
+            'PARAM_NUM_RSS' => 'bx_albums_rss_num',
+        	'PARAM_SEARCHABLE_FIELDS' => 'bx_albums_searchable_fields',
+
+            // objects
+            'OBJECT_STORAGE' => 'bx_albums_files',
+            'OBJECT_IMAGES_TRANSCODER_PREVIEW' => 'bx_albums_proxy_preview',
+            'OBJECT_IMAGES_TRANSCODER_GALLERY' => '',
+            'OBJECT_IMAGES_TRANSCODER_BIG' => 'bx_albums_big',
+            'OBJECT_VIDEOS_TRANSCODERS' => array('poster' => 'bx_albums_video_poster_big', 'poster_preview' => 'bx_albums_video_poster_preview', 'mp4' => 'bx_albums_video_mp4', 'webm' => 'bx_albums_video_webm'),
+            'OBJECT_VIDEO_TRANSCODER_HEIGHT' => '480px',
+            'OBJECT_TRANSCODER_BROWSE' => 'bx_albums_proxy_browse',
+            'OBJECT_TRANSCODER_COVER' => 'bx_albums_proxy_cover',
+        	'OBJECT_REPORTS' => 'bx_albums',
+            'OBJECT_VIEWS' => 'bx_albums',
+            'OBJECT_VIEWS_MEDIA' => 'bx_albums_media',
+            'OBJECT_VOTES' => 'bx_albums',
+            'OBJECT_VOTES_MEDIA' => 'bx_albums_media',
+        	'OBJECT_FAVORITES' => 'bx_albums',
+            'OBJECT_FAVORITES_MEDIA' => 'bx_albums_media',
+            'OBJECT_METATAGS' => 'bx_albums',
+            'OBJECT_METATAGS_MEDIA' => 'bx_albums_media',
+            'OBJECT_METATAGS_MEDIA_CAMERA' => 'bx_albums_media_camera',
+            'OBJECT_COMMENTS' => 'bx_albums',
+            'OBJECT_COMMENTS_MEDIA' => 'bx_albums_media',
+            'OBJECT_PRIVACY_VIEW' => 'bx_albums_allow_view_to',
+            'OBJECT_FORM_ENTRY' => 'bx_albums',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW' => 'bx_albums_entry_view',
+            'OBJECT_FORM_ENTRY_DISPLAY_ADD' => 'bx_albums_entry_add',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT' => 'bx_albums_entry_edit',
+            'OBJECT_FORM_ENTRY_DISPLAY_DELETE' => 'bx_albums_entry_delete',
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY' => 'bx_albums_view', // actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_VIEW_MEDIA' => 'bx_albums_view_media', // actions menu on view media page
+            'OBJECT_MENU_ACTIONS_MY_ENTRIES' => 'bx_albums_my', // actions menu on my entries page
+            'OBJECT_MENU_SUBMENU' => 'bx_albums_submenu', // main module submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY' => 'bx_albums_view_submenu', // view entry submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY_MAIN_SELECTION' => 'albums-home', // first item in view entry submenu from main module submenu
+            'OBJECT_MENU_MANAGE_TOOLS' => 'bx_albums_menu_manage_tools', //manage menu in content administration tools
+            'OBJECT_GRID_ADMINISTRATION' => 'bx_albums_administration',
+        	'OBJECT_GRID_COMMON' => 'bx_albums_common',
+        	'OBJECT_UPLOADERS' => array('bx_albums_simple', 'bx_albums_html5', 'bx_albums_crop'),
+
+            // menu items which visibility depends on custom visibility checking
+            'MENU_ITEM_TO_METHOD' => array (
+                'bx_albums_my' => array (
+                    'create-album' => 'checkAllowedAdd',
+                ),
+                'bx_albums_view' => $aMenuItems2Methods,
+            ),
+
+            // some language keys
+            'T' => array (
+                'txt_sample_single' => '_bx_albums_txt_sample_single',
+            	'txt_sample_comment_single' => '_bx_albums_txt_sample_comment_single',
+            	'txt_sample_vote_single' => '_bx_albums_txt_sample_vote_single',
+                'txt_media_single' => '_bx_albums_txt_media_single',
+                'txt_media_comment_single' => '_bx_albums_txt_media_comment_single',
+            	'grid_action_err_delete' => '_bx_albums_grid_action_err_delete',
+            	'grid_txt_account_manager' => '_bx_albums_grid_txt_account_manager',
+				'filter_item_active' => '_bx_albums_grid_filter_item_title_adm_active',
+            	'filter_item_hidden' => '_bx_albums_grid_filter_item_title_adm_hidden',
+            	'filter_item_select_one_filter1' => '_bx_albums_grid_filter_item_title_adm_select_one_filter1',
+            	'menu_item_manage_my' => '_bx_albums_menu_item_title_manage_my',
+            	'menu_item_manage_all' => '_bx_albums_menu_item_title_manage_all',
+                'txt_all_entries_by' => '_bx_albums_txt_all_entries_by',
+            	'txt_all_entries_by_author' => '_bx_albums_page_title_browse_by_author'
+            ),
+        );
+
+        $this->_aJsClasses = array(
+        	'main' => 'BxAlbumsMain',
+        	'manage_tools' => 'BxAlbumsManageTools'
+        );
+
+        $this->_aJsObjects = array(
+        	'main' => 'oBxAlbumsMain',
+        	'manage_tools' => 'oBxAlbumsManageTools'
+        );
+
+        $this->_aGridObjects = array(
+        	'common' => $this->CNF['OBJECT_GRID_COMMON'],
+        	'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION'],
+        	
+        );
+    }
+}
+
+/** @} */

+ 78 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsDb.php

@@ -0,0 +1,78 @@
+<?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, $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` = ?, `title` = ?, `data` = ?, `exif` = ?, `order` = ? ON DUPLICATE KEY UPDATE `title` = ?, `data` = ?, `exif` = ?", $iContentId, $iFileId, $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)
+    {
+        $sQuery = $this->prepare ("SELECT `f2e`.*, `f`.`added` FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` AS `f2e` INNER JOIN `" . $this->_oConfig->CNF['TABLE_FILES'] . "` AS `f` ON (`f`.`id` = `f2e`.`file_id`) INNER JOIN `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` AS `e` ON (`e`.`id` = `f2e`.`content_id`) 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 getMediaListByContentId($iContentId)
+    {
+        $sQuery = $this->prepare ("SELECT * FROM `" . $this->_oConfig->CNF['TABLE_FILES2ENTRIES'] . "` WHERE `content_id` = ? ORDER BY `order`", $iContentId);
+        return $this->getAll($sQuery);
+    }
+
+}
+
+/** @} */

+ 93 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsFormEntry.php

@@ -0,0 +1,93 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Create/Edit entry form
+ */
+class BxAlbumsFormEntry extends BxBaseModTextFormEntry
+{
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($aInfo, $oTemplate);
+    }
+
+    protected function _associalFileWithContent($oStorage, $iFileId, $iProfileId, $iContentId, $sPictureField = '')
+    {
+        parent::_associalFileWithContent($oStorage, $iFileId, $iProfileId, $iContentId, $sPictureField);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sData = '';
+        $sExif = '';
+        $aExif = false;
+        $aFile = $oStorage->getFile($iFileId);
+        if (0 === strncmp('image/', $aFile['mime_type'], 6)) {
+            $oImageReize = BxDolImageResize::getInstance();
+
+            $oTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_BIG']);            
+            $a = $oImageReize->getImageSize($oTranscoder->getFileUrl($iFileId));
+            $sData = isset($a['w']) && isset($a['h']) ? $a['w'] . 'x' . $a['h'] : '';
+
+            if ($aExif = $oImageReize->getExifInfo($oStorage->getFileUrlById($iFileId))) {
+                $a = array('Make', 'Model', 'FocalLength', 'ShutterSpeedValue', 'ExposureTime', 'ISOSpeedRatings', 'Orientation', 'Artist', 'Copyright', 'Flash', 'WhiteBalance', 'DateTimeOriginal', 'DateTimeDigitized', 'ExifVersion', 'COMPUTED', 'GPSLatitudeRef', 'GPSLatitude', 'GPSLongitudeRef', 'GPSLongitude', 'GPSAltitudeRef', 'GPSAltitude', 'GPSTimeStamp', 'GPSImgDirectionRef', 'GPSImgDirection', 'GPSDateStamp');
+                $aExifFiltered = array();
+                foreach ($a as $sIndex)
+                    if (isset($aExif[$sIndex]))
+                        $aExifFiltered[$sIndex] = $aExif[$sIndex];
+                $sExif = serialize($aExifFiltered);
+            }
+        }
+        
+        if (false === $this->_oModule->_oDb->associateFileWithContent ($iContentId, $iFileId, $this->getCleanValue('title-' . $iFileId), $sData, $sExif))
+            return;
+
+        $aMediaInfo = $this->_oModule->_oDb->getMediaInfoSimpleByFileId($iFileId);
+
+        if ($aMediaInfo && !empty($CNF['OBJECT_METATAGS_MEDIA'])) {            
+            $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS_MEDIA']);
+            if ($oMetatags->keywordsIsEnabled())
+                $oMetatags->keywordsAdd($aMediaInfo['id'], $aMediaInfo['title']);
+        }
+
+        if ($aMediaInfo && isset($aExif['Make']) && !empty($CNF['OBJECT_METATAGS_MEDIA_CAMERA'])) {
+            $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS_MEDIA_CAMERA']);
+            if ($oMetatags->keywordsIsEnabled())
+                $oMetatags->keywordsAddOne($aMediaInfo['id'], $oMetatags->keywordsCameraModel($aExif));
+        }
+    }
+
+    function _deleteFile ($iFileId, $sStorage = '')
+    {
+        if (!($bRet = parent::_deleteFile ($iFileId)))
+            return false;
+
+        $this->_oModule->serviceDeleteFileAssociations ($iFileId);
+
+        return true;
+    }
+
+    function delete ($iContentId, $aContentInfo = array())
+    {
+        if (!($bRet = parent::delete ($iContentId, $aContentInfo)))
+            return $bRet;
+
+        if (!($aMediaList = $this->_oModule->_oDb->getMediaListByContentId($iContentId)))
+            return $bRet;
+
+        foreach ($aMediaList as $aMediaInfo)
+            $this->_oModule->serviceDeleteFileAssociations($aMediaInfo['file_id']);
+
+        return $bRet;
+    }
+}
+
+/** @} */

+ 23 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsFormsEntryHelper.php

@@ -0,0 +1,23 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Entry forms helper functions
+ */
+class BxAlbumsFormsEntryHelper extends BxBaseModTextFormsEntryHelper
+{
+    public function __construct($oModule)
+    {
+        parent::__construct($oModule);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsGridAdministration.php

@@ -0,0 +1,21 @@
+<?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 BxAlbumsGridAdministration extends BxBaseModTextGridAdministration
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_albums';
+        parent::__construct ($aOptions, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsGridCommon.php

@@ -0,0 +1,21 @@
+<?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 BxAlbumsGridCommon extends BxBaseModTextGridCommon
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_albums';
+        parent::__construct ($aOptions, $oTemplate);
+    }
+}
+
+/** @} */

+ 24 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsMenu.php

@@ -0,0 +1,24 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * General class for module menu.
+ */
+class BxAlbumsMenu extends BxBaseModTextMenu
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 25 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsMenuManageTools.php

@@ -0,0 +1,25 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * 'Albums manage tools' menu.
+ */
+class BxAlbumsMenuManageTools extends BxBaseModTextMenuManageTools
+{
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 32 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsMenuView.php

@@ -0,0 +1,32 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * View entry menu
+ */
+class BxAlbumsMenuView extends BxBaseModTextMenuView
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($aObject, $oTemplate);
+
+        $sURI = bx_process_input(bx_get('i'));
+        if ($sURI == 'view-album-media') {
+            $aMediaInfo = $this->_oModule->_oDb->getMediaInfoById((int)bx_get('id'));
+            $this->_aContentInfo = $aMediaInfo ? $this->_oModule->_oDb->getContentInfoById($aMediaInfo['content_id']) : false;
+            if ($this->_aContentInfo)
+                $this->addMarkers(array('content_id' => (int)$aMediaInfo['content_id']));
+        }
+    }
+}
+
+/** @} */

+ 226 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsModule.php

@@ -0,0 +1,226 @@
+<?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
+ *
+ * @{
+ */
+
+bx_import('BxDolAcl');
+
+/**
+ * Albums module
+ */
+class BxAlbumsModule extends BxBaseModTextModule
+{
+    protected $_aContexts = array('popular', 'public', 'author');
+
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    public function serviceEntityAddFiles ($iContentId = 0)
+    {
+        return $this->_serviceEntityForm ('editDataForm', $iContentId, 'bx_albums_entry_add_images');
+    }
+    
+    public function serviceDeleteFileAssociations($iFileId)
+    {        
+        $CNF = &$this->_oConfig->CNF;
+
+        if (!($aMediaInfo = $this->_oDb->getMediaInfoSimpleByFileId($iFileId))) // file is already deleted
+            return true; 
+    
+        if (!$this->_oDb->deassociateFileWithContent(0, $iFileId))
+            return false;
+
+        if (!empty($CNF['OBJECT_VIEWS_MEDIA'])) {
+            $o = BxDolView::getObjectInstance($CNF['OBJECT_VIEWS_MEDIA'], $aMediaInfo['id']);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_VOTES_MEDIA'])) {
+            $o = BxDolVote::getObjectInstance($CNF['OBJECT_VOTES_MEDIA'], $aMediaInfo['id']);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_COMMENTS_MEDIA'])) {
+            $o = BxDolCmts::getObjectInstance($CNF['OBJECT_COMMENTS_MEDIA'], $aMediaInfo['id']);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_METATAGS_MEDIA'])) {
+            $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS_MEDIA']);
+            $oMetatags->onDeleteContent($aMediaInfo['id']);
+        }
+
+        if (!empty($CNF['OBJECT_METATAGS_MEDIA_CAMERA'])) {
+            $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS_MEDIA_CAMERA']);
+            $oMetatags->onDeleteContent($aMediaInfo['id']);
+        }
+
+        return true;
+    }
+
+    public function serviceMediaExif ($iMediaId = 0)
+    {
+        return $this->_serviceTemplateFunc ('mediaExif', $iMediaId, 'getMediaInfoById');
+    }
+
+    public function serviceMediaComments ($iMediaId = 0)
+    {
+        return $this->_entityComments($this->_oConfig->CNF['OBJECT_COMMENTS_MEDIA'], $iMediaId);
+    }
+
+    public function serviceMediaAuthor ($iMediaId = 0)
+    {
+        return $this->_serviceTemplateFunc ('mediaAuthor', $iMediaId, 'getMediaInfoById');
+    }
+
+    public function serviceMediaSocialSharing ($iMediaId = 0, $bEnableCommentsBtn = false, $bEnableSocialSharing = true)
+    {
+        if (!$iMediaId)
+            $iMediaId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        if (!$iMediaId)
+            return false;
+        $aMediaInfo = $this->_oDb->getMediaInfoById($iMediaId);
+        if (!$aMediaInfo)
+            return false;
+
+        $CNF = &$this->_oConfig->CNF;
+        return $this->_entitySocialSharing ($iMediaId, array(
+            'id_timeline' => 0,
+        	'id_thumb' => $aMediaInfo['file_id'],
+        	'title' => $aMediaInfo['title'],
+        	'object_storage' => false,
+            'object_transcoder' => $CNF['OBJECT_IMAGES_TRANSCODER_BIG'],
+        	'object_vote' => $CNF['OBJECT_VOTES_MEDIA'],
+        	'object_favorite' => $CNF['OBJECT_FAVORITES_MEDIA'],
+        	'object_report' => '',
+        	'object_comments' => $bEnableCommentsBtn ? $CNF['OBJECT_COMMENTS_MEDIA'] : '',
+        	'uri_view_entry' => $CNF['URI_VIEW_MEDIA'],
+            'social_sharing' => $bEnableSocialSharing
+        ));
+    }
+
+    public function serviceMediaView ($iMediaId = 0, $mixedContext = false)
+    {
+        if (!$iMediaId)
+            $iMediaId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        if (!$iMediaId)
+            return false;
+
+        if (!$mixedContext) {
+            $mixedContext = bx_process_input(bx_get('context'));
+            if (!in_array($mixedContext, $this->_aContexts)) // when no context specified, it is assumed that it is an album context
+                $mixedContext = bx_process_input($mixedContext, BX_DATA_INT); // numeric context is reserved for future use
+        }
+
+        return $this->_oTemplate->entryMediaView ($iMediaId, $mixedContext);
+    }
+
+    public function checkAllowedSetThumb ()
+    {
+        return CHECK_ACTION_RESULT_NOT_ALLOWED;
+    }
+
+    public function serviceBrowseRecentMedia ($sUnitView = false, $bDisplayEmptyMsg = true, $bAjaxPaginate = true)
+    {
+        return $this->_serviceBrowse ('recent', array('unit_view' => $sUnitView), BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate, 'SearchResultMedia');
+    }
+
+    public function serviceBrowsePopularMedia ($sUnitView = false, $bDisplayEmptyMsg = true, $bAjaxPaginate = true)
+    {
+        return $this->_serviceBrowse ('popular', array('unit_view' => $sUnitView), BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate, 'SearchResultMedia');
+    }
+
+    public function serviceBrowseFavoriteMedia ($iProfileId = 0, $aParams = array())
+    {
+        $oProfile = null;
+        if((int)$iProfileId)
+            $oProfile = BxDolProfile::getInstance($iProfileId);
+        if(!$oProfile && bx_get('profile_id') !== false)
+            $oProfile = BxDolProfile:: getInstance(bx_process_input(bx_get('profile_id'), BX_DATA_INT));
+        if(!$oProfile)
+            $oProfile = BxDolProfile::getInstance();
+        if(!$oProfile)
+            return '';
+
+        return $this->_serviceBrowse ('favorite', array_merge(array('user' => $oProfile->id()), $aParams), BX_DB_PADDING_DEF, true, true, 'SearchResultMedia');
+    }
+
+    public function actionGetSiblingMedia($iMediaId, $mixedContext)
+    {
+        $aSiblings = false;
+        $sErrorMsg = false;
+        if (!($aMediaInfo = $this->_oDb->getMediaInfoById((int)$iMediaId))) 
+            $sErrorMsg = _t('_sys_txt_error_occured');
+
+        if (empty($sErrorMsg) && !($aContentInfo = $this->_oDb->getContentInfoById($aMediaInfo['content_id'])))
+            $sErrorMsg = _t('_sys_txt_error_occured');
+
+        if (empty($sErrorMsg) && (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->checkAllowedView($aContentInfo))))
+            $sErrorMsg = $sMsg;
+
+        if (empty($sErrorMsg)) {
+            $aSiblings = array (
+                'next' => $this->_oTemplate->getNextPrevMedia($aMediaInfo, true, $mixedContext),
+                'prev' => $this->_oTemplate->getNextPrevMedia($aMediaInfo, false, $mixedContext),
+            );
+        }
+    
+        $a = $sErrorMsg ? array('error' => $sErrorMsg) : array('next' => $aSiblings['next'], 'prev' => $aSiblings['prev']);
+
+        $s = json_encode($a);
+
+        header('Content-type: text/html; charset=utf-8');
+        echo $s;
+    }
+
+    public function actionRssMedia ()
+    {
+        $aArgs = func_get_args();
+        $this->_rss($aArgs, 'SearchResultMedia');
+    }
+
+    protected function _buildRssParams($sMode, $aArgs)
+    {        
+        if ($aParams = parent::_buildRssParams($sMode, $aArgs))
+            return $aParams;
+
+        $sMode = bx_process_input($sMode);
+        switch ($sMode) {
+            case 'album':
+                $aParams = array('album_id' => isset($aArgs[0]) ? (int)$aArgs[0] : '');
+                break;
+        }
+
+        return $aParams;
+    }
+
+    protected function _getImagesForTimelinePost($aEvent, $aContentInfo, $sUrl)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if (!($aMediaList = $this->_oDb->getMediaListByContentId($aContentInfo[$CNF['FIELD_ID']])))
+            return array();
+
+        $oTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_PREVIEW']);
+        $aMediaList = array_slice($aMediaList, 0, 3);
+        $aOutput = array();
+        foreach ($aMediaList as $aMedia) {
+            $aOutput[] = array (
+                'url' => $this->_oTemplate->getViewMediaUrl($CNF, $aMedia['id']), 
+                'src' => $oTranscoder->getFileUrl($aMedia['file_id']),
+            );
+        }
+
+        return $aOutput;
+    }
+}
+
+/** @} */

+ 24 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageAuthor.php

@@ -0,0 +1,24 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Profile's entries page.
+ */
+class BxAlbumsPageAuthor extends BxBaseModTextPageAuthor
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+
+}
+
+/** @} */

+ 24 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageBrowse.php

@@ -0,0 +1,24 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Browse entries pages.
+ */
+class BxAlbumsPageBrowse extends BxBaseModTextPageBrowse
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 24 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageBrowseMedia.php

@@ -0,0 +1,24 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Browse entries pages.
+ */
+class BxAlbumsPageBrowseMedia extends BxBaseModTextPageBrowse
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 36 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageEntry.php

@@ -0,0 +1,36 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Entry create/edit pages
+ */
+class BxAlbumsPageEntry extends BxBaseModTextPageEntry
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($aObject, $oTemplate);
+    }
+
+    protected function _getThumbForMetaObject ()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if (!($aMediaList = $this->_oModule->_oDb->getMediaListByContentId($this->_aContentInfo[$CNF['FIELD_ID']])))
+            return false;
+
+        $aMedia = array_shift($aMediaList);
+
+        return array('id' => $aMedia['file_id'], 'transcoder' => $CNF['OBJECT_TRANSCODER_COVER']);
+    }
+}
+
+/** @} */

+ 137 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsPageMedia.php

@@ -0,0 +1,137 @@
+<?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
+ *
+ * @{
+ */
+
+bx_import('BxDolAcl');
+
+/**
+ * Entry create/edit pages
+ */
+class BxAlbumsPageMedia extends BxTemplPage
+{
+    protected $MODULE;
+    protected $_oModule;
+    protected $_aAlbumInfo = false;
+    protected $_aMediaInfo = false;
+    protected $_mixedContext = false;
+
+    public function __construct($aObject, $oTemplate = false)
+    {        
+        parent::__construct($aObject, $oTemplate);
+        $this->MODULE = 'bx_albums';
+        $this->_oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $iMediaId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        if ($iMediaId)
+            $this->_aMediaInfo = $this->_oModule->_oDb->getMediaInfoById($iMediaId);
+
+        if ($this->_aMediaInfo)
+            $this->_aAlbumInfo = $this->_oModule->_oDb->getContentInfoById($this->_aMediaInfo['content_id']);
+
+        if ($this->_aAlbumInfo) {
+            $this->addMarkers(array_merge($this->_aAlbumInfo, $this->_aMediaInfo)); // every field can be used as marker
+            $this->addMarkers(array(
+                'title' => !empty($this->_aMediaInfo['title']) ? $this->_aMediaInfo['title'] : _t('_bx_albums_txt_media_title_alt', $this->_aAlbumInfo[$CNF['FIELD_TITLE']]),
+            ));
+
+            $sTitle = isset($this->_aAlbumInfo[$CNF['FIELD_TITLE']]) ? $this->_aAlbumInfo[$CNF['FIELD_TITLE']] : strmaxtextlen($this->_aAlbumInfo[$CNF['FIELD_TEXT']], 20, '...');
+            $sUrl = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $this->_aAlbumInfo[$CNF['FIELD_ID']]);
+
+
+            $oMenuSubmenu = BxDolMenu::getObjectInstance('sys_site_submenu');
+
+            // add actions menu to submenu
+            if (isset($CNF['OBJECT_MENU_ACTIONS_VIEW_MEDIA']))
+                $oMenuSubmenu->setObjectActionsMenu($CNF['OBJECT_MENU_ACTIONS_VIEW_MEDIA']);
+        
+            // select view entry submenu
+            $oMenuSubmenu->setObjectSubmenu($CNF['OBJECT_MENU_SUBMENU_VIEW_ENTRY'], array (
+                'title' => $sTitle,
+                'link' => $sUrl,
+                'icon' => $CNF['ICON'],
+            ));
+        }
+    }
+
+    public function getCode ()
+    {
+        // check if content exists
+        if (!$this->_aAlbumInfo || !$this->_aMediaInfo) { // if entry is not found - display standard "404 page not found" page
+            $this->_oTemplate->displayPageNotFound();
+            exit;
+        }
+
+        // permissions check 
+        if (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->_oModule->checkAllowedView($this->_aAlbumInfo))) {
+            $this->_oTemplate->displayAccessDenied($sMsg);
+            exit;
+        }
+        $this->_oModule->checkAllowedView($this->_aAlbumInfo, true);
+
+        // count views
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        if (!empty($CNF['OBJECT_VIEWS_MEDIA']))
+            BxDolView::getObjectInstance($CNF['OBJECT_VIEWS_MEDIA'], $this->_aMediaInfo['id'])->doView();
+
+        // set cover image
+        if ($aThumb = $this->_getThumbForMetaObject()) {
+            $oCover = BxDolCover::getInstance($this->_oModule->_oTemplate);
+            $oCover->setCoverImageUrl($aThumb);
+        }
+
+        // add content metatags
+        if (!empty($CNF['OBJECT_METATAGS_MEDIA'])) {
+            $o = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS_MEDIA']);
+            if ($o) {
+                $o->metaAdd($this->_aMediaInfo['id'], $aThumb);
+            }
+        }
+
+        $aVars = array();
+        $this->_oTemplate->addInjection ('injection_footer', 'text', $this->_oModule->_oTemplate->parseHtmlByName('photoswipe.html', $aVars));
+
+        return parent::getCode ();
+    }
+
+    protected function _getThumbForMetaObject ()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;        
+
+        if (!($aMediaList = $this->_oModule->_oDb->getMediaListByContentId($this->_aAlbumInfo[$CNF['FIELD_ID']])))
+            return false;
+
+        $aMedia = array_shift($aMediaList);
+        
+        if (!$aMedia['file_id'] || empty($CNF['OBJECT_IMAGES_TRANSCODER_BIG']))
+            return false;
+
+        return array('id' => $aMedia['file_id'], 'transcoder' => $CNF['OBJECT_IMAGES_TRANSCODER_BIG']);
+    }
+    
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+
+        $this->_oModule->_oTemplate->addCss(array(
+            BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'photo-swipe/|photoswipe.css',
+            BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'photo-swipe/default-skin/|default-skin.css',
+        ));
+
+        $this->_oModule->_oTemplate->addJs(array(
+            'media_view.js',
+            'history.js',
+            'photo-swipe/photoswipe.min.js',
+            'photo-swipe/photoswipe-ui-default.min.js',
+        ));
+    }
+}
+
+/** @} */

+ 145 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsSearchResult.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    Albums Albums
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAlbumsSearchResult extends BxBaseModTextSearchResult
+{
+    function __construct($sMode = '', $aParams = array())
+    {
+        $this->aUnitViews = array('extended' => 'unit.html', 'gallery' => 'unit_gallery.html',);
+    
+        if (empty($aParams['unit_view']))
+            $aParams['unit_view'] = 'extended';
+
+        parent::__construct($sMode, $aParams);
+
+        $this->aCurrent = array(
+            'name' => 'bx_albums',
+            'module_name' => 'bx_albums',
+            'object_metatags' => 'bx_albums',
+            'title' => _t('_bx_albums_page_title_browse'),
+            'table' => 'bx_albums_albums',
+            'ownFields' => array('id', 'title', 'text', 'thumb', 'author', 'added'),
+            'searchFields' => array(),
+            'restriction' => array(
+                'author' => array('value' => '', 'field' => 'author', 'operator' => '='),
+        		'status' => array('value' => 'active', 'field' => 'status', 'operator' => '='),
+            ),
+            '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' => 'text',
+                ),
+            ),
+            'ident' => 'id',
+        );
+
+        $this->sFilterName = 'bx_albums_filter';
+        $this->oModule = $this->getMain();
+        $this->aCurrent['searchFields'] = explode(',', getParam($this->oModule->_oConfig->CNF['PARAM_SEARCHABLE_FIELDS']));
+
+        $oProfileAuthor = null;
+
+        $CNF = &$this->oModule->_oConfig->CNF;
+
+        switch ($sMode) {
+
+            case 'author':
+                if(!$this->_updateCurrentForAuthor($sMode, $aParams, $oProfileAuthor)) {
+                    $this->isError = true;
+                    break;
+                }
+                break;
+
+            case 'favorite':
+                if(!$this->_updateCurrentForFavorite($sMode, $aParams, $oProfileAuthor)) {
+                    $this->isError = true;
+                    break;
+                }
+                break;
+
+            case 'public':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_HOME']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_recent');
+                $this->aCurrent['rss']['link'] = 'modules/?r=albums/rss/' . $sMode;
+                break;
+
+            case 'popular':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_POPULAR']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_popular');
+                $this->aCurrent['rss']['link'] = 'modules/?r=albums/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'popular';
+                break;
+
+            case 'updated':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_UPDATED']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_updated');
+                $this->aCurrent['rss']['link'] = 'modules/?r=albums/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'updated';
+                break;
+
+            case '': // search results
+                $this->sBrowseUrl = BX_DOL_SEARCH_KEYWORD_PAGE;
+                $this->aCurrent['title'] = _t('_bx_albums');
+                $this->aCurrent['paginate']['perPage'] = 3;
+                unset($this->aCurrent['rss']);
+                break;
+
+            default:
+                $sMode = '';
+                $this->isError = true;
+        }
+
+        $this->processReplaceableMarkers($oProfileAuthor);
+
+        $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+    }
+
+    function displayResultBlock ()
+    {
+        $s = parent::displayResultBlock ();
+        $s = '<div class="bx-albums-wrapper">' . $s . '</div>';
+        return $this->oModule->_oTemplate->getJsCode('main') . $s;
+    }
+
+    function getAlterOrder()
+    {
+        $aSql = array();
+        switch ($this->aCurrent['sorting']) {
+            case 'last':
+                $aSql['order'] = ' ORDER BY `bx_albums_albums`.`added` DESC';
+                break;
+            case 'updated':
+                $aSql['order'] = ' ORDER BY `bx_albums_albums`.`changed` DESC';
+                break;
+            case 'popular':
+                $aSql['order'] = ' ORDER BY `bx_albums_albums`.`views` DESC';
+                break;
+        }
+
+        return $aSql;
+    }
+
+    function getDesignBoxMenu ()
+    {
+        return BxBaseModGeneralSearchResult::getDesignBoxMenu ();
+    }
+}
+
+/** @} */

+ 190 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsSearchResultMedia.php

@@ -0,0 +1,190 @@
+<?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->sCenterContentUnitSelector = '.bx-albums-media-wrapper';
+
+        $this->aUnitViews = array('gallery' => 'unit_media.html');
+
+        $this->sUnitTemplateLiveSearch = 'unit_media_live_search.html';
+    
+        if (empty($aParams['unit_view']))
+            $aParams['unit_view'] = 'gallery';
+
+        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' => '='),
+            ),
+            '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':
+                $this->aCurrent['restriction']['album']['value'] = (int)$aParams['album_id'];
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . (int)$aParams['album_id']);
+                $this->aCurrent['title'] = _t('_bx_albums_page_title_browse_media_in_album');
+                $this->aCurrent['rss']['link'] = 'modules/?r=albums/rss_media/' . $sMode . '/' . (int)$aParams['album_id'];
+                $this->aCurrent['sorting'] = 'order';
+                $this->sOrderDirection = 'ASC';                
+                break;
+
+            case 'favorite':
+                if(!$this->_updateCurrentForFavorite($sMode, array_merge($aParams, array('system' => $CNF['OBJECT_FAVORITES_MEDIA'])), $oProfileAuthor)) {
+                    $this->isError = true;
+                    break;
+                }
+                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';
+                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';
+                break;
+
+            case '': // search results
+                $this->sBrowseUrl = BX_DOL_SEARCH_KEYWORD_PAGE;
+                $this->aCurrent['paginate']['perPage'] = 3;
+                unset($this->aCurrent['rss']);
+                break;
+
+            default:
+                $sMode = '';
+                $this->isError = true;
+        }
+
+        $this->sBrowseUrl = $this->_replaceMarkers($this->sBrowseUrl);
+        $this->aCurrent['title'] = $this->_replaceMarkers($this->aCurrent['title']);
+
+        $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+    }
+
+    function getAlterOrder()
+    {
+        $aSql = array();
+        switch ($this->aCurrent['sorting']) {
+            case 'order':
+                $aSql['order'] = " ORDER BY `{$this->aCurrent['table']}`.`order` {$this->sOrderDirection}, `{$this->aCurrent['table']}`.`id` {$this->sOrderDirection} ";
+                break;
+            case 'last':
+                $aSql['order'] = " ORDER BY `f`.`added` {$this->sOrderDirection}, `{$this->aCurrent['table']}`.`id` {$this->sOrderDirection} ";
+                break;
+            case 'popular':
+                $aSql['order'] = " ORDER BY `{$this->aCurrent['table']}`.`views` {$this->sOrderDirection}, `{$this->aCurrent['table']}`.`id` {$this->sOrderDirection}";
+                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' => '!=');
+
+        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 '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 '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_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $this->oModule->_oConfig->CNF['URI_VIEW_MEDIA'] . '&id=' . $a['id']);
+    }
+
+    function displaySearchUnit ($aData)
+    {
+        $oMain = $this->getMain();
+        return $oMain->_oTemplate->unit($aData, $this->bProcessPrivateContent, $this->_bLiveSearch ? $this->sUnitTemplateLiveSearch : $this->sUnitTemplate, $this->aUnitParams);
+    }
+}
+
+/** @} */

+ 25 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsSearchResultMediaCamera.php

@@ -0,0 +1,25 @@
+<?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
+ *
+ * @{
+ */
+
+bx_import('SearchResultMedia', 'bx_albums');
+
+class BxAlbumsSearchResultMediaCamera extends BxAlbumsSearchResultMedia
+{
+
+    function __construct($sMode = '', $aParams = array())
+    {
+        parent::__construct($sMode, $aParams);
+        $this->aCurrent['name'] = 'bx_albums_media_camera';
+        $this->aCurrent['object_metatags'] = 'bx_albums_media_camera';
+    }
+}
+
+/** @} */

+ 395 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsTemplate.php

@@ -0,0 +1,395 @@
+<?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 representation.
+ */
+class BxAlbumsTemplate extends BxBaseModTextTemplate
+{
+    /**
+     * Constructor
+     */
+    function __construct(&$oConfig, &$oDb)
+    {
+        $this->MODULE = 'bx_albums';
+        parent::__construct($oConfig, $oDb);
+
+        $this->addCss(array(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'flickity/|flickity.css'));
+        $this->addJs(array('flickity/flickity.pkgd.min.js', 'main.js'));
+    }
+
+    function unit ($aData, $isCheckPrivateContent = true, $sTemplateName = 'unit.html', $aParams = array())
+    {
+        if ('unit_media.html' == $sTemplateName || 'unit_media_live_search.html' == $sTemplateName)
+            return $this->unitMedia($aData, $isCheckPrivateContent, $sTemplateName, $aParams);
+
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+
+        if ($s = $this->checkPrivacy ($aData, $isCheckPrivateContent, $oModule))
+            return $s;
+
+        // get entry url        
+        $sUrl = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $aData[$CNF['FIELD_ID']]);
+
+        $oProfile = BxDolProfile::getInstance($aData[$CNF['FIELD_AUTHOR']]);
+        if (!$oProfile) 
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+        $oTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_TRANSCODER_BROWSE']);
+
+        $aBrowseUnits = array ();
+        $aMediaList = $oModule->_oDb->getMediaListByContentId($aData[$CNF['FIELD_ID']]);
+        foreach ($aMediaList as $k => $a) {
+            $aBrowseUnits[] = array (
+                'img_url' => $oTranscoder->getFileUrl($a['file_id']),
+                'url' => $this->getViewMediaUrl($CNF, $a['id']),
+                'title_attr' => bx_html_attribute($a['title']),
+            );
+        }
+
+        // get summary
+        $sLinkMore = ' <a title="' . bx_html_attribute(_t('_sys_read_more', $aData[$CNF['FIELD_TITLE']])) . '" href="' . $sUrl . '"><i class="sys-icon ellipsis-h"></i></a>';
+        $sSummary = strmaxtextlen($aData[$CNF['FIELD_TEXT']], (int)getParam($CNF['PARAM_CHARS_SUMMARY']), $sLinkMore);
+        $sSummaryPlain = BxTemplFunctions::getInstance()->getStringWithLimitedLength(strip_tags($sSummary), (int)getParam($CNF['PARAM_CHARS_SUMMARY_PLAIN']));
+
+        // generate html
+        $aVars = array (
+            'id' => $aData[$CNF['FIELD_ID']],
+            'content_url' => $sUrl,
+            'title' => bx_process_output($aData[$CNF['FIELD_TITLE']]),
+        	'summary' => $sSummary,
+            'author' => $oProfile->getDisplayName(),
+            'author_url' => $oProfile->getUrl(),
+            'entry_posting_date' => bx_time_js($aData[$CNF['FIELD_ADDED']], BX_FORMAT_DATE),
+            'module_name' => _t($CNF['T']['txt_sample_single']),
+            'ts' => $aData[$CNF['FIELD_ADDED']],
+            'bx_repeat:browse' => $aBrowseUnits,
+
+            'bx_if:thumb' => array (
+                'condition' => $aBrowseUnits,
+                'content' => array (
+                    'title' => bx_process_output($aData[$CNF['FIELD_TITLE']]),
+                    'summary_attr' => '',
+                    'content_url' => $sUrl,
+                    'thumb_url' => $aBrowseUnits ? $aBrowseUnits[0]['img_url'] : '',
+                    'gallery_url' => '',
+                    'strecher' => '',
+                ),
+            ),
+            'bx_if:no_thumb' => array (
+                'condition' => !$aBrowseUnits,
+                'content' => array (
+                    'content_url' => $sUrl,
+                    'summary_plain' => '',
+                    'strecher' => '',
+                ),
+            ),
+
+        );
+
+        return $this->parseHtmlByName($sTemplateName, $aVars);
+    }
+
+    function unitMedia ($aData, $isCheckPrivateContent = true, $sTemplateName = 'unit.html', $aParams = array())
+    {
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+
+        if (!($aMediaInfo = $oModule->_oDb->getMediaInfoById($aData['id'])))
+            return '';
+
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+        if (!($aFile = $oStorage->getFile($aMediaInfo['file_id'])))
+            return '';
+
+        $aVars = $this->mediaVars($aMediaInfo, $CNF['OBJECT_IMAGES_TRANSCODER_PREVIEW'], $CNF['OBJECT_VIDEOS_TRANSCODERS']['poster_preview'], $aParams);
+    
+        $aVarsTmp = $aVars['bx_if:image']['condition'] ? $aVars['bx_if:image']['content'] : $aVars['bx_if:video']['content'];
+
+        $aVars = array_merge($aVars, array (
+            'title' => $this->getMediaTitle($aData),
+            'module_name' => _t($CNF['T']['txt_media_single']),
+            'content_url' => $aVarsTmp['url'],
+            'ts' => $aFile['added'],
+            'actions' => $oModule->serviceMediaSocialSharing($aData['id'], true, false),
+            'bx_if:thumb' => array (
+                'condition' => $aData['file_id'],
+                'content' => array (
+                    'title' => bx_process_output($aData['title']),
+                    'summary_attr' => '',
+                    'content_url' => $aVarsTmp['url'],
+                    'thumb_url' => $aVarsTmp['url_img'],
+                    'gallery_url' => '',
+                    'strecher' => '',
+                ),
+            ),
+            'bx_if:no_thumb' => array (
+                'condition' => false,
+                'content' => array (),
+            ),
+
+        ));
+
+        return $this->parseHtmlByName($sTemplateName, $aVars);
+    }
+
+    function entryMediaView ($iMediaId, $mixedContext = false)
+    {
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+
+        if (!($aMediaInfo = $oModule->_oDb->getMediaInfoById($iMediaId)))
+            return '';
+
+        if (!($aAlbumInfo = $oModule->_oDb->getContentInfoById($aMediaInfo['content_id'])))
+            return '';        
+
+        $sUrlAlbum = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $aAlbumInfo[$CNF['FIELD_ID']]);
+         
+        $sText = $this->getMediaTitle($aMediaInfo);
+
+        $iProfileId = $aAlbumInfo[$CNF['FIELD_AUTHOR']];
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        if (!$oProfile) 
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+        $aVars = array(
+            'title' => $sText,
+            'album' => _t('_bx_albums_txt_media_album_link', $sUrlAlbum,  bx_process_output($aAlbumInfo[$CNF['FIELD_TITLE']]), $oProfile->getUrl(), $oProfile->getDisplayName()),
+        );
+
+        $aNextPrev = array (
+            'next' => $this->getNextPrevMedia($aMediaInfo, true, $mixedContext),
+            'prev' => $this->getNextPrevMedia($aMediaInfo, false, $mixedContext),
+        );
+
+        foreach ($aNextPrev as $k => $a) {
+            $aVars['bx_if:' . $k] = array (
+                'condition' => $a,
+                'content' => $a,
+            );
+            $aVars['bx_if:' . $k . '-na'] = array (
+                'condition' => !$a,
+                'content' => array(),
+            );
+        }
+
+        $aVars = array_merge($aVars, $this->mediaVars($aMediaInfo, $CNF['OBJECT_IMAGES_TRANSCODER_BIG'], $CNF['OBJECT_VIDEOS_TRANSCODERS']['poster'], array('context' => $mixedContext)));
+
+        return $this->parseHtmlByName('media-view.html', $aVars);
+    }
+
+    protected function mediaVars ($aMediaInfo, $sImageTranscoder = false, $sVideoPosterTranscoder = false, $aParams = array())
+    {
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+        $oTranscoder = BxDolTranscoderImage::getObjectInstance($sImageTranscoder);
+        $aTranscodersVideo = false;
+
+        if ($CNF['OBJECT_VIDEOS_TRANSCODERS'])
+            $aTranscodersVideo = array (
+                'poster' => BxDolTranscoderImage::getObjectInstance($sVideoPosterTranscoder),
+                'mp4' => BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_VIDEOS_TRANSCODERS']['mp4']),
+                'webm' => BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_VIDEOS_TRANSCODERS']['webm']),
+            );
+        
+        $aFileInfo = $oStorage->getFile ($aMediaInfo['file_id']);
+        if (!$aFileInfo)
+            return '';
+
+        $isImage = 0 === strncmp('image/', $aFileInfo['mime_type'], 6); // preview for images, transcoder object for preview must be defined
+        $isVideo = $aTranscodersVideo && (0 === strncmp('video/', $aFileInfo['mime_type'], 6)); // preview for videos, transcoder object for video must be defined
+
+        $sMediaTitle = bx_process_output($aMediaInfo['title']);
+        $sMediaTitleAttr = bx_html_attribute($aMediaInfo['title']);
+        $mixedContext = isset($aParams['context']) ? $aParams['context'] : '';
+        $sUrl = $this->getViewMediaUrl($CNF, $aMediaInfo['id'], $mixedContext);
+        $aSize = $aMediaInfo['data'] ? explode('x', $aMediaInfo['data']) : array(0, 0);
+
+        $aVars = array(
+            'bx_if:image' => array (
+                'condition' => $isImage,
+                'content' => array (
+                    'title_attr' => $sMediaTitleAttr,
+                    'title' => $sMediaTitle,
+                    'url' => $sUrl,
+                    'url_img' => $oTranscoder ? $oTranscoder->getFileUrl($aFileInfo['id']) : $oStorage->getFileUrlById($aFileInfo['id']),
+                    'media_id' => $aMediaInfo['id'],
+                    'w' => $aSize[0],
+                    'h' => $aSize[1],
+                    'context' => $mixedContext,
+                ),
+            ),
+            'bx_if:video' => array (
+                'condition' => $isVideo,
+                'content' => array (
+                    'title_attr' => $sMediaTitleAttr,
+                    'title' => $sMediaTitle,
+                    'url' => $sUrl,
+                    'url_img' => $isVideo ? $aTranscodersVideo['poster']->getFileUrl($aMediaInfo['file_id']) : '',
+                    'video' => $isVideo && $aTranscodersVideo ? BxTemplFunctions::getInstance()->videoPlayer(
+                        $aTranscodersVideo['poster']->getFileUrl($aMediaInfo['file_id']), 
+                        $aTranscodersVideo['mp4']->getFileUrl($aMediaInfo['file_id']), 
+                        $aTranscodersVideo['webm']->getFileUrl($aMediaInfo['file_id']),
+                        false, 'max-height:' . $CNF['OBJECT_VIDEO_TRANSCODER_HEIGHT']
+                    ) : '',
+                ),
+            ),
+        );
+
+        return $aVars;
+    }
+
+    public function getViewMediaUrl($CNF, $iMediaId, $sContext = '')
+    {
+        return BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_MEDIA'] . '&id=' . $iMediaId . (!empty($sContext) ? '&context=' . $sContext : ''));
+    }
+
+    public function getNextPrevMedia($aMediaInfo, $isNext, $sContext, $aParamsSearchResult = array())
+    {
+        if (!$sContext) {
+            $sContext = 'album';
+            $aParamsSearchResult = array('album_id' => $aMediaInfo['content_id']);
+        }
+
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+
+        bx_import('SearchResultMedia', $oModule->_aModule);
+        $sClass = $oModule->_aModule['class_prefix'] . 'SearchResultMedia';
+        $o = new $sClass($sContext, $aParamsSearchResult);
+        if ($o->isError)
+            return false;
+
+        $o->setUnitParams(array('context' => $sContext));
+
+        $a = $o->getNextPrevItem($aMediaInfo, $isNext);
+    
+        if ($a) {
+
+            $aVars = $this->mediaVars($a, $CNF['OBJECT_IMAGES_TRANSCODER_BIG'], $CNF['OBJECT_VIDEOS_TRANSCODERS']['poster'], array('context' => $sContext));
+            $aMedia = $aVars['bx_if:image']['condition'] ? $aVars['bx_if:image']['content'] : $aVars['bx_if:video']['content'];
+
+            $a['url'] = $aMedia['url'];
+            $a['url_img'] = $aMedia['url_img'];
+            $a['title_attr'] = $aMedia['title_attr'];
+            $a['title_js_string'] = bx_js_string($aMedia['title']);
+            $a['w'] = $aVars['bx_if:image']['condition'] ? $aVars['bx_if:image']['content']['w'] : 0;
+            $a['h'] = $aVars['bx_if:image']['condition'] ? $aVars['bx_if:image']['content']['h'] : 0;
+            $a['html'] = $aVars['bx_if:video']['condition'] ? '<div class="pswp__video">' . $aVars['bx_if:video']['content']['video'] . '</div>': 0;
+        }
+
+        return $a;
+    }
+
+    function entryAttachments ($aData)
+    {
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+
+        return $oModule->_serviceBrowse ('album', array('unit_view' => 'gallery', 'album_id' => $aData[$CNF['FIELD_ID']], 'author' => $aData[$CNF['FIELD_AUTHOR']]), BX_DB_PADDING_DEF, true, true, 'SearchResultMedia');
+    }
+
+    function mediaAuthor ($aMediaInfo, $iProfileId = false, $sFuncAuthorDesc = '', $sTemplateName = '') 
+    {
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+
+        if (!($aAlbumInfo = $oModule->_oDb->getContentInfoById($aMediaInfo['content_id'])))
+            return '';
+
+        return $this->entryAuthor ($aMediaInfo, $aAlbumInfo[$CNF['FIELD_AUTHOR']], 'getMediaDesc');
+    }
+
+    function getMediaDesc ($aData)
+    {
+        return _t('_sys_txt_n_by', bx_time_js($aData['added'], BX_FORMAT_DATE));
+    }
+
+    function getMediaTitle ($aMediaInfo)
+    {
+        $oModule = BxDolModule::getInstance($this->MODULE);
+        $CNF = &$oModule->_oConfig->CNF;
+         
+        $sText = bx_process_output($aMediaInfo['title']);
+        if (!empty($CNF['OBJECT_METATAGS_MEDIA'])) {
+            $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS_MEDIA']);
+    
+            if ($oMetatags->keywordsIsEnabled())
+                $sText = $oMetatags->keywordsParse($aMediaInfo['id'], $sText);
+        }
+
+        return $sText;
+    }
+
+    function mediaExif ($aMediaInfo, $iProfileId = false, $sFuncAuthorDesc = '', $sTemplateName = 'media-exif.html') 
+    {
+        if (!$aMediaInfo['exif'])
+            return '';
+
+        $a = unserialize($aMediaInfo['exif']);
+
+        $s = '';
+        if (!empty($a['Make'])) {
+            $oModule = BxDolModule::getInstance($this->MODULE);
+            $CNF = &$oModule->_oConfig->CNF;
+
+            $sCamera = BxDolMetatags::keywordsCameraModel($a);
+            if (!empty($CNF['OBJECT_METATAGS_MEDIA_CAMERA'])) {
+                $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS_MEDIA_CAMERA']);
+                if ($oMetatags->keywordsIsEnabled()) {
+                    $sCamera = $oMetatags->keywordsParseOne($aMediaInfo['id'], $sCamera);
+                }
+            }
+
+            if (!empty($sCamera))
+                $s .= $this->parseHtmlByName('media-exif-value.html', array(
+                    'key' => _t('_bx_albums_txt_media_album_camera'), 
+                    'val' => $sCamera,
+                ));
+        }
+
+        if (!empty($a['FocalLength']))
+            $s .= $this->parseHtmlByName('media-exif-value.html', array(
+                'key' => _t('_bx_albums_txt_media_album_focal_length'),
+                'val' => _t('_bx_albums_txt_media_album_focal_length_value', $a['FocalLength']),
+            ));
+
+        if (!empty($a['COMPUTED']['ApertureFNumber']))
+            $s .= $this->parseHtmlByName('media-exif-value.html', array(
+                'key' => _t('_bx_albums_txt_media_album_aperture'),
+                'val' => $a['COMPUTED']['ApertureFNumber'],
+            ));
+
+        if (!empty($a['ExposureTime']))
+            $s .= $this->parseHtmlByName('media-exif-value.html', array(
+                'key' => _t('_bx_albums_txt_media_album_shutter_speed'),
+                'val' => _t('_bx_albums_txt_media_album_shutter_speed_value', $a['ExposureTime']),
+            ));
+
+        if (!empty($a['ISOSpeedRatings']))
+            $s .= $this->parseHtmlByName('media-exif-value.html', array(
+                'key' => _t('_bx_albums_txt_media_album_iso'),
+                'val' => $a['ISOSpeedRatings'],
+            ));
+
+        if (empty($s))
+            return '';
+
+        return $this->parseHtmlByName($sTemplateName, array('content' => $s));
+    }
+
+}
+
+/** @} */

+ 33 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsUploaderCrop.php

@@ -0,0 +1,33 @@
+<?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 BxAlbumsUploaderCrop extends BxTemplUploaderCrop
+{
+    protected $_oModule;
+
+    public function __construct ($aObject, $sStorageObject, $sUniqId, $oTemplate)
+    {
+        parent::__construct($aObject, $sStorageObject, $sUniqId, $oTemplate);
+        $this->_oModule = BxDolModule::getInstance('bx_albums');
+    }
+
+    protected function getGhostTemplateVars($aFile, $iProfileId, $iContentId, $oStorage, $oImagesTranscoder)
+    {
+        $sTitle = $this->_oModule->_oDb->getFileTitle($aFile['id']);
+        $a = array(
+            'file_title' => $sTitle ? $sTitle : $aFile['file_name']
+        );
+        $a['file_title_attr'] = bx_html_attribute($a['file_title']);
+        return $a;
+    }
+}
+
+/** @} */

+ 33 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsUploaderHTML5.php

@@ -0,0 +1,33 @@
+<?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 BxAlbumsUploaderHTML5 extends BxTemplUploaderHTML5
+{
+    protected $_oModule;
+
+    public function __construct ($aObject, $sStorageObject, $sUniqId, $oTemplate)
+    {
+        parent::__construct($aObject, $sStorageObject, $sUniqId, $oTemplate);
+        $this->_oModule = BxDolModule::getInstance('bx_albums');
+    }
+
+    protected function getGhostTemplateVars($aFile, $iProfileId, $iContentId, $oStorage, $oImagesTranscoder)
+    {
+        $sTitle = $this->_oModule->_oDb->getFileTitle($aFile['id']);
+        $a = array(
+            'file_title' => $sTitle ? $sTitle : $aFile['file_name']
+        );
+        $a['file_title_attr'] = bx_html_attribute($a['file_title']);
+        return $a;
+    }
+}
+
+/** @} */

+ 33 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/classes/BxAlbumsUploaderSimple.php

@@ -0,0 +1,33 @@
+<?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 BxAlbumsUploaderSimple extends BxTemplUploaderSimple
+{
+    protected $_oModule;
+
+    public function __construct ($aObject, $sStorageObject, $sUniqId, $oTemplate)
+    {
+        parent::__construct($aObject, $sStorageObject, $sUniqId, $oTemplate);
+        $this->_oModule = BxDolModule::getInstance('bx_albums');
+    }
+
+    protected function getGhostTemplateVars($aFile, $iProfileId, $iContentId, $oStorage, $oImagesTranscoder)
+    {
+        $sTitle = $this->_oModule->_oDb->getFileTitle($aFile['id']);
+        $a = array(
+            'file_title' => $sTitle ? $sTitle : $aFile['file_name']
+        );
+        $a['file_title_attr'] = bx_html_attribute($a['file_title']);
+        return $a;
+    }
+}
+
+/** @} */

+ 130 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/install/config.php

@@ -0,0 +1,130 @@
+<?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' => '9.0.2',
+    'vendor' => 'BoonEx',
+	'help_url' => 'http://feed.boonex.com/?section={module_name}',
+
+    'compatible_with' => array(
+        '9.0.0-B3'
+    ),
+
+    /**
+     * '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'
+    ),
+
+    /**
+     * Storages.
+     */
+    '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_webm',
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_storages' => 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,
+    	'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'
+    ),
+
+);
+
+/** @} */

+ 20 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/install/installer.php

@@ -0,0 +1,20 @@
+<?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 BxAlbumsInstaller extends BxBaseModTextInstaller
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}
+
+/** @} */

+ 192 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/install/langs/en.xml

@@ -0,0 +1,192 @@
+<?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_media"><![CDATA[Images]]></string>
+	<string name="_bx_albums_media_cmts"><![CDATA[Images' Comments]]></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_rss_num"><![CDATA[Number of items in RSS feed]]></string>
+	<string name="_bx_albums_option_searchable_fields"><![CDATA[Searchable fields]]></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_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. Please, fill in this field]]></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_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_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_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_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[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_recent_entries"><![CDATA[Latest Albums]]></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_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[Top Images]]></string>
+    <string name="_bx_albums_page_title_entries_popular_media"><![CDATA[Top Images]]></string>
+    <string name="_bx_albums_page_block_title_popular_entries"><![CDATA[Popular]]></string>
+    <string name="_bx_albums_page_block_title_popular_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_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_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_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_my_entries"><![CDATA[My Albums]]></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_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[New Album]]></string>
+    <string name="_bx_albums_menu_item_title_create_entry"><![CDATA[New 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_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_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[Top Images]]></string>
+    <string name="_bx_albums_menu_item_title_entries_popular_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_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[{addon} 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 - {addon} hidden]]></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_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></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_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_more_actions"><![CDATA[More Actions]]></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_txt_all_entries_by"><![CDATA[<a href="{0}">All Albums by {1} ({2})</a>]]></string>
+    <string name="_bx_albums_txt_sample_single"><![CDATA[album]]></string>
+    <string name="_bx_albums_txt_sample_comment_single"><![CDATA[album's comment]]></string>
+    <string name="_bx_albums_txt_sample_vote_single"><![CDATA[album's vote]]></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_title_alt"><![CDATA[{0}'s media]]></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>
+</resources>

+ 395 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/install/sql/enable.sql

@@ -0,0 +1,395 @@
+
+-- 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_albums', '_bx_albums', 'bx_albums@modules/boonex/albums/|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_albums', '_bx_albums', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
+('bx_albums_summary_chars', '700', @iCategId, '_bx_albums_option_summary_chars', 'digit', '', '', '', 1),
+('bx_albums_plain_summary_chars', '200', @iCategId, '_bx_albums_option_plain_summary_chars', 'digit', '', '', '', 2),
+('bx_albums_per_page_browse', '12', @iCategId, '_bx_albums_option_per_page_browse', 'digit', '', '', '', 10),
+('bx_albums_rss_num', '10', @iCategId, '_bx_albums_option_rss_num', 'digit', '', '', '', 20),
+('bx_albums_searchable_fields', 'title,text', @iCategId, '_bx_albums_option_searchable_fields', 'list', '', '', 'a:2:{s:6:"module";s:9:"bx_albums";s:6:"method";s:21:"get_searchable_fields";}', 30);
+
+-- PAGE: create entry
+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_albums_create_entry', '_bx_albums_page_title_sys_create_entry', '_bx_albums_page_title_create_entry', 'bx_albums', 5, 2147483647, 1, 'create-album', 'page.php?i=create-album', '', '', '', 0, 1, 0, 'BxAlbumsPageBrowse', 'modules/boonex/albums/classes/BxAlbumsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_albums_create_entry', 1, 'bx_albums', '_bx_albums_page_block_title_create_entry', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_albums";s:6:"method";s:13:"entity_create";}', 0, 1, 1);
+
+
+-- PAGE: add images
+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_albums_add_images', '_bx_albums_page_title_sys_add_images', '_bx_albums_page_title_add_images', 'bx_albums', 5, 2147483647, 1, 'add-images', '', '', '', '', 0, 1, 0, 'BxAlbumsPageEntry', 'modules/boonex/albums/classes/BxAlbumsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_albums_add_images', 1, 'bx_albums', '_bx_albums_page_block_title_add_images', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_albums";s:6:"method";s:16:"entity_add_files";}', 0, 0, 0);
+
+-- PAGE: edit entry
+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_albums_edit_entry', '_bx_albums_page_title_sys_edit_entry', '_bx_albums_page_title_edit_entry', 'bx_albums', 5, 2147483647, 1, 'edit-album', '', '', '', '', 0, 1, 0, 'BxAlbumsPageEntry', 'modules/boonex/albums/classes/BxAlbumsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_albums_edit_entry', 1, 'bx_albums', '_bx_albums_page_block_title_edit_entry', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_albums";s:6:"method";s:11:"entity_edit";}', 0, 0, 0);
+
+
+-- PAGE: delete entry
+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_albums_delete_entry', '_bx_albums_page_title_sys_delete_entry', '_bx_albums_page_title_delete_entry', 'bx_albums', 5, 2147483647, 1, 'delete-album', '', '', '', '', 0, 1, 0, 'BxAlbumsPageEntry', 'modules/boonex/albums/classes/BxAlbumsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_albums_delete_entry', 1, 'bx_albums', '_bx_albums_page_block_title_delete_entry', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_albums";s:6:"method";s:13:"entity_delete";}', 0, 0, 0);
+
+
+-- PAGE: view entry
+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_albums_view_entry', '_bx_albums_page_title_sys_view_entry', '_bx_albums_page_title_view_entry', 'bx_albums', 10, 2147483647, 1, 'view-album', '', '', '', '', 0, 1, 0, 'BxAlbumsPageEntry', 'modules/boonex/albums/classes/BxAlbumsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_albums_view_entry', 4, 'bx_albums', '_bx_albums_page_block_title_entry_text', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:17:\"entity_text_block\";}', 0, 0, 1, 0),
+('bx_albums_view_entry', 4, 'bx_albums', '_bx_albums_page_block_title_entry_attachments', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:18:\"entity_attachments\";}', 0, 0, 1, 1),
+('bx_albums_view_entry', 4, 'bx_albums', '_bx_albums_page_block_title_entry_social_sharing', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:21:\"entity_social_sharing\";}', 0, 0, 1, 2),
+('bx_albums_view_entry', 4, 'bx_albums', '_bx_albums_page_block_title_entry_comments', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:15:\"entity_comments\";}', 0, 0, 1, 3),
+('bx_albums_view_entry', 4, 'bx_albums', '_bx_albums_page_block_title_entry_location', 3, 2147483647, 'service', 'a:4:{s:6:\"module\";s:6:\"system\";s:6:\"method\";s:13:\"locations_map\";s:6:\"params\";a:2:{i:0;s:9:\"bx_albums\";i:1;s:4:\"{id}\";}s:5:\"class\";s:20:\"TemplServiceMetatags\";}', 0, 0, 1, 4),
+
+('bx_albums_view_entry', 2, 'bx_albums', '_bx_albums_page_block_title_entry_author', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:13:\"entity_author\";}', 0, 0, 1, 0),
+
+('bx_albums_view_entry', 3, 'bx_albums', '_bx_albums_page_block_title_entry_info', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:11:\"entity_info\";}', 0, 0, 1, 0);
+
+
+-- PAGE: view media
+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_albums_view_media', 'view-album-media', '_bx_albums_page_title_sys_view_media', '_bx_albums_page_title_view_media', 'bx_albums', 10, 2147483647, 1, '', '', '', '', 0, 1, 0, 'BxAlbumsPageMedia', 'modules/boonex/albums/classes/BxAlbumsPageMedia.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_albums_view_media', 1, 'bx_albums', '', '_bx_albums_page_block_title_entry_view_media', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:10:\"media_view\";}', 0, 0, 1, 0),
+('bx_albums_view_media', 2, 'bx_albums', '', '_bx_albums_page_block_title_entry_author', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:12:\"media_author\";}', 0, 0, 1, 0),
+('bx_albums_view_media', 3, 'bx_albums', '', '_bx_albums_page_block_title_entry_social_sharing', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:20:\"media_social_sharing\";}', 0, 0, 1, 0),
+('bx_albums_view_media', 4, 'bx_albums', '', '_bx_albums_page_block_title_entry_comments', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:14:\"media_comments\";}', 0, 0, 1, 0),
+('bx_albums_view_media', 4, 'bx_albums', '', '_bx_albums_page_block_title_entry_view_media_exif', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:10:\"media_exif\";}', 0, 0, 1, 1);
+
+
+-- 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_albums_view_entry_comments', '_bx_albums_page_title_sys_view_entry_comments', '_bx_albums_page_title_view_entry_comments', 'bx_albums', 5, 2147483647, 1, 'view-album-comments', '', '', '', '', 0, 1, 0, 'BxAlbumsPageEntry', 'modules/boonex/albums/classes/BxAlbumsPageEntry.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_albums_view_entry_comments', 1, 'bx_albums', '_bx_albums_page_block_title_entry_comments', '_bx_albums_page_block_title_entry_comments_link', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:15:\"entity_comments\";}', 0, 0, 1);
+
+
+-- PAGE: popular albums
+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_albums_popular', '_bx_albums_page_title_sys_entries_popular', '_bx_albums_page_title_entries_popular', 'bx_albums', 5, 2147483647, 1, 'albums-popular', 'page.php?i=albums-popular', '', '', '', 0, 1, 0, 'BxAlbumsPageBrowse', 'modules/boonex/albums/classes/BxAlbumsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_albums_popular', 1, 'bx_albums', '_bx_albums_page_block_title_popular_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_albums";s:6:"method";s:14:"browse_popular";s:6:"params";a:3:{s:9:"unit_view";s:7:"gallery";s:13:"empty_message";b:1;s:13:"ajax_paginate";b:0;}}', 0, 1, 1);
+
+-- PAGE: popular media
+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_albums_popular_media', '_bx_albums_page_title_sys_entries_popular_media', '_bx_albums_page_title_entries_popular_media', 'bx_albums', 5, 2147483647, 1, 'albums-popular-media', 'page.php?i=albums-popular-media', '', '', '', 0, 1, 0, 'BxAlbumsPageBrowseMedia', 'modules/boonex/albums/classes/BxAlbumsPageBrowseMedia.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_albums_popular_media', 1, 'bx_albums', '_bx_albums_page_block_title_popular_media', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_albums";s:6:"method";s:20:"browse_popular_media";s:6:"params";a:3:{s:9:"unit_view";s:7:"gallery";s:13:"empty_message";b:1;s:13:"ajax_paginate";b:0;}}', 0, 1, 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_albums_author', 'albums-author', '_bx_albums_page_title_sys_entries_of_author', '_bx_albums_page_title_entries_of_author', 'bx_albums', 5, 2147483647, 1, '', '', '', '', 0, 1, 0, 'BxAlbumsPageAuthor', 'modules/boonex/albums/classes/BxAlbumsPageAuthor.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_albums_author', 1, 'bx_albums', '', '_bx_albums_page_block_title_entries_actions', 13, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:18:\"my_entries_actions\";}', 0, 0, 1, 1),
+('bx_albums_author', 1, 'bx_albums', '_bx_albums_page_block_title_sys_favorites_of_author', '_bx_albums_page_block_title_favorites_of_author', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:15:\"browse_favorite\";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 1, 0, 2),
+('bx_albums_author', 1, 'bx_albums', '_bx_albums_page_block_title_sys_favorites_of_author_media', '_bx_albums_page_block_title_favorites_of_author_media', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:21:\"browse_favorite_media\";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 1, 1, 3),
+('bx_albums_author', 1, 'bx_albums', '_bx_albums_page_block_title_sys_entries_of_author', '_bx_albums_page_block_title_entries_of_author', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:13:\"browse_author\";}', 0, 0, 1, 4);
+
+-- 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_albums_home', 'albums-home', '_bx_albums_page_title_sys_home', '_bx_albums_page_title_home', 'bx_albums', 2, 2147483647, 1, 'page.php?i=albums-home', '', '', '', 0, 1, 0, 'BxAlbumsPageBrowse', 'modules/boonex/albums/classes/BxAlbumsPageBrowse.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_albums_home', 1, 'bx_albums', '', '_bx_albums_page_block_title_updated_entries', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:14:\"browse_updated\";s:6:\"params\";a:3:{s:9:\"unit_view\";s:8:\"extended\";s:13:\"empty_message\";b:1;s:13:\"ajax_paginate\";b:0;}}', 0, 1, 1, 1),
+('bx_albums_home', 2, 'bx_albums', '', '_bx_albums_page_block_title_popular_keywords_albums', 11, 2147483647, 'service', 'a:4:{s:6:\"module\";s:6:\"system\";s:6:\"method\";s:14:\"keywords_cloud\";s:6:\"params\";a:3:{i:0;s:9:\"bx_albums\";i:1;s:9:\"bx_albums\";i:2;a:1:{s:10:\"show_empty\";b:1;}}s:5:\"class\";s:20:\"TemplServiceMetatags\";}', 1, 0, 1, 0),
+('bx_albums_home', 2, 'bx_albums', '', '_bx_albums_page_block_title_popular_keywords_media', 11, 2147483647, 'service', 'a:4:{s:6:\"module\";s:6:\"system\";s:6:\"method\";s:14:\"keywords_cloud\";s:6:\"params\";a:2:{i:0;s:15:\"bx_albums_media\";i:1;s:15:\"bx_albums_media\";}s:5:\"class\";s:20:\"TemplServiceMetatags\";}', 1, 0, 1, 1),
+('bx_albums_home', 2, 'bx_albums', '', '_bx_albums_page_block_title_popular_keywords_media_camera', 11, 2147483647, 'service', 'a:4:{s:6:\"module\";s:6:\"system\";s:6:\"method\";s:14:\"keywords_cloud\";s:6:\"params\";a:2:{i:0;s:22:\"bx_albums_media_camera\";i:1;s:22:\"bx_albums_media_camera\";}s:5:\"class\";s:20:\"TemplServiceMetatags\";}', 1, 0, 1, 2);
+
+-- PAGE: module manage 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_albums_manage', '_bx_albums_page_title_sys_manage', '_bx_albums_page_title_manage', 'bx_albums', 5, 2147483647, 1, 'albums-manage', 'page.php?i=albums-manage', '', '', '', 0, 1, 0, 'BxAlbumsPageBrowse', 'modules/boonex/albums/classes/BxAlbumsPageBrowse.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_albums_manage', 1, 'bx_albums', '_bx_albums_page_block_title_system_manage', '_bx_albums_page_block_title_manage', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:12:\"manage_tools\";}}', 0, 1, 0);
+
+-- 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_albums_administration', '_bx_albums_page_title_sys_manage_administration', '_bx_albums_page_title_manage', 'bx_albums', 5, 192, 1, 'albums-administration', 'page.php?i=albums-administration', '', '', '', 0, 1, 0, 'BxAlbumsPageBrowse', 'modules/boonex/albums/classes/BxAlbumsPageBrowse.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_albums_administration', 1, 'bx_albums', '_bx_albums_page_block_title_system_manage_administration', '_bx_albums_page_block_title_manage', 11, 192, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_albums\";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`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('sys_home', 1, 'bx_albums', '_bx_albums_page_block_title_recent_entries', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:13:\"browse_public\";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 1),
+('sys_home', 1, 'bx_albums', '_bx_albums_page_block_title_updated_entries', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_albums\";s:6:\"method\";s:14:\"browse_updated\";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 2);
+
+-- 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_albums', '_bx_albums_page_block_title_my_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_albums";s:6:"method";s:13:"browse_author";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 0);
+
+-- 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_albums', '', '_bx_albums_page_block_title_recent_media', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_albums";s:6:"method";s:20:"browse_popular_media";s:6:"params";a:3:{s:9:"unit_view";s:7:"gallery";s:13:"empty_message";b:1;s:13:"ajax_paginate";b:1;}}', 0, 1, 1, @iBlockOrder + 1),
+('', 0, 'bx_albums', '', '_bx_albums_page_block_title_popular_keywords_albums', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:14:"keywords_cloud";s:6:"params";a:2:{i:0;s:9:"bx_albums";i:1;s:9:"bx_albums";}s:5:"class";s:20:"TemplServiceMetatags";}', 0, 1, 1, @iBlockOrder + 2),
+('', 0, 'bx_albums', '', '_bx_albums_page_block_title_popular_keywords_media', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:14:"keywords_cloud";s:6:"params";a:2:{i:0;s:15:"bx_albums_media";i:1;s:15:"bx_albums_media";}s:5:"class";s:20:"TemplServiceMetatags";}', 0, 1, 1, @iBlockOrder + 3),
+('', 0, 'bx_albums', '', '_bx_albums_page_block_title_popular_keywords_media_camera', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:14:"keywords_cloud";s:6:"params";a:2:{i:0;s:22:"bx_albums_media_camera";i:1;s:22:"bx_albums_media_camera";}s:5:"class";s:20:"TemplServiceMetatags";}', 0, 1, 1, @iBlockOrder + 4),
+('', 0, 'bx_albums', '', '_bx_albums_page_block_title_recent_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_albums";s:6:"method";s:13:"browse_public";s:6:"params";a:3:{s:9:"unit_view";s:8:"extended";s:13:"empty_message";b:1;s:13:"ajax_paginate";b:0;}}', 0, 1, 1, @iBlockOrder + 5);
+
+
+-- MENU: add to site menu
+
+SET @iSiteMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_site' 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_site', 'bx_albums', 'albums-home', '_bx_albums_menu_item_title_system_entries_home', '_bx_albums_menu_item_title_entries_home', 'page.php?i=albums-home', '', '', 'picture-o col-blue1', 'bx_albums_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_albums', 'albums-home', '_bx_albums_menu_item_title_system_entries_home', '_bx_albums_menu_item_title_entries_home', 'page.php?i=albums-home', '', '', 'picture-o col-blue1', 'bx_albums_submenu', 2147483647, 1, 1, IFNULL(@iHomepageMenuOrder, 0) + 1);
+
+-- MENU: add to "add content" menu
+
+SET @iAddMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_add_content_links' 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_add_content_links', 'bx_albums', 'create-album', '_bx_albums_menu_item_title_system_create_entry', '_bx_albums_menu_item_title_create_entry', 'page.php?i=create-album', '', '', 'picture-o col-blue1', '', 2147483647, 1, 1, IFNULL(@iAddMenuOrder, 0) + 1);
+
+
+-- MENU: actions 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_albums_view', '_bx_albums_menu_title_view_entry', 'bx_albums_view', 'bx_albums', 9, 0, 1, 'BxAlbumsMenuView', 'modules/boonex/albums/classes/BxAlbumsMenuView.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_albums_view', 'bx_albums', '_bx_albums_menu_set_title_view_entry', 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_albums_view', 'bx_albums', 'add-images-to-album', '_bx_albums_menu_item_title_system_add_images', '_bx_albums_menu_item_title_add_images', 'page.php?i=add-images&id={content_id}', '', '', 'plus', '', 2147483647, 1, 0, 10),
+('bx_albums_view', 'bx_albums', 'edit-album', '_bx_albums_menu_item_title_system_edit_entry', '_bx_albums_menu_item_title_edit_entry', 'page.php?i=edit-album&id={content_id}', '', '', 'pencil', '', 2147483647, 1, 0, 20),
+('bx_albums_view', 'bx_albums', 'delete-album', '_bx_albums_menu_item_title_system_delete_entry', '_bx_albums_menu_item_title_delete_entry', 'page.php?i=delete-album&id={content_id}', '', '', 'remove', '', 2147483647, 1, 0, 30);
+
+-- MENU: actions menu for view media
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_albums_view_media', '_bx_albums_menu_title_view_media', 'bx_albums_view_media', 'bx_albums', 9, 0, 1, 'BxAlbumsMenuView', 'modules/boonex/albums/classes/BxAlbumsMenuView.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_albums_view_media', 'bx_albums', '_bx_albums_menu_set_title_view_media', 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_albums_view_media', 'bx_albums', 'add-images-to-album', '_bx_albums_menu_item_title_system_add_images', '_bx_albums_menu_item_title_add_images', 'page.php?i=add-images&id={content_id}', '', '', 'plus', '', 2147483647, 1, 0, 10),
+('bx_albums_view_media', 'bx_albums', 'edit-album', '_bx_albums_menu_item_title_system_edit_album', '_bx_albums_menu_item_title_edit_album', 'page.php?i=edit-album&id={content_id}', '', '', 'pencil', '', 2147483647, 1, 0, 20);
+
+-- 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_albums_my', '_bx_albums_menu_title_entries_my', 'bx_albums_my', 'bx_albums', 9, 0, 1, 'BxAlbumsMenu', 'modules/boonex/albums/classes/BxAlbumsMenu.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_albums_my', 'bx_albums', '_bx_albums_menu_set_title_entries_my', 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_albums_my', 'bx_albums', 'create-album', '_bx_albums_menu_item_title_system_create_entry', '_bx_albums_menu_item_title_create_entry', 'page.php?i=create-album', '', '', 'plus', '', 2147483647, 1, 0, 0);
+
+
+-- 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_albums_submenu', '_bx_albums_menu_title_submenu', 'bx_albums_submenu', 'bx_albums', 8, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_albums_submenu', 'bx_albums', '_bx_albums_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_albums_submenu', 'bx_albums', 'albums-home', '_bx_albums_menu_item_title_system_entries_recent', '_bx_albums_menu_item_title_entries_recent', 'page.php?i=albums-home', '', '', '', '', 2147483647, 1, 1, 1),
+('bx_albums_submenu', 'bx_albums', 'albums-popular', '_bx_albums_menu_item_title_system_entries_popular', '_bx_albums_menu_item_title_entries_popular', 'page.php?i=albums-popular', '', '', '', '', 2147483647, 1, 1, 2),
+('bx_albums_submenu', 'bx_albums', 'albums-popular-media', '_bx_albums_menu_item_title_system_entries_popular_media', '_bx_albums_menu_item_title_entries_popular_media', 'page.php?i=albums-popular-media', '', '', '', '', 2147483647, 1, 1, 3),
+('bx_albums_submenu', 'bx_albums', 'albums-manage', '_bx_albums_menu_item_title_system_entries_manage', '_bx_albums_menu_item_title_entries_manage', 'page.php?i=albums-manage', '', '', '', '', 2147483646, 1, 1, 4);
+
+-- MENU: sub-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_albums_view_submenu', '_bx_albums_menu_title_view_entry_submenu', 'bx_albums_view_submenu', 'bx_albums', 8, 0, 1, 'BxAlbumsMenuView', 'modules/boonex/albums/classes/BxAlbumsMenuView.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_albums_view_submenu', 'bx_albums', '_bx_albums_menu_set_title_view_entry_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_albums_view_submenu', 'bx_albums', 'view-album', '_bx_albums_menu_item_title_system_view_entry', '_bx_albums_menu_item_title_view_entry_submenu_entry', 'page.php?i=view-album&id={content_id}', '', '', '', '', 2147483647, 1, 0, 1),
+('bx_albums_view_submenu', 'bx_albums', 'view-album-comments', '_bx_albums_menu_item_title_system_view_entry_comments', '_bx_albums_menu_item_title_view_entry_submenu_comments', 'page.php?i=view-album-comments&id={content_id}', '', '', '', '', 2147483647, 1, 0, 2);
+
+-- MENU: profile stats
+SET @iNotifMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_profile_stats' AND `active` = 1 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_profile_stats', 'bx_albums', 'profile-stats-manage-albums', '_bx_albums_menu_item_title_system_manage_my_albums', '_bx_albums_menu_item_title_manage_my_albums', 'page.php?i=albums-manage', '', '_self', 'picture-o col-blue1', 'a:2:{s:6:"module";s:9:"bx_albums";s:6:"method";s:41:"get_menu_addon_manage_tools_profile_stats";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 1);
+
+-- 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_albums_menu_manage_tools', '_bx_albums_menu_title_manage_tools', 'bx_albums_menu_manage_tools', 'bx_albums', 6, 0, 1, 'BxAlbumsMenuManageTools', 'modules/boonex/albums/classes/BxAlbumsMenuManageTools.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_albums_menu_manage_tools', 'bx_albums', '_bx_albums_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_albums_menu_manage_tools', 'bx_albums', 'delete-with-content', '_bx_albums_menu_item_title_system_delete_with_content', '_bx_albums_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'trash-o', '', 128, 1, 0, 0);
+
+-- 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_albums', 'albums-administration', '_bx_albums_menu_item_title_system_admt_albums', '_bx_albums_menu_item_title_admt_albums', 'page.php?i=albums-administration', '', '_self', '', 'a:2:{s:6:"module";s:9:"bx_albums";s:6:"method";s:27:"get_menu_addon_manage_tools";}', '', 192, 1, 0, @iManageMenuOrder + 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_albums', 'albums-author', '_bx_albums_menu_item_title_system_view_entries_author', '_bx_albums_menu_item_title_view_entries_author', 'page.php?i=albums-author&profile_id={profile_id}', '', '', 'picture-o col-blue1', '', 2147483647, 1, 0, 0);
+
+
+-- PRIVACY 
+
+INSERT INTO `sys_objects_privacy` (`object`, `module`, `action`, `title`, `default_group`, `table`, `table_field_id`, `table_field_author`, `override_class_name`, `override_class_file`) VALUES
+('bx_albums_allow_view_to', 'bx_albums', 'view', '_bx_albums_form_entry_input_allow_view_to', '3', 'bx_albums_albums', 'id', 'author', '', '');
+
+
+-- ACL
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_albums', 'create entry', NULL, '_bx_albums_acl_action_create_entry', '', 1, 3);
+SET @iIdActionEntryCreate = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_albums', 'delete entry', NULL, '_bx_albums_acl_action_delete_entry', '', 1, 3);
+SET @iIdActionEntryDelete = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_albums', 'view entry', NULL, '_bx_albums_acl_action_view_entry', '', 1, 0);
+SET @iIdActionEntryView = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_albums', 'set thumb', NULL, '_bx_albums_acl_action_set_thumb', '', 1, 3);
+SET @iIdActionSetThumb = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_albums', 'edit any entry', NULL, '_bx_albums_acl_action_edit_any_entry', '', 1, 3);
+SET @iIdActionEntryEditAny = 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
+
+-- entry create
+(@iStandard, @iIdActionEntryCreate),
+(@iModerator, @iIdActionEntryCreate),
+(@iAdministrator, @iIdActionEntryCreate),
+(@iPremium, @iIdActionEntryCreate),
+
+-- entry delete
+(@iStandard, @iIdActionEntryDelete),
+(@iModerator, @iIdActionEntryDelete),
+(@iAdministrator, @iIdActionEntryDelete),
+(@iPremium, @iIdActionEntryDelete),
+
+-- entry view
+(@iUnauthenticated, @iIdActionEntryView),
+(@iAccount, @iIdActionEntryView),
+(@iStandard, @iIdActionEntryView),
+(@iUnconfirmed, @iIdActionEntryView),
+(@iPending, @iIdActionEntryView),
+(@iModerator, @iIdActionEntryView),
+(@iAdministrator, @iIdActionEntryView),
+(@iPremium, @iIdActionEntryView),
+
+-- set entry thumb
+(@iStandard, @iIdActionSetThumb),
+(@iModerator, @iIdActionSetThumb),
+(@iAdministrator, @iIdActionSetThumb),
+(@iPremium, @iIdActionSetThumb),
+
+-- edit any entry
+(@iModerator, @iIdActionEntryEditAny),
+(@iAdministrator, @iIdActionEntryEditAny);
+
+
+-- SEARCH
+SET @iSearchOrder = (SELECT IFNULL(MAX(`Order`), 0) FROM `sys_objects_search`);
+INSERT INTO `sys_objects_search` (`ObjectName`, `Title`, `Order`, `GlobalSearch`, `ClassName`, `ClassPath`) VALUES
+('bx_albums', '_bx_albums', @iSearchOrder + 1, 1, 'BxAlbumsSearchResult', 'modules/boonex/albums/classes/BxAlbumsSearchResult.php'),
+('bx_albums_cmts', '_bx_albums_cmts', @iSearchOrder + 2, 1, 'BxAlbumsCmtsSearchResult', 'modules/boonex/albums/classes/BxAlbumsCmtsSearchResult.php'),
+('bx_albums_media', '_bx_albums_media', @iSearchOrder + 3, 1, 'BxAlbumsSearchResultMedia', 'modules/boonex/albums/classes/BxAlbumsSearchResultMedia.php'),
+('bx_albums_media_cmts', '_bx_albums_media_cmts', @iSearchOrder + 4, 1, 'BxAlbumsCmtsSearchResultMedia', 'modules/boonex/albums/classes/BxAlbumsCmtsSearchResultMedia.php'),
+('bx_albums_media_camera', '_bx_albums_media', @iSearchOrder + 5, 0, 'BxAlbumsSearchResultMediaCamera', 'modules/boonex/albums/classes/BxAlbumsSearchResultMediaCamera.php');
+
+-- METATAGS
+INSERT INTO `sys_objects_metatags` (`object`, `table_keywords`, `table_locations`, `table_mentions`, `override_class_name`, `override_class_file`) VALUES
+('bx_albums', 'bx_albums_meta_keywords', 'bx_albums_meta_locations', '', '', ''),
+('bx_albums_media', 'bx_albums_meta_keywords_media', '', '', '', ''),
+('bx_albums_media_camera', 'bx_albums_meta_keywords_media_camera', '', '', '', '');
+
+-- GRIDS: moderation tools
+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_albums_administration', 'Sql', 'SELECT * FROM `bx_albums_albums` WHERE 1 ', 'bx_albums_albums', 'id', 'added', 'status_admin', '', 20, NULL, 'start', '', 'title,text', '', 'like', '', '', 192, 'BxAlbumsGridAdministration', 'modules/boonex/albums/classes/BxAlbumsGridAdministration.php'),
+('bx_albums_common', 'Sql', 'SELECT * FROM `bx_albums_albums` WHERE 1 ', 'bx_albums_albums', 'id', 'added', 'status', '', 20, NULL, 'start', '', 'title,text', '', 'like', '', '', 2147483647, 'BxAlbumsGridCommon', 'modules/boonex/albums/classes/BxAlbumsGridCommon.php');
+
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_albums_administration', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_albums_administration', 'switcher', '_bx_albums_grid_column_title_adm_active', '8%', 0, '', '', 2),
+('bx_albums_administration', 'title', '_bx_albums_grid_column_title_adm_title', '25%', 0, '', '', 3),
+('bx_albums_administration', 'added', '_bx_albums_grid_column_title_adm_added', '20%', 1, '25', '', 4),
+('bx_albums_administration', 'author', '_bx_albums_grid_column_title_adm_author', '25%', 0, '25', '', 5),
+('bx_albums_administration', 'actions', '', '20%', 0, '', '', 6),
+('bx_albums_common', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_albums_common', 'switcher', '', '8%', 0, '', '', 2),
+('bx_albums_common', 'title', '_bx_albums_grid_column_title_adm_title', '40%', 0, '', '', 3),
+('bx_albums_common', 'added', '_bx_albums_grid_column_title_adm_added', '30%', 1, '25', '', 4),
+('bx_albums_common', 'actions', '', '20%', 0, '', '', 5);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_albums_administration', 'bulk', 'delete', '_bx_albums_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_albums_administration', 'single', 'edit', '_bx_albums_grid_action_title_adm_edit', 'pencil', 1, 0, 1),
+('bx_albums_administration', 'single', 'delete', '_bx_albums_grid_action_title_adm_delete', 'remove', 1, 1, 2),
+('bx_albums_administration', 'single', 'settings', '_bx_albums_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
+('bx_albums_common', 'bulk', 'delete', '_bx_albums_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_albums_common', 'single', 'edit', '_bx_albums_grid_action_title_adm_edit', 'pencil', 1, 0, 1),
+('bx_albums_common', 'single', 'delete', '_bx_albums_grid_action_title_adm_delete', 'remove', 1, 1, 2),
+('bx_albums_common', 'single', 'settings', '_bx_albums_grid_action_title_adm_more_actions', 'cog', 1, 0, 3);
+
+-- UPLOADERS
+INSERT INTO `sys_objects_uploader` (`object`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_albums_simple', 1, 'BxAlbumsUploaderSimple', 'modules/boonex/albums/classes/BxAlbumsUploaderSimple.php'),
+('bx_albums_html5', 1, 'BxAlbumsUploaderHTML5', 'modules/boonex/albums/classes/BxAlbumsUploaderHTML5.php'),
+('bx_albums_crop', 1, 'BxAlbumsUploaderCrop', 'modules/boonex/albums/classes/BxAlbumsUploaderCrop.php');
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers` (`name`, `class`, `file`, `service_call`) VALUES 
+('bx_albums', 'BxAlbumsAlertsResponse', 'modules/boonex/albums/classes/BxAlbumsAlertsResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('system', 'save_setting', @iHandler),
+('bx_albums_files', 'file_deleted', @iHandler),
+('profile', 'delete', @iHandler);

+ 379 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/install/sql/install.sql

@@ -0,0 +1,379 @@
+
+-- 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,
+  `views` int(11) NOT NULL default '0',
+  `rate` float NOT NULL default '0',
+  `votes` 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',
+  `allow_view_to` int(11) NOT NULL DEFAULT '3',
+  `status` enum('active','hidden') NOT NULL DEFAULT 'active',
+  `status_admin` enum('active','hidden') 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(255) 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` 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(255) 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` 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_files2albums` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `content_id` int(10) unsigned NOT NULL,
+  `file_id` int(11) NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `views` int(11) NOT NULL,
+  `rate` float NOT NULL,
+  `votes` int(11) NOT NULL,
+  `favorites` int(11) NOT NULL default '0',
+  `comments` int(11) NOT NULL,
+  `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(10) unsigned 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',
+  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(10) unsigned 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',
+  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` (
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  UNIQUE KEY `object_id` (`object_id`)
+) ENGINE=MYISAM DEFAULT CHARSET=utf8;
+
+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`)
+) ENGINE=MYISAM DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_albums_votes_media` (
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  UNIQUE KEY `object_id` (`object_id`)
+) ENGINE=MYISAM DEFAULT CHARSET=utf8;
+
+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`)
+) ENGINE=MYISAM DEFAULT CHARSET=utf8;
+
+-- TABLE: views
+
+CREATE TABLE `bx_albums_views_track` (
+  `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',
+  KEY `id` (`object_id`,`viewer_id`,`viewer_nip`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+CREATE TABLE `bx_albums_views_media_track` (
+  `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',
+  KEY `id` (`object_id`,`viewer_id`,`viewer_nip`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- TABLE: metas
+
+CREATE TABLE `bx_albums_meta_keywords` (
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+CREATE TABLE `bx_albums_meta_keywords_media` (
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+CREATE TABLE `bx_albums_meta_keywords_media_camera` (
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+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))
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+-- TABLE: reports
+CREATE TABLE IF NOT EXISTS `bx_albums_reports` (
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  UNIQUE KEY `object_id` (`object_id`)
+) ENGINE=MYISAM DEFAULT CHARSET=utf8;
+
+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',
+  PRIMARY KEY (`id`),
+  KEY `report` (`object_id`, `author_nip`)
+) ENGINE=MYISAM DEFAULT CHARSET=utf8;
+
+-- TABLE: favorites
+CREATE TABLE `bx_albums_favorites_track` (
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  KEY `id` (`object_id`,`author_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+CREATE TABLE `bx_albums_favorites_media_track` (
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  KEY `id` (`object_id`,`author_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+
+-- STORAGES & TRANSCODERS
+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', 'Local', '', 360, 2592000, 3, 'bx_albums_files', 'allow-deny', 'jpg,jpeg,jpe,gif,png,avi,flv,mpg,mpeg,wmv,mp4,m4v,mov,qt,divx,xvid,3gp,3g2,webm,mkv,ogv,ogg,rm,rmvb,asf,drc', '', 0, 0, 0, 0, 0, 0),
+('bx_albums_photos_resized', 'Local', '', 360, 2592000, 3, 'bx_albums_photos_resized', 'allow-deny', 'jpg,jpeg,jpe,gif,png,avi,flv,mpg,mpeg,wmv,mp4,m4v,mov,qt,divx,xvid,3gp,3g2,webm,mkv,ogv,ogg,rm,rmvb,asf,drc', '', 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_webm', '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:20:"bx_albums_video_webm";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:20:"bx_albums_video_webm";}}', '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:20:"bx_albums_video_webm";}}', '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_webm', 'Webm', 'a:2:{s:1:"h";s:3:"480";s:10:"force_type";s:4:"webm";}', 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', 'title', '', '', 0, 'text', '_bx_albums_form_entry_input_sys_title', '_bx_albums_form_entry_input_title', '', 1, 0, 0, '', '', '', 'avail', '', '_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:14:"bx_albums_crop";}', 'a:3:{s:16:"bx_albums_simple";s:26:"_sys_uploader_simple_title";s:15:"bx_albums_html5";s:25:"_sys_uploader_html5_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', '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, '', '', '', '', '', '', '', '', 0, 0),
+('bx_albums', 'bx_albums', 'location', '', '', 0, 'location', '_sys_form_input_sys_location', '_sys_form_input_location', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 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);
+
+
+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', 'location', 2147483647, 1, 6),
+('bx_albums_entry_add', 'do_submit', 2147483647, 1, 7),
+
+('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, 0, 4),
+('bx_albums_entry_edit', 'allow_view_to', 2147483647, 1, 5),
+('bx_albums_entry_edit', 'location', 2147483647, 1, 6),
+('bx_albums_entry_edit', 'do_submit', 2147483647, 1, 7),
+
+('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);
+
+-- COMMENTS
+
+INSERT INTO `sys_objects_cmts` (`Name`, `Table`, `CharsPostMin`, `CharsPostMax`, `CharsDisplayMax`, `Nl2br`, `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_cmts', 1, 5000, 1000, 1, 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_media', 'bx_albums_cmts_media', 1, 5000, 1000, 1, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-album-media&id={object_id}', '', 'bx_albums_files2albums', 'id', '', 'title', 'comments', '', '');
+
+-- VOTES
+
+INSERT INTO `sys_objects_vote` (`Name`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
+('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_votes_media', 'bx_albums_votes_media_track', '604800', '1', '1', '0', '1', 'bx_albums_files2albums', 'id', '', 'rate', 'votes', '', '');
+
+-- REPORTS
+
+INSERT INTO `sys_objects_report` (`name`, `table_main`, `table_track`, `is_on`, `base_url`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_albums', 'bx_albums_reports', 'bx_albums_reports_track', '1', 'page.php?i=view-album&id={object_id}', 'bx_albums_albums', 'id', 'author', 'reports', '', '');
+
+-- VIEWS
+
+INSERT INTO `sys_objects_view` (`name`, `table_track`, `period`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_albums', 'bx_albums_views_track', '86400', '1', 'bx_albums_albums', 'id', 'views', '', ''),
+('bx_albums_media', 'bx_albums_views_media_track', '86400', '1', 'bx_albums_files2albums', 'id', 'views', '', '');
+
+-- FAFORITES
+INSERT INTO `sys_objects_favorite` (`name`, `table_track`, `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', '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', '', 'favorites', '', '');
+
+
+-- 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`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_albums', '{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));
+

+ 41 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/install/sql/uninstall.sql

@@ -0,0 +1,41 @@
+
+-- TABLES
+
+DROP TABLE IF EXISTS `bx_albums_albums`, `bx_albums_files`, `bx_albums_photos_resized`, `bx_albums_files2albums`, `bx_albums_cmts`, `bx_albums_cmts_media`, `bx_albums_votes`, `bx_albums_votes_track`, `bx_albums_votes_media`, `bx_albums_votes_media_track`, `bx_albums_views_track`, `bx_albums_views_media_track`, `bx_albums_meta_keywords`, `bx_albums_meta_keywords_media`, `bx_albums_meta_keywords_media_camera`, `bx_albums_meta_locations`, `bx_albums_reports`, `bx_albums_reports_track`, `bx_albums_favorites_track`, `bx_albums_favorites_media_track`;
+
+-- STORAGES & TRANSCODERS
+
+DELETE FROM `sys_objects_storage` WHERE `object` = 'bx_albums_files' OR `object` = 'bx_albums_photos_resized';
+DELETE FROM `sys_storage_tokens` WHERE `object` = 'bx_albums_files' OR `object` = 'bx_albums_photos_resized';
+
+DELETE FROM `sys_objects_transcoder` WHERE `object` IN('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_webm', 'bx_albums_proxy_preview', 'bx_albums_proxy_browse', 'bx_albums_proxy_cover');
+DELETE FROM `sys_transcoder_filters` WHERE `transcoder_object` IN('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_webm');
+DELETE FROM `sys_transcoder_images_files` WHERE `transcoder_object` IN('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_webm');
+
+-- FORMS
+
+DELETE FROM `sys_objects_form` WHERE `module` = 'bx_albums';
+DELETE FROM `sys_form_displays` WHERE `module` = 'bx_albums';
+DELETE FROM `sys_form_inputs` WHERE `module` = 'bx_albums';
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN ('bx_albums_entry_add', 'bx_albums_entry_edit', 'bx_albums_entry_add_images', 'bx_albums_entry_view', 'bx_albums_entry_delete');
+
+-- COMMENTS
+DELETE FROM `sys_objects_cmts` WHERE `Name` = 'bx_albums' OR `Name` = 'bx_albums_media';
+
+-- VOTES
+DELETE FROM `sys_objects_vote` WHERE `Name` = 'bx_albums' OR `Name` = 'bx_albums_media';
+
+-- REPORTS
+DELETE FROM `sys_objects_report` WHERE `name` = 'bx_albums';
+
+-- VIEWS
+DELETE FROM `sys_objects_view` WHERE `name` = 'bx_albums' OR `Name` = 'bx_albums_media';
+
+-- FAFORITES
+DELETE FROM `sys_objects_favorite` WHERE `name` IN ('bx_albums', 'bx_albums_media');
+
+
+-- STUDIO: page & widget
+DELETE FROM `tp`, `tw`, `tpw`
+USING `sys_std_pages` AS `tp`, `sys_std_widgets` AS `tw`, `sys_std_pages_widgets` AS `tpw`
+WHERE `tp`.`id` = `tw`.`page_id` AND `tw`.`id` = `tpw`.`widget_id` AND `tp`.`name` = 'bx_albums';

+ 36 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/js/main.js

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Albums Albums
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxAlbumsMain(oOptions) {
+	this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxAlbumsMain' : oOptions.sObjName;
+
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+
+    var $this = this;
+    $(document).ready(function() {
+    	$this.init();
+    });
+}
+
+BxAlbumsMain.prototype.init = function() {
+	$('.bx-albums-unit-images').flickity({
+		cellSelector: '.bx-albums-unit-image',
+		cellAlign: 'left',
+		pageDots: false,
+		imagesLoaded: true
+	});
+};
+
+/** @} */

+ 51 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/js/manage_tools.js

@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Albums Albums
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxAlbumsManageTools(oOptions) {
+	this._sActionsUrl = oOptions.sActionUrl;
+	this._sObjNameGrid = oOptions.sObjNameGrid;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxAlbumsManageTools' : oOptions.sObjName;
+
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._sParamsDivider = oOptions.sParamsDivider == undefined ? '#-#' : oOptions.sParamsDivider;
+
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxAlbumsManageTools.prototype.onChangeFilter = function(oFilter) {
+	var oFilter1 = $('#bx-grid-filter1-' + this._sObjNameGrid);
+	var sValueFilter1 = oFilter1.length > 0 ? oFilter1.val() : '';
+
+	var oSearch = $('#bx-grid-search-' + this._sObjNameGrid);
+	var sValueSearch = oSearch.length > 0 ? oSearch.val() : '';
+	if(sValueSearch == _t('_sys_grid_search'))
+		sValueSearch = '';
+
+	glGrids[this._sObjNameGrid].setFilter(sValueFilter1 + this._sParamsDivider + sValueSearch, true);
+};
+
+BxAlbumsManageTools.prototype.onClickSettings = function(sMenuObject, oButton) {
+	if($(oButton).hasClass('bx-btn-disabled'))
+		return false;
+
+	bx_menu_popup(sMenuObject, oButton, {}, {
+		content_id: $(oButton).attr('bx_grid_action_data')
+	});
+};
+
+BxAlbumsManageTools.prototype.onClickDelete = function(iContentId) {
+	$('.bx-popup-applied:visible').dolPopupHide();
+
+	glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete', {}, '', false, 1);
+};
+
+/** @} */

+ 239 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/js/media_view.js

@@ -0,0 +1,239 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Albums Albums
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+Array.prototype.indexOfMediaObject = function (o) {
+    var l = this.length;
+    for (var i=0 ; i < l ; ++i) {
+        if (o['media-id'] == this[i]['media-id'])
+            return i;
+    }
+    return -1;
+}
+
+function bx_albums_open_gallery(eve, e, sContext) {
+    var ePswp = document.querySelectorAll('.pswp')[0];
+    var $e = $(e);
+    var aItems = []; // more items are added dynamically
+    var oItem = {};
+    var iCount = 0;
+    var options = {
+        bx_albums_context: sContext,
+        shareEl: false,
+        counterEl: false,
+        history: false,
+        loop: false,
+        showHideOpacity: true,
+        index: 0,
+        getThumbBoundsFn: function(index) {
+            var e = $('a[data-media-id] img');
+            if (!e.length)
+                return false;
+            return {x:e.offset().left, y:e.offset().top, w:e.width()};
+        }
+    };
+    var fnProcessRetina = function (o) {
+        var dpr = ((window.glBxDisableRetina !== undefined && window.glBxDisableRetina) || window.devicePixelRatio === undefined ? 1 : window.devicePixelRatio);
+        if (dpr < 2)
+            return o;
+        o.w *= 2;
+        o.h *= 2;
+        return o;
+    }
+
+    eve.preventDefault ? eve.preventDefault() : eve.returnValue = false;
+
+    
+
+    // get data for initial item from the attributes of the item which was clicked
+    $.each(e.attributes, function(i, attr) {
+        var sName = attr.name;
+        if (sName.indexOf('data-') !== 0)
+            return;
+        sName = sName.replace('data-', '');
+        oItem[sName] = attr.value;        
+
+        ++iCount;
+    });
+
+    oItem.msrc = oItem.src;
+    oItem = fnProcessRetina(oItem);
+
+    if (!iCount) // no image - no concert!
+        return false;
+
+    aItems.push(oItem);
+
+    var fnConverMedia = function (oMedia) {
+        var oMap = {
+            'id': 'media-id',
+            'url': 'url',
+            'url_img' : 'src',
+            'w': 'w',
+            'h': 'h',
+            'title': 'title'
+        };
+
+        if ('undefined' !== typeof(oMedia.html) && oMedia.html.length)
+            return {'media-id': oMedia['id'], 'title': oMedia['title'], 'html': oMedia['html'], 'url': oMedia['url']};
+
+        var o = {};
+        for (var i in oMap)
+            o[oMap[i]] = oMedia[i];
+        return fnProcessRetina(o);
+    };
+
+    var fnDisableArrows = function (oItem) {
+
+        // disable prev item and action if we are on the first item
+        if (0 == aItems.indexOfMediaObject(oItem)) {
+            $('.pswp__button--arrow--left').hide();
+            glBxAlbumsPrevFn = glBxAlbumsGallery.prev;
+            glBxAlbumsGallery.prev = function () { };
+        } else {
+            $('.pswp__button--arrow--left').show();
+            if ('undefined' !== typeof(glBxAlbumsPrevFn))
+                glBxAlbumsGallery.prev = glBxAlbumsPrevFn;
+        }
+
+        // disable next item and action when we are in the last item
+        if ((aItems.length-1) == aItems.indexOfMediaObject(oItem)) {
+            $('.pswp__button--arrow--right').hide();
+            glBxAlbumsNextFn = glBxAlbumsGallery.next;
+            glBxAlbumsGallery.next = function () { };
+        } else {
+            $('.pswp__button--arrow--right').show();
+            if ('undefined' !== typeof(glBxAlbumsNextFn))
+                glBxAlbumsGallery.next = glBxAlbumsNextFn;
+        }
+    };
+
+    var fnLoadMoreItem = function (oItemCurrent, bFirstLoad) {
+
+        // load addutional items only for items on the border
+        if (0 != aItems.indexOfMediaObject(oItemCurrent) && (aItems.length-1) != aItems.indexOfMediaObject(oItemCurrent))
+            return;
+
+        var sUrl = sUrlRoot + 'modules/index.php?r=albums/get_sibling_media/' + oItemCurrent['media-id'] + '/' + sContext;
+        $.getJSON(sUrl, function(oData) {
+
+            if ('undefined' !== typeof(oData.error)) {
+                if ('undefined' !== typeof(console))
+                    console.log(oData.error);
+                return;
+            }
+
+            var l = aItems.length;
+
+            if (0 == aItems.indexOfMediaObject(oItemCurrent) && 'undefined' !== typeof(oData.prev.url)) {
+                aItems.unshift(fnConverMedia(oData.prev));
+                if (bFirstLoad)
+                    options.index = 1;
+                else
+                    glBxAlbumsGallery.goTo(glBxAlbumsGallery.getCurrentIndex() + 1);
+            }
+    
+            if ((aItems.length - 1) == aItems.indexOfMediaObject(oItemCurrent) && 'undefined' !== typeof(oData.next.url))
+                aItems.push(fnConverMedia(oData.next));
+
+            if (!bFirstLoad && l != aItems.length) {
+                glBxAlbumsGallery.invalidateCurrItems();
+                glBxAlbumsGallery.updateSize(true);
+            }
+
+            if (bFirstLoad) {
+                glBxAlbumsGallery = new PhotoSwipe(ePswp, PhotoSwipeUI_Default, aItems, options);
+                glBxAlbumsGallery.init();
+
+                glBxAlbumsGallery.listen('beforeChange', function() { 
+                    // load more items if we are on first or on the last item
+                    fnLoadMoreItem(glBxAlbumsGallery.currItem, false);
+                });
+
+                glBxAlbumsGallery.listen('afterChange', function() {
+                    var bChangeHistory = 'undefined' !== typeof(glBxAlbumsGallery._bx_albums_skip_history) && glBxAlbumsGallery._bx_albums_skip_history ? false : true;
+                    bx_albums_reload_page(glBxAlbumsGallery.currItem.url, glBxAlbumsGallery.currItem.title, bChangeHistory, false); // refresh url when we navigate to next/prev item
+                    glBxAlbumsGallery._bx_albums_skip_history = false;
+
+                    fnDisableArrows(glBxAlbumsGallery.currItem);
+                });
+
+                glBxAlbumsGallery.listen('close', function() {
+                    // upon gallery close reload page via AJAX, since url was changes when we navigate through items in the gallery
+                    bx_albums_reload_page(glBxAlbumsGallery.currItem.url, glBxAlbumsGallery.currItem.title, false, true);
+                });
+
+                fnDisableArrows(glBxAlbumsGallery.currItem);
+            }
+        });
+    };
+
+    fnLoadMoreItem(oItem, true);
+
+    return false;
+}
+
+function bx_albums_reload_page(sUrl, sTitle, bChangeHistory, bChangeContent) {
+
+    // if HTML5 history API isn't supported then use href attribute in the link
+    if (!(window.history && history.pushState))
+        return true; // when onclick returns true, page is reloaded using href attribute
+
+    // reload all blocks on the page using AJAX
+    if ('undefined' === typeof(bChangeContent) || bChangeContent) {
+        $('.bx-layout-wrapper .bx-page-block-container').each(function () {
+            var iId = parseInt($(this).attr('id').replace('bx-page-block-',''));
+            if (iId)
+                loadDynamicBlock(iId, sUrl);
+        });
+    }
+
+    if (glBxAlbumsActualUrl == sUrl)
+        return false;
+
+    // remember actual url, to not load it twice
+    glBxAlbumsActualUrl = sUrl;
+
+    // change history
+    if ('undefined' === typeof(bChangeHistory) || bChangeHistory) {
+        if ('undefined' === typeof(sTitle))
+            sTitle = null;
+        History.pushState({title:sTitle, url:sUrl}, sTitle, sUrl);
+    }
+
+    return false; // when onclick returns false, href attr isn't used
+}
+
+$(document).ready(function () {
+    glBxAlbumsActualUrl = location.href;    
+    // listen when we press back and forward buttons in browser
+    History.Adapter.bind(window, 'statechange', function(event) { 
+        var oState = History.getState();        
+        if ('undefined' === typeof(oState) || glBxAlbumsActualUrl == oState.url)
+            return;
+
+        if ($('.pswp:visible').length && 'undefined' !== typeof(glBxAlbumsGallery)) {
+            var a = /id=(\d+)/.exec(document.location);
+            if (a && a.length > 0 && a[1]) {
+                for (var i in glBxAlbumsGallery.items) {
+                    if (glBxAlbumsGallery.items[i]['media-id'] != a[1])
+                        continue;
+                    glBxAlbumsGallery._bx_albums_skip_history = true;
+                    glBxAlbumsGallery.goTo(i);
+                    break;
+                }
+                        
+            }
+        } else {
+            bx_albums_reload_page(oState.url, oState.title, false);
+        }
+    });
+});
+
+/** @} */

+ 18 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/request.php

@@ -0,0 +1,18 @@
+<?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
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+check_logged();
+
+BxBaseModTextRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

+ 49 - 0
modules/boonex/albums/updates/9.0.1_9.0.2/source/template/images/icons/std-icon.svg

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="600px" height="600px" viewBox="0 0 600 600" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
+    <title>Group 2 Copy 4</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
+            <stop stop-color="#FFFFFF" stop-opacity="0.2" offset="0%"></stop>
+            <stop stop-color="#000000" stop-opacity="0.138870018" offset="100%"></stop>
+        </linearGradient>
+        <path d="M285,135.714286 C307.477679,135.714286 325.714286,117.477679 325.714286,95 C325.714286,72.5223214 307.477679,54.2857143 285,54.2857143 C262.522321,54.2857143 244.285714,72.5223214 244.285714,95 C244.285714,117.477679 262.522321,135.714286 285,135.714286 L285,135.714286 Z" id="path-2"></path>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-3">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.2 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-4">
+            <stop stop-color="#FFFFFF" offset="0%"></stop>
+            <stop stop-color="#F6F6F6" offset="35.2427758%"></stop>
+            <stop stop-color="#E5E5E5" offset="100%"></stop>
+        </linearGradient>
+        <path d="M356.25,-5.68434189e-14 L23.75,-5.68434189e-14 C10.5178571,-5.68434189e-14 0,10.7723214 0,24.0044643 L0,301.709821 C0,314.941964 10.5178571,325.714286 23.75,325.714286 L356.25,325.714286 C369.482143,325.714286 380,314.941964 380,301.709821 L380,24.0044643 C380,10.7723214 369.482143,-5.68434189e-14 356.25,-5.68434189e-14 L356.25,-5.68434189e-14 Z M268.799107,167.691964 C266.254464,164.723214 262.352679,162.433036 257.941964,162.433036 C253.616071,162.433036 250.5625,164.46875 247.084821,167.267857 L231.223214,180.669643 C227.915179,183.044643 225.285714,184.65625 221.46875,184.65625 C217.821429,184.65625 214.513393,183.299107 212.138393,181.178571 C211.290179,180.415179 209.763393,178.973214 208.491071,177.700893 L162.857143,128.334821 C159.464286,124.433036 154.375,121.973214 148.691964,121.973214 C143.008929,121.973214 137.75,124.772321 134.441964,128.589286 L27.1428571,258.026786 L27.1428571,37.0669643 C27.9910714,31.2991071 32.4866071,27.1428571 38.2544643,27.1428571 L341.660714,27.1428571 C347.513393,27.1428571 352.263393,31.46875 352.602679,37.3214286 L352.857143,258.196429 L268.799107,167.691964 L268.799107,167.691964 Z" id="path-5"></path>
+        <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-6">
+            <feMorphology radius="1" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.08 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group-2-Copy-4">
+            <path d="M552.268958,552.268958 C600,504.537813 600,431.458333 600,300 C600,168.541667 600,95.4621875 552.268958,47.7310417 C504.537813,0 431.458333,0 300,0 C168.541667,0 95.4621875,0 47.7310417,47.7310417 C0,95.4621875 0,168.541667 0,300 C0,431.458333 0,504.537813 47.7310417,552.268958 C95.4621875,600 168.541667,600 300,600 C431.458333,600 504.537813,600 552.268958,552.268958 L552.268958,552.268958 Z" id="path" fill="#2BAF92"></path>
+            <path d="M552.268958,552.268958 C600,504.537813 600,431.458333 600,300 C600,168.541667 600,95.4621875 552.268958,47.7310417 C504.537813,0 431.458333,0 300,0 C168.541667,0 95.4621875,0 47.7310417,47.7310417 C0,95.4621875 0,168.541667 0,300 C0,431.458333 0,504.537813 47.7310417,552.268958 C95.4621875,600 168.541667,600 300,600 C431.458333,600 504.537813,600 552.268958,552.268958 L552.268958,552.268958 Z" id="path-copy" fill="url(#linearGradient-1)"></path>
+            <g id="image" transform="translate(300.000000, 300.000000) rotate(-360.000000) translate(-300.000000, -300.000000) translate(110.000000, 137.000000)">
+                <g id="Layer_1" transform="translate(-0.000000, 0.000000)">
+                    <g id="Group">
+                        <g id="Shape" opacity="0.496773098">
+                            <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                            <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-2"></use>
+                        </g>
+                        <g id="Shape">
+                            <use fill="black" fill-opacity="1" filter="url(#filter-6)" xlink:href="#path-5"></use>
+                            <use fill="url(#linearGradient-4)" fill-rule="evenodd" xlink:href="#path-5"></use>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 56 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/install/config.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Antispam',
+    'version_from' => '9.0.0',
+	'version_to' => '9.0.1',
+    'vendor' => 'BoonEx',
+
+	'compatible_with' => array(
+        '9.0.0-B3'
+    ),
+
+    /**
+     * '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_9.0.0_9.0.1/',
+	'home_uri' => 'antispam_update_900_901',
+
+	'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(
+		'template/images/icons/std-mi.png',
+		'template/images/icons/std-pi.png',
+		'template/images/icons/std-si.png',
+		'template/images/icons/std-wi.png'
+	),
+);

+ 13 - 0
modules/boonex/antispam/updates/9.0.0_9.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 BxAntispamUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+	{
+        parent::__construct($aConfig);
+    }
+}

+ 23 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/install/langs/en.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_upd 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_upd>
+</resources>

+ 6 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/install/sql/enable.sql

@@ -0,0 +1,6 @@
+-- SETTINGS
+UPDATE `sys_options_types` SET `icon`='bx_antispam@modules/boonex/antispam/|std-icon.svg' WHERE `name`='bx_antispam';
+
+
+-- FORMS
+UPDATE `sys_form_inputs` SET `db_pass`='DateTimeTs' WHERE `object`='bx_antispam_ip_table_form' AND `name`='LastDT';

+ 3 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/install/sql/install.sql

@@ -0,0 +1,3 @@
+-- STUDIO PAGE & WIDGET
+UPDATE `sys_std_pages` SET `icon`='bx_antispam@modules/boonex/antispam/|std-icon.svg' WHERE `name`='bx_antispam';
+UPDATE `sys_std_widgets` SET `icon`='bx_antispam@modules/boonex/antispam/|std-icon.svg' WHERE `module`='bx_antispam' AND `caption`='_bx_antispam';

+ 54 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamAkismet.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
+ *
+ * @{
+ */
+
+/**
+ * Spam detection based on the message content and logged in user info - http://akismet.com
+ */
+class BxAntispamAkismet extends BxDol
+{
+    protected $oAkismet = null;
+
+    public function __construct($iAccoutId = 0)
+    {
+        parent::__construct();
+        $sKey = getParam('bx_antispam_akismet_api_key');
+        if ($sKey && $oAccount = BxDolAccount::getInstance((int)$iAccoutId)) {
+            require_once (BX_DIRECTORY_PATH_PLUGINS . 'akismet/Akismet.class.php');
+            $this->oAkismet = new Akismet(BX_DOL_URL_ROOT, $sKey);
+
+            $oProfile = BxDolProfile::getInstanceByAccount((int)$iAccoutId);
+
+            $this->oAkismet->setCommentAuthorEmail($oAccount->getEmail());
+            $this->oAkismet->setCommentAuthor($oProfile->getDisplayName());
+            $this->oAkismet->setCommentAuthorURL($oProfile->getUrl());
+        }
+    }
+
+    public function isSpam ($s, $sPermalink = false)
+    {
+        if (!$this->oAkismet)
+            return false;
+
+        $this->oAkismet->setCommentContent($s);
+        if ($sPermalink)
+            $this->oAkismet->setPermalink($sPermalink);
+
+        return $this->oAkismet->isCommentSpam();
+    }
+
+    public function onPositiveDetection ($sExtraData = '')
+    {
+        $o = bx_instance('DNSBlacklists', array(), 'bx_antispam');
+        $o->onPositiveDetection (getVisitorIP(), $sExtraData, 'akismet');
+    }
+}
+
+/** @} */

+ 42 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamAlertsResponse.php

@@ -0,0 +1,42 @@
+<?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':
+                    $oAlert->aExtras['is_spam'] = BxDolService::call('bx_antispam', 'is_spam', array($oAlert->aExtras['content']));
+                    break;
+            }
+
+        }
+    }
+}
+
+/** @} */

+ 71 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamConfig.php

@@ -0,0 +1,71 @@
+<?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 BxAntispamConfig extends BxDolModuleConfig
+{
+    /**
+     * map system options to the local options names
+     */
+    protected $_aOptionsMap = array (
+        'antispam_block' => 'bx_antispam_block',
+        'antispam_report' => 'bx_antispam_report',
+        'dnsbl_enable' => 'bx_antispam_dnsbl_enable',
+        'uridnsbl_enable' => 'bx_antispam_uridnsbl_enable',
+        'dnsbl_behaviour_login' => 'bx_antispam_dnsbl_behaviour_login',
+        'dnsbl_behaviour_join' => 'bx_antispam_dnsbl_behaviour_join',
+        'akismet_enable' => 'bx_antispam_akismet_enable',
+    );
+    /**
+     * default local options, it is filled in with real system options in class contructor, @see restoreAntispamOptions
+     */
+    protected $_aOptions = array (
+        'antispam_block' => '',
+        'antispam_report' => '',
+        'dnsbl_enable' => '',
+        'uridnsbl_enable' => '',
+        'dnsbl_behaviour_login' => 'block',
+        'dnsbl_behaviour_join' => 'approval',
+        'akismet_enable' => '',
+    );
+
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+        $this->restoreAntispamOptions ();
+    }
+
+    /**
+     * Set local option value, system value stays intact, useful for testing and debugging
+     */
+    public function setAntispamOption ($sOption, $mixedVal)
+    {
+        $this->_aOptions[$sOption] = $mixedVal;
+    }
+
+    /**
+     * Get an option, local value
+     */
+    public function getAntispamOption ($sOption)
+    {
+        return $this->_aOptions[$sOption];
+    }
+
+    /**
+     * set local options values from system options
+     */
+    public function restoreAntispamOptions ()
+    {
+        foreach ($this->_aOptionsMap as $sNameLocal => $sNameParam)
+            $this->setAntispamOption($sNameLocal, getParam($sNameParam));
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamCronPruning.php

@@ -0,0 +1,20 @@
+<?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 BxAntispamCronPruning extends BxDolCron
+{
+    function processing()
+    {
+        BxDolService::call('bx_antispam', 'pruning');
+    }
+}
+
+/** @} */

+ 194 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamDNSBlacklists.php

@@ -0,0 +1,194 @@
+<?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']) {
+                // we got some result from the DNS query, not NXDOMAIN. should we consider 'positive'?
+                $postvresp = $r['postvresp'];	// check positive match criteria
+                if (
+                    BX_DOL_DNSBL_MATCH_ANY == $postvresp ||
+                    (preg_match("/^\d+\.\d+\.\d+\.\d+$/", $postvresp) && $resultaddr == $postvresp) ||
+                    (is_numeric($postvresp) && (ip2long($resultaddr) & $postvresp))
+                ) {
+                    $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];
+
+    }
+
+}
+
+/** @} */

+ 181 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamDNSURIBlacklists.php

@@ -0,0 +1,181 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Spam detection based on URIs in the message
+ */
+class BxAntispamDNSURIBlacklists extends BxDol
+{
+    protected $aZonesUrls = array (
+        2 => "http://www.surbl.org/tld/two-level-tlds",
+        3 => "http://www.surbl.org/tld/three-level-tlds",
+    );
+
+    protected $oDb;
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->aZonesUrls = array ( // for some reason original urls are restricted to fetch, so copies are created locally
+            2 => BX_DIRECTORY_PATH_MODULES . "boonex/antispam/data/two-level-tlds",
+            3 => BX_DIRECTORY_PATH_MODULES . "boonex/antispam/data/three-level-tlds",
+        );
+
+        $this->oDb = BxDolDb::getInstance();;
+        $this->initZones();
+    }
+
+    public function isSpam ($s)
+    {
+        $aURIs = $this->parseUrls ($s);
+        if (!$aURIs)
+            return false;
+
+        $aURIs = $this->validateUrls ($aURIs);
+        if (!$aURIs)
+            return false;
+
+        $oModule = BxDolModule::getInstance('bx_antispam');
+        $o = bx_instance('BxAntispamDNSBlacklists', array(), $oModule->_aModule);
+        foreach ($aURIs as $sURI) {
+            if (BX_DOL_DNSBL_POSITIVE == $o->dnsbl_lookup_uri ($sURI))
+                return true;
+        }
+
+        return false;
+    }
+
+    public function parseUrls (&$s)
+    {
+        $aMatches = array ();
+        if (!preg_match_all("!(https?|ftp|gopher|telnet|file|notes|ms-help):[/\\\\]+([\w\d\.-]*)!", $s, $aMatches))
+            return false;
+
+        if (!$aMatches || !isset($aMatches[2]) || !$aMatches[2])
+            return false;
+
+        $aUrlsUniq = array ();
+        foreach ($aMatches[2] as $sUrl) {
+            if (isset($aUrlsUniq[$sUrl]))
+                continue;
+            $aUrlsUniq[$sUrl] = $sUrl;
+        }
+
+        return $aUrlsUniq;
+    }
+
+    public function validateUrls ($aUrlsUniq)
+    {
+        $aUrls = array ();
+        foreach ($aUrlsUniq as $sUrl) {
+
+            if (0 === strncasecmp('www.', $sUrl, 4))
+                $sUrl = substr($sUrl, 4);
+
+            $aZones = explode ('.', $sUrl);
+            $iLevels = count($aZones);
+
+            if ($iLevels <= 2) {
+
+                $aUrls[] = $sUrl;
+
+            } elseif (3 == $iLevels) {
+
+                if ($this->isDbZoneMatch (2, $aZones[1] . '.' . $aZones[2]))
+                    $aUrls[] = $sUrl;
+                else
+                    $aUrls[] = $aZones[1] . '.' . $aZones[2];
+
+            } else {
+
+                $iExt = count($aZones) - 1;
+                $iDom = $iExt - 1;
+                $iSubDom = $iExt - 2;
+                $iSubSubDom = $iExt - 3;
+
+                if ($this->isDbZoneMatch (3, $aZones[$iSubDom] . '.' . $aZones[$iDom] . '.' . $aZones[$iExt])) {
+                    $aUrls[] = $aZones[$iSubSubDom] . '.' . $aZones[$iSubDom] . '.' . $aZones[$iDom] . '.' . $aZones[$iExt];
+                } else {
+
+                    if ($this->isDbZoneMatch (2, $aZones[$iDom] . '.' . $aZones[$iExt]))
+                        $aUrls[] = $aZones[$iSubDom] . '.' . $aZones[$iDom] . '.' . $aZones[$iExt];
+                    else
+                        $aUrls[] = $aZones[$iDom] . '.' . $aZones[$iExt];
+                }
+            }
+
+        }
+
+        $aUrlsUniq = array ();
+        foreach ($aUrls as $sUrl) {
+            if (isset($aUrlsUniq[$sUrl]))
+                continue;
+            $aUrlsUniq[$sUrl] = $sUrl;
+        }
+
+        return array_values($aUrlsUniq);
+    }
+
+    public function onPositiveDetection ($sExtraData = '')
+    {
+        $oModule = BxDolModule::getInstance('bx_antispam');
+        $o = bx_instance('BxAntispamDNSBlacklists', array(), $oModule->_aModule);
+
+        $o->onPositiveDetection (getVisitorIP(false), $sExtraData, 'dnsbluri');
+    }
+
+    public function getURIDNSBLConfigValues ()
+    {
+        return array (
+            'block' => _t('_bx_antispam_dnsbl_behaviour_join_block'),
+            'approval' => _t('_bx_antispam_dnsbl_behaviour_join_approval'),
+        );
+    }
+
+    /*************** private function ***************/
+
+    private function isDbZoneMatch ($iLevel, $sZone)
+    {
+        $sQuery = $this->oDb->prepare("SELECT `level` FROM `bx_antispam_dnsbluri_zones` WHERE `level` = ? AND `zone` = ? LIMIT 1", $iLevel, $sZone);
+        return $this->oDb->getOne($sQuery) ? true : false;
+    }
+
+    private function initZones()
+    {
+        if (0 == $this->oDb->getOne("SELECT COUNT(*) FROM `bx_antispam_dnsbluri_zones`")) {
+
+            $this->oDb->query("TRUNCATE TABLE `bx_antispam_dnsbluri_zones`");
+
+            foreach ($this->aZonesUrls as $iLevel => $sUrl) {
+                $f = fopen ($sUrl, 'r');
+                if (!$f)
+                    return false;
+                while (!feof($f)) {
+                    $sZone = fgets($f);
+                    $sZone = trim($sZone);
+                    if ($sZone) {
+                        $sQuery = $this->oDb->prepare("INSERT INTO `bx_antispam_dnsbluri_zones` SET `level` = ?, `zone` = ?", $iLevel, $sZone);
+                        $this->oDb->query($sQuery);
+                    }
+                }
+                fclose($f);
+            }
+        }
+
+        return true;
+    }
+
+}
+
+/** @} */

+ 20 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamDb.php

@@ -0,0 +1,20 @@
+<?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 BxAntispamDb extends BxDolModuleDb
+{
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+}
+
+/** @} */

+ 44 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamGridBlockLog.php

@@ -0,0 +1,44 @@
+<?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
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+class BxAntispamGridBlockLog extends BxTemplGrid
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+        $this->_sDefaultSortingOrder = 'DESC';
+    }
+
+    protected function _getCellIp ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault (long2ip($mixedValue), $sKey, $aField, $aRow);
+    }
+    protected function _getCellProfileId ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $s = '<span class="bx-def-font-grayed">' . _t('_undefined') . '</span>';
+        if ($mixedValue && ($oProfile = BxDolProfile::getInstance((int)$mixedValue)))
+            $s = '<a href="' . $oProfile->getUrl() . '">' . $oProfile->getDisplayName() . '</span>';
+
+        return parent::_getCellDefault ($s, $sKey, $aField, $aRow);
+    }
+    protected function _getCellType ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault (_t('_bx_antispam_type_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+    protected function _getCellAdded ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault (bx_time_js($mixedValue, BX_FORMAT_DATE), $sKey, $aField, $aRow);
+    }
+}
+
+/** @} */

+ 316 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamGridDNSBL.php

@@ -0,0 +1,316 @@
+<?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 BxAntispamGridDNSBL extends BxTemplGrid
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    /**
+     * add js file for AJAX form submission
+     */
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+
+        $this->_oTemplate->addJs('jquery.form.min.js');
+
+        $oForm = new BxTemplFormView(array());
+        $oForm->addCssJs();
+    }
+
+    /**
+     * 'help' action handler
+     */
+    public function performActionHelp()
+    {
+        $s = BxTemplFunctions::getInstance()->popupBox(
+            'bx_antispam_poppup_help',
+            _t('_bx_antispam_popup_help'),
+            _t('_bx_antispam_help_dnsbl')
+        );
+
+        echoJson(array('popup' => array('html' => $s)));
+    }
+
+    /**
+     * 'log' action handler
+     */
+    public function performActionLog()
+    {
+        $oModule = BxDolModule::getInstance('bx_antispam');
+        $sGrid = $oModule->serviceBlockLog();
+
+        if (!$sGrid) {
+            echoJson(array('msg' => _t('_sys_txt_error_occured')));
+            exit;
+        }
+
+        $s = BxTemplFunctions::getInstance()->popupBox(
+            'bx_antispam_popup_block_log',
+            _t('_bx_antispam_popup_block_log'),
+            $sGrid . '<button class="bx-btn" style="float:none;" onclick ="$(\'.bx-popup-applied:visible\').dolPopupHide();">' . _t('_sys_close') . '</button>'
+        );
+
+        echoJson(array('popup' => array('html' => $s)));
+
+    }
+
+    /**
+     * 'recheck' action handler
+     */
+    public function performActionRecheckItem()
+    {
+        $oModule = BxDolModule::getInstance('bx_antispam');
+        $o = bx_instance('BxAntispamDNSBlacklists', array(), $oModule->_aModule);
+
+        $aChain = $o->getRule((int)bx_get('id'));
+
+        $iRet = BX_DOL_DNSBL_FAILURE;
+        if ($aChain) {
+            if (bx_get('chain') == 'dnsbl') {
+                $iRet = $o->dnsbl_lookup_ip($aChain, bx_process_input(bx_get('test')));
+            } elseif (bx_get('chain') == 'uridnsbl') {
+                $sUrl = preg_replace('/^\w+:\/\//', '', bx_process_input(bx_get('test')));
+                $sUrl = preg_replace('/^www\./', '', $sUrl);
+                $oBxDolDNSURIBlacklists = bx_instance('BxAntispamDNSURIBlacklists', array(), $oModule->_aModule);
+                $aUrls = $oBxDolDNSURIBlacklists->validateUrls(array($sUrl));
+                if ($aUrls)
+                    $iRet = $o->dnsbl_lookup_uri($aUrls[0], $aChain);
+            }
+        }
+
+        $s = '';
+        switch ($iRet) {
+            case BX_DOL_DNSBL_POSITIVE:
+                $s = 'LISTED';
+                break;
+            case BX_DOL_DNSBL_NEGATIVE:
+                $s = 'NOT LISTED';
+                break;
+            default:
+            case BX_DOL_DNSBL_FAILURE:
+                $s = 'FAIL';
+        }
+
+        echo $s;
+        exit;
+    }
+
+    /**
+     * 'recheck' action handler
+     */
+    public function performActionRecheck()
+    {
+        $oModule = BxDolModule::getInstance('bx_antispam');
+        $oDNSBlacklists = bx_instance('BxAntispamDNSBlacklists', array(), $oModule->_aModule);
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'bx_antispam_form_dnsbl_recheck',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=bx_antispam_grid_dnsbl&a=recheck',
+                'onsubmit' => "return bx_antispam_recheck($('#bx_antispam_ip_url').val(), $('[name=dnsbl_uridnsbl]:checked').val());",
+                'method' => 'post',
+            ),
+            'params' => array (
+                'db' => array(
+                    'submit_name' => 'do_submit',
+                ),
+            ),
+            'inputs' => array(
+
+                'ip_url' => array(
+                    'type' => 'text',
+                    'name' => 'ip_url',
+                    'value' => getVisitorIP(),
+                    'caption' => _t('_bx_antispam_field_ip_url'),
+                    'attrs' => array('id' => 'bx_antispam_ip_url'),
+                ),
+
+                'dnsbl_uridnsbl' => array(
+                    'type' => 'radio_set',
+                    'name' => 'dnsbl_uridnsbl',
+                    'caption' => _t('_bx_antispam_field_dnsbl_uridnsbl'),
+                    'values' => array ('dnsbl' => _t('_bx_antispam_dnsbl'), 'uridnsbl' => _t('_bx_antispam_uri_dnsbl')),
+                    'value' => 'dnsbl',
+                ),
+
+                'submit' => array(
+                    'type' => 'input_set',
+                    0 => array (
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_sys_submit'),
+                    ),
+                    1 => array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_sys_close'),
+                        'attrs' => array('class' => 'bx-def-margin-sec-left', 'onclick' => '$(\'.bx-popup-applied:visible\').dolPopupHide();'),
+                    ),
+                ),
+
+            ),
+        );
+
+        $oForm = new BxTemplFormView($aForm);
+
+        $s = $oModule->_oTemplate->parseHtmlByName('recheck.html', array (
+            'form' => $oForm->getCode(),
+            'url_recheck_item' => BX_DOL_URL_ROOT . 'grid.php?o=bx_antispam_grid_dnsbl&a=recheck_item',
+            'bx_repeat:items' => $oDNSBlacklists->getRules(array(BX_DOL_DNSBL_CHAIN_SPAMMERS, BX_DOL_DNSBL_CHAIN_WHITELIST, BX_DOL_DNSBL_CHAIN_URIDNS)),
+        ));
+
+        $s = BxTemplFunctions::getInstance()->popupBox($oForm->getId() . '_form', _t('_bx_antispam_popup_dnsbl_recheck'), $s);
+
+        echoJson(array('popup' => array('html' => $s, 'options' => array('closeOnOuterClick' => false))));
+    }
+
+    /**
+     * 'recheck' action handler
+     */
+    public function performActionAdd()
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'bx_antispam_form_dnsbl_add',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=bx_antispam_grid_dnsbl&a=add',
+                'method' => 'post',
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'bx_antispam_dnsbl_rules',
+                    'key' => 'id',
+                    'submit_name' => 'do_submit',
+                ),
+            ),
+            'inputs' => array(
+
+                'country' => array(
+                    'type' => 'select',
+                    'name' => 'country',
+                    'caption' => _t('_bx_antispam_field_country'),
+                    'values' => BxDolForm::getDataItems('Country'),
+                ),
+
+                'chain' => array(
+                    'type' => 'radio_set',
+                    'name' => 'chain',
+                    'caption' => _t('_bx_antispam_field_action'),
+                    'values' => array ('spammers' => _t('_bx_antispam_chain_spammers'), 'whitelist' => _t('_bx_antispam_chain_whitelist')),
+                    'value' => 'spammers',
+                ),
+
+                'submit' => array(
+                    'type' => 'input_set',
+                    0 => array (
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_sys_submit'),
+                    ),
+                    1 => array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_sys_close'),
+                        'attrs' => array('class' => 'bx-def-margin-sec-left', 'onclick' => '$(\'.bx-popup-applied:visible\').dolPopupHide();'),
+                    ),
+                ),
+
+            ),
+        );
+
+        $oForm = new BxTemplFormView($aForm);
+        $oForm->initChecker();
+
+        if ($oForm->isSubmittedAndValid()) { // if form is submitted and all fields are valid
+
+            $aCustomValues = array(
+                'chain' => $oForm->getCleanValue('chain'),
+                'zonedomain' => sprintf("%s.countries.nerd.dk.", strtolower($oForm->getCleanValue('country'))),
+                'postvresp' => '127.0.0.2',
+                'url' => 'http://countries.nerd.dk/',
+                'comment' => '_bx_antispam_rule_note_country',
+                'added' => time(),
+                'active' => 1,
+
+            );
+
+            $iRecentId = $oForm->insert ($aCustomValues, true); // insert new record
+
+            if ($iRecentId)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iRecentId); // if record is successfully added, reload grid and highlight added row
+            else
+                $aRes = array('msg' => _t('_sys_txt_error_occured')); // if record adding failed, display error message
+
+            echoJson($aRes);
+
+        } else { // if form is not submitted or some fields are invalid, display popup with form
+
+            $s = BxTemplFunctions::getInstance()->popupBox($oForm->getId() . '_form', _t('_bx_antispam_popup_dnsbl_add'), $oForm->getCode() . '
+                <script>
+                    $(document).ready(function () {
+                        $("#' . $oForm->getId() . '").ajaxForm({
+                            dataType: "json",
+                            beforeSubmit: function (formData, jqForm, options) {
+                                bx_loading($("#' . $oForm->getId() . '"), true);
+                            },
+                            success: function (data) {
+                                $(".bx-popup-applied:visible").dolPopupHide();
+                                glGrids.' . $this->_sObject . '.processJson(data, "add");
+                            }
+                        });
+                    });
+                </script>');
+
+            echoJson(array('popup' => array('html' => $s, 'options' => array('closeOnOuterClick' => false))));
+
+        }
+    }
+
+    protected function _getCellChain ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault (_t('_bx_antispam_chain_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellZonedomain ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $s = $mixedValue;
+        if ($aRow['url'])
+            $s = '<a target="_blank" href="' . $aRow['url'] . '">' . $mixedValue . '</a>';
+        return parent::_getCellDefault ($s, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellComment ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $sCountry = '';
+        $aMatches = array();
+        if (preg_match('/^(\w{2})\.countries\.nerd\.dk\.$/', $aRow['zonedomain'], $aMatches) && isset($aMatches[1])) {
+            $sCountry = $aMatches[1];
+        }
+
+        return parent::_getCellDefault (_t($mixedValue, $sCountry), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellActions ($mixedValue, $sKey, $aField, $aRow)
+    {
+        if (preg_match('/^(\w{2})\.countries\.nerd\.dk\.$/', $aRow['zonedomain'], $aMatches)) {
+            $aRow['active'] = 1;
+            return parent::_getCellActions ($mixedValue, $sKey, $aField, $aRow);
+        }
+
+        return parent::_getCellDefault ('', $sKey, $aField, $aRow);
+    }
+
+}
+
+/** @} */

+ 151 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamGridIpTable.php

@@ -0,0 +1,151 @@
+<?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
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+class BxAntispamGridIpTable extends BxTemplGrid
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    /**
+     * add js file for AJAX form submission
+     */
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+
+        $this->_oTemplate->addJs('jquery.form.min.js');
+
+        $oForm = new BxTemplFormView(array());
+        $oForm->addCssJs();
+    }
+
+    /**
+     * 'add' action handler
+     */
+    public function performActionAdd()
+    {
+        $this->_performAction('add', 'bx_antispam_ip_table_form_add');
+    }
+
+    /**
+     * 'edit' action handler
+     */
+    public function performActionEdit()
+    {
+        $iId = 0;
+        $aIds = bx_get('ids');
+        if ($aIds && is_array($aIds))
+            $iId = (int)array_pop($aIds);
+        if (!$iId)
+            $iId = (int)bx_get('ID');
+
+        if (!$iId) {
+            echoJson(array('msg' => _t('_sys_txt_error_occured')));
+            exit;
+        }
+
+        $this->_performAction('edit', 'bx_antispam_ip_table_form_edit', $iId);
+    }
+
+    protected function _performAction($sAction, $sDisplay, $iId = 0)
+    {
+        $oForm = BxDolForm::getObjectInstance('bx_antispam_ip_table_form', $sDisplay); // get form instance for specified form object and display
+        if (!$oForm) {
+            echoJson(array('msg' => _t('_sys_txt_error_occured')));
+            exit;
+        }
+
+        $oForm->addMarkers(array(
+            'grid_object' => $this->_sObject,
+            'grid_action' => $sAction,
+        ));
+
+        $aIpTableDirective = array();
+        if ($iId) {
+            $oModule = BxDolModule::getInstance('bx_antispam');
+            $oAntispamIp = bx_instance('BxAntispamIP', array(), $oModule->_aModule);
+            $aIpTableDirective = $oAntispamIp->getIpTableDirective($iId);
+            $aIpTableDirective['From'] = long2ip($aIpTableDirective['From']);
+            $aIpTableDirective['To'] = long2ip($aIpTableDirective['To']);
+        }
+        $oForm->initChecker($aIpTableDirective);
+
+        if ($oForm->isSubmittedAndValid()) { // if form is submitted and all fields are valid
+
+            $aCustomValues = array(
+                'From' => sprintf("%u", ip2long($oForm->getCleanValue('From'))),
+                'To' => sprintf("%u", ip2long($oForm->getCleanValue('To'))),
+            );
+
+            if ($iId) {
+                if ($oForm->update ($iId, $aCustomValues)) // update record
+                    $iRecentId = $iId;
+            } else {
+                $iRecentId = $oForm->insert ($aCustomValues, true); // insert new record
+            }
+
+            if ($iRecentId)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iRecentId); // if record is successfully added, reload grid and highlight added row
+            else
+                $aRes = array('msg' => _t('_sys_txt_error_occured')); // if record adding failed, display error message
+
+            echoJson($aRes);
+
+        } else { // if form is not submitted or some fields are invalid, display popup with form
+
+            $s = BxTemplFunctions::getInstance()->popupBox($oForm->getId() . '_form', _t('_bx_antispam_form_ip_table_add'), $oForm->getCode() . '
+                <script>
+                    $(document).ready(function () {
+                        $("#' . $oForm->getId() . '").ajaxForm({
+                            dataType: "json",
+                            beforeSubmit: function (formData, jqForm, options) {
+                                bx_loading($("#' . $oForm->getId() . '"), true);
+                            },
+                            success: function (data) {
+                                $(".bx-popup-applied:visible").dolPopupHide();
+                                glGrids.' . $this->_sObject . '.processJson(data, "' . $sAction . '");
+                            }
+                        });
+                    });
+                </script>');
+
+            echoJson(array('popup' => array('html' => $s, 'options' => array('closeOnOuterClick' => false))));
+
+        }
+    }
+
+    protected function _getCellType ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault ('allow' == $mixedValue ? _t('_bx_antispam_ip_allow') : _t('_bx_antispam_ip_deny'), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellLastDT ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault (bx_time_js($mixedValue, BX_FORMAT_DATE), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellFrom ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault (long2ip($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellTo ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault (long2ip($mixedValue), $sKey, $aField, $aRow);
+    }
+
+}
+
+/** @} */

+ 157 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamIP.php

@@ -0,0 +1,157 @@
+<?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_ANTISPAM_IP_TABLE_DISABLED', 0);
+define('BX_ANTISPAM_IP_TABLE_ALL_ALLOWED_EXCEPT_LISTED', 1);
+define('BX_ANTISPAM_IP_TABLE_ALL_BLOCKED_EXCEPT_LISTED', 2);
+
+/**
+ * Blocking/whitelisting by IP using local database
+ */
+class BxAntispamIP extends BxDol
+{
+    protected $_aIpTableConfigMap;
+
+    public function __construct()
+    {
+        parent::__construct();
+        $this->_aIpTableConfigMap = array (
+            0 => _t('_bx_antispam_ip_table_disabled'),
+            1 => _t('_bx_antispam_ip_table_all_allowed_except_listed'),
+            2 => _t('_bx_antispam_ip_table_all_blocked_except_listed'),
+        );
+    }
+
+    /**
+     * Check if IP is directly whitelisted by IP address or by IP address range
+     *
+     * @param $sCurIP IP to check, or empty for current IP
+     * @return true - if IP is whitelisted or under cron execution; false - if IP is not whitelisted, or feature is not enabled
+     */
+    public function isIpWhitelisted ($sCurIP = '')
+    {
+        if (defined('BX_DOL_CRON_EXECUTE'))
+            return true;
+
+        $iIPGlobalType = (int)getParam('bx_antispam_ip_list_type');
+        if (BX_ANTISPAM_IP_TABLE_DISABLED == $iIPGlobalType)
+            return false;
+
+        if (!$sCurIP)
+            $sCurIP = getVisitorIP();
+
+        return $this->_isIpListed('allow', $sCurIP);
+    }
+
+    /**
+     * Check if IP is directly bloked by IP address or by IP address range
+     *
+     * @param $sCurIP IP to check, or empty for current IP
+     * @return true - if IP is blocked; false - if IP is not blocked, or feature is not enabled, or it is run under cron
+     */
+    public function isIpBlocked ($sCurIP = '')
+    {
+        if (defined('BX_DOL_CRON_EXECUTE'))
+            return false;
+
+        $iIPGlobalType = (int)getParam('bx_antispam_ip_list_type');
+        if (BX_ANTISPAM_IP_TABLE_DISABLED == $iIPGlobalType)
+            return false;
+
+        if (!$sCurIP)
+            $sCurIP = getVisitorIP();
+
+        if ($this->isIpWhitelisted($sCurIP))
+            return false;
+
+        if ($this->_isIpListed('deny', $sCurIP))
+            return true;
+
+        return BX_ANTISPAM_IP_TABLE_ALL_BLOCKED_EXCEPT_LISTED == $iIPGlobalType ? true : false;
+    }
+
+    /**
+     * Add IP to blocklist
+     *
+     * @param $sIP IP as string or long integer
+     * @param $iExpirationInSec set expiration in seconds
+     * @param $sComment comment about blocking
+     * @return false on error, not false on success
+     */
+    public function blockIp($mixedIP, $iExpirationInSec = 86400, $sComment = '')
+    {
+        if (preg_match('/^[0-9]+$/', $mixedIP))
+            $iIP = $mixedIP;
+        else
+            $iIP = sprintf("%u", ip2long($mixedIP));
+
+        $iExpirationInSec = time() + (int)$iExpirationInSec;
+
+        $oDb = BxDolDb::getInstance();
+        $sQuery = $oDb->prepare("SELECT ID FROM `bx_antispam_ip_table` WHERE `From` = ? AND `To` = ? LIMIT 1", $iIP, $iIP);
+        if (!$oDb->getOne($sQuery)) {
+            $sQuery = $oDb->prepare("INSERT INTO `bx_antispam_ip_table` SET `From` = ?, `To` = ?, `Type` = 'deny', `LastDT` = ?, `Desc` = ?", $iIP, $iIP, $iExpirationInSec, $sComment);
+            return $oDb->res($sQuery);
+        }
+        return false;
+    }
+
+    /**
+     * Get IP table direcitve by ID
+     *
+     * @param $sId
+     * @return array with IP table directive data
+     */
+    public function getIpTableDirective($iId)
+    {
+        $oDb = BxDolDb::getInstance();
+        $sQuery = $oDb->prepare("SELECT * FROM `bx_antispam_ip_table` WHERE `ID` = ? LIMIT 1", $iId);
+        return $oDb->getRow($sQuery);
+    }
+
+    public function getIpTableConfigValues ()
+    {
+        return $this->_aIpTableConfigMap;
+    }
+
+    public function getIpTableConfigTitle ($i)
+    {
+        return $this->_aIpTableConfigMap[$i];
+    }
+
+    /**
+     * Clean up expired entries
+     * @return number of affected rows
+     */
+    public function pruning ()
+    {
+        $oDb = BxDolDb::getInstance();
+        $sQuery = $oDb->prepare("DELETE FROM `bx_antispam_ip_table` WHERE `LastDT` <= ?", time());
+        $iAffectedRows = $oDb->query($sQuery);
+
+        if ($iAffectedRows) {
+            $oDb->query("OPTIMIZE TABLE `bx_antispam_ip_table`");
+            return $iAffectedRows;
+        }
+
+        return 0;
+    }
+
+    protected function _isIpListed($sType, $sIp)
+    {
+        $iIp = sprintf("%u", ip2long($sIp));
+        $oDb = BxDolDb::getInstance();
+        $sQuery = $oDb->prepare("SELECT `ID` FROM `bx_antispam_ip_table` WHERE `Type` = ? AND `LastDT` > ? AND `From` <= ? AND `To` >= ? LIMIT 1", $sType, time(), $iIp, $iIp);
+        return $oDb->getOne($sQuery) ? true : false;
+    }
+}
+
+/** @} */

+ 283 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamModule.php

@@ -0,0 +1,283 @@
+<?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
+{
+    public function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    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');
+    }
+
+    /**
+     * 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 (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;
+    }
+
+    /**
+     * 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'));
+        $bApproval = false;
+        $sErrorMsg = '';
+        $sNote = $bJoinBlock ? 'join block' : 'join approval';
+
+        if (!$sIp)
+            $sIp = getVisitorIP();
+
+        if (!$sErrorMsg && $this->serviceIsIpBlocked($sIp))
+            $sErrorMsg = $this->getErrorMessageIpBlocked();
+
+        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();
+        }
+
+        if (!$sErrorMsg) {
+            $oStopForumSpam = bx_instance('BxAntispamStopForumSpam', array(), $this->_aModule);
+            if ($oStopForumSpam->isSpammer(array('email' => $sEmail, 'ip' => $sIp), $sNote))
+                $sErrorMsg = $this->getErrorMessageSpam();
+        }
+
+        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();
+        }
+    }
+
+    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() : '';
+    }
+}
+
+/** @} */

+ 24 - 0
modules/boonex/antispam/updates/9.0.0_9.0.1/source/classes/BxAntispamPage.php

@@ -0,0 +1,24 @@
+<?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
+ *
+ * @{
+ */
+
+/**
+ * Antispam module pages.
+ */
+class BxAntispamPage extends BxTemplPage
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+
+}
+
+/** @} */

部分文件因文件數量過多而無法顯示