Index: /branches/rel_ag_9_4_5/lighttpd/saml2.diff
===================================================================
--- /branches/rel_ag_9_4_5/lighttpd/saml2.diff	(revision 20354)
+++ /branches/rel_ag_9_4_5/lighttpd/saml2.diff	(working copy)
@@ -836,7 +836,7 @@
 +    $b0 = proc_idp_attr_map('clear', $idp_name);
 +    $b1 = proc_idp_attr_map('create_update', $idp_name, 'u', $user, TRUE);
 +    $b2 = (strlen($group) !== 0)
-+        ? proc_idp_attr_map('create_update', $idp_name, 'g', $group, FALSE)
++        ? proc_idp_attr_map('create_update', $idp_name, 'g', $group, TRUE)
 +        : TRUE;
 +    $b3 = (strlen($acl) !== 0)
 +        ? proc_idp_attr_map('create_update', $idp_name, 'c', $acl, FALSE)
@@ -1859,9 +1859,11 @@
 diff -urNa simplesamlphp-1.14.16/lib/AN/ANSession.php simplesamlphp/lib/AN/ANSession.php
 --- simplesamlphp-1.14.16/lib/AN/ANSession.php	1970-01-01 08:00:00.000000000 +0800
 +++ simplesamlphp/lib/AN/ANSession.php	2021-03-26 15:34:31.000000000 +0800
-@@ -0,0 +1,420 @@
+@@ -0,0 +1,438 @@
 +<?php
 +
++include_once("GroupAPI.php");
++
 +class ANSession
 +{
 +    const HOST = '127.0.0.1';
@@ -1921,6 +1923,18 @@
 +            if (array_key_exists($idpAttrName, $attributes)) {
 +                $values = $attributes[$idpAttrName];
 +                $anAttributes[$anAttrName] = implode(',', $values);
++                if ($anAttrName == "g") {
++                    $anAttributes[$anAttrName] = implode(':', $values);
++                    $apiParam = GroupAPI::getParam();
++                    if (!empty($apiParam['tenant_id'])) {
++                        GroupAPI::setToken(trim($apiParam['tenant_id']),
++                                           trim($apiParam['client_id']),
++                                           trim($apiParam['client_secret']));
++                        $group_ids = $attributes[$idpAttrName];
++                        $group_names = GroupAPI::groupIdsToNames($group_ids);
++                        $anAttributes[$anAttrName] = implode(':', $group_names);
++                    }
++                }
 +            } else {
 +                if ($required) {
 +                    return FALSE;
@@ -1947,6 +1961,10 @@
 +    {
 +        $request = '#' . $checkCode . '#' . $action . '#';
 +        foreach ($params as $key => $value) {
++            if ($key === "g") {
++                $request .= $key . '#' . strlen($value) . '#' . $value . '#';
++                continue;
++            }
 +            $enc_value = urlencode($value);
 +            $request .= $key . '#' . strlen($enc_value) . '#' . $enc_value . '#';
 +        }
@@ -2280,6 +2298,213 @@
 +        return TRUE;
 +    }
 +}
+diff -urNa simplesamlphp-1.14.16/lib/AN/GroupAPI.php simplesamlphp/lib/AN/GroupAPI.php
+--- simplesamlphp-1.14.16/lib/AN/GroupAPI.php	1970-01-01 08:00:00.000000000 +0800
++++ simplesamlphp/lib/AN/GroupAPI.php	2024-12-20 14:10:12.000000000 +0800
+@@ -0,0 +1,203 @@
++<?php
++
++class GroupAPI
++{
++    const URL_OF_GET_TOKEN = "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"; // Need to replace tenant_id
++    const URL_OF_GROUP_ID = "https://graph.microsoft.com/v1.0/groups/"; // Need add group id to the end
++    const CONFIG_FILE = "GraphAPIConfig.php";
++
++    private static $access_token;
++
++    /**
++     * Send http request in different method
++     * @param string $method: Method of request, such as POST, GET
++     * @param string $url: The target url or API path
++     * @param string $vars: Parameter send to server side
++     * @param array [$headers]: Setting header for the request
++     * @return string: Response from the url
++     */
++    public static function doRequest($method, $url, $vars, $headers = array())
++    {
++        $cmd = "/ca/bin/curl {timeout} {header} {method} {param} {url}";
++        // Set timeout (unit: secs)
++        $cmd = str_replace("{timeout}", "--connect-timeout 5", $cmd);
++        // Handle url
++        if (substr( $url, 0, 5) === "https") {
++            // Starts with https, -k for no check CA
++            $cmd = str_replace("{url}", "-k {url}", $cmd);
++        }
++        $cmd = str_replace("{url}", $url, $cmd);
++        // Handle method
++        switch(strtoupper($method)) {
++            case "POST":
++                $x_param = "-X POST";
++                break;
++            case "PUT":
++                $x_param = "-X PUT";
++                break;
++            case "DELETE":
++                $x_param = "-X DELETE";
++                break;
++            default:
++                // GET
++                $x_param = "-X GET";
++        }
++        $cmd = str_replace("{method}", $x_param, $cmd);
++        // Handle headers
++        $header_str = array();
++        foreach ($headers as $_header) {
++            $header_str[] ='-H "' . $_header . '"';
++        }
++        // -H "header1" -H "header2", ...
++        $cmd = str_replace("{header}", implode(" ", $header_str), $cmd);
++        // Handle parameters
++        $cmd = str_replace("{param}", ('-d "' . $vars . '"'), $cmd);
++        $response = shell_exec($cmd);
++        return $response;
++    }
++
++    /**
++     * Send POST request to server side or API
++     * @param string $url: The target url or API path
++     * @param string $vars: Parameter send to server side
++     * @param array [$headers]: Setting header for the request
++     * @return string: Response from the url
++     */
++    public static function post($url, $vars, $headers = array('Content-Type: application/x-www-form-urlencoded'))
++    {
++        try {
++            $result = self::doRequest('POST', $url, $vars, $headers);
++        } catch (\ErrorException $e) {
++            $error = 1;
++        }
++        if(isset($error)){
++            error_log("error in post: " . $error);
++            return;
++        }
++        return $result;
++    }
++
++    /**
++     * Send GET request to server side or API
++     * @param string $url: The target url or API path
++     * @param array [$headers]: Setting header for the request
++     * @return string: Response from the url
++     */
++    public static function get($url, $headers = array())
++    {
++        try {
++            $result = self::doRequest('GET', $url, null, $headers);
++        } catch (\ErrorException $e) {
++            $error = $e->getMessage();
++        }
++        if(isset($error)){
++            error_log("error in get: " . $error);
++            return;
++        }
++        return $result;
++    }
++
++    /**
++     * Send GET request with token to server side or API
++     * @param string $url: The target url or API path
++     * @return string: Response from the url
++     */
++    public static function getByToken($url)
++    {
++        $authorization = "Authorization: Bearer " . self::$access_token;
++        $headers = array($authorization);
++        return self::get($url, $headers);
++    }
++
++    /**
++     * Set access token by parameter
++     * @param string $tenant_id: Tenant Id for token url
++     * @param string $client_id: Clitent Id for APP
++     * @param string $client_secret: Secret Key
++     */
++    public static function setToken($tenant_id, $client_id, $client_secret)
++    {
++        $url = str_replace("{tenant_id}", $tenant_id, self::URL_OF_GET_TOKEN);
++        $param = array(
++            "client_id" => $client_id,
++            "client_secret" => $client_secret,
++            "scope" => "https://graph.microsoft.com/.default",
++            "grant_type" => "client_credentials"
++        );
++        $response = self::post($url, http_build_query($param));
++        try {
++            $json = json_decode($response, true);
++        } catch (\JsonException $exception) {
++            error_log("[getToken] Cannot parse response to json format");
++            return;
++        }
++        if (empty($json['access_token'])) {
++            error_log("[getToken] Cannot get token from API");
++            return;
++        }
++        self::$access_token = $json['access_token'];
++    }
++
++    /**
++     * Get group info by id
++     * @param string $group_id: Certain group id for query
++     * @return string: group name
++     */
++    public static function getGroupById($group_id)
++    {
++        if (empty(self::$access_token)) {
++            return;
++        }
++        $url = self::URL_OF_GROUP_ID . $group_id;
++        $response = self::getByToken($url);
++        try {
++            $json = json_decode($response, true);
++        } catch (\JsonException $exception) {
++            error_log("[getGroupById] Cannot parse response to json format");
++            return;
++        }
++        if (empty($json['displayName'])) {
++            error_log("[getGroupById] Response from API is invalid: " . $response);
++            return;
++        }
++        return $json['displayName'];
++    }
++
++    /**
++     * Return group names by group id array
++     * @param array $group_ids: group id array
++     * @return array: group name array
++     */
++    public static function groupIdsToNames($group_ids)
++    {
++        $result = array();
++        foreach ($group_ids as $group_id) {
++            $group_name = self::getGroupById($group_id);
++            $result[] = urlencode($group_name);
++        }
++        return $result;
++    }
++
++    /**
++     * Get parameter of Graph API from configuration file
++     * @return array: param of Graph API
++     */
++    public static function getParam()
++    {
++        $config = array(
++            'tenant_id' => '',
++            'client_id' => '',
++            'client_secret' => '',
++        );
++        if (!file_exists(__DIR__ . "/" . self::CONFIG_FILE)) {
++            return $config;
++        }
++        $get_config = file_get_contents(__DIR__ . "/" . self::CONFIG_FILE);
++        if ($get_config !== false && !empty($get_config)) {
++            $remove_new_line = str_replace("\n", "", $get_config);
++            $config = json_decode($remove_new_line, true);
++        }
++        return $config;
++    }
++
++}
 diff -urNa simplesamlphp-1.14.16/lib/AN/idpnamemap.php simplesamlphp/lib/AN/idpnamemap.php
 --- simplesamlphp-1.14.16/lib/AN/idpnamemap.php	1970-01-01 08:00:00.000000000 +0800
 +++ simplesamlphp/lib/AN/idpnamemap.php	2021-03-26 14:58:47.000000000 +0800
Index: /branches/rel_ag_9_4_5/lighttpd/saml_init.sh
===================================================================
--- /branches/rel_ag_9_4_5/lighttpd/saml_init.sh	(revision 20354)
+++ /branches/rel_ag_9_4_5/lighttpd/saml_init.sh	(working copy)
@@ -31,6 +31,11 @@
 
 mkdir -p lib/AN/attributemap
 
+if [ ! -e lib/AN/GraphAPIConfig.php ];then
+    echo "" > lib/AN/GraphAPIConfig.php
+    chmod 0777 lib/AN/GraphAPIConfig.php
+fi
+
 secretsalt=`head -c 1000 /dev/urandom | tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' | head -c 32`
 sed -e "s#'baseurlpath' => 'simplesaml/'#'baseurlpath' => '/prx/000/http/localhost/saml2/${vsite_name}/'#;
         s#'secretsalt' => 'defaultsecretsalt'#'secretsalt' => '${secretsalt}'#" -i "" config/config.php
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/class.anLib_pageTree.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/class.anLib_pageTree.php	(revision 20354)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/class.anLib_pageTree.php	(working copy)
@@ -926,9 +926,12 @@
 					$this->addNode(NODE_TAB, language::translate('navNode_vGpMap'), $defaultRef);
 						$this->addPage('vAAAGroupMap', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TAB], array(array(), array('aaa'), "site", array('aaa')));
 					$this->addNode(NODE_TAB, language::translate('navNode_vSAML'), $defaultRef);
-						$this->addPage('vAAASAML', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TAB], array(array(), array('aaa'), "site", array('aaa')));
-						$this->addPage('vAAASAMLAddIdp', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TAB], array(array(), array('aaa'), "site", array('aaa')));
-						$this->addPage('vAAASAMLEditIdp', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TAB], array(array(), array('aaa'), "site", array('aaa')));
+						$this->addNode(NODE_TABSUBMENU, language::translate('saml_configuration'), $defaultRef);
+							$this->addPage('vAAASAML', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TABSUBMENU], array(array(), array('aaa'), "site", array('aaa')));
+							$this->addPage('vAAASAMLAddIdp', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TABSUBMENU], array(array(), array('aaa'), "site", array('aaa')));
+							$this->addPage('vAAASAMLEditIdp', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TABSUBMENU], array(array(), array('aaa'), "site", array('aaa')));
+						$this->addNode(NODE_TABSUBMENU, language::translate('navNode_vAAASamlMicrosoftGraphAPI'), $defaultRef);
+							$this->addPage('vAAASAMLEditGraphAPIParam', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TABSUBMENU], array(array(), array('aaa'), "site", array('aaa')));
 					$this->addNode(NODE_TAB, language::translate('navNode_vOAuth'), $defaultRef);
 						$this->addPage('vAAAOAuth', 'incVirtual/siteConfig/aaa/class.anPage_vAAA.php', $this->currentNode[NODE_TAB], array(array(), array('aaa'), "site", array('aaa')));
 					$this->addNode(NODE_TAB, language::translate('navNode_vFederation'), $defaultRef);
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/chinese.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/chinese.php	(revision 20354)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/chinese.php	(working copy)
@@ -4981,6 +4981,11 @@
 		'saml_idp_desc'                            => 'IdP描述',
 		'saml_idp_del'                             => '删除IdP',
 		'saml_idp_add'                             => '增加IdP',
+		'navNode_vAAASamlMicrosoftGraphAPI'        => 'Microsoft Graph 接口',
+		'graph_api_tenant_id'                      => '目录(租户) ID',
+		'graph_api_client_id'                      => '应用程序(客户端) ID',
+		'graph_api_client_secret'                  => '机密(值)',
+		'graph_api_param_save_failed'              => '保存设置失败',
 		'oauth_enable'                             => '启用OAuth认证',
 		'oauth_login_page'                         => '浏览器的登录URL',
 		'oauth_tokenurl'                           => '获取访问Token的URL',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/default.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/default.php	(revision 20354)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/default.php	(working copy)
@@ -1594,6 +1594,11 @@
 	'saml_idp_desc' => 'IdP Description',
 	'saml_idp_del' => 'Delete IdP',
 	'saml_idp_add' => 'Add IdP',
+	'navNode_vAAASamlMicrosoftGraphAPI' => 'Microsoft Graph API',
+	'graph_api_tenant_id' => 'Tenant ID',
+	'graph_api_client_id' => 'Application(Client) ID',
+	'graph_api_client_secret' => 'Client Secret(value)',
+	'graph_api_param_save_failed' => 'Save Configuration Failed',
 	'oauth_enable' => 'Enable OAuth Authentication',
 	'oauth_login_page' => 'Login URL for Browsers',
 	'oauth_tokenurl' => 'URL to Obtain Access Token',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/japanese.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/japanese.php	(revision 20354)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/japanese.php	(working copy)
@@ -4305,6 +4305,11 @@
 	'saml_idp_desc'                            => 'IdP 説明',
 	'saml_idp_del'                             => 'IdP 削除',
 	'saml_idp_add'                             => 'IdP 追加',
+	'navNode_vAAASamlMicrosoftGraphAPI'        => 'Microsoft Graph API',
+	'graph_api_tenant_id'                      => 'ディレクトリ (テナント) ID',
+	'graph_api_client_id'                      => 'アプリケーション (クライアント) ID',
+	'graph_api_client_secret'                  => 'シークレット(値)',
+	'graph_api_param_save_failed'              => '設定の保存に失敗しました',
 	'oauth_enable'                             => 'OAuth 認証の有効化',
 	'oauth_login_page'                         => 'ブラウザ用ログイン URL',
 	'oauth_tokenurl'                           => 'アクセストークン取得 URL',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/traditional_chinese.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/traditional_chinese.php	(revision 20354)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/inc/language/traditional_chinese.php	(working copy)
@@ -4981,6 +4981,11 @@
 		'saml_idp_desc' => 'IdP描述',
 		'saml_idp_del' => '刪除IdP',
 		'saml_idp_add' => '增加IdP',
+		'navNode_vAAASamlMicrosoftGraphAPI' => 'Microsoft Graph API',
+		'graph_api_tenant_id' => '租用戶識別碼',
+		'graph_api_client_id' => '應用程式 (用戶端) 識別碼',
+		'graph_api_client_secret' => '祕密識別碼(值)',
+		'graph_api_param_save_failed' => '儲存設定失敗',
 		'oauth_enable' => '啟用OAuth認證',
 		'oauth_login_page' => '瀏覽器的登入URL',
 		'oauth_tokenurl' => '取得存取Token的URL',
Index: /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/siteConfig/aaa/class.anPage_vAAA.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/siteConfig/aaa/class.anPage_vAAA.php	(revision 20354)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/siteConfig/aaa/class.anPage_vAAA.php	(working copy)
@@ -192,6 +192,9 @@
 			$_SESSION['currentPageId'] = 'vAAASAML';
 		}
 		break;
+	case 'vAAASAMLEditGraphAPIParam':
+		require_once('incVirtual/siteConfig/aaa/class.cliWrap_vAAAMSGraphAPI.php'); 
+		break;
 	case 'vAAAOAuth':
 		require_once('incVirtual/siteConfig/aaa/class.cliWrap_vAAAOAuth.php');
 		break;
@@ -459,6 +462,9 @@
 					$cli_vAAASAML = anLib_baseCliWrapper::getSingleton('cliWrap_vAAASAML');
 				}
 				break;
+			case 'vAAASAMLEditGraphAPIParam':
+				$cli_vAAAMSGraphAPI = anLib_baseCliWrapper::getSingleton('cliWrap_vAAAMSGraphAPI');
+				break;
 			case 'vAAAOAuth':
 				$cli_vAAAOAuth = anLib_baseCliWrapper::getSingleton('cliWrap_vAAAOAuth');
 				break;
@@ -610,6 +616,9 @@
 			case 'vAAASAMLEditIdp':
 				$this->syncWithCliWrapper($cli_vAAASAMLEditIdp);
 				break;
+			case 'vAAASAMLEditGraphAPIParam':
+				$this->syncWithCliWrapper($cli_vAAAMSGraphAPI);
+				break;
 			case 'vAAAOAuth':
 				$this->syncWithCliWrapper($cli_vAAAOAuth);
 				break;
Index: /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/siteConfig/aaa/class.cliWrap_vAAAMSGraphAPI.php
===================================================================
--- /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/siteConfig/aaa/class.cliWrap_vAAAMSGraphAPI.php	(revision 0)
+++ /branches/rel_ag_9_4_5/webui/proxy/new/incVirtual/siteConfig/aaa/class.cliWrap_vAAAMSGraphAPI.php	(working copy)
@@ -0,0 +1,327 @@
+<?PHP
+/*************************************************************************
+*
+* Copyright Notice
+*
+* (c) 2024 ArrayNetworks All Rights Reserved
+*
+* @author
+* @package - ArrayNetworks CLI Wrapper Library
+*
+*************************************************************************/
+
+
+// ========================================================================
+// INCLUDE CLASS DEPENDANCIES
+// ========================================================================
+require_once('inc/class.anLib_baseCliWrapper.php');
+
+/*************************************************************************
+*
+* WRAPPER FOR CLI COMMANDS:
+*
+* (none)
+*
+* PUBLIC FUNCTIONS:
+*
+*
+* PRIVATE FUNCTIONS:
+*
+* setMainContent ()
+* getVSiteNmae ()
+* setFormContent ()
+* setJsAppend ()
+* setJsOnLoadStart ()
+* setJsOnLoadEnd()
+* setCSSAppend ()
+* setJsOnResetAppend ()
+* setJsOnSaveAppend()
+*
+*************************************************************************/
+class cliWrap_vAAAMSGraphAPI extends anLib_baseCliWrapper {
+
+    // ------------------------------------------------------------
+    // Class variables
+    // ------------------------------------------------------------
+    /********************************************************************
+    *
+    * Constructor
+    *
+    * $param    string - class id
+    * @return   void
+    *
+    ********************************************************************/
+    function cliWrap_vAAAMSGraphAPI($in_classId) {
+        $this->classId = $in_classId;
+        // ------------------------------------------------------------
+        // Initialize content strings
+        // ------------------------------------------------------------
+        $this->mainContent = '';
+        $this->formContent = '';
+        $this->jsAppend = '';
+        $this->jsOnLoadStart = '';
+        $this->jsOnLoadEnd = '';
+        $this->cssAppend = '';
+        $this->jsOnResetAppend = '';
+        // ------------------------------------------------------------
+        // Configuration file path
+        // ------------------------------------------------------------
+        $this->configFilePath = '/ca/an_lighttpd/simplesamlphp-{vsite_name}/lib/AN/GraphAPIConfig.php';
+
+        // ------------------------------------------------------------
+        // Check overall authorization based on license and group
+        // requirements.  This authorization point checks against a
+        // merged policy based on all licenses and group features used
+        // in this particular CLI wrapper.  The results are stored in
+        // the 'viewMode' class variable.  For more granular behavior,
+        // the developer must implement the pilot::cliCmdAuthorization
+        // function on a case by case basis throughout the rest of the
+        // code.
+        // ------------------------------------------------------------
+        $this->viewMode = pilot::cliCmdAuthorization('','aaa','site');
+        if ($this->viewMode == CLI_HIDE) {
+        // User is not currently authorizaed to access this cli wrapper...must exit.
+            return;
+        }
+
+        // ------------------------------------------------------------
+        // Check for submission of form.  Process form as necessary.
+        // ------------------------------------------------------------
+        $temp_arr_actionStr = explode(',', $_POST['actionStr']);
+        while (list($temp_txt_key, $temp_txt_data) = each($temp_arr_actionStr)) {
+            switch($temp_txt_data) {
+                case ($this->classId . '_gSAMLGraphAPIParamSaveChange'):
+                    if (empty($_POST[$this->classId . '_vs_aaa_saml_graph_api_param_tenant_id'])) {
+                        $this->jsOnLoadEnd .= 'g_errStr += "' . language::translate('valid_range_not_empty') . '";';
+                        break;
+                    }
+                    if (empty($_POST[$this->classId . '_vs_aaa_saml_graph_api_param_client_id'])) {
+                        $this->jsOnLoadEnd .= 'g_errStr += "' . language::translate('valid_range_not_empty') . '";';
+                        break;
+                    }
+                    if (empty($_POST[$this->classId . '_vs_aaa_saml_graph_api_param_client_secret'])) {
+                        $this->jsOnLoadEnd .= 'g_errStr += "' . language::translate('valid_range_not_empty') . '";';
+                        break;
+                    }
+                    $config = array(
+                        'tenant_id' => $_POST[$this->classId . '_vs_aaa_saml_graph_api_param_tenant_id'],
+                        'client_id' => $_POST[$this->classId . '_vs_aaa_saml_graph_api_param_client_id'],
+                        'client_secret' => $_POST[$this->classId . '_vs_aaa_saml_graph_api_param_client_secret'],
+                    );
+                    $content = json_encode($config);
+                    $temp_out_clicmd_showInfo = cli::exec_direct('show info');
+                    $vsite_name = $this->getVSiteNmae();
+                    if (empty($vsite_name)) {
+                        $this->jsOnLoadEnd .= 'g_errStr += "' . language::translate('graph_api_param_save_failed') . '";';
+                        break;
+                    }
+                    $this->configFilePath = str_replace("{vsite_name}", $vsite_name, $this->configFilePath);
+                    if (file_exists($this->configFilePath)) {
+                        if (file_put_contents($this->configFilePath, $content) === false) {
+                            $this->jsOnLoadEnd .= 'g_errStr += "' . language::translate('graph_api_param_save_failed') . '";';
+                            break;
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        // ------------------------------------------------------------
+        // Build main content object
+        // ------------------------------------------------------------
+    }
+
+    /*********************************************************************
+     * Get name of virtual site
+     */
+    function getVSiteNmae () {
+        $temp_out_clicmd_showInfo = cli::exec_direct('show info');
+        $vsite_name = "";
+        if ($temp_out_clicmd_showInfo->result) {
+            $vsite_name = $temp_out_clicmd_showInfo->content[1]->vsite_name;
+        }
+        return $vsite_name;
+    }
+
+    /********************************************************************
+    *
+    * Create main HTML content for the respective CLI command
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setMainContent () {
+        global $g_tabIndex;
+
+        //Initialize local vars here.
+        $this->mainContent = '';
+
+        // ------------------------------------------------------------
+        // Parse cli response for data here.
+        // ------------------------------------------------------------
+        $config = array(
+            'tenant_id' => '',
+            'client_id' => '',
+            'client_secret' => '',
+        );
+        $vsite_name = $this->getVSiteNmae();
+        if (!empty($vsite_name)) {
+            $this->configFilePath = str_replace("{vsite_name}", $vsite_name, $this->configFilePath);
+            $get_config = file_get_contents($this->configFilePath);
+            if ($get_config !== false && !empty($get_config)) {
+                $config = json_decode($get_config, true);
+            }
+        }
+
+        // ------------------------------------------------------------
+        // Context based section header buttons
+        // ------------------------------------------------------------
+        switch ($this->viewMode) {
+            case CLI_CONFIG:
+                $this->htmlSectionHeaderBtns .= '<a style="font-weight: bold" href="javascript:formAction_setPage(\'vAAASAMLEditGraphAPIParam\');">' . language::translate('global_saveconfig') . '</a>';
+                break;
+            case CLI_ENABLE:
+            case CLI_HIDE:
+            default:
+                break;
+        }
+
+        // ------------------------------------------------------------
+        // Call content function based on user's current mode
+        // ------------------------------------------------------------
+        switch ($this->viewMode) {
+            case CLI_CONFIG:
+            case CLI_ENABLE:
+                $this->mainContent = anLib_htmlCode::pageSectionStart('generalsettings', 1, '');
+                $this->mainContent .= anLib_htmlCode::cliWrap_startTable();
+                $this->mainContent .= anLib_htmlCode::cliWrap_rowTextField('_vs_aaa_saml_graph_api_param_tenant_id',
+                                        'graph_api_tenant_id',
+                                        $this, 
+                                        $config['tenant_id'],
+                                        $g_tabIndex++,
+                                        'maxlength="64" size="50" onfocus="trackEdit(this, \'' . $this->classId . '_gSAMLGraphAPIParamSaveChange\');"');
+                $this->mainContent .= anLib_htmlCode::cliWrap_rowTextField('_vs_aaa_saml_graph_api_param_client_id',
+                                        'graph_api_client_id',
+                                        $this, 
+                                        $config['client_id'],
+                                        $g_tabIndex++,
+                                        'maxlength="64" size="50" onfocus="trackEdit(this, \'' . $this->classId . '_gSAMLGraphAPIParamSaveChange\');"');
+                $this->mainContent .= anLib_htmlCode::cliWrap_rowTextField('_vs_aaa_saml_graph_api_param_client_secret',
+                                        'graph_api_client_secret',
+                                        $this, 
+                                        $config['client_secret'],
+                                        $g_tabIndex++,
+                                        'maxlength="64" size="50" onfocus="trackEdit(this, \'' . $this->classId . '_gSAMLGraphAPIParamSaveChange\');"');
+
+                $this->mainContent .= anLib_htmlCode::pageSectionSpacer();
+                $this->mainContent .= anLib_htmlCode::pageSectionEnd();
+
+                break;
+            case CLI_HIDE:
+            default:
+                // Should not normally reach this case
+                $this->mainContent .= '<TR><TD>' . language::translate('debug_unexpectedErrorIn') . $this->classId . '</TD></TR>';
+                break;
+        }
+    }
+
+    /********************************************************************
+    *
+    *  Create extra <INPUT> tags for form submission
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setFormContent () {
+        // Example:  $this->formContent .= '<INPUT type="hidden" name="foobar">';
+    }
+
+    /********************************************************************
+    *
+    * Add supporting javascript code to be appended at the end of 
+    * the <SCRIPT></SCRIPT> container
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setJsAppend () {
+        // ------------------------------------------------------------
+        // Build grid table
+        // ------------------------------------------------------------
+        // Declare grid table column headers
+
+        // Instantiate and configure style for grid table
+    }
+
+    /********************************************************************
+    *
+    * Add supporting javascript code to be executed at the beginning
+    * of the page "onload" event
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setJsOnLoadStart () {
+        // Example:  $this->jsOnLoadStart .= 'window.alert("Hello");';
+
+    } //End of function.
+
+    /********************************************************************
+    *
+    * Add supporting javascript code to be executed at the end
+    * of the page "onload" event
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setJsOnLoadEnd () {
+        // Example:  $this->jsOnLoadEnd .= 'window.alert("Hello");';
+
+    }
+
+    /********************************************************************
+    *
+    * Add supporting CSS code to be inserted inside 
+    * the global <STYLE></STYLE> container
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setCSSAppend () {
+        // Example:  $this->cssAppend .= 'BODY {background-color: #000000;}';
+    }
+
+    /********************************************************************
+    *
+    * Add supporting JavaScript code to be executed during the
+    * <FORM> onReset event.
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setJsOnResetAppend () {
+        // Example:  $this->jsOnResetAppend .= 'window.alert("Hello from setJsOnResetAppend...");';
+    }
+
+    /********************************************************************
+    *
+    * Add supporting JavaScript code to be executed during the
+    * <FORM> onSave event.
+    *
+    * $param    void
+    * @return   void
+    *
+    ********************************************************************/
+    function setJsOnSaveAppend () {
+        // Example:  $this->jsOnSaveAppend .= 'window.alert("Hello from setJsOnSaveAppend...");';
+    }
+}
+?>
