File: C:/Program Files/MySQL/MySQL Workbench 8.0/modules/wb_admin_utils.py
# Copyright (c) 2007, 2021, Oracle and/or its affiliates.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0,
# as published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms, as
# designated in a particular file or component or in included license
# documentation. The authors of MySQL hereby grant you an additional
# permission to link the program and your derivative works with the
# separately licensed software that they have included with MySQL.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import weakref
import sys
from mforms import newLabel
import mforms
from workbench.log import log_error, log_debug3
#-------------------------------------------------------------------------------
def weakcb(object, cbname):
"""Create a callback that holds a weak reference to the object. When passing a callback
for mforms, use this to create a ref to it and prevent circular references that are never freed.
"""
def call(ref, cbname):
callback = getattr(ref(), cbname, None)
if callback is None:
print("Object has no callback %s"%cbname)
else:
return callback()
return lambda ref=weakref.ref(object): call(ref, cbname)
def no_remote_admin_warning_label(server_instance_settings):
if server_instance_settings.uses_ssh:
warning = newLabel("There is no SSH connection to the server.\nTo use this functionality, the server where MySQL is located must have a SSH server running\nand you must provide its login information in the server profile.")
else:
if server_instance_settings.uses_wmi:
warning = newLabel("There is no WMI connection to the server.\nTo use this functionality, the server where MySQL is located must be configured to use WMI\nand you must provide its login information in the server profile.")
else:
warning = newLabel("Remote Administration is disabled.\nTo use this functionality, the server where MySQL is located must either have an SSH server running,\nor if it is a Windows machine, must have WMI enabled.\nAdditionally you must enable remote administration in the server profile, providing login details for it.")
warning.set_style(mforms.BigStyle)
warning.set_text_align(mforms.MiddleCenter)
return warning
def make_panel_header(icon, title, subtitle, button=None):
table = mforms.newTable()
table.set_name(title + " Panel")
table.set_row_count(2)
table.set_column_count(3)
table.set_row_spacing(0)
table.set_column_spacing(15)
image = mforms.newImageBox()
image.set_name(title + " Icon")
image.set_image(mforms.App.get().get_resource_path(icon))
image.set_image_align(mforms.TopCenter)
table.add(image, 0, 1, 0, 2, mforms.VFillFlag | mforms.HFillFlag)
label = mforms.newLabel(title)
label.set_name(title)
label.set_text_align(mforms.BottomLeft)
label.set_style(mforms.SmallStyle)
table.add(label, 1, 2, 0, 1, mforms.HFillFlag | mforms.HExpandFlag | mforms.VFillFlag)
label = mforms.newLabel(subtitle)
label.set_name(subtitle)
label.set_text_align(mforms.TopLeft)
label.set_style(mforms.VeryBigStyle)
table.add(label, 1, 2, 1, 2, mforms.HFillFlag | mforms.HExpandFlag | mforms.VFillFlag)
if button:
table.add(button, 2, 3, 0, 2, mforms.VFillFlag | mforms.HFillFlag)
return table
def show_error_page(parent, text):
if not hasattr(parent, '_error_label'):
parent._error_label = None
if parent._error_label is None:
parent._error_label = mforms.newLabel(text)
parent._error_label.set_style(mforms.BoldStyle)
parent._error_label.set_text_align(mforms.MiddleCenter)
parent.add(parent._error_label, True, True)
def remove_error_page_if_exists(parent):
if not hasattr(parent, '_error_label'):
parent._error_label = None
if parent._error_label:
parent.remove(parent._error_label)
class WbAdminValidationBase:
def __init__(self, error_message = ""):
self._error_message = error_message
def validate(self):
pass
def set_error_message(self, error_message):
self._error_message = error_message
def errorScreen(self):
warning = newLabel("\n\n\n\n" + self._error_message)
warning.set_style(mforms.BigStyle)
warning.set_text_align(mforms.MiddleCenter)
warning.show(True)
return warning
ValidationErrorServerNotRunning = "There is no connection to the MySQL Server.\nThis functionality requires an established connection to a running MySQL server."
ValidationErrorBadConfigFile = "Location of MySQL configuration file (ie: my.cnf) not specified"
class WbAdminValidationConnection(WbAdminValidationBase):
def __init__(self, ctrl_be):
super().__init__(ValidationErrorServerNotRunning)
self._ctrl_be = ctrl_be
def validate(self):
return self._ctrl_be.is_sql_connected()
class WbAdminValidationConfigFile(WbAdminValidationBase):
def __init__(self, instance_info):
super().__init__(ValidationErrorBadConfigFile)
self._instance_info = instance_info
def validate(self):
return self._instance_info.config_file_path
class WbAdminValidationRemoteAccess(WbAdminValidationBase):
def __init__(self, instance_info):
super().__init__()
self._instance_info = instance_info
if self._instance_info.uses_ssh:
self.set_error_message("There is no SSH connection to the server.\nTo use this functionality, the server where MySQL is located must have an SSH server running\nand you must provide its login information in the server profile.")
elif self._instance_info.uses_wmi:
self.set_error_message("There is no WMI connection to the server.\nTo use this functionality, the server where MySQL is located must be configured to use WMI\nand you must provide its login information in the server profile.")
else:
self.set_error_message("Remote Administration is disabled.\nTo use this functionality, the server where MySQL is located must have either an SSH server running,\nor if it is a computer running Windows, must have WMI enabled.\nAdditionally, you must enable remote administration in the server profile and provide login details for it.")
def validate(self):
return self._instance_info.admin_enabled
#
# New features can be easily implemented, now...like:
# - Auto page update
# - Update the page periodically (like vars screen). Several pages will benefit from this
# - Show error screen as soon as the validation don't pass and vice-versa
# - Show loading screen before the UI page (good for slower tabs, like config file tab, maybe others)
# - Force minimum height/width depending on the header/body/footer minimuns
#
class WbAdminTabBase(mforms.Box):
def __init__(self, ctrl_be, instance_info, main_view):
mforms.Box.__init__(self, False)
self.set_managed()
self.set_release_on_add()
self._instance_info = instance_info
self._ctrl_be = ctrl_be
self._main_view = main_view
self._page_active = False
self._error_screen_displayed = False
self._page_header = mforms.newBox(False)
if sys.platform.lower() == "darwin": # No scrollbox on macOS as this is not needed and breaks selection.
self._page_body = mforms.newBox(False)
else:
self._page_body = mforms.newScrollPanel()
self._page_body.set_padding(8)
self._page_footer = mforms.newBox(False)
self.add(self._page_header, False, True)
self.add(self._page_body, True, True)
self.add(self._page_footer, False, True)
self._header_contents = None
self._body_contents = None
self._footer_contents = None
#if sys.platform.lower() != "darwin": # No scrollbox on macOS as this is not needed and breaks selection.
#self._body_scroller = mforms.newScrollPanel()
#self._page_body.add(self._body_scroller, True, True)
self._validations = []
@property
def editor(self):
return self._main_view.editor
@property
def backend(self):
return self._ctrl_be
@property
def ctrl_be(self):
return self._ctrl_be
@property
def instance_info(self):
return self._instance_info
@property
def server_profile(self):
return self._instance_info
@property
def main_view(self):
return self._main_view
def validate(self):
for validation in self._validations:
if not validation.validate():
return validation
return None
def add_validation(self, validation):
self._validations.append(validation)
def create_standard_header(self, icon, title, description, rightViewObject = None):
header = mforms.newTable()
header.set_name(description + " Header")
header.set_row_count(2)
header.set_column_count(3)
header.set_row_spacing(0)
header.set_column_spacing(15)
header.set_padding(12)
header.set_name("Header")
image = mforms.newImageBox()
image.set_image(mforms.App.get().get_resource_path(icon))
image.set_image_align(mforms.TopCenter)
image.set_name(description + " Image")
label1 = mforms.newLabel(title)
label1.set_name(title)
label1.set_text_align(mforms.BottomLeft)
label1.set_style(mforms.SmallStyle)
label2 = mforms.newLabel(description)
label2.set_name(description)
label2.set_text_align(mforms.TopLeft)
label2.set_style(mforms.VeryBigStyle)
header.add(image, 0, 1, 0, 2, mforms.VFillFlag | mforms.HFillFlag)
header.add(label1, 1, 2, 0, 1, mforms.HFillFlag | mforms.HExpandFlag | mforms.VFillFlag)
header.add(label2, 1, 2, 1, 2, mforms.HFillFlag | mforms.HExpandFlag | mforms.VFillFlag)
if rightViewObject:
header.add(rightViewObject, 2, 3, 0, 2, mforms.VFillFlag | mforms.HFillFlag)
return header
def set_header(self, header):
if self._header_contents:
self._page_header.remove(self._header_contents)
self._header_contents = header
self._header_contents.set_padding(10, 10, 10, 5)
self._page_header.add(self._header_contents, True, True)
def set_standard_header(self, icon, title, description):
self.set_header(self.create_standard_header(icon, title, description))
def set_body_contents(self, body_contents):
if self._body_contents:
if isinstance(self._page_body, mforms.Box):
self._page_body.remove(self._body_contents)
else:
self._page_body.remove()
self._body_contents = body_contents
if sys.platform.lower() == "darwin":
self._page_body.add(self._body_contents, True, True)
else:
self._page_body.add(self._body_contents)
def set_error_screen(self, failedValidation):
self.set_body_contents(failedValidation.errorScreen())
self._error_screen_displayed = True
def set_footer(self, footer):
if self._footer_contents:
self._page_footer.remove(self._footer_contents)
self._footer_contents = footer
self._footer_contents.set_name("Footer")
self._footer_contents.set_padding(10, 5, 10, 10)
self._page_footer.add(self._footer_contents, True, True)
def ui_created(self):
return False if self._body_contents is None else True
def needs_to_create_ui(self):
if self._error_screen_displayed:
return True
if not self._body_contents:
return True
return False
def validation_failed_notification(self, failed_validation):
pass
def validation_successful_notification(self):
pass
def create_ui(self):
pass
def update_ui(self):
pass
def page_active(self):
return self._page_active
def page_activated(self):
self._page_active = True
try:
self.suspend_layout()
failed_validation = self.validate()
if failed_validation:
# create error screen
self.validation_failed_notification(failed_validation)
self.set_error_screen(failed_validation)
elif self.needs_to_create_ui():
self.validation_successful_notification()
self._error_screen_displayed = False
self.set_body_contents(self.create_ui())
else:
self.update_ui()
except Exception as e:
import traceback
traceback.print_exc()
log_error("Exception activating the page - %s" % str(e))
finally:
self.resume_layout()
def page_deactivated(self):
self._page_active = False
class MessageButtonPanel(mforms.Table):
def __init__(self, icon, title, text, button1, button2=None):
mforms.Table.__init__(self)
self.set_managed()
self.set_release_on_add()
self.set_padding(-1) # center contents
self.set_column_spacing(12)
self.set_row_spacing(12)
self.set_row_count(4)
self.set_column_count(2)
self._buttonBox = None
if icon:
image = mforms.newImageBox()
image.set_image(mforms.App.get().get_resource_path(icon))
self.add(image, 0, 1, 0, 3, 0)
self._label = mforms.newLabel(title)
self._label.set_style(mforms.BigBoldStyle)
self._label.set_text_align(mforms.MiddleCenter)
self.add(self._label, 1, 2, 0, 1, mforms.VFillFlag | mforms.HFillFlag)
self.add(mforms.newLabel(text), 1, 2, 1, 2, mforms.VFillFlag | mforms.HFillFlag)
if button1 or button2:
self._buttonBox = mforms.newBox(True)
self._buttonBox.set_spacing(12)
if button1:
button_caption, button_action = button1
self._button = mforms.newButton()
self._button.set_text(button_caption)
self._button.add_clicked_callback(button_action)
self._buttonBox.add_end(self._button, False, True)
else:
self._button = None
if button2:
button_caption, button_action = button2
self._alt_button = mforms.newButton()
self._alt_button.set_text(button_caption)
self._alt_button.add_clicked_callback(button_action)
self._buttonBox.add_end(self._alt_button, False, True)
else:
self._alt_button = None
if button1 or button2:
self.add(self._buttonBox, 1, 2, 2, 3, mforms.VFillFlag | mforms.HFillFlag)