Skip to content

Commit 56c0aad

Browse files
Merge remote-tracking branch 'upstream/master' into ai_server
2 parents 1a580e5 + 381a5d0 commit 56c0aad

File tree

7 files changed

+138
-8
lines changed

7 files changed

+138
-8
lines changed

db/zm_update-1.39.3.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,27 @@ PREPARE stmt FROM @s;
173173
EXECUTE stmt;
174174
DEALLOCATE PREPARE stmt;
175175

176+
--
177+
-- Ensure all expected menu items exist (adds any missing entries)
178+
--
179+
180+
INSERT IGNORE INTO `Menu_Items` (`MenuKey`, `Enabled`, `SortOrder`) VALUES
181+
('Console', 1, 10),
182+
('Watch', 1, 15),
183+
('Montage', 1, 20),
184+
('MontageReview', 1, 30),
185+
('Events', 1, 40),
186+
('Options', 1, 50),
187+
('Log', 1, 60),
188+
('Devices', 1, 70),
189+
('IntelGpu', 1, 80),
190+
('Groups', 1, 90),
191+
('Filters', 1, 100),
192+
('Snapshots', 1, 110),
193+
('Reports', 1, 120),
194+
('ReportEventAudit', 1, 130),
195+
('Map', 1, 140);
196+
176197
--
177198
-- Add Icon and IconType columns if they don't exist
178199
--

web/ajax/console.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
ajaxResponse($data);
1212
return;
1313
}
14+
15+
if ($task == 'export') {
16+
exportMonitorsJSON();
17+
return;
18+
}
1419
}
1520

1621
// Handle legacy action-based requests
@@ -589,4 +594,83 @@ function queryRequest() {
589594

590595
return $data;
591596
}
597+
598+
function exportMonitorsJSON() {
599+
require_once('includes/Monitor.php');
600+
require_once('includes/Zone.php');
601+
require_once('includes/Group.php');
602+
require_once('includes/Group_Monitor.php');
603+
604+
if (!canView('Monitors')) {
605+
ajaxError('Insufficient permissions');
606+
return;
607+
}
608+
609+
// Fields to exclude from export (runtime/instance-specific, not useful for import)
610+
$exclude_fields = array('Id', 'Deleted', 'ServerId', 'StorageId', 'Sequence', 'ZoneCount');
611+
$zone_exclude_fields = array('Id', 'MonitorId');
612+
613+
// Get all non-deleted visible monitors
614+
$sql = 'SELECT M.* FROM Monitors AS M WHERE M.Deleted=false ORDER BY M.Sequence ASC';
615+
$db_monitors = dbFetchAll($sql);
616+
617+
// Build group name lookup: MonitorId => array of group names
618+
$group_names_by_monitor = array();
619+
$all_groups = ZM\Group::find();
620+
$groups_by_id = array();
621+
foreach ($all_groups as $G) {
622+
$groups_by_id[$G->Id()] = $G;
623+
}
624+
foreach (ZM\Group_Monitor::find() as $GM) {
625+
$mid = $GM->MonitorId();
626+
$gid = $GM->GroupId();
627+
if (isset($groups_by_id[$gid])) {
628+
if (!isset($group_names_by_monitor[$mid])) {
629+
$group_names_by_monitor[$mid] = array();
630+
}
631+
$group_names_by_monitor[$mid][] = $groups_by_id[$gid]->Name();
632+
}
633+
}
634+
635+
$export = array(
636+
'version' => ZM_VERSION,
637+
'exported' => date('c'),
638+
'monitors' => array()
639+
);
640+
641+
foreach ($db_monitors as $db_row) {
642+
if (!visibleMonitor($db_row['Id'])) continue;
643+
644+
$Monitor = new ZM\Monitor($db_row);
645+
646+
// Serialize all monitor fields via to_json, then filter
647+
$monitor_data = json_decode($Monitor->to_json(), true);
648+
foreach ($exclude_fields as $field) {
649+
unset($monitor_data[$field]);
650+
}
651+
652+
// Add group names
653+
$monitor_data['Groups'] = isset($group_names_by_monitor[$db_row['Id']])
654+
? $group_names_by_monitor[$db_row['Id']]
655+
: array();
656+
657+
// Add zones
658+
$zones = ZM\Zone::find(array('MonitorId' => $db_row['Id']));
659+
$monitor_data['Zones'] = array();
660+
foreach ($zones as $zone) {
661+
$zone_data = json_decode($zone->to_json(), true);
662+
foreach ($zone_exclude_fields as $field) {
663+
unset($zone_data[$field]);
664+
}
665+
$monitor_data['Zones'][] = $zone_data;
666+
}
667+
668+
$export['monitors'][] = $monitor_data;
669+
}
670+
671+
header('Content-Type: application/json; charset=utf-8');
672+
header('Content-Disposition: attachment; filename="zm_monitors_export.json"');
673+
echo json_encode($export, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
674+
exit();
675+
}
592676
?>

web/ajax/modals/add_monitors_import.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
</div>
3535
<div class="modal-footer">
3636
<input type="file" name="import_file" id="import_file"/>
37-
<button name="action" id="importSubmitBtn" type="submit" class="btn btn-primary" value="save"><?php echo translate('Save') ?></button>
37+
<button name="action" id="importSubmitBtn" type="submit" class="btn btn-primary" value="save"><?php echo translate('Import') ?></button>
3838
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel') ?></button>
3939
</div>
4040
</form>

web/skins/classic/views/_options_menu.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@
1212
<div class="col">
1313
<div id="contentButtons">
1414
<?php if ($canEdit) { ?>
15-
<button type="submit" class="btn btn-primary"><?php echo translate('Save') ?></button>
16-
<button type="button" id="sortMenuBtn" data-on-click-this="sortMenuItems">
15+
<button type="submit" class="btn btn-primary">
16+
<i class="material-icons">save</i>
17+
<span class="text"><?php echo translate('Save') ?></span>
18+
</button>
19+
<button type="button" id="sortMenuBtn" class="btn btn-secondary" data-on-click-this="sortMenuItems">
1720
<i class="material-icons" title="<?php echo translate('Click and drag rows to change order') ?>">swap_vert</i>
1821
<span class="text"><?php echo translate('Sort') ?></span>
1922
</button>
2023
<button type="submit" name="action" value="resetmenu" class="btn btn-warning"
2124
onclick="return confirm('<?php echo addslashes(translate('Reset menu items to defaults?')) ?>');"
22-
><?php echo translate('Reset') ?></button>
25+
><i class="material-icons">restart_alt</i>
26+
<span class="text"><?php echo translate('Reset') ?></span>
27+
</button>
2328
<?php } ?>
2429
</div>
2530
</div>

web/skins/classic/views/console.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@
242242
data-cookie-expire="2y"
243243
data-remember-order="false"
244244
data-show-columns="true"
245-
data-show-export="true"
245+
data-show-export="false"
246246
data-show-footer="true"
247247
data-toolbar="#toolbar"
248248
data-sort-name="Sequence"

web/skins/classic/views/js/add_monitors.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function addMonitor(btn) {
141141
}
142142

143143
function import_csv() {
144-
const form = $j('#contentForm');
144+
const form = $j('#importModalForm')[0];
145145
var formData = new FormData( form );
146146
console.log(formData);
147147

web/skins/classic/views/js/console.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,13 @@ function processRows(rows) {
268268
row.Function = functionHtml;
269269

270270
// Format Source column with link and dimensions
271+
// Apply status class to the cell itself so it doesn't leak into CSV export
272+
row._Source_class = source_class;
271273
var sourceHtml = '';
272274
if (canEdit.Monitors) {
273-
sourceHtml = '<a href="?view=monitor&amp;mid=' + mid + '"><span class="' + source_class + '">' + row.Source + '</span></a>';
275+
sourceHtml = '<a href="?view=monitor&amp;mid=' + mid + '">' + row.Source + '</a>';
274276
} else {
275-
sourceHtml = '<span class="' + source_class + '">' + row.Source + '</span>';
277+
sourceHtml = row.Source;
276278
}
277279
sourceHtml += '<br/>' + row.Width + 'x' + row.Height;
278280
row.Source = sourceHtml;
@@ -467,6 +469,15 @@ function manageFunctionModal(evt) {
467469
$j('#modalFunction').modal('show');
468470
} // end function manageFunctionModal
469471

472+
function exportMonitors() {
473+
var link = document.createElement('a');
474+
link.href = thisUrl + '?request=console&task=export';
475+
link.download = 'zm_monitors_export.json';
476+
document.body.appendChild(link);
477+
link.click();
478+
document.body.removeChild(link);
479+
}
480+
470481
function resetSort() {
471482
table.bootstrapTable('deleteCookie', 'sortName');
472483
table.bootstrapTable('deleteCookie', 'sortOrder');
@@ -483,6 +494,15 @@ function initPage() {
483494
icons: icons,
484495
buttons: function() {
485496
return {
497+
exportMonitors: {
498+
text: 'Export',
499+
icon: 'fa-download',
500+
event: exportMonitors,
501+
attributes: {
502+
'aria-label': 'Export monitors as JSON',
503+
'title': 'Export monitors as JSON'
504+
}
505+
},
486506
resetSort: {
487507
text: 'Default Sort',
488508
icon: 'fa-sort',

0 commit comments

Comments
 (0)