Alex Trofimov пре 4 година
родитељ
комит
1ecd2779f0

+ 21 - 0
modules/boonex/azure_connect/classes/BxAzrConAlerts.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    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrConAlerts extends BxBaseModConnectAlerts
+{
+    function __construct()
+    {
+        parent::__construct();
+        $this -> oModule = BxDolModule::getInstance('bx_azrcon');
+    }
+}
+
+/** @} */

+ 46 - 0
modules/boonex/azure_connect/classes/BxAzrConConfig.php

@@ -0,0 +1,46 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrConConfig extends BxBaseModConnectConfig
+{
+    public $sTenantID = 'common';
+    public $sClientID;
+    public $sSecret;
+
+    public $sAuthMethod = 'secret'; // certificate isn't supported yet
+    public $sScope = 'User.Read';// 'openid%20offline_access%20profile%20user.read';
+    public $sLogoutUrl = 'https://login.microsoftonline.com/common/wsfederation?wa=wsignout1.0';
+
+    public $sPageStart;
+    public $sPageHandle;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this -> sTenantID = getParam('bx_azrcon_tenant_id');
+        $this -> sClientID = getParam('bx_azrcon_client_id');
+        $this -> sSecret = getParam('bx_azrcon_secret');
+
+        $this -> sEmailTemplatePasswordGenerated = 'bx_azrcon_password_generated';
+        $this -> sDefaultTitleLangKey = '_bx_azrcon';
+
+        $this -> sRedirectPage = getParam('bx_azrcon_redirect_page');
+        $this -> sProfilesModule = getParam('bx_azrcon_module');
+        $this -> isAlwaysConfirmEmail = (bool)getParam('bx_azrcon_confirm_email'); 
+        $this -> isAlwaysAutoApprove = (bool)getParam('bx_azrcon_approve');
+
+        $this -> sPageStart = BX_DOL_URL_ROOT . $this -> getBaseUri() . 'start';
+        $this -> sPageHandle = BX_DOL_URL_ROOT . $this -> getBaseUri() . 'handle';
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/azure_connect/classes/BxAzrConDb.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    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrConDb extends BxBaseModConnectDb
+{
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+}
+
+/** @} */

+ 160 - 0
modules/boonex/azure_connect/classes/BxAzrConModule.php

@@ -0,0 +1,160 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+
+/**
+ * This Azude AD connect code was created with instructions from:
+ * https://www.sipponen.com/archives/4024
+ */
+class BxAzrConModule extends BxBaseModConnectModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    /**
+     * Redirect to remote site login form
+     *
+     * @return n/a - redirect or HTML page in case of error
+     */
+    function actionStart()
+    {
+        if (isset($_GET["error"])) {
+            $this->_oTemplate->getPage(_t($this->_oConfig->sDefaultTitleLangKey), DesignBoxContent(_t($this->_oConfig->sDefaultTitleLangKey), MsgBox(bx_get('error'))));
+            exit;
+        }
+
+        if (isLogged()) {
+            $this->_redirect ($this -> _oConfig -> sDefaultRedirectUrl);
+        }
+
+        if (!$this->_oConfig->sClientID || !$this->_oConfig->sSecret) {
+            require_once(BX_DIRECTORY_PATH_INC . 'design.inc.php');
+            bx_import('BxDolLanguages');
+            $sCode =  MsgBox( _t('_bx_azrcon_profile_error_api_keys') );
+            $this->_oTemplate->getPage(_t('_bx_azrcon'), $sCode);
+        } 
+        else {
+
+            // First stage of the authentication process; This is just a simple redirect (first load of this page)
+            $sUrl = bx_append_url_params("https://login.microsoftonline.com/" . $this->_oConfig->sTenantID . "/oauth2/v2.0/authorize", [
+                'state' => $this->_genToken(), // BxDolSession::getInstance()->getId(), // This at least semi-random string is likely good enough as state identifier
+                'scope' => $this->_oConfig->sScope, // User.Read scope seems to be enough, but you can try "&scope=profile+openid+email+offline_access+User.Read" if you like
+                'response_type' => 'code',
+                'approval_prompt' => 'auto',
+                'client_id' => $this->_oConfig->sClientID,
+                'redirect_uri' => $this->_oConfig->sPageHandle,
+            ]);
+            $this->_redirect($sUrl); // So off you go my dear browser and welcome back for round two after some redirects at Azure end
+        }
+    }
+
+    function actionHandle()
+    {
+        require_once(BX_DIRECTORY_PATH_INC . 'design.inc.php');
+
+        // check CSRF token
+        if ($this->_getToken() != bx_get('state')) {
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox(_t('_sys_connect_state_invalid')));
+            return;
+        }
+
+        // check code
+        $sCode = bx_get('code');
+        if (!$sCode || bx_get('error')) {
+            $sErrorDescription = bx_get('error_description') ? bx_get('error_description') : _t('_error occured');
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox($sErrorDescription));
+            return;
+        }
+
+        // Verifying the received tokens with Azure and finalizing the authentication part
+        $s = bx_file_get_contents("https://login.microsoftonline.com/" . $this->_oConfig->sTenantID . "/oauth2/v2.0/token", [
+            'grant_type'    => 'authorization_code',
+            'client_id'     => $this->_oConfig->sClientID,
+            'redirect_uri'  => $this->_oConfig->sPageHandle,
+            'code'          => $sCode,
+            'client_secret' => $this->_oConfig->sSecret,
+        ], 'post', array ('Content-Type: application/x-www-form-urlencoded'));
+
+        // handle error
+        if (!$s || NULL === ($aAuthData = json_decode($s, true)) || !isset($aAuthData['access_token']) || isset($aAuthData['error'])) {
+            $sErrorDescription = isset($aAuthData['error_description']) ? $aAuthData['error_description'] : _t('_error occured');
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox($sErrorDescription));
+            return;
+        }
+
+        // get the data, especially access_token
+        $sAccessToken = $aAuthData['access_token'];
+        $sExpiresIn = $aAuthData['expires_in'];
+        $sExpiresAt = new \DateTime('+' . $sExpiresIn . ' seconds');
+
+        // request info about profile
+        // In case you need some other attributes from the user object that are not coming with "/me" by default, you can use $select argument at line 74 to select the properties you really need:
+        // https://graph.microsoft.com/v1.0/me?$select=givenName,surname,userPrincipalName,country
+        $s = bx_file_get_contents("https://graph.microsoft.com/v1.0/me", array(), 'get', array(
+            'Accept: application/json',
+            'Authorization: Bearer ' . $sAccessToken,
+        ));
+
+        // handle error
+        if (!$s || NULL === ($aUserData = json_decode($s, true)) || !$aUserData || isset($aUserData['error'])) {
+            $sErrorDescription = isset($aUserData['error_description']) ? $aUserData['error_description'] : _t('_error occured'); 
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox($sErrorDescription));
+            return;
+        }
+
+        $aRemoteProfileInfo = $aUserData;
+
+        if ($aRemoteProfileInfo) {
+
+            // check if user logged in before
+            $iLocalProfileId = $this->_oDb->getProfileId($aRemoteProfileInfo['id']);
+            
+            if ($iLocalProfileId && $oProfile = BxDolProfile::getInstance($iLocalProfileId)) {
+                // user already exists
+                $this->setLogged($oProfile ->id());
+            }             
+            else {  
+                // register new user
+                $this->_createProfile($aRemoteProfileInfo);
+            }
+        } 
+        else {
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox(_t('_sys_connect_profile_error_info')));
+        }
+    }
+
+    /**
+     * @param $aProfileInfo - remote profile info
+     * @param $sAlternativeName - suffix to add to NickName to make it unique
+     * @return profile array info, ready for the local database
+     */
+    protected function _convertRemoteFields($aProfileInfo, $sAlternativeName = '')
+    {
+        $aProfileFields = $aProfileInfo;
+
+        $a = explode('@', $aProfileInfo['userPrincipalName']);
+        $sName = isset($a[0]) ? $a[0] : $aProfileInfo['userPrincipalName'];
+    
+        $aProfileFields['name'] = !empty($aProfileInfo['displayName']) ? $aProfileInfo['displayName'] : $sName;
+        $aProfileFields['fullname'] = !empty($aProfileInfo['givenName']) ? $aProfileInfo['givenName'] : $aProfileFields['name'];
+        $aProfileFields['last_name'] = !empty($aProfileInfo['surname']) ? ' ' . $aProfileInfo['surname'] : '';
+        $aProfileFields['email'] = isset($aProfileInfo['mail']) ? $aProfileInfo['mail'] : $aProfileInfo['userPrincipalName'];
+        $aProfileFields['picture'] = false; // isset($aProfileInfo['picture']) ? $aProfileInfo['picture'] : '';
+        $aProfileFields['allow_view_to'] = getParam('bx_azrcon_privacy');
+
+        return $aProfileFields;
+    }
+
+}
+
+/** @} */

+ 31 - 0
modules/boonex/azure_connect/classes/BxAzrConStudioPage.php

@@ -0,0 +1,31 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrConStudioPage extends BxTemplStudioModule
+{
+    function __construct($sModule, $mixedPageName, $sPage = "")
+    {
+        parent::__construct($sModule, $mixedPageName, $sPage);
+
+        $this->aMenuItems = array(
+            'settings' => array('name' => 'settings', 'icon' => 'cogs', 'title' => '_adm_lmi_cpt_settings'),
+            'help' => array('name' => 'help', 'icon' => 'question', 'title' => '_sys_connect_information'),
+        );
+    }
+    
+    function getHelp ()
+    {
+        $oModule = BxDolModule::getInstance('bx_azrcon'); 
+        return _t('_bx_azrcon_information_block', BX_DOL_URL_ROOT . $oModule -> _oConfig -> getBaseUri() . 'handle');
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/azure_connect/classes/BxAzrConTemplate.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    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrConTemplate extends BxBaseModConnectTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+    }
+}
+
+/** @} */

+ 68 - 0
modules/boonex/azure_connect/install/config.php

@@ -0,0 +1,68 @@
+<?php 
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_azrcon',
+    'title' => 'Azure AD Connect',
+    'note' => 'Join the site using LinkedIn account.',
+    'version' => '12.0.1.DEV',
+    'vendor' => 'Boonex',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '12.0.x'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/azure_connect/',
+    'home_uri' => 'azrcon',
+
+    'db_prefix' => 'bx_azrcon_',
+    'class_prefix' => 'BxAzrCon',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Azure Connect',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 20 - 0
modules/boonex/azure_connect/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    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrConInstaller extends BxDolStudioInstaller
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}
+
+/** @} */

+ 31 - 0
modules/boonex/azure_connect/install/langs/en.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string name="_sys_module_azrcon"><![CDATA[Azure AD Connect]]></string>
+	<string name="_bx_azrcon"><![CDATA[Azure AD Connect]]></string>
+	<string name="_bx_azrcon_settings"><![CDATA[Azure AD Connect]]></string>
+	<string name="_bx_azrcon_information_block"><![CDATA[
+        <p>This module can be used to login to this site using Azure Active Directory accounts.</p>
+        <p>To make it work make new registration in <a target="_blank" href="https://portal.azure.com/">Azure portal</a> then goto Azure AD B2C service. <br />
+        When asked specify the following Redirect URI:<br />
+            <ul>
+                <li>Redirect URL: <strong>{0}</strong></li>
+            </ul>
+            When registration is complete go to "Overview"  to copy Application (client) ID and Directory (tenant) ID. <br /> 
+            Client secret can be generated in "Certificates & secrets". <br />
+            For more info please refer to <a target="_blank" href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app">official documentation</a>.
+        </p>]]></string>
+	<string name="_bx_azrcon_profile_error_api_keys"><![CDATA[Please configure the module in admin panel (can't define Client ID & Secret)]]></string>
+	<string name="_bx_azrcon_auth_title"><![CDATA[Login with Azure AD]]></string>
+
+    <string name="_bx_azrcon_adm_stg_cpt_type"><![CDATA[Azure AD Connect]]></string>
+    <string name="_bx_azrcon_option_tenant_id"><![CDATA[Directory (Tenant) ID]]></string>
+    <string name="_bx_azrcon_option_client_id"><![CDATA[Application (Client) ID]]></string>
+    <string name="_bx_azrcon_option_secret"><![CDATA[Client Secret Value]]></string>
+
+	<string name="_bx_azrcon_et_password_generated"><![CDATA[Azure AD connect temporary password]]></string>
+	<string name="_bx_azrcon_et_password_generated_subject"><![CDATA[Temporary password]]></string>
+	<string name="_bx_azrcon_et_password_generated_body"><![CDATA[{email_header}
+<p>Temporary password - <b>{password}</b></p>
+<p>Change it after logging-in.</p>
+{email_footer}]]></string>
+</resources>

+ 25 - 0
modules/boonex/azure_connect/install/sql/disable.sql

@@ -0,0 +1,25 @@
+
+-- Email template
+
+DELETE FROM `sys_email_templates` WHERE `Name` = 'bx_azrcon_password_generated';
+
+-- Auth objects
+
+DELETE FROM `sys_objects_auths` WHERE `Name` = 'bx_azrcon';
+
+-- Alerts
+
+SET @iHandlerId := (SELECT `id` FROM `sys_alerts_handlers`  WHERE `name`  =  'bx_azrcon');
+
+DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandlerId;
+DELETE FROM `sys_alerts` WHERE `handler_id` =  @iHandlerId;
+
+-- Options
+
+DELETE FROM `top`, `toc`, `to` USING `sys_options_types` AS `top` LEFT JOIN `sys_options_categories` AS `toc` ON `top`.`id`=`toc`.`type_id` LEFT JOIN `sys_options` AS `to` ON `toc`.`id`=`to`.`category_id` WHERE `top`.`name` = 'bx_azrcon';
+
+-- Pages
+
+DELETE FROM `sys_objects_page` WHERE `module` = 'bx_azrcon';
+DELETE FROM `sys_pages_blocks` WHERE `module` = 'bx_azrcon';
+

+ 50 - 0
modules/boonex/azure_connect/install/sql/enable.sql

@@ -0,0 +1,50 @@
+
+-- Email template
+
+INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES
+('bx_azrcon', '_bx_azrcon_et_password_generated', 'bx_azrcon_password_generated', '_bx_azrcon_et_password_generated_subject', '_bx_azrcon_et_password_generated_body');
+
+-- Auth objects
+
+INSERT INTO `sys_objects_auths` (`Name`, `Title`, `Link`, `Icon`, `Style`) VALUES
+('bx_azrcon', '_bx_azrcon_auth_title', 'modules/?r=azrcon/start', 'fab microsoft', 'a:1:{s:7:".bx-btn";a:1:{s:10:"background";s:18:"#0078d4 !important";}}');
+
+-- Alerts
+
+INSERT INTO `sys_alerts_handlers` SET `name` = 'bx_azrcon', `class` = 'BxAzrConAlerts', `file` = 'modules/boonex/azure_connect/classes/BxAzrConAlerts.php';
+
+SET @iHandlerId := (SELECT `id` FROM `sys_alerts_handlers`  WHERE `name` = 'bx_azrcon');
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('account', 'logout', @iHandlerId),
+('profile', 'delete', @iHandlerId),
+('profile', 'add', @iHandlerId);
+
+-- Options
+
+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_azrcon', '_bx_azrcon_adm_stg_cpt_type', 'bx_azrcon@modules/boonex/azure_connect/|std-icon.svg', IF(NOT ISNULL(@iTypeOrder), @iTypeOrder + 1, 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order` )  
+VALUES (@iTypeId, 'bx_azrcon_general', '_sys_connect_adm_stg_cpt_category_general', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `order`, `extra`) VALUES
+('bx_azrcon_tenant_id', 'common', @iCategId, '_bx_azrcon_option_tenant_id', 'digit', '', '', 20, ''),
+('bx_azrcon_client_id', '', @iCategId, '_bx_azrcon_option_client_id', 'digit', '', '', 22, ''),
+('bx_azrcon_secret', '', @iCategId, '_bx_azrcon_option_secret', 'digit', '', '', 24, ''),
+('bx_azrcon_redirect_page', 'index', @iCategId, '_sys_connect_option_redirect', 'select', '', '', 40, 'join,settings,dashboard,index'),
+('bx_azrcon_module', 'bx_persons', @iCategId, '_sys_connect_option_module', 'select', '', '', 50, 'a:2:{s:6:"module";s:12:"bx_azrcon";s:6:"method";s:20:"get_profiles_modules";}'),
+('bx_azrcon_privacy', '3', @iCategId, '_sys_connect_option_privacy', 'select', '', '', 54, 'a:2:{s:6:"module";s:12:"bx_azrcon";s:6:"method";s:18:"get_privacy_groups";}'),
+('bx_azrcon_confirm_email', 'on', @iCategId, '_sys_connect_option_confirm_email', 'checkbox', '', '', 70, ''),
+('bx_azrcon_approve', '', @iCategId, '_sys_connect_option_approve', 'checkbox', '', '', 80, '');
+
+-- Pages
+
+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_azrcon_error', 'azrcon-error', '_bx_azrcon_error', '_bx_azrcon_error', 'bx_azrcon', 5, 2147483647, 0, '', '', '', '', 0, 1, 0, 'BxAzrConPage', 'modules/boonex/azure_connect/classes/BxAzrConPage.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_azrcon_error', 1, 'bx_azrcon', '_bx_azrcon_error', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_azrcon\";s:6:\"method\";s:10:\"last_error\";}', 0, 0, 1, 1);

+ 21 - 0
modules/boonex/azure_connect/install/sql/install.sql

@@ -0,0 +1,21 @@
+
+CREATE TABLE `bx_azrcon_accounts` (
+  `local_profile` int(10) unsigned NOT NULL,
+  `remote_profile` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
+  PRIMARY KEY (`local_profile`),
+  KEY `remote_profile` (`remote_profile`)
+);
+
+-- Studio page and widget
+
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_azrcon', '_bx_azrcon', '_bx_azrcon', 'bx_azrcon@modules/boonex/azure_connect/|std-icon.svg');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_azrcon', 'integrations', '{url_studio}module.php?name=bx_azrcon', '', 'bx_azrcon@modules/boonex/azure_connect/|std-icon.svg', '_bx_azrcon', '', '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));
+

+ 8 - 0
modules/boonex/azure_connect/install/sql/uninstall.sql

@@ -0,0 +1,8 @@
+
+DROP TABLE IF EXISTS `bx_azrcon_accounts`;
+
+
+-- Studio page and widget
+DELETE FROM `tp`, `tw`, `twb`, `tpw` 
+USING `sys_std_pages` AS `tp` LEFT JOIN `sys_std_widgets` AS `tw` ON `tp`.`id` = `tw`.`page_id` LEFT JOIN `sys_std_widgets_bookmarks` AS `twb` ON `tw`.`id` = `twb`.`widget_id` LEFT JOIN `sys_std_pages_widgets` AS `tpw` ON `tw`.`id` = `tpw`.`widget_id`
+WHERE  `tp`.`name` = 'bx_azrcon';

+ 18 - 0
modules/boonex/azure_connect/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    AzureConnect Azure Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . 'profiles.inc.php');
+
+check_logged();
+
+BxDolRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

+ 57 - 0
modules/boonex/azure_connect/template/images/icons/std-icon.svg

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="606px" height="606px" viewBox="0 0 606 606" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 40.2 (33826) - http://www.bohemiancoding.com/sketch -->
+    <title>Azure</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.2" offset="100%"></stop>
+        </linearGradient>
+        <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 Z" id="path-2"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-3"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-5"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-7"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-9"></path>
+    </defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="google" transform="translate(3.000000, 3.000000)">
+            <g id="Squircle2-Copy-6">
+                <g id="Shape">
+                    <use fill="#FFFFFF" xlink:href="#path-2"></use>
+                    <use stroke="#000000" stroke-width="4.04" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
+                </g>
+            </g>
+
+<!-- FIGURE [START] -->
+<g transform="translate(60,50)">
+<g transform="scale(5.0, 5.0)">
+    <defs>
+        <linearGradient id="e399c19f-b68f-429d-b176-18c2117ff73c" x1="-1032.172" x2="-1059.213" y1="145.312" y2="65.426" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
+            <stop offset="0" stop-color="#114a8b"/>
+            <stop offset="1" stop-color="#0669bc"/>
+        </linearGradient>
+        <linearGradient id="ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15" x1="-1023.725" x2="-1029.98" y1="108.083" y2="105.968" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
+            <stop offset="0" stop-opacity=".3"/>
+            <stop offset=".071" stop-opacity=".2"/>
+            <stop offset=".321" stop-opacity=".1"/>
+            <stop offset=".623" stop-opacity=".05"/>
+            <stop offset="1" stop-opacity="0"/>
+        </linearGradient>
+        <linearGradient id="a7fee970-a784-4bb1-af8d-63d18e5f7db9" x1="-1027.165" x2="-997.482" y1="147.642" y2="68.561" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
+            <stop offset="0" stop-color="#3ccbf4"/>
+            <stop offset="1" stop-color="#2892df"/>
+        </linearGradient>
+    </defs>
+    <path fill="url(#e399c19f-b68f-429d-b176-18c2117ff73c)" d="M33.338 6.544h26.038l-27.03 80.087a4.152 4.152 0 0 1-3.933 2.824H8.149a4.145 4.145 0 0 1-3.928-5.47L29.404 9.368a4.152 4.152 0 0 1 3.934-2.825z"/>
+    <path fill="#0078d4" d="M71.175 60.261h-41.29a1.911 1.911 0 0 0-1.305 3.309l26.532 24.764a4.171 4.171 0 0 0 2.846 1.121h23.38z"/>
+    <path fill="url(#ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15)" d="M33.338 6.544a4.118 4.118 0 0 0-3.943 2.879L4.252 83.917a4.14 4.14 0 0 0 3.908 5.538h20.787a4.443 4.443 0 0 0 3.41-2.9l5.014-14.777 17.91 16.705a4.237 4.237 0 0 0 2.666.972H81.24L71.024 60.261l-29.781.007L59.47 6.544z"/>
+    <path fill="url(#a7fee970-a784-4bb1-af8d-63d18e5f7db9)" d="M66.595 9.364a4.145 4.145 0 0 0-3.928-2.82H33.648a4.146 4.146 0 0 1 3.928 2.82l25.184 74.62a4.146 4.146 0 0 1-3.928 5.472h29.02a4.146 4.146 0 0 0 3.927-5.472z"/>
+</g>
+</g>
+
+<!-- FIGURE [ END ] -->
+
+        </g>
+    </g>
+</svg>