preferences: Disable XML editing by default

This adds a field in gsettings and preferences UI to enable
XML editing. It's off by default. The XML tab page is still visible,
but the textview is not editable, and there's a warning at the top:

  XML editing is disabled in 'Preferences'.
  Only enable it if you know what you are doing.

When the setting is enabled, the warning goes away and the textview
is editable. This puts a roadblock up for people that don't know what
they are doing, but still advertises the feature and keeps the
UI surface difference fairly small between on/off states.
This commit is contained in:
Cole Robinson 2019-06-05 17:40:54 -04:00
parent 2a1cf411dd
commit 2266b650a5
16 changed files with 161 additions and 12 deletions

View File

@ -53,6 +53,12 @@
<description>Show system tray icon while app is running</description> <description>Show system tray icon while app is running</description>
</key> </key>
<key name="xmleditor-enabled" type="b">
<default>false</default>
<summary>Enable XML editting UI</summary>
<description>Enable XML editting UI</description>
</key>
<key name="enable-libguestfs-vm-inspection" type="b"> <key name="enable-libguestfs-vm-inspection" type="b">
<default>true</default> <default>true</default>
<summary>Enable libguestfs VM inspection</summary> <summary>Enable libguestfs VM inspection</summary>

View File

@ -660,6 +660,7 @@ class AddHardware(uiutils.UITestCase):
""" """
Test XML editor integration Test XML editor integration
""" """
self.app.open(xmleditor_enabled=True)
details = self._open_details_window() details = self._open_details_window()
win = self._open_addhw_window(details) win = self._open_addhw_window(details)
finish = win.find("Finish", "push button") finish = win.find("Finish", "push button")

View File

@ -59,6 +59,7 @@ class CreateNet(uiutils.UITestCase):
def testCreateNetXMLEditor(self): def testCreateNetXMLEditor(self):
self.app.open(xmleditor_enabled=True)
hostwin = self._open_host_window("Virtual Networks") hostwin = self._open_host_window("Virtual Networks")
win = self._open_create_win(hostwin) win = self._open_create_win(hostwin)
name = win.find("Name:", "text") name = win.find("Name:", "text")

View File

@ -89,6 +89,7 @@ class CreatePool(uiutils.UITestCase):
def testCreatePoolXMLEditor(self): def testCreatePoolXMLEditor(self):
self.app.open(xmleditor_enabled=True)
hostwin = self._open_host_window("Storage") hostwin = self._open_host_window("Storage")
win = self._open_create_win(hostwin) win = self._open_create_win(hostwin)
finish = win.find("Finish", "push button") finish = win.find("Finish", "push button")

View File

@ -66,6 +66,7 @@ class CreateVol(uiutils.UITestCase):
def testCreateVolXMLEditor(self): def testCreateVolXMLEditor(self):
self.app.open(xmleditor_enabled=True)
hostwin = self._open_host_window("Storage") hostwin = self._open_host_window("Storage")
poolcell = hostwin.find("default-pool", "table cell") poolcell = hostwin.find("default-pool", "table cell")
poolcell.click() poolcell.click()

View File

@ -487,6 +487,7 @@ class Details(uiutils.UITestCase):
""" """
Test XML editing interaction Test XML editing interaction
""" """
self.app.open(xmleditor_enabled=True)
win = self._open_details_window(vmname="test-clone-simple") win = self._open_details_window(vmname="test-clone-simple")
finish = win.find("config-apply") finish = win.find("config-apply")
xmleditor = win.find("XML editor") xmleditor = win.find("XML editor")

View File

@ -30,6 +30,7 @@ class Host(uiutils.UITestCase):
""" """
Test edits to net config Test edits to net config
""" """
self.app.open(xmleditor_enabled=True)
win = self._open_host_window("Virtual Networks").find("network-grid") win = self._open_host_window("Virtual Networks").find("network-grid")
finish = win.find("Apply", "push button") finish = win.find("Apply", "push button")
@ -69,6 +70,7 @@ class Host(uiutils.UITestCase):
""" """
Test edits to pool config Test edits to pool config
""" """
self.app.open(xmleditor_enabled=True)
win = self._open_host_window("Storage").find("storage-grid") win = self._open_host_window("Storage").find("storage-grid")
finish = win.find("Apply", "push button") finish = win.find("Apply", "push button")

View File

@ -432,6 +432,7 @@ class NewVM(uiutils.UITestCase):
""" """
Test new VM with raw XML editing via customize wizard Test new VM with raw XML editing via customize wizard
""" """
self.app.open(xmleditor_enabled=True)
newvm = self._open_create_wizard() newvm = self._open_create_wizard()
# Create a custom named VM, using CDROM media, and default storage # Create a custom named VM, using CDROM media, and default storage

View File

@ -55,3 +55,34 @@ class VMMPrefs(uiutils.UITestCase):
win.find_fuzzy("Close", "push button").click() win.find_fuzzy("Close", "push button").click()
uiutils.check_in_loop(lambda: win.visible is False) uiutils.check_in_loop(lambda: win.visible is False)
def testPrefsXMLEditor(self):
managerwin = self.app.topwin
detailswin = self._open_details_window(vmname="test-clone-simple")
finish = detailswin.find("config-apply")
xmleditor = detailswin.find("XML editor")
detailswin.find("XML", "page tab").click()
warnlabel = detailswin.find_fuzzy("XML editing is disabled")
self.assertTrue(warnlabel.visible)
origtext = xmleditor.text
xmleditor.typeText("1234abcd")
self.assertEqual(xmleditor.text, origtext)
managerwin.click()
managerwin.find("Edit", "menu").click()
managerwin.find("Preferences", "menu item").click()
prefswin = self.app.root.find_fuzzy("Preferences", "frame")
prefswin.find_fuzzy("Enable XML").click()
prefswin.find_fuzzy("Close", "push button").click()
uiutils.check_in_loop(lambda: prefswin.visible is False)
managerwin.keyCombo("<alt>F4")
detailswin.click()
xmleditor.text = xmleditor.text.replace(">",
"><title>FOOTITLE</title>", 1)
finish.click()
detailswin.find("Details", "page tab").click()
uiutils.check_in_loop(lambda:
detailswin.find("Title:", "text").text == "FOOTITLE")

View File

@ -444,7 +444,7 @@ class VMMDogtailApp(object):
return bool(self._proc and self._proc.poll() is None) return bool(self._proc and self._proc.poll() is None)
def open(self, extra_opts=None, check_already_running=True, use_uri=True, def open(self, extra_opts=None, check_already_running=True, use_uri=True,
window_name=None): window_name=None, xmleditor_enabled=False):
extra_opts = extra_opts or [] extra_opts = extra_opts or []
if tests.utils.clistate.debug: if tests.utils.clistate.debug:
@ -460,9 +460,12 @@ class VMMDogtailApp(object):
cmd += ["-m", "coverage", "run", "--append", cmd += ["-m", "coverage", "run", "--append",
"--omit", "/usr/*"] "--omit", "/usr/*"]
cmd += [os.path.join(os.getcwd(), "virt-manager"), cmd += [os.path.join(os.getcwd(), "virt-manager"),
"--test-first-run", "--no-fork"] "--test-first-run",
"--no-fork"]
if use_uri: if use_uri:
cmd += ["--connect", self.uri] cmd += ["--connect", self.uri]
if xmleditor_enabled:
cmd += ["--test-options=xmleditor-enabled"]
cmd += extra_opts cmd += extra_opts
if check_already_running: if check_already_running:

View File

@ -119,6 +119,21 @@
</packing> </packing>
</child> </child>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="prefs-xmleditor">
<property name="label" translatable="yes">Enable _XML editing</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_prefs_xmleditor_toggled" swapped="no"/>
</object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>

View File

@ -36,11 +36,18 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkBox" id="xml-box"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_left">6</property> <property name="margin_left">6</property>
<property name="margin_top">6</property> <property name="margin_top">6</property>
<property name="row_spacing">6</property>
<child>
<object class="GtkBox" id="xml-box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">6</property> <property name="spacing">6</property>
<child> <child>
@ -59,6 +66,49 @@
</packing> </packing>
</child> </child>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="xml-warning-box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-dialog-warning</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;small&gt;XML editing is disabled in 'Preferences'. Only enable it if you know what you are doing.&lt;/small&gt;</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing> <packing>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>

View File

@ -192,6 +192,7 @@ class CLITestOptionsClass:
self.leak_debug = _get("leak-debug") self.leak_debug = _get("leak-debug")
self.old_poll = _get("old-poll") self.old_poll = _get("old-poll")
self.no_events = _get("no-events") self.no_events = _get("no-events")
self.xmleditor_enabled = _get("xmleditor-enabled")
if opts: if opts:
print("Unknown --test-options keys: %s" % opts) print("Unknown --test-options keys: %s" % opts)

View File

@ -169,6 +169,8 @@ class vmmConfig(object):
self.conf = _SettingsWrapper("org.virt-manager.virt-manager") self.conf = _SettingsWrapper("org.virt-manager.virt-manager")
self.CLITestOptions = CLITestOptions self.CLITestOptions = CLITestOptions
if self.CLITestOptions.xmleditor_enabled:
self.set_xmleditor_enabled(True)
# We don't create it straight away, since we don't want # We don't create it straight away, since we don't want
# to block the app pending user authorization to access # to block the app pending user authorization to access
@ -364,6 +366,16 @@ class vmmConfig(object):
def set_view_system_tray(self, val): def set_view_system_tray(self, val):
self.conf.set("/system-tray", val) self.conf.set("/system-tray", val)
# XML editor enabled
def on_xmleditor_enabled_changed(self, cb):
return self.conf.notify_add("/xmleditor-enabled", cb)
def get_xmleditor_enabled(self):
return self.conf.get("/xmleditor-enabled")
def set_xmleditor_enabled(self, val):
self.conf.set("/xmleditor-enabled", val)
# Libguestfs VM inspection # Libguestfs VM inspection
def on_libguestfs_inspect_vms_changed(self, cb): def on_libguestfs_inspect_vms_changed(self, cb):
return self.conf.notify_add("/enable-libguestfs-vm-inspection", cb) return self.conf.notify_add("/enable-libguestfs-vm-inspection", cb)

View File

@ -36,6 +36,7 @@ class vmmPreferences(vmmGObjectUI):
self._orig_libguestfs_val = None self._orig_libguestfs_val = None
self.refresh_view_system_tray() self.refresh_view_system_tray()
self.refresh_xmleditor()
self.refresh_libguestfs() self.refresh_libguestfs()
self.refresh_update_interval() self.refresh_update_interval()
self.refresh_console_accels() self.refresh_console_accels()
@ -65,6 +66,7 @@ class vmmPreferences(vmmGObjectUI):
"on_prefs_close_clicked": self.close, "on_prefs_close_clicked": self.close,
"on_prefs_system_tray_toggled": self.change_view_system_tray, "on_prefs_system_tray_toggled": self.change_view_system_tray,
"on_prefs_xmleditor_toggled": self.change_xmleditor,
"on_prefs_libguestfs_toggled": self.change_libguestfs, "on_prefs_libguestfs_toggled": self.change_libguestfs,
"on_prefs_stats_update_interval_changed": self.change_update_interval, "on_prefs_stats_update_interval_changed": self.change_update_interval,
"on_prefs_console_accels_toggled": self.change_console_accels, "on_prefs_console_accels_toggled": self.change_console_accels,
@ -203,6 +205,10 @@ class vmmPreferences(vmmGObjectUI):
val = self.config.get_view_system_tray() val = self.config.get_view_system_tray()
self.widget("prefs-system-tray").set_active(bool(val)) self.widget("prefs-system-tray").set_active(bool(val))
def refresh_xmleditor(self):
val = self.config.get_xmleditor_enabled()
self.widget("prefs-xmleditor").set_active(bool(val))
def refresh_libguestfs(self): def refresh_libguestfs(self):
val = self.config.get_libguestfs_inspect_vms() val = self.config.get_libguestfs_inspect_vms()
if self._orig_libguestfs_val is None: if self._orig_libguestfs_val is None:
@ -364,6 +370,8 @@ class vmmPreferences(vmmGObjectUI):
def change_view_system_tray(self, src): def change_view_system_tray(self, src):
self.config.set_view_system_tray(src.get_active()) self.config.set_view_system_tray(src.get_active())
def change_xmleditor(self, src):
self.config.set_xmleditor_enabled(src.get_active())
def change_libguestfs(self, src): def change_libguestfs(self, src):
val = src.get_active() val = src.get_active()
self.config.set_libguestfs_inspect_vms(val) self.config.set_libguestfs_inspect_vms(val)

View File

@ -6,6 +6,7 @@ import gi
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import GtkSource from gi.repository import GtkSource
from . import uiutil
from .baseclass import vmmGObjectUI from .baseclass import vmmGObjectUI
_PAGE_DETAILS = 0 _PAGE_DETAILS = 0
@ -33,6 +34,10 @@ class vmmXMLEditor(vmmGObjectUI):
self._srcbuf = None self._srcbuf = None
self._init_ui() self._init_ui()
self.add_gsettings_handle(
self.config.on_xmleditor_enabled_changed(
self._xmleditor_enabled_changed_cb))
def _cleanup(self): def _cleanup(self):
pass pass
@ -42,6 +47,12 @@ class vmmXMLEditor(vmmGObjectUI):
# UI init # # UI init #
########### ###########
def _set_xmleditor_enabled_from_config(self):
enabled = self.config.get_xmleditor_enabled()
self._srcview.set_editable(enabled)
uiutil.set_grid_row_visible(self.widget("xml-warning-box"),
not enabled)
def _init_ui(self): def _init_ui(self):
self._srcview = GtkSource.View() self._srcview = GtkSource.View()
self._srcbuff = self._srcview.get_buffer() self._srcbuff = self._srcview.get_buffer()
@ -63,6 +74,7 @@ class vmmXMLEditor(vmmGObjectUI):
self._srcview.show_all() self._srcview.show_all()
self.widget("xml-scroll").add(self._srcview) self.widget("xml-scroll").add(self._srcview)
self._set_xmleditor_enabled_from_config()
#################### ####################
@ -178,3 +190,6 @@ class vmmXMLEditor(vmmGObjectUI):
def _after_page_changed_cb(self, notebook, gparam): def _after_page_changed_cb(self, notebook, gparam):
self._curpage = notebook.get_current_page() self._curpage = notebook.get_current_page()
def _xmleditor_enabled_changed_cb(self):
self._set_xmleditor_enabled_from_config()