Browse Source

[ui][sidebar] Make visibility of the help menu in the sidebar configurable (#3722)

Co-authored-by: tarunjangid <tj@cloudera.com>
Tarun Jangid 1 year ago
parent
commit
f678becc03

+ 4 - 0
desktop/conf.dist/hue.ini

@@ -334,6 +334,10 @@ http_500_debug_mode=false
 # default value is false
 # default value is false
 ## disable_local_storage = false
 ## disable_local_storage = false
 
 
+# Whether or not to show the Help menu in the Sidebar.
+# Default value is true
+## enable_help_menu=true
+
 # Administrators
 # Administrators
 # ----------------
 # ----------------
 [[django_admins]]
 [[django_admins]]

+ 4 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -339,6 +339,10 @@
   # default value is false
   # default value is false
   ## disable_local_storage = false
   ## disable_local_storage = false
 
 
+  # Whether or not to show the Help menu in the Sidebar.
+  # Default value is true
+  ## enable_help_menu=true
+
   # Administrators
   # Administrators
   # ----------------
   # ----------------
   [[django_admins]]
   [[django_admins]]

+ 101 - 36
desktop/core/src/desktop/conf.py

@@ -1,5 +1,5 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
-## -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
 # Licensed to Cloudera, Inc. under one
 # Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
 # distributed with this work for additional information
@@ -16,29 +16,39 @@
 # See the License for the specific language governing permissions and
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # limitations under the License.
 
 
-import datetime
-import glob
-import logging
 import os
 import os
-import socket
-import stat
 import sys
 import sys
-
+import glob
+import stat
+import socket
+import logging
+import datetime
 from collections import OrderedDict
 from collections import OrderedDict
 
 
 from django.db import connection
 from django.db import connection
 
 
-from metadata.metadata_sites import get_navigator_audit_log_dir, get_navigator_audit_max_file_size
-
 from desktop import appmanager
 from desktop import appmanager
-from desktop.redaction.engine import parse_redaction_policy_from_file
-from desktop.lib.conf import Config, ConfigSection, UnspecifiedConfigSection, coerce_bool, coerce_csv, coerce_json_dict, \
-    validate_path, list_of_compiled_res, coerce_str_lowercase, coerce_password_from_script, coerce_string
+from desktop.lib.conf import (
+  Config,
+  ConfigSection,
+  UnspecifiedConfigSection,
+  coerce_bool,
+  coerce_csv,
+  coerce_json_dict,
+  coerce_password_from_script,
+  coerce_str_lowercase,
+  coerce_string,
+  list_of_compiled_res,
+  validate_path,
+)
 from desktop.lib.i18n import force_unicode
 from desktop.lib.i18n import force_unicode
 from desktop.lib.paths import get_desktop_root, get_run_root
 from desktop.lib.paths import get_desktop_root, get_run_root
+from desktop.redaction.engine import parse_redaction_policy_from_file
+from metadata.metadata_sites import get_navigator_audit_log_dir, get_navigator_audit_max_file_size
 
 
 if sys.version_info[0] > 2:
 if sys.version_info[0] > 2:
   from builtins import str as new_str
   from builtins import str as new_str
+
   from django.utils.translation import gettext_lazy as _
   from django.utils.translation import gettext_lazy as _
 else:
 else:
   new_str = unicode
   new_str = unicode
@@ -52,6 +62,7 @@ def is_oozie_enabled():
   """Oozie needs to be available as it is the backend."""
   """Oozie needs to be available as it is the backend."""
   return len([app for app in appmanager.DESKTOP_MODULES if app.name == 'oozie']) > 0
   return len([app for app in appmanager.DESKTOP_MODULES if app.name == 'oozie']) > 0
 
 
+
 def coerce_database(database):
 def coerce_database(database):
   if database == 'mysql':
   if database == 'mysql':
     return 'django.db.backends.mysql'
     return 'django.db.backends.mysql'
@@ -84,6 +95,7 @@ def coerce_file(path):
 def coerce_timedelta(value):
 def coerce_timedelta(value):
   return datetime.timedelta(seconds=int(value))
   return datetime.timedelta(seconds=int(value))
 
 
+
 def get_dn(fqdn=None):
 def get_dn(fqdn=None):
   """This function returns fqdn(if possible)"""
   """This function returns fqdn(if possible)"""
   val = []
   val = []
@@ -102,11 +114,12 @@ def get_dn(fqdn=None):
     else:
     else:
       LOG.warning("allowed_hosts value to '*'. It is a security risk")
       LOG.warning("allowed_hosts value to '*'. It is a security risk")
       val.append('*')
       val.append('*')
-  except:
+  except Exception as e:
     LOG.warning("allowed_hosts value to '*'. It is a security risk")
     LOG.warning("allowed_hosts value to '*'. It is a security risk")
     val.append('*')
     val.append('*')
   return val
   return val
 
 
+
 def coerce_positive_integer(integer):
 def coerce_positive_integer(integer):
   integer = int(integer)
   integer = int(integer)
 
 
@@ -115,10 +128,12 @@ def coerce_positive_integer(integer):
 
 
   return integer
   return integer
 
 
+
 def is_lb_enabled():
 def is_lb_enabled():
   """Check for Hue Load Balancer is available"""
   """Check for Hue Load Balancer is available"""
   return bool(HUE_LOAD_BALANCER.get())
   return bool(HUE_LOAD_BALANCER.get())
 
 
+
 def coerce_zero_or_positive_integer(integer):
 def coerce_zero_or_positive_integer(integer):
   integer = int(integer)
   integer = int(integer)
 
 
@@ -127,34 +142,42 @@ def coerce_zero_or_positive_integer(integer):
 
 
   return integer
   return integer
 
 
+
 def is_https_enabled():
 def is_https_enabled():
   """Hue is configured for HTTPS."""
   """Hue is configured for HTTPS."""
   return bool(SSL_CERTIFICATE.get() and SSL_PRIVATE_KEY.get())
   return bool(SSL_CERTIFICATE.get() and SSL_PRIVATE_KEY.get())
 
 
+
 def get_slack_client_id():
 def get_slack_client_id():
   """Returns Slack Client ID if set as environment variable else None"""
   """Returns Slack Client ID if set as environment variable else None"""
   return os.environ.get('SLACK_CLIENT_ID')
   return os.environ.get('SLACK_CLIENT_ID')
 
 
+
 def get_slack_client_secret():
 def get_slack_client_secret():
   """Returns Slack Client Secret if set as environment variable else None"""
   """Returns Slack Client Secret if set as environment variable else None"""
   return os.environ.get('SLACK_CLIENT_SECRET')
   return os.environ.get('SLACK_CLIENT_SECRET')
 
 
+
 def get_slack_verification_token():
 def get_slack_verification_token():
   """Returns Slack Verification Token if set as environment variable else None"""
   """Returns Slack Verification Token if set as environment variable else None"""
   return os.environ.get('SLACK_VERIFICATION_TOKEN')
   return os.environ.get('SLACK_VERIFICATION_TOKEN')
 
 
+
 def get_slack_bot_user_token():
 def get_slack_bot_user_token():
   """Returns Slack Bot User Token if set as environment variable else None"""
   """Returns Slack Bot User Token if set as environment variable else None"""
   return os.environ.get('SLACK_BOT_USER_TOKEN')
   return os.environ.get('SLACK_BOT_USER_TOKEN')
 
 
+
 def is_python2():
 def is_python2():
   """Hue is running on Python 2."""
   """Hue is running on Python 2."""
   return sys.version_info[0] == 2
   return sys.version_info[0] == 2
 
 
+
 def is_custom_jwt_auth_enabled():
 def is_custom_jwt_auth_enabled():
   """Returns True if key server url is set else returns False"""
   """Returns True if key server url is set else returns False"""
   return bool(AUTH.JWT.KEY_SERVER_URL.get())
   return bool(AUTH.JWT.KEY_SERVER_URL.get())
 
 
+
 USE_CHERRYPY_SERVER = Config(
 USE_CHERRYPY_SERVER = Config(
   key="use_cherrypy_server",
   key="use_cherrypy_server",
   help=_("If set to true, CherryPy will be used. Otherwise, Gunicorn will be used as the webserver."),
   help=_("If set to true, CherryPy will be used. Otherwise, Gunicorn will be used as the webserver."),
@@ -280,9 +303,11 @@ SSL_CIPHER_LIST = Config(
     '!KRB5-DES-CBC3-SHA',
     '!KRB5-DES-CBC3-SHA',
   ]))
   ]))
 
 
+
 def has_ssl_no_renegotiation():
 def has_ssl_no_renegotiation():
   return sys.version_info[:2] >= (3, 7)
   return sys.version_info[:2] >= (3, 7)
 
 
+
 SSL_NO_RENEGOTIATION = Config(
 SSL_NO_RENEGOTIATION = Config(
   key="ssl_no_renegotiation",
   key="ssl_no_renegotiation",
   type=coerce_bool,
   type=coerce_bool,
@@ -349,12 +374,12 @@ SECURE_CONTENT_SECURITY_POLICY = Config(
   help=_('X-Content-Type-Options: nosniff. This is a HTTP response header feature that helps prevent attacks '
   help=_('X-Content-Type-Options: nosniff. This is a HTTP response header feature that helps prevent attacks '
     'based on MIME-type confusion.'),
     'based on MIME-type confusion.'),
   type=str,
   type=str,
-  default="script-src 'self' 'unsafe-inline' 'unsafe-eval' *.googletagmanager.com *.doubleclick.net data:;"+
-          "img-src 'self' *.doubleclick.net http://*.tile.osm.org *.tile.osm.org *.gstatic.com data:;"+
-          "style-src 'self' 'unsafe-inline' fonts.googleapis.com;"+
-          "connect-src 'self' *.google-analytics.com;"+
-          "frame-src *;"+
-          "child-src 'self' data: *.vimeo.com;"+
+  default="script-src 'self' 'unsafe-inline' 'unsafe-eval' *.googletagmanager.com *.doubleclick.net data:;" +
+          "img-src 'self' *.doubleclick.net http://*.tile.osm.org *.tile.osm.org *.gstatic.com data:;" +
+          "style-src 'self' 'unsafe-inline' fonts.googleapis.com;" +
+          "connect-src 'self' *.google-analytics.com;" +
+          "frame-src *;" +
+          "child-src 'self' data: *.vimeo.com;" +
           "object-src 'none'")
           "object-src 'none'")
 
 
 SECURE_SSL_REDIRECT = Config(
 SECURE_SSL_REDIRECT = Config(
@@ -397,15 +422,18 @@ LDAP_USERNAME = Config(
   private=True,
   private=True,
   default="hue")
   default="hue")
 
 
+
 def get_auth_username():
 def get_auth_username():
   """Backward compatibility"""
   """Backward compatibility"""
   return LDAP_USERNAME.get()
   return LDAP_USERNAME.get()
 
 
+
 AUTH_USERNAME = Config(
 AUTH_USERNAME = Config(
   key="auth_username",
   key="auth_username",
   help=_("Auth username of the hue user used for authentications. For example for LDAP Authentication with HiveServer2/Impala."),
   help=_("Auth username of the hue user used for authentications. For example for LDAP Authentication with HiveServer2/Impala."),
   dynamic_default=get_auth_username)
   dynamic_default=get_auth_username)
 
 
+
 def get_auth_password():
 def get_auth_password():
   """Get from script or backward compatibility"""
   """Get from script or backward compatibility"""
 
 
@@ -421,12 +449,13 @@ def get_auth_password():
   if password is not None:
   if password is not None:
     return password
     return password
 
 
-  password = LDAP_PASSWORD.get() # 2 levels for backward compatibility
+  password = LDAP_PASSWORD.get()  # 2 levels for backward compatibility
   if password:
   if password:
     return password
     return password
 
 
   return LDAP_PASSWORD_SCRIPT.get()
   return LDAP_PASSWORD_SCRIPT.get()
 
 
+
 AUTH_PASSWORD = Config(
 AUTH_PASSWORD = Config(
   key="auth_password",
   key="auth_password",
   help=_("LDAP/PAM/.. password of the hue user used for authentications. Inactive if empty. "
   help=_("LDAP/PAM/.. password of the hue user used for authentications. Inactive if empty. "
@@ -534,9 +563,9 @@ REDIRECT_WHITELIST = Config(
   key="redirect_whitelist",
   key="redirect_whitelist",
   help=_("Comma-separated list of regular expressions, which match the redirect URL."
   help=_("Comma-separated list of regular expressions, which match the redirect URL."
          "For example, to restrict to your local domain and FQDN, the following value can be used:"
          "For example, to restrict to your local domain and FQDN, the following value can be used:"
-         "  ^\/.*$,^http:\/\/www.mydomain.com\/.*$"),
+         r"  ^\/.*$,^http:\/\/www.mydomain.com\/.*$"),
   type=list_of_compiled_res(skip_empty=True),
   type=list_of_compiled_res(skip_empty=True),
-  default='^(\/[a-zA-Z0-9]+.*|\/)$')
+  default=r'^(\/[a-zA-Z0-9]+.*|\/)$')
 
 
 USE_X_FORWARDED_HOST = Config(
 USE_X_FORWARDED_HOST = Config(
   key="use_x_forwarded_host",
   key="use_x_forwarded_host",
@@ -575,7 +604,7 @@ CLUSTER_ID = Config(
   private=False,
   private=False,
   default='default')
   default='default')
 
 
-DEMO_ENABLED = Config( # Internal and Temporary
+DEMO_ENABLED = Config(  # Internal and Temporary
   key="demo_enabled",
   key="demo_enabled",
   help=_("To set to true in combination when using Hue demo backend."),
   help=_("To set to true in combination when using Hue demo backend."),
   type=coerce_bool,
   type=coerce_bool,
@@ -643,18 +672,23 @@ def default_secure_cookie():
   """Enable secure cookies if HTTPS is enabled."""
   """Enable secure cookies if HTTPS is enabled."""
   return is_https_enabled()
   return is_https_enabled()
 
 
+
 def default_ssl_cacerts():
 def default_ssl_cacerts():
   """Path to default Certificate Authority certificates"""
   """Path to default Certificate Authority certificates"""
   return SSL_CACERTS.get()
   return SSL_CACERTS.get()
 
 
+
 def default_ssl_validate():
 def default_ssl_validate():
   """Choose whether Hue should validate certificates received from the server."""
   """Choose whether Hue should validate certificates received from the server."""
   return SSL_VALIDATE.get()
   return SSL_VALIDATE.get()
 
 
+
 #
 #
 # Email (SMTP) settings
 # Email (SMTP) settings
 #
 #
 _default_from_email = None
 _default_from_email = None
+
+
 def default_from_email():
 def default_from_email():
   """Email for hue@<host-fqdn>"""
   """Email for hue@<host-fqdn>"""
   global _default_from_email
   global _default_from_email
@@ -676,6 +710,7 @@ def default_database_options():
   else:
   else:
     return {}
     return {}
 
 
+
 def get_deprecated_login_lock_out_by_combination_browser_user_agent():
 def get_deprecated_login_lock_out_by_combination_browser_user_agent():
   """Return value of deprecated LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP config"""
   """Return value of deprecated LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP config"""
   return AUTH.LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP.get()
   return AUTH.LOGIN_LOCK_OUT_BY_COMBINATION_BROWSER_USER_AGENT_AND_IP.get()
@@ -753,11 +788,13 @@ METRICS = ConfigSection(
   )
   )
 )
 )
 
 
+
 def is_gunicorn_report_enabled():
 def is_gunicorn_report_enabled():
   return 'rungunicornserver' in sys.argv \
   return 'rungunicornserver' in sys.argv \
     and METRICS.LOCATION.get() is not None \
     and METRICS.LOCATION.get() is not None \
     and METRICS.COLLECTION_INTERVAL.get() is not None
     and METRICS.COLLECTION_INTERVAL.get() is not None
 
 
+
 SLACK = ConfigSection(
 SLACK = ConfigSection(
   key='slack',
   key='slack',
   help=_("""Configuration options for slack """),
   help=_("""Configuration options for slack """),
@@ -905,7 +942,7 @@ SESSION = ConfigSection(
       key='ttl',
       key='ttl',
       help=_("The cookie containing the users' session ID will expire after this amount of time in seconds."),
       help=_("The cookie containing the users' session ID will expire after this amount of time in seconds."),
       type=int,
       type=int,
-      default=60*60*24*14,
+      default=60 * 60 * 24 * 14,
     ),
     ),
     SECURE=Config(
     SECURE=Config(
       key='secure',
       key='secure',
@@ -929,7 +966,7 @@ SESSION = ConfigSection(
       key='csrf_cookie_age',
       key='csrf_cookie_age',
       help=_("CRSF cookie age defaults to 1 year. If the value is set to 0, it means per session. Time in seconds"),
       help=_("CRSF cookie age defaults to 1 year. If the value is set to 0, it means per session. Time in seconds"),
       type=int,
       type=int,
-      default=60*60*24*7*52,
+      default=60 * 60 * 24 * 7 * 52,
     ),
     ),
     CONCURRENT_USER_SESSION_LIMIT=Config(
     CONCURRENT_USER_SESSION_LIMIT=Config(
       key="concurrent_user_session_limit",
       key="concurrent_user_session_limit",
@@ -990,7 +1027,7 @@ KERBEROS = ConfigSection(
       key='reinit_frequency',
       key='reinit_frequency',
       help=_("Frequency in seconds with which Hue will renew its keytab."),
       help=_("Frequency in seconds with which Hue will renew its keytab."),
       type=int,
       type=int,
-      default=60*60), #1h
+      default=60 * 60),  # 1h
     CCACHE_PATH=Config(
     CCACHE_PATH=Config(
       key='ccache_path',
       key='ccache_path',
       help=_("Path to keep Kerberos credentials cached."),
       help=_("Path to keep Kerberos credentials cached."),
@@ -1008,7 +1045,7 @@ KERBEROS = ConfigSection(
       key='kinit_path',
       key='kinit_path',
       help=_("Path to Kerberos 'kinit' command."),
       help=_("Path to Kerberos 'kinit' command."),
       type=str,
       type=str,
-      default="kinit", # use PATH!
+      default="kinit",  # use PATH!
     ),
     ),
     MUTUAL_AUTHENTICATION=Config(
     MUTUAL_AUTHENTICATION=Config(
       key='mutual_authentication',
       key='mutual_authentication',
@@ -1022,7 +1059,7 @@ KERBEROS = ConfigSection(
 SASL_MAX_BUFFER = Config(
 SASL_MAX_BUFFER = Config(
   key="sasl_max_buffer",
   key="sasl_max_buffer",
   help=_("This property specifies the maximum size of the receive buffer in bytes in thrift sasl communication."),
   help=_("This property specifies the maximum size of the receive buffer in bytes in thrift sasl communication."),
-  default=2*1024*1024,  # 2 MB
+  default=2 * 1024 * 1024,  # 2 MB
   type=int
   type=int
 )
 )
 
 
@@ -1669,6 +1706,7 @@ def default_feedback_url():
   """A version-specific URL."""
   """A version-specific URL."""
   return "http://groups.google.com/a/cloudera.org/group/hue-user"
   return "http://groups.google.com/a/cloudera.org/group/hue-user"
 
 
+
 FEEDBACK_URL = Config(
 FEEDBACK_URL = Config(
   key="feedback_url",
   key="feedback_url",
   help=_("Link for 'feedback' tab."),
   help=_("Link for 'feedback' tab."),
@@ -1752,10 +1790,12 @@ HTTP_500_DEBUG_MODE = Config(
   default=True
   default=True
 )
 )
 
 
+
 def get_instrumentation_default():
 def get_instrumentation_default():
   """If django_debug_mode is True, this is automatically enabled"""
   """If django_debug_mode is True, this is automatically enabled"""
   return DJANGO_DEBUG_MODE.get()
   return DJANGO_DEBUG_MODE.get()
 
 
+
 INSTRUMENTATION = Config(
 INSTRUMENTATION = Config(
   key='instrumentation',
   key='instrumentation',
   help=_('Enable or disable instrumentation. If django_debug_mode is True, this is automatically enabled.'),
   help=_('Enable or disable instrumentation. If django_debug_mode is True, this is automatically enabled.'),
@@ -1838,8 +1878,10 @@ ENABLE_NEW_STORAGE_BROWSER = Config(
   default=False
   default=False
 )
 )
 
 
+
 def is_chunked_fileuploader_enabled():
 def is_chunked_fileuploader_enabled():
-  return ENABLE_CHUNKED_FILE_UPLOADER.get();
+  return ENABLE_CHUNKED_FILE_UPLOADER.get()
+
 
 
 ENABLE_CHUNKED_FILE_UPLOADER = Config(
 ENABLE_CHUNKED_FILE_UPLOADER = Config(
   key="enable_chunked_file_uploader",
   key="enable_chunked_file_uploader",
@@ -1848,7 +1890,7 @@ ENABLE_CHUNKED_FILE_UPLOADER = Config(
   default=False
   default=False
 )
 )
 
 
-USE_NEW_EDITOR = Config( # To remove in Hue 4
+USE_NEW_EDITOR = Config(  # To remove in Hue 4
   key='',
   key='',
   default=True,
   default=True,
   type=coerce_bool,
   type=coerce_bool,
@@ -1948,6 +1990,7 @@ TRACING = ConfigSection(
     ),
     ),
 ))
 ))
 
 
+
 def task_server_default_result_directory():
 def task_server_default_result_directory():
   """Local directory to store task results."""
   """Local directory to store task results."""
   return 'file://%s' % get_run_root('logs')
   return 'file://%s' % get_run_root('logs')
@@ -2083,6 +2126,7 @@ def has_multi_clusters():
   '''If Hue is configured to talk to more than one completely independent clusters'''
   '''If Hue is configured to talk to more than one completely independent clusters'''
   return len(CLUSTERS.get()) > 1
   return len(CLUSTERS.get()) > 1
 
 
+
 def has_connectors():
 def has_connectors():
   '''When the connector feature is turned on'''
   '''When the connector feature is turned on'''
   return ENABLE_CONNECTORS.get()
   return ENABLE_CONNECTORS.get()
@@ -2123,10 +2167,12 @@ ENABLE_GIST = Config(
   help=_('Turn on the Gist snippet sharing.')
   help=_('Turn on the Gist snippet sharing.')
 )
 )
 
 
+
 def default_gist_preview():
 def default_gist_preview():
   """Gist preview only enabled automatically in private setups."""
   """Gist preview only enabled automatically in private setups."""
   return not ENABLE_ORGANIZATIONS.get()
   return not ENABLE_ORGANIZATIONS.get()
 
 
+
 ENABLE_GIST_PREVIEW = Config(
 ENABLE_GIST_PREVIEW = Config(
   key='enable_gist_preview',
   key='enable_gist_preview',
   dynamic_default=default_gist_preview,
   dynamic_default=default_gist_preview,
@@ -2156,6 +2202,13 @@ DISABLE_LOCAL_STORAGE = Config(
          "please make this value true in case local storage should not be used.")
          "please make this value true in case local storage should not be used.")
 )
 )
 
 
+ENABLE_HELP_MENU = Config(
+  key='enable_help_menu',
+  default=True,
+  type=coerce_bool,
+  help=_("Whether or not to show the Help menu in the Sidebar")
+)
+
 ENABLE_CONNECTORS = Config(
 ENABLE_CONNECTORS = Config(
   key='enable_connectors',
   key='enable_connectors',
   default=False,
   default=False,
@@ -2219,11 +2272,13 @@ CONNECTORS = UnspecifiedConfigSection(
   )
   )
 )
 )
 
 
+
 def _get_raz_url():
 def _get_raz_url():
   """Check if we can guess if Raz is configured"""
   """Check if we can guess if Raz is configured"""
   from hadoop.core_site import get_raz_api_url  # Avoid circular import
   from hadoop.core_site import get_raz_api_url  # Avoid circular import
   return get_raz_api_url()
   return get_raz_api_url()
 
 
+
 def has_raz_url():
 def has_raz_url():
   """Check if we can guess if Raz is configured"""
   """Check if we can guess if Raz is configured"""
   return bool(_get_raz_url())
   return bool(_get_raz_url())
@@ -2242,10 +2297,12 @@ SDXAAS = ConfigSection(
   )
   )
 )
 )
 
 
+
 def is_sdxaas_jwt_enabled():
 def is_sdxaas_jwt_enabled():
   """Check if SDXaaS token url is configured"""
   """Check if SDXaaS token url is configured"""
   return bool(SDXAAS.TOKEN_URL.get())
   return bool(SDXAAS.TOKEN_URL.get())
 
 
+
 def handle_raz_api_auth():
 def handle_raz_api_auth():
   """Return RAZ authentication type from JWT (if SDXaaS token URL is set) or KERBEROS"""
   """Return RAZ authentication type from JWT (if SDXaaS token URL is set) or KERBEROS"""
   return 'jwt' if is_sdxaas_jwt_enabled() else 'kerberos'
   return 'jwt' if is_sdxaas_jwt_enabled() else 'kerberos'
@@ -2355,7 +2412,7 @@ def validate_ldap(user, config):
       bind_password = get_ldap_bind_password(config)
       bind_password = get_ldap_bind_password(config)
 
 
       if bool(bind_dn) != bool(bind_password):
       if bool(bind_dn) != bool(bind_password):
-        if bind_dn == None:
+        if bind_dn is None:
           res.append((LDAP.BIND_DN,
           res.append((LDAP.BIND_DN,
                     new_str(_("If you set bind_password, then you must set bind_dn."))))
                     new_str(_("If you set bind_password, then you must set bind_dn."))))
         else:
         else:
@@ -2383,6 +2440,7 @@ def validate_ldap(user, config):
 
 
   return res
   return res
 
 
+
 def validate_database(user):
 def validate_database(user):
   res = []
   res = []
   cursor = connection.cursor()
   cursor = connection.cursor()
@@ -2440,6 +2498,7 @@ def validate_database(user):
 
 
   return res
   return res
 
 
+
 def config_validator(user):
 def config_validator(user):
   """
   """
   config_validator() -> [ (config_variable, error_message) ]
   config_validator() -> [ (config_variable, error_message) ]
@@ -2448,8 +2507,8 @@ def config_validator(user):
   """
   """
   from beeswax.models import QueryHistory, SavedQuery, Session
   from beeswax.models import QueryHistory, SavedQuery, Session
   from desktop.lib import i18n
   from desktop.lib import i18n
-  from desktop.models import Document, Document2 # Avoid cyclic loop
-  from desktop.settings import DOCUMENT2_MAX_ENTRIES # Avoid cyclic loop
+  from desktop.models import Document, Document2  # Avoid cyclic loop
+  from desktop.settings import DOCUMENT2_MAX_ENTRIES  # Avoid cyclic loop
   from oozie.models import Job
   from oozie.models import Job
 
 
   res = []
   res = []
@@ -2530,14 +2589,14 @@ def config_validator(user):
   except Exception as e:
   except Exception as e:
     LOG.warning('Config check failed because Oozie app not installed %s' % e)
     LOG.warning('Config check failed because Oozie app not installed %s' % e)
 
 
-  from notebook.models import make_notebook
   from notebook.api import _save_notebook
   from notebook.api import _save_notebook
+  from notebook.models import make_notebook
 
 
   notebook = make_notebook(name='test', editor_type='hive', statement='select "ทดสอบ"', status='ready')
   notebook = make_notebook(name='test', editor_type='hive', statement='select "ทดสอบ"', status='ready')
   notebook_doc = None
   notebook_doc = None
   try:
   try:
     notebook_doc, save_as = _save_notebook(notebook.get_data(), user)
     notebook_doc, save_as = _save_notebook(notebook.get_data(), user)
-  except:
+  except Exception as e:
     res.append(('DATABASE_CHARACTER_SET', new_str(
     res.append(('DATABASE_CHARACTER_SET', new_str(
       _('Character set of <i>search</i> field in <i>desktop_document2</i> table is not UTF-8. <br>'
       _('Character set of <i>search</i> field in <i>desktop_document2</i> table is not UTF-8. <br>'
         '<b>NOTE:</b> Configure the database for character set AL32UTF8 and national character set UTF8.'))
         '<b>NOTE:</b> Configure the database for character set AL32UTF8 and national character set UTF8.'))
@@ -2551,6 +2610,7 @@ def config_validator(user):
 
 
   return res
   return res
 
 
+
 def get_redaction_policy():
 def get_redaction_policy():
   """
   """
   Return the configured redaction policy.
   Return the configured redaction policy.
@@ -2618,6 +2678,7 @@ def get_ldap_bind_password(ldap_config):
 
 
   return password
   return password
 
 
+
 PERMISSION_ACTION_GS = "gs_access"
 PERMISSION_ACTION_GS = "gs_access"
 
 
 GC_ACCOUNTS = UnspecifiedConfigSection(
 GC_ACCOUNTS = UnspecifiedConfigSection(
@@ -2635,21 +2696,24 @@ GC_ACCOUNTS = UnspecifiedConfigSection(
   )
   )
 )
 )
 
 
+
 def is_cm_managed():
 def is_cm_managed():
   return 'cloudera-scm-agent' in os.path.realpath(os.getenv("HUE_CONF_DIR", get_desktop_root("conf")))
   return 'cloudera-scm-agent' in os.path.realpath(os.getenv("HUE_CONF_DIR", get_desktop_root("conf")))
 
 
 
 
 def is_gs_enabled():
 def is_gs_enabled():
-  from desktop.lib.idbroker import conf as conf_idbroker # Circular dependencies  desktop.conf -> idbroker.conf -> desktop.conf
+  from desktop.lib.idbroker import conf as conf_idbroker  # Circular dependencies  desktop.conf -> idbroker.conf -> desktop.conf
 
 
   return ('default' in list(GC_ACCOUNTS.keys()) and GC_ACCOUNTS['default'].JSON_CREDENTIALS.get()) or is_raz_gs() or \
   return ('default' in list(GC_ACCOUNTS.keys()) and GC_ACCOUNTS['default'].JSON_CREDENTIALS.get()) or is_raz_gs() or \
     conf_idbroker.is_idbroker_enabled('gs')
     conf_idbroker.is_idbroker_enabled('gs')
 
 
+
 def has_gs_access(user):
 def has_gs_access(user):
   from desktop.auth.backend import is_admin
   from desktop.auth.backend import is_admin
   return user.is_authenticated and user.is_active and (
   return user.is_authenticated and user.is_active and (
     is_admin(user) or user.has_hue_permission(action="gs_access", app="filebrowser") or is_raz_gs())
     is_admin(user) or user.has_hue_permission(action="gs_access", app="filebrowser") or is_raz_gs())
 
 
+
 def is_raz_gs():
 def is_raz_gs():
   from desktop.conf import RAZ  # Must be imported dynamically in order to have proper value
   from desktop.conf import RAZ  # Must be imported dynamically in order to have proper value
 
 
@@ -2717,6 +2781,7 @@ OZONE = UnspecifiedConfigSection(
   )
   )
 )
 )
 
 
+
 def is_ofs_enabled():
 def is_ofs_enabled():
   return ('default' in list(OZONE.keys()) and OZONE['default'].get_raw() and OZONE['default'].WEBHDFS_URL.get())
   return ('default' in list(OZONE.keys()) and OZONE['default'].get_raw() and OZONE['default'].WEBHDFS_URL.get())
 
 

+ 14 - 6
desktop/core/src/desktop/js/components/sidebar/HueSidebar.vue

@@ -213,15 +213,15 @@
       userDrawerItem: UserDrawerItem;
       userDrawerItem: UserDrawerItem;
       userDrawerChildren: SidebarAccordionSubItem[];
       userDrawerChildren: SidebarAccordionSubItem[];
 
 
-      helpDrawerItem: HelpDrawerItem;
-      helpDrawerChildren: SidebarAccordionSubItem[];
+      helpDrawerItem?: HelpDrawerItem;
+      helpDrawerChildren?: SidebarAccordionSubItem[];
 
 
       lastEditorDatabase?: EditorDatabaseDetails;
       lastEditorDatabase?: EditorDatabaseDetails;
       lastAssistDatabase?: AssistDatabaseDetails;
       lastAssistDatabase?: AssistDatabaseDetails;
 
 
       subTracker: SubscriptionTracker;
       subTracker: SubscriptionTracker;
     } {
     } {
-      return {
+      let sideBarList = {
         userDrawerItem: {
         userDrawerItem: {
           displayName: (<hueWindow>window).LOGGED_USERNAME || '',
           displayName: (<hueWindow>window).LOGGED_USERNAME || '',
           logoutLabel: I18n('Log Out'),
           logoutLabel: I18n('Log Out'),
@@ -229,14 +229,22 @@
         },
         },
         userDrawerChildren: USER_DRAWER_CHILDREN,
         userDrawerChildren: USER_DRAWER_CHILDREN,
 
 
+        subTracker: new SubscriptionTracker()
+      };
+
+      const helpDrawerList = {
         helpDrawerItem: {
         helpDrawerItem: {
           displayName: I18n('Help'),
           displayName: I18n('Help'),
           iconHtml: getIconHtml('support')
           iconHtml: getIconHtml('support')
         },
         },
-        helpDrawerChildren: HELP_DRAWER_CHILDREN,
-
-        subTracker: new SubscriptionTracker()
+        helpDrawerChildren: HELP_DRAWER_CHILDREN
       };
       };
+
+      if ((<hueWindow>window).ENABLE_HELP_MENU) {
+        sideBarList = { ...sideBarList, ...helpDrawerList };
+      }
+
+      return sideBarList;
     },
     },
 
 
     data(): {
     data(): {

+ 1 - 0
desktop/core/src/desktop/js/types/types.ts

@@ -68,4 +68,5 @@ export interface hueWindow {
   hueDebug?: HueDebug;
   hueDebug?: HueDebug;
   hueDebugAnalytics?: boolean;
   hueDebugAnalytics?: boolean;
   gtag?: (type: string, name: string, parameters: { action: string; version: string }) => void;
   gtag?: (type: string, name: string, parameters: { action: string; version: string }) => void;
+  ENABLE_HELP_MENU?: boolean;
 }
 }

+ 1 - 0
desktop/core/src/desktop/templates/global_js_constants.mako

@@ -798,6 +798,7 @@
   window.IS_LDAP_SETUP = '${ 'desktop.auth.backend.LdapBackend' in conf.AUTH.BACKEND.get() }' === 'True';
   window.IS_LDAP_SETUP = '${ 'desktop.auth.backend.LdapBackend' in conf.AUTH.BACKEND.get() }' === 'True';
   window.LOGGED_USERNAME = '${ user.username }';
   window.LOGGED_USERNAME = '${ user.username }';
   window.LOGGED_USER_ID = ${ user.id };
   window.LOGGED_USER_ID = ${ user.id };
+  window.ENABLE_HELP_MENU = '${conf.ENABLE_HELP_MENU.get()}' === 'True';
 
 
   <%
   <%
     # TODO remove
     # TODO remove