diff --git a/CHANGELOG.md b/CHANGELOG.md index 594e3954..f4de5c6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ IMPORTANT NOTE: This version only works on CACTI 1.x++! --- develop --- +* Fix editor placement reset, destructive action controls and theme contrast * Reduce gallery preview cards by 20% while preserving generated thumbnail resolution * Add a searchable node icon picker and bundled enterprise site icons diff --git a/css/editor.css b/css/editor.css index c850da35..163688f0 100644 --- a/css/editor.css +++ b/css/editor.css @@ -26,9 +26,12 @@ input { margin-right: auto; border: 0px; padding: 0px; - cursor: crosshair; + cursor: auto; } +#xycapture, #existingdata.wm-placement-mode, .mapData.wm-placement-mode map area { cursor: crosshair; } +.mapData:not(.wm-placement-mode) map area { cursor: pointer; } + #frmMain { position: relative; top: 0; @@ -86,6 +89,12 @@ input { padding: 2px; } +#tb_help { + font-size: 13px; + font-weight: normal; + font-style: italic; +} + #toolbar ul li { //display:table; } @@ -316,6 +325,26 @@ body#mainView #wm-node-delete-bar { min-height: 32px; } .ui-dialog.wm-editor-dialog { overflow: visible; } +/* Use Cacti's own theme heading colours in every grouped editor dialog. */ +.dlgProperties .wm-editor-section { display: table; width: 100%; box-sizing: border-box; margin: 10px 0 6px; } +.dlgProperties table .wm-editor-section { margin: 0; } +/* Destructive node/link controls share readable red styling in every theme. */ +#node_delete, #link_delete, #note_delete, +.wm-delete-action, .wm-delete-action .ui-menu-item-wrapper { + color: #c62828 !important; + background: #fff !important; +} +#node_delete, #link_delete, #note_delete, button.wm-delete-action { + border: 1px solid #c62828 !important; +} + +/* Standalone dialogs need a matching text/surface pair in legacy dark themes. */ +body#mainView .ui-dialog .ui-button:not(.wm-asset-delete):not(.wm-image-delete):not(#note_delete):not(.wm-delete-action):not(#node_delete):not(#link_delete):not(.wm-icon-choice):not(.wm-note-colour), +body#mainView .ui-dialog input.ui-state-default, +body#mainView .ui-dialog textarea.ui-state-default { + background: var(--background-content, #e6e6e6) !important; + color: var(--text-color-lighter, #333) !important; +} /* Searchable node icon gallery; keep backgrounds on their existing selector. */ body#mainView .wm-icon-source { display: none; } diff --git a/js/editor.js b/js/editor.js index c15fbcef..4ee8f470 100644 --- a/js/editor.js +++ b/js/editor.js @@ -379,6 +379,7 @@ $(function() { function initJS() { // if the xycapture element is there, then we are in the main edit screen if ($('#xycapture').length) { + if ($('#tb_help').data('idle-help') === undefined) $('#tb_help').data('idle-help', $('#tb_help').text()); attach_click_events(); attach_help_events(); //show_context_help('node_label', 'node_help'); @@ -391,7 +392,7 @@ function initJS() { $('#frmMain').off('submit').on('submit', function(event) { event.preventDefault(); - form_submit(); + form_submit(event); }); /* Cacti core may have already converted this select to select2; don't @@ -433,7 +434,7 @@ function initContextMenu() { {title: txtMove, cmd: 'move', uiIcon: 'ui-icon-arrow-4'}, {title: txtClone, cmd: 'clone', uiIcon: 'ui-icon-copy'}, {title: txtEdit, cmd: 'edit', uiIcon: 'ui-icon-pencil'}, - {title: txtDelete, cmd: 'delete', uiIcon: 'ui-icon-trash'}, + {title: txtDelNode, cmd: 'delete', uiIcon: 'ui-icon-trash', addClass: 'wm-delete-action'}, {title: "----"}, {title: txtProperties, cmd: 'properties', uiIcon: 'ui-icon-gear'} ]; @@ -443,7 +444,7 @@ function initContextMenu() { {title: txtTidy, cmd: 'tidy', uiIcon: 'ui-icon-arrow-4'}, {title: txtVia, cmd: 'via', uiIcon: 'ui-icon-copy'}, {title: txtEdit, cmd: 'edit', uiIcon: 'ui-icon-pencil'}, - {title: txtDelete, cmd: 'delete', uiIcon: 'ui-icon-trash'}, + {title: txtDelLink, cmd: 'delete', uiIcon: 'ui-icon-trash', addClass: 'wm-delete-action'}, {title: "----"}, {title: txtProperties, cmd: 'properties', uiIcon: 'ui-icon-gear'} ]; @@ -461,6 +462,10 @@ function initContextMenu() { }, beforeOpen: function(event, ui) { var target = ui.target[0].id; + if (target.startsWith('LINK') || target.startsWith('NODE')) { + if (['add_link', 'add_link2', 'add_node', 'place_note', 'move_node', 'via_link', 'place_legend', 'place_stamp'].indexOf($('#action').val()) !== -1) $('#action').val(''); + mapmode('existing'); + } if (target.startsWith('LINK')) { $(this).contextmenu('replaceMenu', linkMenu); @@ -658,7 +663,7 @@ function click_execute(event, alt) { if (objecttype == 'NODE' && $('#action').val() == 'add_link') { $('#param').val(NodeIDs[objectid]); $('#action').val('add_link2'); - $('#tb_help').text('Click on the second node for the end of the link.'); + $('#tb_help').addClass('wm-action-help').text('Click on the second node for the end of the link.'); } else if (objecttype == 'NODE' && $('#action').val() == 'add_link2') { $('#param2').val(NodeIDs[objectid]); form_submit(); @@ -667,7 +672,8 @@ function click_execute(event, alt) { // reset back to standard state, and see if we can oblige them // alert('A bit confused'); $('#action').val(''); - hide_all_dialogs() + hide_all_dialogs(); + mapmode('existing'); } } } @@ -714,7 +720,15 @@ function new_file() { self.location = '?action=newfile'; } +function wmResetActionHelp() { + $('#tb_help').removeClass('wm-action-help').text($('#tb_help').data('idle-help')); +} + function mapmode(m) { + var placing = m == 'xy' || ['add_link', 'add_link2'].indexOf($('#action').val()) !== -1; + $('#existingdata, .mapData').toggleClass('wm-placement-mode', placing); + $('#tb_help').toggleClass('wm-action-help', placing); + if (!placing) wmResetActionHelp(); if (m == 'xy') { $('#debug').val('xy'); $('#xycapture').show(); @@ -738,7 +752,7 @@ function setCanvasSize(element) { } function add_node() { - $('#tb_help').text(addNodeHelp); + $('#tb_help').addClass('wm-action-help').text(addNodeHelp); $('#action').val('add_node'); mapmode('xy'); @@ -757,14 +771,16 @@ function delete_node() { } $('.dlgConfirm').text(message + ' ' + delNodeKeepDevice); - mapmode('xy'); + if (['add_link', 'add_link2', 'add_node', 'place_note', 'move_node', 'via_link', 'place_legend', 'place_stamp'].indexOf($('#action').val()) !== -1) $('#action').val(''); + mapmode('existing'); $('.dlgConfirm').dialog({ + close: function() { mapmode('existing'); }, resizable: false, title: delNodeTitle, height: 'auto', width: 400, - modal: false, + modal: true, buttons: [ { text: txtCancel, @@ -775,9 +791,11 @@ function delete_node() { }, { text: txtDelNode, + 'class': 'wm-delete-action', click: function() { $(this).dialog('close'); hide_all_dialogs(); + $('#param').val(name); $('#action').val('delete_node'); form_submit(); } @@ -807,7 +825,7 @@ function edit_link() { function move_node() { hide_dialog('dlgNodeProperties'); - $('#tb_help').text(moveNodeHelp); + $('#tb_help').addClass('wm-action-help').text(moveNodeHelp); $('#action').val('move_node'); mapmode('xy'); @@ -816,14 +834,14 @@ function move_node() { function via_link() { hide_dialog('dlgLinkProperties'); - $('#tb_help').text(viaLinkHelp); + $('#tb_help').addClass('wm-action-help').text(viaLinkHelp); $('#action').val('via_link'); mapmode('xy'); } function add_link() { - $('#tb_help').text(addLinkHelp); + $('#tb_help').addClass('wm-action-help').text(addLinkHelp); $('#action').val('add_link'); mapmode('existing'); @@ -841,9 +859,12 @@ function delete_link() { }); $('.dlgConfirm').text(prompt + ' ' + wmEditorText.keepLinkNodes); - mapmode('xy'); + if (['add_link', 'add_link2', 'add_node', 'place_note', 'move_node', 'via_link', 'place_legend', 'place_stamp'].indexOf($('#action').val()) !== -1) $('#action').val(''); + $('#param').val($('#link_name').val()); + mapmode('existing'); $('.dlgConfirm').dialog({ + close: function() { mapmode('existing'); }, resizable: false, title: delLinkTitle, height: 'auto', @@ -859,6 +880,7 @@ function delete_link() { }, { text: txtDelLink, + 'class': 'wm-delete-action', click: function() { $(this).dialog('close'); hide_all_dialogs(); @@ -870,7 +892,30 @@ function delete_link() { }); } -function form_submit() { +function form_submit(event) { + // Property dialogs can remain open while a toolbar placement action is active. + // Their own Save buttons always dispatch the properties they are showing. + var button = event && event.currentTarget && event.currentTarget.id; + var sources = { + tb_node_submit: ['set_node_properties', '#node_name'], + tb_link_submit: ['set_link_properties', '#link_name'], + tb_map_submit: ['set_map_properties'], + tb_mapstyle_submit: ['set_map_style'], + tb_editorsettings_submit: ['editor_settings'], + tb_note_submit: ['set_note_properties', '#note_name'], + tb_textedit_submit: [$('#dlgTextEdit').data('wm-config-action')] + }; + if (button === 'frmMain' || !button) { + var focused = $(button === 'frmMain' ? document.activeElement : event && event.currentTarget).closest('.ui-dialog-content').attr('id'); + var dialogs = {dlgNodeProperties: 'tb_node_submit', dlgLinkProperties: 'tb_link_submit', dlgMapProperties: 'tb_map_submit', dlgMapStyle: 'tb_mapstyle_submit', dlgEditorSettings: 'tb_editorsettings_submit', dlgNoteProperties: 'tb_note_submit', dlgTextEdit: 'tb_textedit_submit'}; + button = dialogs[focused]; + } + if (sources[button] && sources[button][0]) { + $('#action').val(sources[button][0]); + if (sources[button][1]) $('#param').val($(sources[button][1]).val()); + mapmode('existing'); + } + var data = $('input, select, textarea').serialize(); $.ajax({ @@ -924,7 +969,7 @@ function map_style() { } function position_timestamp() { - $('#tb_help').text(timeStHelp); + $('#tb_help').addClass('wm-action-help').text(timeStHelp); $('#action').val('place_stamp'); mapmode('xy'); @@ -960,7 +1005,7 @@ function position_legend(event) { } function real_position_legend(scalename) { - $('#tb_help').text(posLegendHelp); + $('#tb_help').addClass('wm-action-help').text(posLegendHelp); $('#action').val('place_legend'); $('#param').val(scalename); @@ -982,6 +1027,7 @@ function show_itemtext(itemtype,name) { $('#action').val('set_link_config'); } + $('#dlgTextEdit').data('wm-config-action', $('#action').val()); show_dialog('dlgTextEdit'); $.ajax({ @@ -1197,6 +1243,8 @@ show_dialog('dlgLinkProperties'); } function show_dialog(dlg) { + mapmode('existing'); + wmResetActionHelp(); if (dlg == 'dlgMapProperties') { var selectedNode = $('#map_bgfile').val(); diff --git a/lib/editor.actions.php b/lib/editor.actions.php index bf976588..8117a40b 100644 --- a/lib/editor.actions.php +++ b/lib/editor.actions.php @@ -952,6 +952,9 @@ function editorSettings($mapfile) { $use_overlay = (isset_request_var('editorsettings_showvias') ? intval(get_nfilter_request_var('editorsettings_showvias')) : false); $use_relative_overlay = (isset_request_var('editorsettings_showrelative') ? intval(get_nfilter_request_var('editorsettings_showrelative')) : false); $grid_snap_value = (isset_request_var('editorsettings_gridsnap') ? intval(get_nfilter_request_var('editorsettings_gridsnap')) : 0); + + // This AJAX action exits before the entry page's cookie write. + cacti_cookie_set('wmeditor', ($use_overlay ? '1' : '0') . ':' . ($use_relative_overlay ? '1' : '0') . ':' . intval($grid_snap_value)); } /** diff --git a/lib/editor.inc.php b/lib/editor.inc.php index ece87ac9..50d97c5a 100644 --- a/lib/editor.inc.php +++ b/lib/editor.inc.php @@ -1058,9 +1058,9 @@ function getEditorJs() { 'deleteLinkPrompt' => __('Remove the link between %1$s and %2$s?', 'weathermap'), 'keepLinkNodes' => __('The nodes and Cacti graphs will remain.', 'weathermap'), ], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>; - var delNodePrompt = ; + var delNodePrompt = ; var delNodeConnected = ; - var delNodeKeepDevice = ; + var delNodeKeepDevice = ; var delNodeWarning = ''; var delNodeTitle = ''; @@ -1075,7 +1075,7 @@ function getEditorJs() { var txtLinkActions = ''; var txtMove = ''; var txtClone = ''; - var txtEdit = ''; + var txtEdit = ''; var txtDelete = ''; var txtVia = ''; var txtTidy = ''; @@ -1108,7 +1108,7 @@ function getEditorJs() { 'link_default': '', 'map_default': '', 'node_default': '', - 'tb_default': '' + 'tb_default': '' }; \n" "Language-Team: Cacti Developers \n" @@ -363,7 +363,7 @@ msgstr "" #: lib/editor.inc.php #, php-format -msgid "Remove %s from this map?" +msgid "Delete %s from this map?" msgstr "" #: lib/editor.inc.php @@ -372,7 +372,7 @@ msgid "Connected links to remove: %s." msgstr "" #: lib/editor.inc.php -msgid "The Cacti device and graphs will remain." +msgid "The Cacti device and its graphs will remain." msgstr "" #: lib/editor.inc.php @@ -420,9 +420,8 @@ msgstr "" msgid "Clone" msgstr "" -#: lib/editor.inc.php weathermap-cacti-plugin-editor.php -#: weathermap-cacti-plugin-mgmt.php -msgid "Edit" +#: lib/editor.inc.php +msgid "Edit Configuration" msgstr "" #: lib/editor.inc.php weathermap-cacti-plugin-editor.php @@ -523,7 +522,7 @@ msgid "This is where help appears for nodes" msgstr "" #: lib/editor.inc.php -msgid "or click a Node or Link to edit it's properties" +msgid "or click a Node or Link to edit its properties" msgstr "" #: setup.php @@ -998,7 +997,7 @@ msgid "Position
---, ---" msgstr "" #: weathermap-cacti-plugin-editor.php -msgid "Select a menu item or either right-click or click on a Node or Link to edit it's properties" +msgid "Select a menu item or either right-click or click on a Node or Link to edit its properties" msgstr "" #: weathermap-cacti-plugin-editor.php @@ -1081,6 +1080,10 @@ msgstr "" msgid "Graph Selector" msgstr "" +#: weathermap-cacti-plugin-editor.php weathermap-cacti-plugin-mgmt.php +msgid "Edit" +msgstr "" + #: weathermap-cacti-plugin-editor.php msgid "Save" msgstr "" @@ -1317,6 +1320,26 @@ msgstr "" msgid "Link Hover Height (pixels)" msgstr "" +#: weathermap-cacti-plugin-editor.php +msgid "Edit Object Configuration" +msgstr "" + +#: weathermap-cacti-plugin-editor.php +msgid "Edit the raw configuration for this map object." +msgstr "" + +#: weathermap-cacti-plugin-editor.php +msgid "Advanced: changes are saved directly to the map configuration without syntax validation. Use Properties for the normal editing controls." +msgstr "" + +#: weathermap-cacti-plugin-editor.php +msgid "Overlays" +msgstr "" + +#: weathermap-cacti-plugin-editor.php +msgid "Placement" +msgstr "" + #: weathermap-cacti-plugin-mgmt.php msgid "Duplicate" msgstr "" diff --git a/tests/Browser/EditorContextMenuTest.cjs b/tests/Browser/EditorContextMenuTest.cjs new file mode 100644 index 00000000..a770e7d5 --- /dev/null +++ b/tests/Browser/EditorContextMenuTest.cjs @@ -0,0 +1,87 @@ +// Exercise real editor mode and confirmation callbacks with jQuery UI. +const { JSDOM } = require('jsdom'); +const jquery = require('jquery'); +const fs = require('node:fs'); +const path = require('node:path'); +const assert = require('node:assert/strict'); +const dom = new JSDOM('
', {runScripts:'outside-only',pretendToBeVisual:true}); +const w = dom.window; +const $ = jquery(w); +w.$ = w.jQuery = $; +w.eval(fs.readFileSync(path.join(process.env.CACTI_JS_DIR, 'jquery-ui.js'),'utf8')); +$('head').append($('