From e34b8abacae43ec765da6b5ca90b5c52133b5b31 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Mon, 20 Nov 2017 14:30:23 -0500 Subject: [PATCH 01/34] Cleanup events view header --- web/skins/classic/css/classic/views/events.css | 10 ++++++++++ web/skins/classic/css/dark/views/events.css | 10 ++++++++++ web/skins/classic/css/flat/views/events.css | 10 ++++++++++ web/skins/classic/views/events.php | 16 ++++++++-------- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/web/skins/classic/css/classic/views/events.css b/web/skins/classic/css/classic/views/events.css index ec4fd1c8a..59d27fd6e 100644 --- a/web/skins/classic/css/classic/views/events.css +++ b/web/skins/classic/css/classic/views/events.css @@ -44,3 +44,13 @@ text-align: right; padding-right: 8px; } + +#header { + display: flex; + justify-content: space-between; +} + +#header h2, #header div { + line-height: 1.1; + margin:0; +} diff --git a/web/skins/classic/css/dark/views/events.css b/web/skins/classic/css/dark/views/events.css index 3e3d1b620..befa94aec 100644 --- a/web/skins/classic/css/dark/views/events.css +++ b/web/skins/classic/css/dark/views/events.css @@ -44,3 +44,13 @@ text-align: right; padding-right: 8px; } + +#header { + display: flex; + justify-content: space-between; +} + +#header h2, #header div { + line-height: 1.1; + margin:0; +} diff --git a/web/skins/classic/css/flat/views/events.css b/web/skins/classic/css/flat/views/events.css index ec4fd1c8a..59d27fd6e 100644 --- a/web/skins/classic/css/flat/views/events.css +++ b/web/skins/classic/css/flat/views/events.css @@ -44,3 +44,13 @@ text-align: right; padding-right: 8px; } + +#header { + display: flex; + justify-content: space-between; +} + +#header h2, #header div { + line-height: 1.1; + margin:0; +} diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 0166fb6b1..1ca91a709 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -96,6 +96,14 @@ xhtmlHeaders(__FILE__, translate('Events') );
@@ -123,13 +130,6 @@ if ( $pages > 1 ) { - -

-

From adc1b924dac4960d815042100568c4b3b7c2b232 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Mon, 20 Nov 2017 14:52:21 -0500 Subject: [PATCH 02/34] Remove events popup --- web/skins/classic/views/console.php | 9 ++++----- web/skins/classic/views/events.php | 8 ++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 584fe6c62..d0f29f75f 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -245,8 +245,8 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { foreach ( array_keys( $eventCounts ) as $i ) { ?> - ' . human_filesize($monitor[$i.'EventDiskSpace']), canView( 'Events' ) ) ?> + ' : '') . + $monitor[$i.'Events'] . '
' . human_filesize($monitor[$i.'EventDiskSpace']) ?>
@@ -281,9 +281,8 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { parseFilter( $eventCounts[$i]['filter'] ); ?> -'.human_filesize($eventCounts[$i]['totaldiskspace']), canView( 'Events' ) ) ?> - + ' : '') . + $eventCounts[$i]['totalevents'].'
'.human_filesize($eventCounts[$i]['totaldiskspace']) ?>
diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 1ca91a709..cd2f865d4 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -81,10 +81,6 @@ if ( !empty($page) ) { $eventsSql .= ' limit 0, '.$limit; } -$maxWidth = 0; -$maxHeight = 0; -$archived = false; -$unarchived = false; $maxShortcuts = 5; $pagination = getPagination( $pages, $page, $maxShortcuts, $filterQuery.$sortQuery.'&limit='.$limit ); @@ -95,6 +91,7 @@ xhtmlHeaders(__FILE__, translate('Events') ); ?>

+
@@ -132,7 +129,6 @@ if ( $pages > 1 ) {

-

From 1289269e6fb9f08264867887493c075f43bf09cf Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Mon, 20 Nov 2017 14:43:24 -0500 Subject: [PATCH 03/34] Change filter to work without popup --- web/skins/classic/includes/functions.php | 4 +++- web/skins/classic/views/js/events.js.php | 4 ++-- web/skins/classic/views/js/filter.js | 4 ---- web/skins/classic/views/js/filter.js.php | 3 +++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index b2e6d17c9..5f916f154 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -160,6 +160,8 @@ if ( file_exists( "skins/$skin/css/$css/graphics/favicon.ico" ) ) { } // end function xhtmlHeaders( $file, $title ) function getNavBarHTML($reload = null) { + parseFilter( $_REQUEST['filter'] ); + $filterQuery = $_REQUEST['filter']['query']; $versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':''; global $running; @@ -210,7 +212,7 @@ if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
  • Devices
  • >
  • -
  • >
  • +
  • >
  • ; var unarchivedEvents = ; -var filterQuery = ''; -var sortQuery = ''; +var filterQuery = ''; +var sortQuery = ''; var maxWidth = ; var maxHeight = ; diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 2a525d1b2..31746c0f9 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -49,7 +49,6 @@ function submitToFilter( element ) { function submitToEvents( element ) { var form = element.form; if ( validateForm( form ) ) { - form.target = 'zmEvents'; form.action = thisUrl + '?view=events'; form.submit(); } @@ -58,7 +57,6 @@ function submitToEvents( element ) { function executeFilter( element ) { var form = element.form; if ( validateForm( form ) ) { - form.target = 'zmEvents'; form.action = thisUrl + '?view=events'; form.elements['action'].value = 'execute'; form.submit(); @@ -67,8 +65,6 @@ function executeFilter( element ) { function saveFilter( element ) { var form = element.form; - - //form.target = 'zmFilter'; form.target = window.name; form.elements['action'].value = element.value; form.action = thisUrl + '?view=filter'; diff --git a/web/skins/classic/views/js/filter.js.php b/web/skins/classic/views/js/filter.js.php index b18251891..96e4be80c 100644 --- a/web/skins/classic/views/js/filter.js.php +++ b/web/skins/classic/views/js/filter.js.php @@ -1,3 +1,6 @@ +var filterQuery = ''; +var sortQuery = ''; + var deleteSavedFilterString = ""; function validateForm( form ) { Date: Mon, 20 Nov 2017 14:45:27 -0500 Subject: [PATCH 04/34] Remove watch popup --- web/skins/classic/views/console.php | 4 ++-- web/skins/classic/views/watch.php | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index d0f29f75f..4004e0094 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -208,11 +208,11 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { - + - + - + + From 1636cf6e9039c2f40c85b6853af448f8476ee9fe Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Tue, 21 Nov 2017 10:11:39 -0500 Subject: [PATCH 06/34] Tighten up headers --- web/skins/classic/css/classic/skin.css | 6 +++--- web/skins/classic/css/classic/views/event.css | 2 -- web/skins/classic/css/dark/skin.css | 8 ++++---- web/skins/classic/css/dark/views/event.css | 2 -- web/skins/classic/css/flat/skin.css | 8 ++++---- web/skins/classic/css/flat/views/event.css | 2 -- 6 files changed, 11 insertions(+), 17 deletions(-) diff --git a/web/skins/classic/css/classic/skin.css b/web/skins/classic/css/classic/skin.css index 218f42e7a..7220e7410 100644 --- a/web/skins/classic/css/classic/skin.css +++ b/web/skins/classic/css/classic/skin.css @@ -346,8 +346,8 @@ th.table-th-sort-rev span.table-th-sort-span { #header { width: 100%; - line-height: 24px; - margin: 8px auto; + margin: 4px auto; + line-height: 1; text-align: left; } @@ -378,7 +378,7 @@ th.table-th-sort-rev span.table-th-sort-span { #content { width: 96%; - margin: 8px auto; + margin: 0 auto 8px auto; line-height: 130%; text-align: center; } diff --git a/web/skins/classic/css/classic/views/event.css b/web/skins/classic/css/classic/views/event.css index dbcc30835..951fbc8cf 100644 --- a/web/skins/classic/css/classic/views/event.css +++ b/web/skins/classic/css/classic/views/event.css @@ -47,7 +47,6 @@ span.noneCue { #menuBar1 { width: 100%; - padding: 3px 0; text-align: center; clear: both; } @@ -72,7 +71,6 @@ span.noneCue { #menuBar2 { width: 100%; - padding: 3px 0; margin-bottom: 4px; } diff --git a/web/skins/classic/css/dark/skin.css b/web/skins/classic/css/dark/skin.css index 3bd2ad1e3..3eac0e201 100644 --- a/web/skins/classic/css/dark/skin.css +++ b/web/skins/classic/css/dark/skin.css @@ -373,10 +373,10 @@ th.table-th-sort-rev span.table-th-sort-span { #header { width: 100%; - line-height: 24px; + line-height: 1; text-align: left; - margin-bottom: 10px; - padding: 10px 20px; + padding: 5px 20px; + margin: 4px auto; font-weight: 300; } @@ -407,7 +407,7 @@ th.table-th-sort-rev span.table-th-sort-span { #content { width: 96%; - margin: 8px auto; + margin: 0 auto 8px auto; line-height: 130%; text-align: center; } diff --git a/web/skins/classic/css/dark/views/event.css b/web/skins/classic/css/dark/views/event.css index a156c2438..cca16607f 100644 --- a/web/skins/classic/css/dark/views/event.css +++ b/web/skins/classic/css/dark/views/event.css @@ -47,7 +47,6 @@ span.noneCue { #menuBar1 { width: 100%; - padding: 3px 0; text-align: center; clear: both; } @@ -72,7 +71,6 @@ span.noneCue { #menuBar2 { width: 100%; - padding: 3px 0; margin-bottom: 4px; } diff --git a/web/skins/classic/css/flat/skin.css b/web/skins/classic/css/flat/skin.css index 2752d0056..5007d1102 100644 --- a/web/skins/classic/css/flat/skin.css +++ b/web/skins/classic/css/flat/skin.css @@ -368,11 +368,11 @@ th.table-th-sort-rev span.table-th-sort-span { #header { width: 100%; - line-height: 24px; + line-height: 1; text-align: left; background-color: #383836; - margin-bottom: 10px; - padding: 10px 20px; + padding: 5px 20px; + margin: 4px auto; color: #f2f2f2; font-weight: 300; } @@ -410,7 +410,7 @@ th.table-th-sort-rev span.table-th-sort-span { #content { width: 96%; - margin: 8px auto; + margin: 0 auto 8px auto; line-height: 130%; text-align: center; } diff --git a/web/skins/classic/css/flat/views/event.css b/web/skins/classic/css/flat/views/event.css index 30758b573..bb93d24ba 100644 --- a/web/skins/classic/css/flat/views/event.css +++ b/web/skins/classic/css/flat/views/event.css @@ -47,7 +47,6 @@ span.noneCue { #menuBar1 { width: 100%; - padding: 3px 0; text-align: center; clear: both; } @@ -72,7 +71,6 @@ span.noneCue { #menuBar2 { width: 100%; - padding: 3px 0; margin-bottom: 4px; } From 7a5dfb71dc14830733eb2e51cd856fd27415bca3 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Tue, 21 Nov 2017 22:50:14 -0500 Subject: [PATCH 07/34] Remove no longer used max from events --- web/skins/classic/views/js/events.js.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/web/skins/classic/views/js/events.js.php b/web/skins/classic/views/js/events.js.php index a50fbc8a9..8a49c00c3 100644 --- a/web/skins/classic/views/js/events.js.php +++ b/web/skins/classic/views/js/events.js.php @@ -7,7 +7,4 @@ var unarchivedEvents = ; var filterQuery = ''; var sortQuery = ''; -var maxWidth = ; -var maxHeight = ; - var confirmDeleteEventsString = ""; From 725814d41dfbca36ee422311033bd507cf3379a1 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Wed, 22 Nov 2017 15:44:53 -0500 Subject: [PATCH 08/34] Remove unused code from events --- web/skins/classic/views/events.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 07c01c189..9acf87e64 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -140,11 +140,6 @@ $disk_space_total = 0; $results = dbQuery( $eventsSql ); while ( $event_row = dbFetchNext( $results ) ) { $event = new Event( $event_row ); - $scale = max( reScale( SCALE_BASE, $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); - $eventWidth = reScale( $event_row['Width'], $scale ); - $eventHeight = reScale( $event_row['Height'], $scale ); - if ( $maxWidth < $eventWidth ) $maxWidth = $eventWidth; - if ( $maxHeight < $eventHeight ) $maxHeight = $eventHeight; if ( $event_row['Archived'] ) $archived = true; else @@ -284,8 +279,6 @@ if ( true || canEdit( 'Events' ) ) { // These are defined in the .js.php but need to be updated down here. archivedEvents = ; unarchivedEvents = ; - maxWidth = ; - maxHeight = ; From 9d1bbfab42fab599a558a63d30faa1147d34c0f9 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Wed, 22 Nov 2017 15:49:29 -0500 Subject: [PATCH 09/34] Remove unused code from event --- web/skins/classic/views/js/event.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 702bbc3c7..c638a364c 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -812,10 +812,6 @@ function getActResponse( respObj, respText ) { if ( checkStreamForErrors( "getActResponse", respObj ) ) return; - if ( respObj.refreshParent ) - if (refreshParent == false) refreshParent = true; //Bypass filter window redirect fix. - refreshParentWindow(); - if ( respObj.refreshEvent ) eventQuery( eventData.Id ); } From e6568d01a3d92ba9966cbb5b567fb61491b1b946 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Mon, 27 Nov 2017 13:43:15 -0500 Subject: [PATCH 10/34] Remove timeline popup --- web/skins/classic/css/classic/views/timeline.css | 14 -------------- web/skins/classic/css/dark/views/timeline.css | 14 -------------- web/skins/classic/css/flat/views/timeline.css | 14 -------------- web/skins/classic/views/events.php | 2 +- web/skins/classic/views/js/timeline.js | 3 +-- web/skins/classic/views/timeline.php | 8 ++++---- 6 files changed, 6 insertions(+), 49 deletions(-) diff --git a/web/skins/classic/css/classic/views/timeline.css b/web/skins/classic/css/classic/views/timeline.css index 6b66ea4e1..d70dacfa1 100644 --- a/web/skins/classic/css/classic/views/timeline.css +++ b/web/skins/classic/css/classic/views/timeline.css @@ -15,20 +15,6 @@ line-height: 20px; } -#listLink { - position: absolute; - top: 5px; - left: 20px; - height: 15px; -} - -#closeLink { - position: absolute; - top: 5px; - right: 20px; - height: 15px; -} - #topPanel { position: relative; height: 220px; diff --git a/web/skins/classic/css/dark/views/timeline.css b/web/skins/classic/css/dark/views/timeline.css index bb1053c68..d3f9783c6 100644 --- a/web/skins/classic/css/dark/views/timeline.css +++ b/web/skins/classic/css/dark/views/timeline.css @@ -15,20 +15,6 @@ line-height: 20px; } -#listLink { - position: absolute; - top: 5px; - left: 20px; - height: 15px; -} - -#closeLink { - position: absolute; - top: 5px; - right: 20px; - height: 15px; -} - #topPanel { position: relative; height: 220px; diff --git a/web/skins/classic/css/flat/views/timeline.css b/web/skins/classic/css/flat/views/timeline.css index 7ea3403c7..0c8d8a72a 100644 --- a/web/skins/classic/css/flat/views/timeline.css +++ b/web/skins/classic/css/flat/views/timeline.css @@ -15,20 +15,6 @@ line-height: 20px; } -#listLink { - position: absolute; - top: 5px; - left: 20px; - height: 15px; -} - -#closeLink { - position: absolute; - top: 5px; - right: 20px; - height: 15px; -} - #topPanel { position: relative; height: 220px; diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 9acf87e64..9b2b231ee 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -129,7 +129,7 @@ if ( $pages > 1 ) {

    - +

    ' : '>') . $monitor['Id'] ?> ' : '>') . $monitor['Name'] ?> '.translate('Fn'.$monitor['Function']).( empty($monitor['Enabled']) ? ', disabled' : '' ) .'', canEdit( 'Monitors' ) ) ?>
    diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 4a581262d..e09146915 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -61,14 +61,11 @@ xhtmlHeaders( __FILE__, $monitor->Name()." - ".translate('Feed') ); ?>
    +
    Id().$filterQuery.$sortQuery.'&page=1', 'zmEvent', array( 'event', reScale( $event->Width(), $scale ), reScale( $event->Height(), $scale ) ), $event->Id().($event->Archived()?'*':'') ) ?>Id().$filterQuery.$sortQuery.'&page=1', 'zmEvent', array( 'event', reScale( $event->Width(), $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ), reScale( $event->Height(), $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ) ), validHtmlStr($event->Name()).($event->Archived()?'*':'' ) ) ?> '.$event->Id().($event->Archived()?'*':'') ?> '.validHtmlStr($event->Name()).($event->Archived()?'*':'') ?> MonitorId(), 'zmMonitor'.$event->Monitorid(), 'monitor', $event->MonitorName(), canEdit( 'Monitors' ) ) ?> Id(), 'zmEventDetail', 'eventdetail', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="'.htmlspecialchars($event->Notes()).'"' ) ?> StartTime()) ) ?>
    diff --git a/web/skins/classic/views/js/timeline.js b/web/skins/classic/views/js/timeline.js index 0c37f0284..94217530f 100644 --- a/web/skins/classic/views/js/timeline.js +++ b/web/skins/classic/views/js/timeline.js @@ -3,8 +3,7 @@ var events = {}; function showEvent( eid, fid, width, height ) { var url = '?view=event&eid='+eid+'&fid='+fid; url += filterQuery; - var pop=createPopup( url, 'zmEvent', 'event', width, height ); - pop.vid=$('preview'); + window.location.href = url; //video element is blocking video elements elsewhere in chrome possible interaction with mouseover event? //FIXME unless an exact cause can be determined should store all video controls and do something to the other controls when we want to load a new video seek etc or whatever may block diff --git a/web/skins/classic/views/timeline.php b/web/skins/classic/views/timeline.php index b6ade3ae1..75e9729f3 100644 --- a/web/skins/classic/views/timeline.php +++ b/web/skins/classic/views/timeline.php @@ -700,12 +700,12 @@ xhtmlHeaders(__FILE__, translate('Timeline') ); ?>
    +
    From 8772d1e99a733a45486ac5866556ab466da95587 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Tue, 28 Nov 2017 08:19:33 -0500 Subject: [PATCH 11/34] Timeline header to flex Moved flex override to timeline.css rather than set in in skin file. --- web/skins/classic/css/classic/views/timeline.css | 10 ++++++++++ web/skins/classic/css/dark/views/timeline.css | 10 ++++++++++ web/skins/classic/css/flat/views/timeline.css | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/web/skins/classic/css/classic/views/timeline.css b/web/skins/classic/css/classic/views/timeline.css index d70dacfa1..14882f2e4 100644 --- a/web/skins/classic/css/classic/views/timeline.css +++ b/web/skins/classic/css/classic/views/timeline.css @@ -5,6 +5,16 @@ margin: 0 auto; } +#header { + display: flex; + justify-content: space-between; +} + +#header h2, #header a { + line-height: 1.1; + margin:5px 0 0 0; +} + #title { position: relative; margin: 0 auto; diff --git a/web/skins/classic/css/dark/views/timeline.css b/web/skins/classic/css/dark/views/timeline.css index d3f9783c6..f8e854d03 100644 --- a/web/skins/classic/css/dark/views/timeline.css +++ b/web/skins/classic/css/dark/views/timeline.css @@ -5,6 +5,16 @@ margin: 0 auto; } +#header { + display: flex; + justify-content: space-between; +} + +#header h2, #header a { + line-height: 1.1; + margin:5px 0 0 0; +} + #title { position: relative; margin: 0 auto; diff --git a/web/skins/classic/css/flat/views/timeline.css b/web/skins/classic/css/flat/views/timeline.css index 0c8d8a72a..feed4a4d4 100644 --- a/web/skins/classic/css/flat/views/timeline.css +++ b/web/skins/classic/css/flat/views/timeline.css @@ -5,6 +5,16 @@ margin: 0 auto; } +#header { + display: flex; + justify-content: space-between; +} + +#header h2, #header a { + line-height: 1.1; + margin:5px 0 0 0; +} + #title { position: relative; margin: 0 auto; From 50d5a8991dcabbd352b0537e0394fc44c79ca56b Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Tue, 28 Nov 2017 08:23:54 -0500 Subject: [PATCH 12/34] Events header Moved all non-event links to the header --- .../classic/css/classic/views/events.css | 47 ++++++------------- web/skins/classic/css/dark/views/events.css | 47 ++++++------------- web/skins/classic/css/flat/views/events.css | 47 ++++++------------- web/skins/classic/views/events.php | 16 ++++--- 4 files changed, 54 insertions(+), 103 deletions(-) diff --git a/web/skins/classic/css/classic/views/events.css b/web/skins/classic/css/classic/views/events.css index 59d27fd6e..c167fc92d 100644 --- a/web/skins/classic/css/classic/views/events.css +++ b/web/skins/classic/css/classic/views/events.css @@ -2,36 +2,6 @@ background-color: #f8f8f8;; } -#controls { - height: 16px; - width: 100%; - text-align: center; - margin: 0 auto; - position: relative; -} - -#controls a { - width: 32%; -} - -#controls #refreshLink { - position: absolute; - left: 0%; - text-align: left; -} - -#controls #filterLink { - position: absolute; - left: 34%; - text-align: center; -} - -#controls #timelineLink { - position: absolute; - left: 68%; - text-align: right; -} - #contentTable.major .colTime { white-space: nowrap; } @@ -50,7 +20,20 @@ justify-content: space-between; } -#header h2, #header div { +#header h2, #header a { line-height: 1.1; - margin:0; + margin:5px 0 0 0; +} + +#header #info, #header #pagination, #header #controls { + display: flex; + flex-direction: column; +} + +#header #controls { + align-items: flex-end; +} + +#header #pagination { + align-items: center; } diff --git a/web/skins/classic/css/dark/views/events.css b/web/skins/classic/css/dark/views/events.css index befa94aec..03b1e0a76 100644 --- a/web/skins/classic/css/dark/views/events.css +++ b/web/skins/classic/css/dark/views/events.css @@ -2,36 +2,6 @@ background-color: #2e2e2e; } -#controls { - height: 16px; - width: 100%; - text-align: center; - margin: 0 auto; - position: relative; -} - -#controls a { - width: 32%; -} - -#controls #refreshLink { - position: absolute; - left: 0%; - text-align: left; -} - -#controls #filterLink { - position: absolute; - left: 34%; - text-align: center; -} - -#controls #timelineLink { - position: absolute; - left: 68%; - text-align: right; -} - #contentTable.major .colTime { white-space: nowrap; } @@ -50,7 +20,20 @@ justify-content: space-between; } -#header h2, #header div { +#header h2, #header a { line-height: 1.1; - margin:0; + margin:5px 0 0 0; +} + +#header #info, #header #pagination, #header #controls { + display: flex; + flex-direction: column; +} + +#header #controls { + align-items: flex-end; +} + +#header #pagination { + align-items: center; } diff --git a/web/skins/classic/css/flat/views/events.css b/web/skins/classic/css/flat/views/events.css index 59d27fd6e..c167fc92d 100644 --- a/web/skins/classic/css/flat/views/events.css +++ b/web/skins/classic/css/flat/views/events.css @@ -2,36 +2,6 @@ background-color: #f8f8f8;; } -#controls { - height: 16px; - width: 100%; - text-align: center; - margin: 0 auto; - position: relative; -} - -#controls a { - width: 32%; -} - -#controls #refreshLink { - position: absolute; - left: 0%; - text-align: left; -} - -#controls #filterLink { - position: absolute; - left: 34%; - text-align: center; -} - -#controls #timelineLink { - position: absolute; - left: 68%; - text-align: right; -} - #contentTable.major .colTime { white-space: nowrap; } @@ -50,7 +20,20 @@ justify-content: space-between; } -#header h2, #header div { +#header h2, #header a { line-height: 1.1; - margin:0; + margin:5px 0 0 0; +} + +#header #info, #header #pagination, #header #controls { + display: flex; + flex-direction: column; +} + +#header #controls { + align-items: flex-end; +} + +#header #pagination { + align-items: center; } diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 9b2b231ee..a8845fc87 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -93,15 +93,18 @@ xhtmlHeaders(__FILE__, translate('Events') );
    Date: Tue, 28 Nov 2017 08:30:06 -0500 Subject: [PATCH 13/34] Event view compress header Combine all three lines of event view to 2. --- web/skins/classic/css/classic/skin.css | 1 + web/skins/classic/css/classic/views/event.css | 91 +++++-------------- web/skins/classic/css/dark/views/event.css | 74 +++++---------- web/skins/classic/css/flat/views/event.css | 75 ++++++--------- web/skins/classic/views/event.php | 28 +++--- 5 files changed, 85 insertions(+), 184 deletions(-) diff --git a/web/skins/classic/css/classic/skin.css b/web/skins/classic/css/classic/skin.css index 7220e7410..5393727e1 100644 --- a/web/skins/classic/css/classic/skin.css +++ b/web/skins/classic/css/classic/skin.css @@ -349,6 +349,7 @@ th.table-th-sort-rev span.table-th-sort-span { margin: 4px auto; line-height: 1; text-align: left; + padding: 3px 0; } #header h2 { diff --git a/web/skins/classic/css/classic/views/event.css b/web/skins/classic/css/classic/views/event.css index 951fbc8cf..a45e6e7b8 100644 --- a/web/skins/classic/css/classic/views/event.css +++ b/web/skins/classic/css/classic/views/event.css @@ -29,87 +29,42 @@ span.noneCue { background: none; } +#header { + display: flex; + justify-content: space-between; + flex-direction: column; +} + +#header h2, #header a { + line-height: 1.1; + margin:5px 0 0 0; +} + #dataBar { - width: 100%; - margin: 2px auto; - text-align: center; -} - -#dataBar #dataTable { - width: 100%; - table-layout: fixed; -} - -#dataBar #dataTable td { - text-align: center; - padding: 2px; + display: flex; + flex-wrap:wrap; + justify-content: space-between; } #menuBar1 { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; width: 100%; text-align: center; - clear: both; -} - -#menuBar1 #nameControl { - float: left; -} - -#menuBar1 #nameControl #eventName { - margin-right: 4px; -} - -#menuBar1 #replayControl { - float: right; - margin-left: 8px; + margin: 4px 0 0 0; } #menuBar1 #scaleControl { - float: right; - margin-left: 8px; + margin: 0 4px 0 auto; } -#menuBar2 { - width: 100%; - margin-bottom: 4px; +#menuBar1 div { +margin: auto 5px; } -#menuBar2 div { - text-align: left; - float: left; - padding: 0 12px; -} - -#menuBar2 #closeWindow { - float: right; - text-align: right; -} - -#menuBar1:after, -#menuBar2:after { - content: "."; - display: block; - height: 0; - font-size: 0; - clear: both; - visibility: hidden; -} - -#videoBar1 div { - text-align: center; - float: center; -} - -#videoBar1 #prevEvent { - float: left; -} - -#videoBar1 #dlEvent { - float: center; -} - -#videoBar1 #nextEvent { - float: right; +#nameControl input[type="button"]{ +height: 100%; } #eventVideo { diff --git a/web/skins/classic/css/dark/views/event.css b/web/skins/classic/css/dark/views/event.css index cca16607f..3f8a1197f 100644 --- a/web/skins/classic/css/dark/views/event.css +++ b/web/skins/classic/css/dark/views/event.css @@ -29,70 +29,42 @@ span.noneCue { background: none; } +#header { + display: flex; + justify-content: space-between; + flex-direction: column; +} + +#header h2, #header a { + line-height: 1.1; + margin:5px 0 0 0; +} + #dataBar { - width: 100%; - margin: 2px auto; - text-align: center; -} - -#dataBar #dataTable { - width: 100%; - table-layout: fixed; -} - -#dataBar #dataTable td { - text-align: center; - padding: 2px; + display: flex; + flex-wrap:wrap; + justify-content: space-between; } #menuBar1 { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; width: 100%; text-align: center; - clear: both; -} - -#menuBar1 #nameControl { - float: left; -} - -#menuBar1 #nameControl #eventName { - margin-right: 4px; -} - -#menuBar1 #replayControl { - float: right; - margin-left: 8px; + margin: 4px 0 0 0; } #menuBar1 #scaleControl { - float: right; - margin-left: 8px; + margin: 0 4px 0 auto; } -#menuBar2 { - width: 100%; - margin-bottom: 4px; +#menuBar1 div { +margin: auto 5px; } -#menuBar2 div { - text-align: left; - float: left; - padding: 0 12px; -} - -#menuBar2 #closeWindow { - float: right; - text-align: right; -} - -#menuBar1:after, -#menuBar2:after { - content: "."; - display: block; - height: 0; - font-size: 0; - clear: both; - visibility: hidden; +#nameControl input[type="button"]{ +height: 100%; } #eventVideo { diff --git a/web/skins/classic/css/flat/views/event.css b/web/skins/classic/css/flat/views/event.css index bb93d24ba..54619f7be 100644 --- a/web/skins/classic/css/flat/views/event.css +++ b/web/skins/classic/css/flat/views/event.css @@ -29,70 +29,47 @@ span.noneCue { background: none; } +#header { + display: flex; + justify-content: space-between; + flex-direction: column; + margin: 0 0 4px 0; +} + +#header h2, #header a { + line-height: 1.1; + margin:5px 0 0 0; +} + #dataBar { - width: 100%; - margin: 2px auto; - text-align: center; -} - -#dataBar #dataTable { - width: 100%; - table-layout: fixed; -} - -#dataBar #dataTable td { - text-align: center; - padding: 2px; + display: flex; + flex-wrap:wrap; + justify-content: space-between; } #menuBar1 { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; width: 100%; text-align: center; - clear: both; + margin: 4px 0 0 0; } -#menuBar1 #nameControl { - float: left; -} - -#menuBar1 #nameControl #eventName { - margin-right: 4px; -} - -#menuBar1 #replayControl { - float: right; - margin-left: 8px; +#menuBar1 input, #menuBar1 select { + padding: 2px 5px; } #menuBar1 #scaleControl { - float: right; - margin-left: 8px; + margin: 0 4px 0 auto; } -#menuBar2 { - width: 100%; - margin-bottom: 4px; +#menuBar1 div { +margin: auto 5px; } -#menuBar2 div { - text-align: left; - float: left; - padding: 0 12px; -} - -#menuBar2 #closeWindow { - float: right; - text-align: right; -} - -#menuBar1:after, -#menuBar2:after { - content: "."; - display: block; - height: 0; - font-size: 0; - clear: both; - visibility: hidden; +#nameControl input[type="button"]{ +height: 100%; } #eventVideo { diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index f3665f724..59dd62fcf 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -97,34 +97,26 @@ xhtmlHeaders(__FILE__, translate('Event') );
    -
    +
    - - - - - - - - -
    Id() ?>Cause()) ?>StartTime() ) ) ?>Length() ?>s">Frames() ?>/AlarmFrames() ?>">TotScore() ?>/AvgScore() ?>/MaxScore() ?>
    + Id() ?> + Cause()) ?> + StartTime() ) ) ?> + Length().'s' ?> + ">Frames() ?>/AlarmFrames() ?> + ">TotScore() ?>/AvgScore() ?>/MaxScore() ?> +
    - +
    +
    DefaultVideo() ) { From 064ce99eac07bfb986df169c78f03fc787625918 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Tue, 28 Nov 2017 22:17:40 -0500 Subject: [PATCH 14/34] Scalecontrol in wrong place --- web/skins/classic/css/classic/views/event.css | 2 +- web/skins/classic/css/dark/views/event.css | 2 +- web/skins/classic/css/flat/views/event.css | 2 +- web/skins/classic/views/event.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/css/classic/views/event.css b/web/skins/classic/css/classic/views/event.css index a45e6e7b8..803315798 100644 --- a/web/skins/classic/css/classic/views/event.css +++ b/web/skins/classic/css/classic/views/event.css @@ -55,7 +55,7 @@ span.noneCue { margin: 4px 0 0 0; } -#menuBar1 #scaleControl { +#menuBar1 #replayControl { margin: 0 4px 0 auto; } diff --git a/web/skins/classic/css/dark/views/event.css b/web/skins/classic/css/dark/views/event.css index 3f8a1197f..139b5ca19 100644 --- a/web/skins/classic/css/dark/views/event.css +++ b/web/skins/classic/css/dark/views/event.css @@ -55,7 +55,7 @@ span.noneCue { margin: 4px 0 0 0; } -#menuBar1 #scaleControl { +#menuBar1 #replayControl { margin: 0 4px 0 auto; } diff --git a/web/skins/classic/css/flat/views/event.css b/web/skins/classic/css/flat/views/event.css index 54619f7be..637e76402 100644 --- a/web/skins/classic/css/flat/views/event.css +++ b/web/skins/classic/css/flat/views/event.css @@ -60,7 +60,7 @@ span.noneCue { padding: 2px 5px; } -#menuBar1 #scaleControl { +#menuBar1 #replayControl { margin: 0 4px 0 auto; } diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index 59dd62fcf..c87ed6178 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -142,8 +142,8 @@ if ( canEdit('Events') ) { } // end if Event->DefaultVideo ?>
    -
    +
    From 58159963fa8162e0488d63e102a3420ff6bc3995 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Wed, 29 Nov 2017 11:10:28 -0500 Subject: [PATCH 15/34] Fix back on timeline view --- web/skins/classic/css/classic/views/timeline.css | 9 +++++++++ web/skins/classic/css/dark/views/timeline.css | 9 +++++++++ web/skins/classic/css/flat/views/timeline.css | 9 +++++++++ web/skins/classic/views/js/timeline.js | 6 +++--- web/skins/classic/views/timeline.php | 5 ++++- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/css/classic/views/timeline.css b/web/skins/classic/css/classic/views/timeline.css index 14882f2e4..fd9832568 100644 --- a/web/skins/classic/css/classic/views/timeline.css +++ b/web/skins/classic/css/classic/views/timeline.css @@ -15,6 +15,15 @@ margin:5px 0 0 0; } +#header #info, #header #headerButtons { + display: flex; + flex-direction: column; +} + +#header #headerButtons { + align-items: flex-end; +} + #title { position: relative; margin: 0 auto; diff --git a/web/skins/classic/css/dark/views/timeline.css b/web/skins/classic/css/dark/views/timeline.css index f8e854d03..09aca0eba 100644 --- a/web/skins/classic/css/dark/views/timeline.css +++ b/web/skins/classic/css/dark/views/timeline.css @@ -15,6 +15,15 @@ margin:5px 0 0 0; } +#header #info, #header #headerButtons { + display: flex; + flex-direction: column; +} + +#header #headerButtons { + align-items: flex-end; +} + #title { position: relative; margin: 0 auto; diff --git a/web/skins/classic/css/flat/views/timeline.css b/web/skins/classic/css/flat/views/timeline.css index feed4a4d4..4e78e82cf 100644 --- a/web/skins/classic/css/flat/views/timeline.css +++ b/web/skins/classic/css/flat/views/timeline.css @@ -15,6 +15,15 @@ margin:5px 0 0 0; } +#header #info, #header #headerButtons { + display: flex; + flex-direction: column; +} + +#header #headerButtons { + align-items: flex-end; +} + #title { position: relative; margin: 0 auto; diff --git a/web/skins/classic/views/js/timeline.js b/web/skins/classic/views/js/timeline.js index 94217530f..a1e59d099 100644 --- a/web/skins/classic/views/js/timeline.js +++ b/web/skins/classic/views/js/timeline.js @@ -143,13 +143,13 @@ function loadEventImage( imagePath, eid, fid, width, height, fps, videoName, dur function tlZoomBounds( minTime, maxTime ) { console.log( "Zooming" ); - window.location = '?view='+currentView+filterQuery+'&minTime='+minTime+'&maxTime='+maxTime; + location.replace('?view='+currentView+filterQuery+'&minTime='+minTime+'&maxTime='+maxTime); } function tlZoomRange( midTime, range ) { - window.location = '?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+range; + location.replace('?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+range); } function tlPan( midTime, range ) { - window.location = '?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+range; + location('?view='+currentView+filterQuery+'&midTime='+midTime+'&range='+range); } diff --git a/web/skins/classic/views/timeline.php b/web/skins/classic/views/timeline.php index 75e9729f3..7bea08323 100644 --- a/web/skins/classic/views/timeline.php +++ b/web/skins/classic/views/timeline.php @@ -702,7 +702,10 @@ xhtmlHeaders(__FILE__, translate('Timeline') );
    +
    From 66d6e1a4a4f91a0f4765fb41ccd2af453591b690 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Fri, 1 Dec 2017 11:29:30 -0500 Subject: [PATCH 19/34] Add header to watch --- web/skins/classic/css/classic/views/watch.css | 36 +++++-------------- web/skins/classic/css/dark/views/watch.css | 36 +++++-------------- web/skins/classic/css/flat/views/watch.css | 36 +++++-------------- web/skins/classic/views/watch.php | 8 ++--- 4 files changed, 28 insertions(+), 88 deletions(-) diff --git a/web/skins/classic/css/classic/views/watch.css b/web/skins/classic/css/classic/views/watch.css index d1f435f7d..28bda54ef 100644 --- a/web/skins/classic/css/classic/views/watch.css +++ b/web/skins/classic/css/classic/views/watch.css @@ -1,37 +1,17 @@ @import url(../control.css); -#menuBar { - margin: 6px auto 4px; - text-align: center; +#header { + display: flex; + justify-content: space-between; } -#menuBar #monitorName { - float: left; +#menuControls { + display: flex; + align-items: center; } -#menuBar #closeControl { - float: right; -} - -#menuBar #menuControls { - margin: 0 auto; - width: 60%; -} - -#menuBar #menuControls #controlControl { - float: left; -} - -#menuBar #menuControls #eventsControl { - float: left; -} - -#menuBar #menuControls #settingsControl { - float: right; -} - -#menuBar #menuControls #scaleControl { - margin: 0 auto; +#menuControls div { + margin: 0 0 0 1em; } #imageFeed{ diff --git a/web/skins/classic/css/dark/views/watch.css b/web/skins/classic/css/dark/views/watch.css index d1f435f7d..28bda54ef 100644 --- a/web/skins/classic/css/dark/views/watch.css +++ b/web/skins/classic/css/dark/views/watch.css @@ -1,37 +1,17 @@ @import url(../control.css); -#menuBar { - margin: 6px auto 4px; - text-align: center; +#header { + display: flex; + justify-content: space-between; } -#menuBar #monitorName { - float: left; +#menuControls { + display: flex; + align-items: center; } -#menuBar #closeControl { - float: right; -} - -#menuBar #menuControls { - margin: 0 auto; - width: 60%; -} - -#menuBar #menuControls #controlControl { - float: left; -} - -#menuBar #menuControls #eventsControl { - float: left; -} - -#menuBar #menuControls #settingsControl { - float: right; -} - -#menuBar #menuControls #scaleControl { - margin: 0 auto; +#menuControls div { + margin: 0 0 0 1em; } #imageFeed{ diff --git a/web/skins/classic/css/flat/views/watch.css b/web/skins/classic/css/flat/views/watch.css index 5fec8ec82..ef7937db2 100644 --- a/web/skins/classic/css/flat/views/watch.css +++ b/web/skins/classic/css/flat/views/watch.css @@ -1,37 +1,17 @@ @import url(../control.css); -#menuBar { - margin: 6px auto 4px; - text-align: center; +#header { + display: flex; + justify-content: space-between; } -#menuBar #monitorName { - float: left; +#menuControls { + display: flex; + align-items: center; } -#menuBar #closeControl { - float: right; -} - -#menuBar #menuControls { - margin: 0 auto; - width: 60%; -} - -#menuBar #menuControls #controlControl { - float: left; -} - -#menuBar #menuControls #eventsControl { - float: left; -} - -#menuBar #menuControls #settingsControl { - float: right; -} - -#menuBar #menuControls #scaleControl { - margin: 0 auto; +#menuControls div { + margin: 0 0 0 1em; } #imageFeed{ diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index e09146915..bc3353319 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -62,10 +62,8 @@ xhtmlHeaders( __FILE__, $monitor->Name()." - ".translate('Feed') );
    -
    - +
    $scale) ); ?>
    From 1c201b0efef2e445208db088ea4192293d27c304 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Sat, 2 Dec 2017 10:08:57 -0500 Subject: [PATCH 20/34] Fix timeline filters --- web/skins/classic/includes/timeline_functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/includes/timeline_functions.php b/web/skins/classic/includes/timeline_functions.php index fd8866c2e..ee3b64808 100644 --- a/web/skins/classic/includes/timeline_functions.php +++ b/web/skins/classic/includes/timeline_functions.php @@ -468,7 +468,7 @@ function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) { - $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'DateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); + $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$minTime, 'sqlValue'=>$minTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'>=', 'sqlValue'=>'>=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); if ( isset($tree) ) @@ -483,7 +483,7 @@ function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) if ( $maxTime ) { - $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'DateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); + $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$maxTime, 'sqlValue'=>$maxTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'<=', 'sqlValue'=>'<=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); $cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode ); From 6d6b4366de3df39e1a4184496a78bfdcb15371a4 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Sat, 2 Dec 2017 10:09:19 -0500 Subject: [PATCH 21/34] Fix MontageReview filters --- web/skins/classic/includes/functions.php | 9 ++++++--- web/skins/classic/views/montagereview.php | 12 ++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 5f916f154..8f2946b5e 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -160,15 +160,18 @@ if ( file_exists( "skins/$skin/css/$css/graphics/favicon.ico" ) ) { } // end function xhtmlHeaders( $file, $title ) function getNavBarHTML($reload = null) { - parseFilter( $_REQUEST['filter'] ); - $filterQuery = $_REQUEST['filter']['query']; $versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':''; global $running; global $user; global $bandwidth_options; global $view; -if ($reload === null) { + global $filterQuery; + if (!$filterQuery) { + parseFilter( $_REQUEST['filter'] ); + $filterQuery = $_REQUEST['filter']['query']; + } + if ($reload === null) { ob_start(); if ( $running == null ) $running = daemonCheck(); diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index 37add6ddc..720416045 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -59,6 +59,18 @@ include('_monitor_filters.php'); $filter_bar = ob_get_contents(); ob_end_clean(); +$filter = array( + 'Query' => array( + 'terms' => array( + array('attr' => 'StartDateTime', 'op' => '>=', 'val' => $_REQUEST[minTime]), + array('attr' => 'StartDateTime', 'op' => '<=', 'val' => $_REQUEST[maxTime]), + ) + ), + ); + +parseFilter( $filter ); +$filterQuery = $filter['query']; + // Note that this finds incomplete events as well, and any frame records written, but still cannot "see" to the end frame // if the bulk record has not been written - to be able to include more current frames reduce bulk frame sizes (event size can be large) // Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly. From 7a188a84f7a496cf84a76c9b794afe667ea039ce Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Sat, 2 Dec 2017 12:25:18 -0500 Subject: [PATCH 22/34] Add montagereview filter passing montagereview now accepts any appropriate filters --- web/skins/classic/includes/functions.php | 16 +++++++++++++++- web/skins/classic/views/montagereview.php | 13 +++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 8f2946b5e..803916162 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -224,9 +224,23 @@ if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
  • >
  • =') $minTime = $term['val']; + if ($term['op'] == '<=') $maxTime = $term['val']; + } + } + if ($count == 2) { + $montageReviewQuery = '&minTime='.$minTime.'&maxTime='.$maxTime; + } +} if ( canView('Events') ) { ?> -
  • >
  • +
  • >
  • diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index 720416045..221d0c181 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -59,17 +59,18 @@ include('_monitor_filters.php'); $filter_bar = ob_get_contents(); ob_end_clean(); -$filter = array( +if (isset($_REQUEST['minTime']) || isset($_REQUEST['maxTime'])) { + $filter = array( 'Query' => array( 'terms' => array( - array('attr' => 'StartDateTime', 'op' => '>=', 'val' => $_REQUEST[minTime]), - array('attr' => 'StartDateTime', 'op' => '<=', 'val' => $_REQUEST[maxTime]), + array('attr' => 'StartDateTime', 'op' => '>=', 'val' => $_REQUEST['minTime']), + array('attr' => 'StartDateTime', 'op' => '<=', 'val' => $_REQUEST['maxTime']), ) ), ); - -parseFilter( $filter ); -$filterQuery = $filter['query']; + parseFilter( $filter ); + $filterQuery = $filter['query']; +} // Note that this finds incomplete events as well, and any frame records written, but still cannot "see" to the end frame // if the bulk record has not been written - to be able to include more current frames reduce bulk frame sizes (event size can be large) From c04ec1b8a83a690768beb51a1e967a5dbc7f0ac6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 3 Dec 2017 12:50:57 -0500 Subject: [PATCH 23/34] fix mem corruption --- src/zm_db.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/zm_db.cpp b/src/zm_db.cpp index bb83d09ab..fb716349e 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -28,8 +28,9 @@ MYSQL dbconn; int zmDbConnected = false; void zmDbConnect() { - if ( zmDbConnected ) - return; + // For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu + //if ( zmDbConnected ) + //return; if ( !mysql_init( &dbconn ) ) { Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) ); From 3e7c573da5bc597d31e771ab884bd06081e9b90c Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Sun, 3 Dec 2017 14:04:33 -0500 Subject: [PATCH 24/34] Add download video option to events view Creates a new popup window for downloading event video files with no directory structure in the archive --- web/ajax/event.php | 13 +++ web/lang/en_gb.php | 2 + .../classic/includes/export_functions.php | 16 ++- web/skins/classic/js/flat.js | 1 + web/skins/classic/views/download.php | 109 ++++++++++++++++++ web/skins/classic/views/events.php | 1 + web/skins/classic/views/js/download.js | 37 ++++++ web/skins/classic/views/js/download.js.php | 19 +++ web/skins/classic/views/js/events.js | 15 +++ 9 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 web/skins/classic/views/download.php create mode 100644 web/skins/classic/views/js/download.js create mode 100644 web/skins/classic/views/js/download.js.php diff --git a/web/ajax/event.php b/web/ajax/event.php index 0920a9423..04ebca9ad 100644 --- a/web/ajax/event.php +++ b/web/ajax/event.php @@ -76,6 +76,19 @@ if ( canView( 'Events' ) ) { ajaxError( 'Export Failed' ); break; } + case 'download' : + { + require_once( ZM_SKIN_PATH.'/includes/export_functions.php' ); + $exportVideo = 1; + $exportFormat = $_REQUEST['exportFormat']; + $exportStructure = 'flat'; + $exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id']; + if ( $exportFile = exportEvents( $exportIds, false, false, false, $exportVideo, false, $exportFormat, $exportStructure ) ) + ajaxResponse( array( 'exportFile'=>$exportFile ) ); + else + ajaxError( 'Export Failed' ); + break; + } } } diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 4d00798be..45d0daef6 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -322,6 +322,8 @@ $SLANG = array( 'ExportDetails' => 'Export Event Details', 'Exif' => 'Embed EXIF data into image', 'Export' => 'Export', + 'DownloadVideo' => 'Download Video', + 'GenerateDownload' => 'Generate Download', 'ExportFailed' => 'Export Failed', 'ExportFormat' => 'Export File Format', 'ExportFormatTar' => 'Tar', diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index 2414e391f..604dcba4e 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -853,7 +853,7 @@ function exportFileList( $eid, $exportDetail, $exportFrames, $exportImages, $exp return( array_values( $exportFileList ) ); } -function exportEvents( $eids, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc, $exportFormat ) +function exportEvents( $eids, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc, $exportFormat, $exportStructure = false ) { if ( canView( 'Events' ) && !empty($eids) ) @@ -907,8 +907,12 @@ function exportEvents( $eids, $exportDetail, $exportFrames, $exportImages, $expo { $archive = ZM_DIR_EXPORTS."/".$export_root.".tar.gz"; @unlink( $archive ); - $command = "tar --create --gzip --file=$archive --files-from=$listFile"; - exec( escapeshellcmd( $command ), $output, $status ); + if ($exportStructure == 'flat') { //strip file paths if we choose + $command = "tar --create --gzip --file=".escapeshellarg($archive)." --files-from=".escapeshellarg($listFile)." --xform='s#^.+/##x'"; + } else { + $command = "tar --create --gzip --file=".escapeshellarg($archive)." --files-from=".escapeshellarg($listFile); + } + exec( $command, $output, $status ); if ( $status ) { Error( "Command '$command' returned with status $status" ); @@ -921,7 +925,11 @@ function exportEvents( $eids, $exportDetail, $exportFrames, $exportImages, $expo { $archive = ZM_DIR_EXPORTS."/".$export_root.".zip"; @unlink( $archive ); - $command = "cat ".escapeshellarg($listFile)." | zip -q ".escapeshellarg($archive)." -@"; + if ($exportStructure == 'flat') { + $command = "cat ".escapeshellarg($listFile)." | zip -q -j ".escapeshellarg($archive)." -@"; + } else { + $command = "cat ".escapeshellarg($listFile)." | zip -q ".escapeshellarg($archive)." -@"; + } //cat zmFileList.txt | zip -q zm_export.zip -@ //-bash: zip: command not found diff --git a/web/skins/classic/js/flat.js b/web/skins/classic/js/flat.js index 83fccaec5..6b78cfaa9 100644 --- a/web/skins/classic/js/flat.js +++ b/web/skins/classic/js/flat.js @@ -33,6 +33,7 @@ var popupSizes = { 'device': { 'width': 260, 'height': 150 }, 'devices': { 'width': 400, 'height': 240 }, 'donate': { 'width': 500, 'height': 280 }, + 'download': { 'width': 350, 'height': 215 }, 'event': { 'addWidth': 108, 'minWidth': 496, 'addHeight': 230, 'minHeight': 540 }, 'eventdetail': { 'width': 600, 'height': 420 }, 'events': { 'width': 1220, 'height': 780 }, diff --git a/web/skins/classic/views/download.php b/web/skins/classic/views/download.php new file mode 100644 index 000000000..cccac537e --- /dev/null +++ b/web/skins/classic/views/download.php @@ -0,0 +1,109 @@ + + +
    + +
    + + + + + + + + + + + + + + + + +
    + + +
    + + +
    + +

    + + + + + +
    + + diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index a8845fc87..f89ccc7be 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -269,6 +269,7 @@ if ( true || canEdit( 'Events' ) ) { +
    4 ) + $('exportProgressTicker').set( 'text', '.' ); + else + $('exportProgressTicker').appendText( '.' ); +} + +function exportResponse( respObj, respText ) { + window.location.replace( thisUrl+'?view='+currentView+'&'+eidParm+'&exportFile='+respObj.exportFile+'&generated='+((respObj.result=='Ok')?1:0) ); +} + +function exportEvent( form ) { + var parms = 'view=request&request=event&action=download'; + parms += '&'+$(form).toQueryString(); + var query = new Request.JSON( { url: thisUrl, method: 'post', data: parms, onSuccess: exportResponse } ); + query.send(); + $('exportProgress').removeClass( 'hidden' ); + $('exportProgress').setProperty( 'class', 'warnText' ); + $('exportProgressText').set( 'text', exportProgressString ); + exportProgress(); + exportTimer = exportProgress.periodical( 500 ); +} + +function initPage() { + if ( exportReady ) { + startDownload.pass( exportFile ).delay( 1500 ); + } +} + +window.addEvent( 'domready', initPage ); diff --git a/web/skins/classic/views/js/download.js.php b/web/skins/classic/views/js/download.js.php new file mode 100644 index 000000000..cc4bbb83f --- /dev/null +++ b/web/skins/classic/views/js/download.js.php @@ -0,0 +1,19 @@ + +var eidParm = ''; + +var eidParm = 'eid='; + + +var exportReady = ; +var exportFile = ''; + +var exportProgressString = ''; diff --git a/web/skins/classic/views/js/events.js b/web/skins/classic/views/js/events.js index 44fc5241d..27d97a9e2 100644 --- a/web/skins/classic/views/js/events.js +++ b/web/skins/classic/views/js/events.js @@ -15,6 +15,7 @@ function toggleCheckbox( element, name ) { form.editBtn.disabled = !checked; form.archiveBtn.disabled = unarchivedEvents?!checked:true; form.unarchiveBtn.disabled = archivedEvents?!checked:true; + form.downloadBtn.disabled = !checked; form.exportBtn.disabled = !checked; form.deleteBtn.disabled = !checked; } @@ -38,6 +39,7 @@ function configureButton( element, name ) { form.editBtn.disabled = !checked; form.archiveBtn.disabled = (!checked)||(!unarchivedEvents); form.unarchiveBtn.disabled = (!checked)||(!archivedEvents); + form.downloadBtn.disabled = !checked; form.exportBtn.disabled = !checked; form.deleteBtn.disabled = !checked; } @@ -74,6 +76,19 @@ function editEvents( element, name ) { createPopup( '?view=eventdetail&'+eids.join( '&' ), 'zmEventDetail', 'eventdetail' ); } +function downloadVideo( element, name ) { + var form = element.form; + var eids = new Array(); + for (var i = 0; i < form.elements.length; i++) { + if (form.elements[i].name.indexOf(name) == 0) { + if ( form.elements[i].checked ) { + eids[eids.length] = 'eids[]='+form.elements[i].value; + } + } + } + createPopup( '?view=download&'+eids.join( '&' ), 'zmDownload', 'download' ); +} + function exportEvents( element, name ) { var form = element.form; var eids = new Array(); From 4f5677eb3b8d4d5f77256880e143443b5593bc64 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Sun, 3 Dec 2017 14:05:11 -0500 Subject: [PATCH 25/34] Remove time limit from archive download for exceptionally large files --- web/views/archive.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/views/archive.php b/web/views/archive.php index 75f51448b..ce4e1b274 100644 --- a/web/views/archive.php +++ b/web/views/archive.php @@ -47,6 +47,7 @@ if ( $archivetype ) { if ( is_readable($filename_path) ) { header( "Content-type: application/$mimetype" ); header( "Content-Disposition: attachment; filename=$filename"); + set_time_limit(0); readfile( $filename_path ); } else { Error("$filename_path does not exist or is not readable."); From 9ca19b003fb68084115a28eb10ef72853aeaf915 Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Sun, 3 Dec 2017 14:08:25 -0500 Subject: [PATCH 26/34] Add download button to montagereview --- web/skins/classic/css/classic/views/montagereview.css | 4 ++++ web/skins/classic/css/dark/views/montagereview.css | 4 ++++ web/skins/classic/css/flat/views/montagereview.css | 4 ++++ web/skins/classic/views/js/montagereview.js | 5 +++++ web/skins/classic/views/js/montagereview.js.php | 2 ++ web/skins/classic/views/montagereview.php | 3 ++- 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/css/classic/views/montagereview.css b/web/skins/classic/css/classic/views/montagereview.css index 84109fbd1..7e6696de1 100644 --- a/web/skins/classic/css/classic/views/montagereview.css +++ b/web/skins/classic/css/classic/views/montagereview.css @@ -36,3 +36,7 @@ input[type=range]::-ms-tooltip { display: none; } + +#downloadVideo { + margin-left: 5px; +} diff --git a/web/skins/classic/css/dark/views/montagereview.css b/web/skins/classic/css/dark/views/montagereview.css index 84109fbd1..7e6696de1 100644 --- a/web/skins/classic/css/dark/views/montagereview.css +++ b/web/skins/classic/css/dark/views/montagereview.css @@ -36,3 +36,7 @@ input[type=range]::-ms-tooltip { display: none; } + +#downloadVideo { + margin-left: 5px; +} diff --git a/web/skins/classic/css/flat/views/montagereview.css b/web/skins/classic/css/flat/views/montagereview.css index 6f8feddf3..e82e5ae95 100644 --- a/web/skins/classic/css/flat/views/montagereview.css +++ b/web/skins/classic/css/flat/views/montagereview.css @@ -42,3 +42,7 @@ input[type=range]::-ms-tooltip { color: white; font-size: 40px; } + +#downloadVideo { + margin-left: 5px; +} diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 03457f38f..07262c3a0 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -335,6 +335,7 @@ function redrawScreen() { $('zoomout').style.display="none"; $('panleft').style.display="none"; $('panright').style.display="none"; + $('downloadVideo').style.display="none"; } else { // switch out of liveview mode @@ -351,6 +352,7 @@ function redrawScreen() { $('panleft').style.display="inline-flex"; $('panright').style.display="inline"; $('panright').style.display="inline-flex"; + $('downloadVideo').style.display="inline"; } if ( fitMode == 1 ) { @@ -553,6 +555,9 @@ function click_panright() { maxTimeSecs = minTimeSecs + rangeTimeSecs - 1; clicknav(minTimeSecs,maxTimeSecs,0); } +function click_download() { + createPopup( '?view=download'+filterQuery, 'zmDownload', 'download' ); +} function click_all_events() { clicknav(0,0,0); } diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 69d9c3d22..e8ac13ff5 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -1,4 +1,6 @@ +var filterQuery = ''; + var server_utc_offset = array( 'terms' => array( array('attr' => 'StartDateTime', 'op' => '>=', 'val' => $_REQUEST['minTime']), - array('attr' => 'StartDateTime', 'op' => '<=', 'val' => $_REQUEST['maxTime']), + array('attr' => 'StartDateTime', 'op' => '<=', 'val' => $_REQUEST['maxTime'], 'cnj' => 'and'), ) ), ); @@ -224,6 +224,7 @@ xhtmlHeaders(__FILE__, translate('MontageReview') ); +
    From af1729792f6a3e1fe4180c7e8c17bfd622211325 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 3 Dec 2017 17:04:11 -0500 Subject: [PATCH 27/34] add AutoMove and AutoMoveTo to Filters --- db/zm_create.sql.in | 2 ++ db/zm_update-1.31.16.sql | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 db/zm_update-1.31.16.sql diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 2685e27d1..01c1ad118 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -233,6 +233,8 @@ CREATE TABLE `Filters` ( `AutoExecute` tinyint(3) unsigned NOT NULL default '0', `AutoExecuteCmd` tinytext, `AutoDelete` tinyint(3) unsigned NOT NULL default '0', + `AutoMove` tinyint(3) unsigned NOT NULL default '0', + `AutoMoveTo` smallint(5) unsigned NOT NULL default 0, `UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0', `Background` tinyint(1) unsigned NOT NULL default '0', `Concurrent` tinyint(1) unsigned NOT NULL default '0', diff --git a/db/zm_update-1.31.16.sql b/db/zm_update-1.31.16.sql new file mode 100644 index 000000000..de323ee51 --- /dev/null +++ b/db/zm_update-1.31.16.sql @@ -0,0 +1,28 @@ +-- +-- Add UpdateDiskSpace action to Filters +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'AutoMove' + ) > 0, +"SELECT 'Column AutoMove already exists in Filters'", +"ALTER TABLE Filters ADD `AutoMove` tinyint(3) unsigned NOT NULL default '0' AFTER `AutoDelete`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'AutoMoveTo' + ) > 0, +"SELECT 'Column AutoMoveTo already exists in Filters'", +"ALTER TABLE Filters ADD `AutoMoveTo` smallint(5) unsigned NOT NULL default '0' AFTER `AutoMove`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + From 1ccd344bf5cf544010b981d9e240dd7655838fcb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 4 Dec 2017 11:05:50 -0500 Subject: [PATCH 28/34] implement Storage Area move --- .../ZoneMinder/lib/ZoneMinder/Config.pm.in | 79 ++++++++++--------- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 50 ++++++++++++ scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 11 +-- scripts/ZoneMinder/lib/ZoneMinder/Object.pm | 2 +- scripts/zmfilter.pl.in | 30 ++++--- web/includes/Filter.php | 2 + web/includes/actions.php | 6 +- web/includes/config.php.in | 26 +++--- web/includes/database.php | 2 +- web/skins/classic/views/_monitor_filters.php | 1 + web/skins/classic/views/add_monitors.php | 2 +- web/skins/classic/views/download.php | 44 +++++------ web/skins/classic/views/filter.php | 6 +- 13 files changed, 169 insertions(+), 92 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in index f69e95b18..8dd016c89 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in @@ -79,13 +79,16 @@ BEGIN { # Search for user created config files. If one or more are found then # update the Config hash with those values if ( -d ZM_CONFIG_SUBDIR ) { - if ( -R ZM_CONFIG_SUBDIR ) { - foreach my $filename ( glob ZM_CONFIG_SUBDIR."/*.conf" ) { - process_configfile($filename); - } - } else { - print( STDERR "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on ".ZM_CONFIG_SUBDIR.".\n" ); + if ( -R ZM_CONFIG_SUBDIR ) { + foreach my $filename ( glob ZM_CONFIG_SUBDIR."/*.conf" ) { + process_configfile($filename); } + } else { + print( STDERR "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on ".ZM_CONFIG_SUBDIR.".\n" ); + } + } + foreach my $k ( keys %Config ) { + print "Initial config $k => $Config{$k}\n"; } use DBI; @@ -94,14 +97,14 @@ BEGIN { if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { - $socket = ";mysql_socket=".$portOrSocket; + $socket = ';mysql_socket='.$portOrSocket; } else { - $socket = ";host=".$host.";port=".$portOrSocket; + $socket = ';host='.$host.';port='.$portOrSocket; } } else { - $socket = ";host=".$Config{ZM_DB_HOST}; + $socket = ';host='.$Config{ZM_DB_HOST}; } - my $sslOptions = ""; + my $sslOptions = ''; if ( $Config{ZM_DB_SSL_CA_CERT} ) { $sslOptions = ';'.join(';', "mysql_ssl=1", @@ -123,7 +126,9 @@ BEGIN { } $sth->finish(); #$dbh->disconnect(); - if ( ! exists $Config{ZM_SERVER_ID} ) { + # + if ( ! $Config{ZM_SERVER_ID} ) { + print "Loading ServerId $Config{ZM_SERVER_NAME} HOST:$Config{ZM_SERVER_HOST}\n"; $Config{ZM_SERVER_ID} = undef; $sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' ); if ( $Config{ZM_SERVER_NAME} ) { @@ -136,36 +141,38 @@ BEGIN { $Config{ZM_SERVER_ID} = $$result{Id}; } $sth->finish(); + } else { + print "Have ZM_SERVER_ID $Config{ZM_SERVER_ID}\n"; } # This subroutine must be inside the BEGIN block sub process_configfile { - my $config_file = shift; + my $config_file = shift; - if ( -R $config_file ) { - open( my $CONFIG, "<", $config_file ) - or croak( "Can't open config file '$config_file': $!" ); - foreach my $str ( <$CONFIG> ) { - next if ( $str =~ /^\s*$/ ); - next if ( $str =~ /^\s*#/ ); - my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/; - if ( ! $name ) { - print( STDERR "Warning, bad line in $config_file: $str\n" ); - next; - } # end if - $name =~ tr/a-z/A-Z/; - $Config{$name} = $value; - } - close( $CONFIG ); - } else { - print( STDERR "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $config_file\n" ); + if ( -R $config_file ) { + open( my $CONFIG, '<', $config_file ) + or croak( "Can't open config file '$config_file': $!" ); + foreach my $str ( <$CONFIG> ) { + next if ( $str =~ /^\s*$/ ); + next if ( $str =~ /^\s*#/ ); + my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/; + if ( ! $name ) { + print( STDERR "Warning, bad line in $config_file: $str\n" ); + next; + } # end if + $name =~ tr/a-z/A-Z/; + $Config{$name} = $value; } + close( $CONFIG ); + } else { + print( STDERR "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $config_file\n" ); + } } } # end BEGIN sub loadConfigFromDB { - print( "Loading config from DB" ); + print( 'Loading config from DB' ); my $dbh = ZoneMinder::Database::zmDbConnect(); if ( !$dbh ) { print( "Error: unable to load options from database: $DBI::errstr\n" ); @@ -188,7 +195,7 @@ sub loadConfigFromDB { #next if ( $option->{category} eq 'hidden' ); if ( defined($value) ) { if ( $option->{type} == $types{boolean} ) { - $option->{value} = $value?"yes":"no"; + $option->{value} = $value?'yes':'no'; } else { $option->{value} = $value; } @@ -201,7 +208,7 @@ sub loadConfigFromDB { } # end sub loadConfigFromDB sub saveConfigToDB { - print( "Saving config to DB " . @options . " entries\n" ); + print( 'Saving config to DB ' . @options . " entries\n" ); my $dbh = ZoneMinder::Database::zmDbConnect(); if ( !$dbh ) { print( "Error: unable to save options to database: $DBI::errstr\n" ); @@ -214,7 +221,7 @@ sub saveConfigToDB { $dbh->do('LOCK TABLE Config WRITE') or croak( "Can't lock Config table: " . $dbh->errstr() ); - my $sql = "delete from Config"; + my $sql = 'DELETE FROM Config'; my $res = $dbh->do( $sql ) or croak( "Can't do '$sql': ".$dbh->errstr() ); @@ -228,8 +235,8 @@ sub saveConfigToDB { $option->{db_hint} = $option->{type}->{hint}; $option->{db_pattern} = $option->{type}->{pattern}; $option->{db_format} = $option->{type}->{format}; - if ( $option->{db_type} eq "boolean" ) { - $option->{db_value} = ($option->{value} eq "yes") ? "1" : "0"; + if ( $option->{db_type} eq 'boolean' ) { + $option->{db_value} = ($option->{value} eq 'yes') ? '1' : '0'; } else { $option->{db_value} = $option->{value}; } @@ -237,7 +244,7 @@ sub saveConfigToDB { $option->{db_requires} = join( ";", map { my $value = $_->{value}; - $value = ($value eq "yes") ? 1 : 0 if ( $options_hash{$_->{name}}->{db_type} eq "boolean" ); + $value = ($value eq 'yes') ? 1 : 0 if ( $options_hash{$_->{name}}->{db_type} eq 'boolean' ); ( "$_->{name}=$value" ) } @$requires ); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 8c4eba093..88a2af1e3 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -33,6 +33,8 @@ require ZoneMinder::Object; require ZoneMinder::Storage; require Date::Manip; require File::Find; +require File::Path; +require File::Copy; #our @ISA = qw(ZoneMinder::Object); use parent qw(ZoneMinder::Object); @@ -401,6 +403,54 @@ sub DiskSpace { } } +sub MoveTo { + my ( $self, $NewStorage ) = @_; + my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint + my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint + if ( ! $$NewStorage{Id} ) { + return "New storage does not have an id. Moving will not happen."; + } elsif ( !$NewPath) { + return "$NewPath is empty."; + }elsif ( $NewPath eq $OldPath ) { + return "New path and old path are the same! $NewPath"; + } elsif ( ! -e $NewPath ) { + return "New path $NewPath does not exist."; + }elsif ( ! -e $OldPath ) { + return "Old path $OldPath does not exist."; + } + + my $error = ''; + File::Path::make_path( $NewPath, {error => \my $err} ); + if ( @$err ) { + for my $diag (@$err) { + my ($file, $message) = %$diag; + if ($file eq '') { + $error .= "general error: $message\n"; + } else { + $error .= "problem unlinking $file: $message\n"; + } + } + } + return $error if $error; + my @files = glob("$OldPath/*"); + + for my $file (@files) { + next if $file =~ /^\./; + ( $file ) = ( $file =~ /^(.*)$/ ); # De-taint + if ( ! File::Copy::copy( $file, $NewPath ) ) { + $error .= "Copy failed: for $file to $NewPath: $!"; + last; + } + } # end foreach file. + + if ( ! $error ) { + # Succeeded in copying all files, so we may now update the Event. + $$self{StorageId} = $$NewStorage{Id}; + $error .= $self->save(); + } + return $error; +} # end sub MoveTo + 1; __END__ diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 2048a7ba8..346aaa985 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -207,10 +207,11 @@ sub Sql { if ( $term->{attr} =~ /^Monitor/ ) { $value = "'$temp_value'"; } elsif ( $term->{attr} eq 'ServerId' ) { + Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})"); if ( $temp_value eq 'ZM_SERVER_ID' ) { - $value = "'$Config{ZM_SERVER_ID}'"; + $value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'"; # This gets used later, I forget for what - $$self{Server} = new ZoneMinder::Server( $Config{ZM_SERVER_ID} ); + $$self{Server} = new ZoneMinder::Server( $ZoneMinder::Config::Config{ZM_SERVER_ID} ); } else { $value = "'$temp_value'"; # This gets used later, I forget for what @@ -225,7 +226,7 @@ sub Sql { ) { $value = "'$temp_value'"; } elsif ( $term->{attr} eq 'DateTime' or $term->{attr} eq 'StartDateTime' or $term->{attr} eq 'EndDateTime' ) { - if ( $temp_value == 'NULL' ) { + if ( $temp_value eq 'NULL' ) { $value = $temp_value; } else { $value = DateTimeToSQL( $temp_value ); @@ -237,7 +238,7 @@ sub Sql { $value = "'$value'"; } } elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) { - if ( $temp_value == 'NULL' ) { + if ( $temp_value eq 'NULL' ) { $value = $temp_value; } else { $value = DateTimeToSQL( $temp_value ); @@ -249,7 +250,7 @@ sub Sql { $value = "to_days( '$value' )"; } } elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' or $term->{attr} eq 'EndTime' ) { - if ( $temp_value == 'NULL' ) { + if ( $temp_value eq 'NULL' ) { $value = $temp_value; } else { $value = DateTimeToSQL( $temp_value ); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index e06486bf0..f8661313e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -47,7 +47,7 @@ use vars qw/ $AUTOLOAD $log $dbh/; *log = \$ZoneMinder::Logger::logger; *dbh = \$ZoneMinder::Database::dbh; -my $debug = 1; +my $debug = 0; use constant DEBUG_ALL=>1; sub new { diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index efe988882..9dcd3548e 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -207,7 +207,7 @@ sub getFilters { } else { $sql .= ' Background = 1 AND'; } - $sql .= '( AutoArchive = 1 + $sql .= '( AutoArchive = 1 or AutoVideo = 1 or AutoUpload = 1 or AutoEmail = 1 @@ -215,16 +215,17 @@ sub getFilters { or AutoExecute = 1 or AutoDelete = 1 or UpdateDiskSpace = 1 + or AutoMove = 1 ) ORDER BY Name'; my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); + or Fatal( "Unable to prepare '$sql': ".$dbh->errstr() ); my $res; if ( $filter_name ) { $res = $sth->execute( $filter_name ) - or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); + or Fatal( "Unable to execute '$sql': ".$sth->errstr() ); } else { $res = $sth->execute() - or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); + or Fatal( "Unable to execute '$sql': ".$sth->errstr() ); } FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) { my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter ); @@ -263,6 +264,7 @@ sub checkFilter { ($filter->{AutoEmail}?'email':()), ($filter->{AutoMessage}?'message':()), ($filter->{AutoExecute}?'execute':()), + ($filter->{AutoMove}?'move':()), ($filter->{UpdateDiskSpace}?'update disk space':()), ), 'returned' , scalar @Events , 'events', @@ -270,12 +272,12 @@ sub checkFilter { ) ); foreach my $event ( @Events ) { - Debug( "Checking event $event->{Id}\n" ); + Debug( "Checking event $event->{Id}" ); my $delete_ok = !undef; $dbh->ping(); if ( $filter->{AutoArchive} ) { - Info( "Archiving event $event->{Id}\n" ); -# Do it individually to avoid locking up the table for new events + Info( "Archiving event $event->{Id}" ); + # Do it individually to avoid locking up the table for new events my $sql = 'UPDATE Events SET Archived = 1 WHERE Id = ?'; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); @@ -315,11 +317,17 @@ sub checkFilter { Error( "Unable toto delete event $event->{Id} as previous operations failed\n" ); } } # end if AutoDelete + if ( $filter->{AutoMove} ) { + my $Event = new ZoneMinder::Event( $$event{Id}, $event ); + my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} ); + $_ = $Event->MoveTo( $NewStorage ); + Error($_) if $_; + } + if ( $filter->{UpdateDiskSpace} ) { - my $Event = new ZoneMinder::Event( $$event{Id}, $event ); - $Event->DiskSpace(undef); - $Event->save(); - + my $Event = new ZoneMinder::Event( $$event{Id}, $event ); + $Event->DiskSpace(undef); + $Event->save(); } # end if UpdateDiskSpace } # end foreach event } diff --git a/web/includes/Filter.php b/web/includes/Filter.php index 11d47973a..5c7fc7b39 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -12,6 +12,8 @@ public $defaults = array( 'AutoArchive' => 0, 'AutoVideo' => 0, 'AutoMessage' => 0, + 'AutoMove' => 0, + 'AutoMoveTo' => 0, 'UpdateDiskSpace' => 0, 'Background' => 0, 'Concurrent' => 0, diff --git a/web/includes/actions.php b/web/includes/actions.php index 1a2a13d56..3d6f898c8 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -142,7 +142,6 @@ if ( canView( 'Events' ) ) { if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) { if ( $action == 'addterm' ) { -Warning("Addterm"); $_REQUEST['filter'] = addFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] ); } elseif ( $action == 'delterm' ) { $_REQUEST['filter'] = delFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] ); @@ -173,6 +172,11 @@ Warning("Addterm"); $sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0); $sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']); $sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0); + if ( !empty($_REQUEST['filter']['AutoMove']) ? 1 : 0) { + $sql .= ', AutoMove = 1, AutoMoveTo='. validInt($_REQUEST['filter']['AutoMoveTo']); + } else { + $sql .= ', AutoMove = 0'; + } $sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0); $sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0); $sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0); diff --git a/web/includes/config.php.in b/web/includes/config.php.in index 66ab5e517..2405b0114 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -137,19 +137,19 @@ require_once( 'database.php' ); loadConfig(); $GLOBALS['defaultUser'] = array( - "Username" => "admin", - "Password" => "", - "Language" => "", - "Enabled" => 1, - "Stream" => 'View', - "Events" => 'Edit', - "Control" => 'Edit', - "Monitors" => 'Edit', - "Groups" => 'Edit', - "Devices" => 'Edit', - "System" => 'Edit', - "MaxBandwidth" => "", - "MonitorIds" => false + 'Username' => "admin", + 'Password' => "", + 'Language' => "", + 'Enabled' => 1, + 'Stream' => 'View', + 'Events' => 'Edit', + 'Control' => 'Edit', + 'Monitors' => 'Edit', + 'Groups' => 'Edit', + 'Devices' => 'Edit', + 'System' => 'Edit', + 'MaxBandwidth' => '', + 'MonitorIds' => false ); function loadConfig( $defineConsts=true ) { diff --git a/web/includes/database.php b/web/includes/database.php index a1bcf66c3..c1ec1c3ca 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -134,7 +134,7 @@ function dbQuery( $sql, $params=NULL ) { } else { $result = $dbConn->query( $sql ); } -if ( 1 ) { +if ( defined(ZM_DB_DEBUG) ) { if ( $params ) Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); else diff --git a/web/skins/classic/views/_monitor_filters.php b/web/skins/classic/views/_monitor_filters.php index fd09a3206..89ff4e3eb 100644 --- a/web/skins/classic/views/_monitor_filters.php +++ b/web/skins/classic/views/_monitor_filters.php @@ -109,6 +109,7 @@ $groupSql = Group::get_group_sql( $group_id ); Warning("Monitor " . $monitors[$i]['Id'] . ' is not visible' ); continue; } + $monitors_dropdown[$monitors[$i]['Id']] = $monitors[$i]['Name']; if ( $monitor_id and ( $monitors[$i]['Id'] != $monitor_id ) ) { continue; diff --git a/web/skins/classic/views/add_monitors.php b/web/skins/classic/views/add_monitors.php index 61f26c4d5..b77c048d5 100644 --- a/web/skins/classic/views/add_monitors.php +++ b/web/skins/classic/views/add_monitors.php @@ -45,7 +45,7 @@ xhtmlHeaders(__FILE__, translate('AddMonitors'));
    Enter by IP or URL - +
    Import CSV Spreadsheet Spreadsheet should have the following format:
    diff --git a/web/skins/classic/views/download.php b/web/skins/classic/views/download.php index cccac537e..69e062078 100644 --- a/web/skins/classic/views/download.php +++ b/web/skins/classic/views/download.php @@ -18,7 +18,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView( 'Events' ) ) { +if ( !canView('Events') ) { $view = 'error'; return; } @@ -50,16 +50,12 @@ xhtmlHeaders(__FILE__, translate('Download') );
    - - + + + + - +
    -

    +

    + + +

    + + - -

    - AutoDelete()) ) { ?> checked="checked" onclick="updateButtons( this )"/> + AutoDelete()) ) { ?> checked="checked" onclick="updateButtons(this)"/> +

    +

    + AutoMove()) ) { ?> checked="checked" onclick="updateButtons(this);if(this.checked){$j(this.form.elements['filter[AutoMoveTo]']).css('display','inline');}else{this.form.elements['filter[AutoMoveTo]'].hide();};"/> + AutoMoveTo(), $filter->AutoMove() ? null : array('style'=>'display:none;' ) ); ?>

    From 203fe793916b16c104a87a456d7df7e3d7597ed7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 4 Dec 2017 11:14:55 -0500 Subject: [PATCH 29/34] cleanup debug --- scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in index 8dd016c89..7a86b51a8 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in @@ -87,9 +87,6 @@ BEGIN { print( STDERR "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on ".ZM_CONFIG_SUBDIR.".\n" ); } } - foreach my $k ( keys %Config ) { - print "Initial config $k => $Config{$k}\n"; - } use DBI; my $socket; @@ -128,7 +125,6 @@ BEGIN { #$dbh->disconnect(); # if ( ! $Config{ZM_SERVER_ID} ) { - print "Loading ServerId $Config{ZM_SERVER_NAME} HOST:$Config{ZM_SERVER_HOST}\n"; $Config{ZM_SERVER_ID} = undef; $sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' ); if ( $Config{ZM_SERVER_NAME} ) { @@ -141,8 +137,6 @@ BEGIN { $Config{ZM_SERVER_ID} = $$result{Id}; } $sth->finish(); - } else { - print "Have ZM_SERVER_ID $Config{ZM_SERVER_ID}\n"; } # This subroutine must be inside the BEGIN block From 6270408c8fd26281ebf59b7f192274815de1971a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 4 Dec 2017 15:52:16 -0500 Subject: [PATCH 30/34] rework group MonitorIds and add GroupId filters to api --- db/zm_create.sql.in | 16 +- db/zm_update-1.31.16.sql | 67 +++ web/api/app/Controller/EventsController.php | 524 +++++++++--------- web/api/app/Controller/GroupsController.php | 108 ++++ web/api/app/Controller/MonitorsController.php | 46 +- web/api/app/Model/Event.php | 24 + web/api/app/Model/Group.php | 68 +++ web/api/app/Model/Monitor.php | 24 + web/api/app/View/Servers/xml/index.ctp | 2 +- web/api/app/View/Servers/xml/view.ctp | 2 +- web/includes/Group.php | 24 +- web/includes/actions.php | 10 +- web/includes/config.php.in | 2 +- web/includes/functions.php | 5 +- web/skins/classic/views/cycle.php | 67 +-- web/skins/classic/views/group.php | 2 +- 16 files changed, 645 insertions(+), 346 deletions(-) create mode 100644 web/api/app/Controller/GroupsController.php create mode 100644 web/api/app/Model/Group.php diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 01c1ad118..eb2e6a9a4 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -270,10 +270,24 @@ CREATE TABLE `Groups` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', `ParentId` int(10) unsigned, - `MonitorIds` text NOT NULL, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; +-- +--Table structure for table `Groups_Monitors` +-- + +DROP TABLE IF EXISTS `Groups_Monitors` +CREATE TABLE `Groups_Monitors` ( + `Id` INT(10) unsigned NOT NULL auto_increment, + `GroupId` int(10) unsigned NOT NULL, + `MonitorId` int(10) unsigned NOT NULL, + PRIMARY KEY (`Id`) +) ENGINE=@ZM_MYSQL_ENGINE@; + +CREATE INDEX `Groups_Monitors_GroupId_idx` ON `Groups` (`GroupId`); +CREATE INDEX `Groups_Monitors_MonitorId_idx` ON `Groups` (`MonitorId`); + -- -- Table structure for table `Logs` -- diff --git a/db/zm_update-1.31.16.sql b/db/zm_update-1.31.16.sql index de323ee51..3d85fb60d 100644 --- a/db/zm_update-1.31.16.sql +++ b/db/zm_update-1.31.16.sql @@ -26,3 +26,70 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.TABLES + WHERE table_name = 'Groups_Monitors' + AND table_schema = DATABASE() + ) > 0, + "SELECT 'Groups_Monitors table exists'", + "CREATE TABLE `Groups_Monitors` ( + `Id` INT(10) unsigned NOT NULL auto_increment, + `GroupId` int(10) unsigned NOT NULL, + `MonitorId` int(10) unsigned NOT NULL, + PRIMARY KEY (`Id`) + )" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Groups_Monitors' + AND table_schema = DATABASE() + AND index_name = 'Groups_Monitors_GroupId_idx' + ) > 0, + "SELECT 'Groups_Monitors_GroupId_idx already exists on Groups table'", + "CREATE INDEX `Groups_Monitors_GroupId_idx` ON `Groups_Monitors` (`GroupId`)" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Groups_Monitors' + AND table_schema = DATABASE() + AND index_name = 'Groups_Monitors_MonitorId_idx' + ) > 0, + "SELECT 'Groups_Monitors_MonitorId_idx already exists on Groups table'", + "CREATE INDEX `Groups_Monitors_MonitorId_idx` ON `Groups_Monitors` (`MonitorId`)" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Groups' + AND column_name = 'MonitorIds' + ) > 0, + "REPLACE INTO Groups_Monitors (GroupId,MonitorId) SELECT Id,SUBSTRING_INDEX(SUBSTRING_INDEX(t.MonitorIds, ',', n.n), ',', -1) value FROM Groups t CROSS JOIN ( SELECT a.N + b.N * 10 + 1 n FROM (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b ORDER BY n ) n WHERE n.n <= 1 + (LENGTH(t.MonitorIds) - LENGTH(REPLACE(t.MonitorIds, ',', ''))) ORDER BY value;", + "SELECT 'MonitorIds has already been removed.'" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Groups' + AND column_name = 'MonitorIds' + ) > 0, +"ALTER TABLE Groups DROP MonitorIds", +"SELECT 'MonitorIds has already been removed.'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index b5a17d461..10cf80099 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -14,16 +14,14 @@ class EventsController extends AppController { */ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator'); -public function beforeFilter() { - parent::beforeFilter(); - $canView = $this->Session->Read('eventPermission'); - if ($canView =='None') - { - throw new UnauthorizedException(__('Insufficient Privileges')); - return; - } - -} + public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('eventPermission'); + if ($canView =='None') { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + } /** * index method @@ -48,14 +46,7 @@ public function beforeFilter() { } else { $conditions = array(); } - - // How many events to return - $this->loadModel('Config'); - $limit = $this->Config->find('list', array( - 'conditions' => array('Name' => 'ZM_WEB_EVENTS_PER_PAGE'), - 'fields' => array('Name', 'Value') - )); - $this->Paginator->settings = array( + $settings = array( // https://github.com/ZoneMinder/ZoneMinder/issues/995 // 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'], // 25 events per page which is what the above @@ -65,11 +56,31 @@ public function beforeFilter() { // make a nice ZM_API_ITEMS_PER_PAGE for all pagination // API - 'limit' => '100', - 'order' => array('StartTime', 'MaxScore'), + 'limit' => ( isset($this->request->params['limit']) ? $this->request->params['limit'] : '100' ), + 'order' => ( isset($this->request->params['order']) ? $this->request->params['order'] : array('StartTime', 'MaxScore') ), 'paramType' => 'querystring', - 'conditions' => array (array($conditions, $mon_options)) - ); + ); + //if ( $this->request->params['GroupId'] ) { + $settings['joins'] = array( + array( + 'table' => 'Groups_Monitors', + 'type' => 'inner', + 'conditions' => array( + 'Groups_Monitors.MonitorId = Event.MonitorId' + ), + ), + ); + $settings['contain'] = array('Group'); + //} + $settings['conditions'] = array($conditions, $mon_options); + + // How many events to return + $this->loadModel('Config'); + $limit = $this->Config->find('list', array( + 'conditions' => array('Name' => 'ZM_WEB_EVENTS_PER_PAGE'), + 'fields' => array('Name', 'Value') + )); + $this->Paginator->settings = $settings; $events = $this->Paginator->paginate('Event'); // For each event, get its thumbnail data (path, width, height) @@ -89,282 +100,281 @@ public function beforeFilter() { * @param string $id * @return void */ - public function view($id = null) -{ - $this->loadModel('Config'); - $configs = $this->Config->find('list', array( - 'fields' => array('Name', 'Value'), - 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) - )); + public function view($id = null) { + $this->loadModel('Config'); + $configs = $this->Config->find('list', array( + 'fields' => array('Name', 'Value'), + 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) + )); - $this->Event->recursive = 1; - if (!$this->Event->exists($id)) { - throw new NotFoundException(__('Invalid event')); - } + $this->Event->recursive = 1; + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } - $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); - if (!empty($allowedMonitors)) - { - $mon_options = array('Event.MonitorId' => $allowedMonitors); - } - else - { - $mon_options=''; - } - - $options = array('conditions' => array(array('Event.' . $this->Event->primaryKey => $id), $mon_options)); - $event = $this->Event->find('first', $options); + if (!empty($allowedMonitors)) + { + $mon_options = array('Event.MonitorId' => $allowedMonitors); + } + else + { + $mon_options=''; + } - $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/'; - $event['Event']['BasePath'] = $path; + $options = array('conditions' => array(array('Event.' . $this->Event->primaryKey => $id), $mon_options)); + $event = $this->Event->find('first', $options); - # Get the previous and next events for any monitor - $this->Event->id = $id; - $event_neighbors = $this->Event->find('neighbors'); - $event['Event']['Next'] = $event_neighbors['next']['Event']['Id']; - $event['Event']['Prev'] = $event_neighbors['prev']['Event']['Id']; + $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/'; + $event['Event']['BasePath'] = $path; - # Also get the previous and next events for the same monitor - $event_monitor_neighbors = $this->Event->find('neighbors', array( - 'conditions'=>array('Event.MonitorId'=>$event['Event']['MonitorId']) - )); - $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; - $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + # Get the previous and next events for any monitor + $this->Event->id = $id; + $event_neighbors = $this->Event->find('neighbors'); + $event['Event']['Next'] = $event_neighbors['next']['Event']['Id']; + $event['Event']['Prev'] = $event_neighbors['prev']['Event']['Id']; - $this->set(array( - 'event' => $event, - '_serialize' => array('event') - )); - } + # Also get the previous and next events for the same monitor + $event_monitor_neighbors = $this->Event->find('neighbors', array( + 'conditions'=>array('Event.MonitorId'=>$event['Event']['MonitorId']) + )); + $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; + $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + + $this->set(array( + 'event' => $event, + '_serialize' => array('event') + )); + } -/** - * add method - * - * @return void - */ - public function add() { + /** + * add method + * + * @return void + */ + public function add() { - if ($this->Session->Read('eventPermission') != 'Edit') - { - throw new UnauthorizedException(__('Insufficient privileges')); - return; - } + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } - if ($this->request->is('post')) { - $this->Event->create(); - if ($this->Event->save($this->request->data)) { - return $this->flash(__('The event has been saved.'), array('action' => 'index')); - } - } - $monitors = $this->Event->Monitor->find('list'); - $this->set(compact('monitors')); - } + if ($this->request->is('post')) { + $this->Event->create(); + if ($this->Event->save($this->request->data)) { + return $this->flash(__('The event has been saved.'), array('action' => 'index')); + } + } + $monitors = $this->Event->Monitor->find('list'); + $this->set(compact('monitors')); + } -/** - * edit method - * - * @throws NotFoundException - * @param string $id - * @return void - */ - public function edit($id = null) { + /** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { - if ($this->Session->Read('eventPermission') != 'Edit') - { - throw new UnauthorizedException(__('Insufficient privileges')); - return; - } + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } - $this->Event->id = $id; + $this->Event->id = $id; - if (!$this->Event->exists($id)) { - throw new NotFoundException(__('Invalid event')); - } + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } - if ($this->Event->save($this->request->data)) { - $message = 'Saved'; - } else { - $message = 'Error'; - } + if ($this->Event->save($this->request->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } - $this->set(array( - 'message' => $message, - '_serialize' => array('message') - )); - } + $this->set(array( + 'message' => $message, + '_serialize' => array('message') + )); + } -/** - * delete method - * - * @throws NotFoundException - * @param string $id - * @return void - */ - public function delete($id = null) { - if ($this->Session->Read('eventPermission') != 'Edit') - { - throw new UnauthorizedException(__('Insufficient privileges')); - return; - } - $this->Event->id = $id; - if (!$this->Event->exists()) { - throw new NotFoundException(__('Invalid event')); - } - $this->request->allowMethod('post', 'delete'); - if ($this->Event->delete()) { - //$this->loadModel('Frame'); - //$this->Event->Frame->delete(); - return $this->flash(__('The event has been deleted.'), array('action' => 'index')); - } else { - return $this->flash(__('The event could not be deleted. Please, try again.'), array('action' => 'index')); - } - } + /** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + $this->Event->id = $id; + if (!$this->Event->exists()) { + throw new NotFoundException(__('Invalid event')); + } + $this->request->allowMethod('post', 'delete'); + if ($this->Event->delete()) { + //$this->loadModel('Frame'); + //$this->Event->Frame->delete(); + return $this->flash(__('The event has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The event could not be deleted. Please, try again.'), array('action' => 'index')); + } + } - public function search() { - $this->Event->recursive = -1; - $conditions = array(); + public function search() { + $this->Event->recursive = -1; + $conditions = array(); - foreach ($this->params['named'] as $param_name => $value) { - // Transform params into mysql - if (preg_match("/interval/i", $value, $matches)) { - $condition = array("$param_name >= (date_sub(now(), $value))"); - } else { - $condition = array($param_name => $value); - } - array_push($conditions, $condition); - } + foreach ($this->params['named'] as $param_name => $value) { + // Transform params into mysql + if (preg_match("/interval/i", $value, $matches)) { + $condition = array("$param_name >= (date_sub(now(), $value))"); + } else { + $condition = array($param_name => $value); + } + array_push($conditions, $condition); + } - $results = $this->Event->find('all', array( - 'conditions' => $conditions - )); + $results = $this->Event->find('all', array( + 'conditions' => $conditions + )); - $this->set(array( - 'results' => $results, - '_serialize' => array('results') - )); + $this->set(array( + 'results' => $results, + '_serialize' => array('results') + )); - - } - // format expected: - // you can changed AlarmFrames to any other named params - // consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json + } - public function consoleEvents($interval = null) { - $this->Event->recursive = -1; - $results = array(); + // format expected: + // you can changed AlarmFrames to any other named params + // consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json - $moreconditions =""; - foreach ($this->request->params['named'] as $name => $param) { - $moreconditions = $moreconditions . " AND ".$name.$param; - } - - $query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;"); + public function consoleEvents($interval = null) { + $this->Event->recursive = -1; + $results = array(); - foreach ($query as $result) { - $results[$result['Events']['MonitorId']] = $result[0]['Count']; - } + $moreconditions =""; + foreach ($this->request->params['named'] as $name => $param) { + $moreconditions = $moreconditions . " AND ".$name.$param; + } - $this->set(array( - 'results' => $results, - '_serialize' => array('results') - )); - } + $query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;"); - // Create a thumbnail and return the thumbnail's data for a given event id. - public function createThumbnail($id = null) { - $this->Event->recursive = -1; + foreach ($query as $result) { + $results[$result['Events']['MonitorId']] = $result[0]['Count']; + } - if (!$this->Event->exists($id)) { - throw new NotFoundException(__('Invalid event')); - } + $this->set(array( + 'results' => $results, + '_serialize' => array('results') + )); + } - $event = $this->Event->find('first', array( - 'conditions' => array('Id' => $id) - )); + // Create a thumbnail and return the thumbnail's data for a given event id. + public function createThumbnail($id = null) { + $this->Event->recursive = -1; - // Find the max Frame for this Event. Error out otherwise. - $this->loadModel('Frame'); - if (! $frame = $this->Frame->find('first', array( - 'conditions' => array( - 'EventId' => $event['Event']['Id'], - 'Score' => $event['Event']['MaxScore'] - ) - ))) { - throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id'])); - } + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } - $this->loadModel('Config'); + $event = $this->Event->find('first', array( + 'conditions' => array('Id' => $id) + )); - // Get the config options required for reScale and getImageSrc - // The $bw, $thumbs and unset() code is a workaround / temporary - // until I have a better way of handing per-bandwidth config options - $bw = (isset($_COOKIE['zmBandwidth']) ? strtoupper(substr($_COOKIE['zmBandwidth'], 0, 1)) : 'L'); - $thumbs = "ZM_WEB_${bw}_SCALE_THUMBS"; + // Find the max Frame for this Event. Error out otherwise. + $this->loadModel('Frame'); + if (! $frame = $this->Frame->find('first', array( + 'conditions' => array( + 'EventId' => $event['Event']['Id'], + 'Score' => $event['Event']['MaxScore'] + ) + ))) { + throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id'])); + } - $config = $this->Config->find('list', array( - 'conditions' => array('OR' => array( - 'Name' => array('ZM_WEB_LIST_THUMB_WIDTH', - 'ZM_WEB_LIST_THUMB_HEIGHT', - 'ZM_EVENT_IMAGE_DIGITS', - 'ZM_DIR_IMAGES', - "$thumbs", - 'ZM_DIR_EVENTS' - ) - )), - 'fields' => array('Name', 'Value') - )); - $config['ZM_WEB_SCALE_THUMBS'] = $config[$thumbs]; - unset($config[$thumbs]); + $this->loadModel('Config'); - // reScale based on either the width, or the hight, of the event. - if ( $config['ZM_WEB_LIST_THUMB_WIDTH'] ) { - $thumbWidth = $config['ZM_WEB_LIST_THUMB_WIDTH']; - $scale = (100 * $thumbWidth) / $event['Event']['Width']; - $thumbHeight = $this->Scaler->reScale( $event['Event']['Height'], $scale ); - } - elseif ( $config['ZM_WEB_LIST_THUMB_HEIGHT'] ) { - $thumbHeight = $config['ZM_WEB_LIST_THUMB_HEIGHT']; - $scale = (100*$thumbHeight)/$event['Event']['Height']; - $thumbWidth = $this->Scaler->reScale( $event['Event']['Width'], $scale ); - } - else { - throw new NotFoundException(__('No thumbnail width or height specified, please check in Options->Web')); - } + // Get the config options required for reScale and getImageSrc + // The $bw, $thumbs and unset() code is a workaround / temporary + // until I have a better way of handing per-bandwidth config options + $bw = (isset($_COOKIE['zmBandwidth']) ? strtoupper(substr($_COOKIE['zmBandwidth'], 0, 1)) : 'L'); + $thumbs = "ZM_WEB_${bw}_SCALE_THUMBS"; - $imageData = $this->Image->getImageSrc( $event, $frame, $scale, $config ); - $thumbData['Path'] = $imageData['thumbPath']; - $thumbData['Width'] = (int)$thumbWidth; - $thumbData['Height'] = (int)$thumbHeight; - - return( $thumbData ); + $config = $this->Config->find('list', array( + 'conditions' => array('OR' => array( + 'Name' => array('ZM_WEB_LIST_THUMB_WIDTH', + 'ZM_WEB_LIST_THUMB_HEIGHT', + 'ZM_EVENT_IMAGE_DIGITS', + 'ZM_DIR_IMAGES', + "$thumbs", + 'ZM_DIR_EVENTS' + ) + )), + 'fields' => array('Name', 'Value') + )); + $config['ZM_WEB_SCALE_THUMBS'] = $config[$thumbs]; + unset($config[$thumbs]); - } + // reScale based on either the width, or the hight, of the event. + if ( $config['ZM_WEB_LIST_THUMB_WIDTH'] ) { + $thumbWidth = $config['ZM_WEB_LIST_THUMB_WIDTH']; + $scale = (100 * $thumbWidth) / $event['Event']['Width']; + $thumbHeight = $this->Scaler->reScale( $event['Event']['Height'], $scale ); + } + elseif ( $config['ZM_WEB_LIST_THUMB_HEIGHT'] ) { + $thumbHeight = $config['ZM_WEB_LIST_THUMB_HEIGHT']; + $scale = (100*$thumbHeight)/$event['Event']['Height']; + $thumbWidth = $this->Scaler->reScale( $event['Event']['Width'], $scale ); + } + else { + throw new NotFoundException(__('No thumbnail width or height specified, please check in Options->Web')); + } - public function archive($id = null) { - $this->Event->recursive = -1; - if (!$this->Event->exists($id)) { - throw new NotFoundException(__('Invalid event')); - } + $imageData = $this->Image->getImageSrc( $event, $frame, $scale, $config ); + $thumbData['Path'] = $imageData['thumbPath']; + $thumbData['Width'] = (int)$thumbWidth; + $thumbData['Height'] = (int)$thumbHeight; - // Get the current value of Archive - $archived = $this->Event->find('first', array( - 'fields' => array('Event.Archived'), - 'conditions' => array('Event.Id' => $id) - )); - // If 0, 1, if 1, 0 - $archiveVal = (($archived['Event']['Archived'] == 0) ? 1 : 0); + return( $thumbData ); - // Save the new value - $this->Event->id = $id; - $this->Event->saveField('Archived', $archiveVal); + } - $this->set(array( - 'archived' => $archiveVal, - '_serialize' => array('archived') - )); - } + public function archive($id = null) { + $this->Event->recursive = -1; + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } + + // Get the current value of Archive + $archived = $this->Event->find('first', array( + 'fields' => array('Event.Archived'), + 'conditions' => array('Event.Id' => $id) + )); + // If 0, 1, if 1, 0 + $archiveVal = (($archived['Event']['Archived'] == 0) ? 1 : 0); + + // Save the new value + $this->Event->id = $id; + $this->Event->saveField('Archived', $archiveVal); + + $this->set(array( + 'archived' => $archiveVal, + '_serialize' => array('archived') + )); + } } diff --git a/web/api/app/Controller/GroupsController.php b/web/api/app/Controller/GroupsController.php new file mode 100644 index 000000000..525d500bf --- /dev/null +++ b/web/api/app/Controller/GroupsController.php @@ -0,0 +1,108 @@ +Group->recursive = -1; + $groups = $this->Group->find('all'); + $this->set(array( + 'groups' => $groups, + '_serialize' => array('groups') + )); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + $this->Group->recursive = -1; + if (!$this->Group->exists($id)) { + throw new NotFoundException(__('Invalid group')); + } + $options = array('conditions' => array('Group.' . $this->Group->primaryKey => $id)); + $group = $this->Group->find('first', $options); + $this->set(array( + 'group' => $group, + '_serialize' => array('group') + )); + } + +/** + * add method + * + * @return void + */ + public function add() { + if ($this->request->is('post')) { + $this->Group->create(); + if ($this->Group->save($this->request->data)) { + return $this->flash(__('The group has been saved.'), array('action' => 'index')); + } + } + $monitors = $this->Group->Monitor->find('list'); + $this->set(compact('monitors')); + } + +/** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { + if (!$this->Group->exists($id)) { + throw new NotFoundException(__('Invalid group')); + } + if ($this->request->is(array('post', 'put'))) { + if ($this->Group->save($this->request->data)) { + return $this->flash(__('The group has been saved.'), array('action' => 'index')); + } + } else { + $options = array('conditions' => array('Group.' . $this->Group->primaryKey => $id)); + $this->request->data = $this->Group->find('first', $options); + } + $monitors = $this->Group->Monitor->find('list'); + $this->set(compact('monitors')); + } + +/** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + $this->Group->id = $id; + if (!$this->Group->exists()) { + throw new NotFoundException(__('Invalid group')); + } + $this->request->allowMethod('post', 'delete'); + if ($this->Group->delete()) { + return $this->flash(__('The group has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The group could not be deleted. Please, try again.'), array('action' => 'index')); + } + }} diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 9ad487917..a49b022dc 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -8,7 +8,6 @@ App::uses('AppController', 'Controller'); */ class MonitorsController extends AppController { - /** * Components * @@ -16,18 +15,14 @@ class MonitorsController extends AppController { */ public $components = array('Paginator', 'RequestHandler'); - -public function beforeFilter() { - parent::beforeFilter(); - $canView = $this->Session->Read('monitorPermission'); - if ($canView =='None') - { - throw new UnauthorizedException(__('Insufficient Privileges')); - return; - } - -} - + public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('monitorPermission'); + if ($canView =='None') { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + } /** * index method @@ -36,7 +31,7 @@ public function beforeFilter() { */ public function index() { $this->Monitor->recursive = 0; - + if ($this->request->params['named']) { $this->FilterComponent = $this->Components->load('Filter'); $conditions = $this->FilterComponent->buildFilter($this->request->params['named']); @@ -48,7 +43,28 @@ public function beforeFilter() { if (!empty($allowedMonitors)) { $conditions['Monitor.Id' ] = $allowedMonitors; } - $monitors = $this->Monitor->find('all',array('conditions'=>$conditions)); + $find_array = array('conditions'=>$conditions,'contain'=>array('Group')); + + //if ( $this->request->params['GroupId'] ) { + $find_array['joins'] = array( + array( + 'table' => 'Groups_Monitors', + 'type' => 'inner', + 'conditions' => array( + 'Groups_Monitors.MonitorId = Monitor.Id' + ), + ), + //array( + //'table' => 'Groups', + //'type' => 'inner', + //'conditions' => array( + //'Groups.Id = Groups_Monitors.GroupId', + //'Groups.Id' => $this->request->params['GroupId'], + //), + //) + ); + //} + $monitors = $this->Monitor->find('all',$find_array); $this->set(array( 'monitors' => $monitors, '_serialize' => array('monitors') diff --git a/web/api/app/Model/Event.php b/web/api/app/Model/Event.php index 8bf6be518..3ce1b93bb 100644 --- a/web/api/app/Model/Event.php +++ b/web/api/app/Model/Event.php @@ -68,4 +68,28 @@ class Event extends AppModel { ) ); + /** + * * * hasMany associations + * * * + * * * @var array + * * */ + public $hasAndBelongsToMany = array( + 'Group' => array( + 'className' => 'Group', + 'joinTable' => 'Groups_Monitors', + 'foreignKey' => 'MonitorId', + 'associationForeignKey' => 'GroupId', + 'unique' => true, + 'dependent' => false, + 'conditions' => 'Groups_Monitors.MonitorId=Event.MonitorId', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ), + ); + } diff --git a/web/api/app/Model/Group.php b/web/api/app/Model/Group.php new file mode 100644 index 000000000..595bc3b3a --- /dev/null +++ b/web/api/app/Model/Group.php @@ -0,0 +1,68 @@ + array( + 'notEmpty' => array( + 'rule' => array('notEmpty'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + public $recursive = -1; + //The Associations below have been created with all possible keys, those that are not needed can be removed + +/** + * hasMany associations + * + * @var array + */ + public $hasMany = array( + 'Monitor' => array( + 'className' => 'Monitor', + 'joinTable' => 'Groups_Monitors', + 'foreignKey' => 'GroupId', + 'associationForeignKey' => 'MonitorId', + 'unique'=>true, + 'dependent' => false, + 'conditions' => '', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ), + ); +} diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index 775be74c1..0504dd22a 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -85,4 +85,28 @@ class Monitor extends AppModel { ) ); + /** + * * hasMany associations + * * + * * @var array + * */ + public $hasAndBelongsToMany = array( + 'Group' => array( + 'className' => 'Group', + 'joinTable' => 'Groups_Monitors', + 'foreignKey' => 'MonitorId', + 'associationForeignKey' => 'GroupId', + 'unique' => true, + 'dependent' => false, + 'conditions' => '', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ), + ); + } diff --git a/web/api/app/View/Servers/xml/index.ctp b/web/api/app/View/Servers/xml/index.ctp index 37afc918b..9bb514fff 100644 --- a/web/api/app/View/Servers/xml/index.ctp +++ b/web/api/app/View/Servers/xml/index.ctp @@ -1,2 +1,2 @@ -$xml = Xml::fromArray(array('response' => $monitors)); +$xml = Xml::fromArray(array('response' => $servers)); echo $xml->asXML(); diff --git a/web/api/app/View/Servers/xml/view.ctp b/web/api/app/View/Servers/xml/view.ctp index b33c6e79a..3b2a3fdad 100644 --- a/web/api/app/View/Servers/xml/view.ctp +++ b/web/api/app/View/Servers/xml/view.ctp @@ -1,2 +1,2 @@ -$xml = Xml::fromArray(array('response' => $monitor)); +$xml = Xml::fromArray(array('response' => $server)); echo $xml->asXML(); diff --git a/web/includes/Group.php b/web/includes/Group.php index 7db8486d2..ca3557076 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -6,7 +6,6 @@ public $defaults = array( 'Id' => null, 'Name' => '', 'ParentId' => null, - 'MonitorIds' => '', ); public function __construct( $IdOrRow=NULL ) { @@ -88,7 +87,8 @@ public $defaults = array( public function delete() { if ( array_key_exists( 'Id', $this ) ) { - dbQuery( 'DELETE FROM Groups WHERE Id = ?', array($this->{'Id'}) ); + dbQuery( 'DELETE FROM Groups WHERE Id=?', array($this->{'Id'}) ); + dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}) ); if ( isset($_COOKIE['zmGroup']) ) { if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) { unset( $_COOKIE['zmGroup'] ); @@ -128,6 +128,13 @@ public $defaults = array( return $this->{'depth'}; } // end public function depth + public function MonitorIds( ) { + if ( ! array_key_exists( 'MonitorIds', $this ) ) { + $this->{'MonitorIds'} = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}) ); + } + return $this->{'MonitorIds'}; + } + public static function get_group_dropdowns() { # This will end up with the group_id of the deepest selection $group_id = 0; @@ -164,19 +171,14 @@ public $defaults = array( return $group_id; } # end public static function get_group_dropdowns() + public static function get_group_sql( $group_id ) { $groupSql = ''; if ( $group_id ) { - if ( $group = dbFetchOne( 'SELECT MonitorIds FROM Groups WHERE Id=?', NULL, array($group_id) ) ) { - $groupIds = array(); - if ( $group['MonitorIds'] ) - $groupIds = explode( ',', $group['MonitorIds'] ); + $MonitorIds = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($group_id) ); - foreach ( dbFetchAll( 'SELECT MonitorIds FROM Groups WHERE ParentId = ?', NULL, array($group_id) ) as $group ) - if ( $group['MonitorIds'] ) - $groupIds = array_merge( $groupIds, explode( ',', $group['MonitorIds'] ) ); - } - $groupSql = " find_in_set( Id, '".implode( ',', $groupIds )."' )"; + $MonitorIds = array_merge( $MonitorIds, dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId = ?)', 'MonitorId', array($group_id) ) ); + $groupSql = " find_in_set( Id, '".implode( ',', $MonitorIds )."' )"; } return $groupSql; } # end public static function get_group_sql( $group_id ) diff --git a/web/includes/actions.php b/web/includes/actions.php index 3d6f898c8..91c5cacf9 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -546,7 +546,7 @@ if ( canEdit( 'Monitors' ) ) { $saferName = basename($_REQUEST['newMonitor']['Name']); symlink( $mid, ZM_DIR_EVENTS.'/'.$saferName ); if ( isset($_COOKIE['zmGroup']) ) { - dbQuery( "UPDATE Groups SET MonitorIds = concat(MonitorIds,',".$mid."') WHERE Id=?", array($_COOKIE['zmGroup']) ); + dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_COOKIE['zmGroup'],$mid) ); } } else { Error("Users with Monitors restrictions cannot create new monitors."); @@ -666,12 +666,16 @@ if ( canEdit( 'Groups' ) ) { if ( $action == 'group' ) { $monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); if ( !empty($_POST['gid']) ) { - dbQuery( 'UPDATE Groups SET Name=?, ParentId=?, MonitorIds=? WHERE Id=?', - array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $monitors, $_POST['gid']) ); + dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', + array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $_POST['gid']) ); + dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($_POST['gid']) ); } else { dbQuery( 'INSERT INTO Groups SET Name=?, ParentId=?, MonitorIds=?', array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $monitors ) ); } + foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) { + dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_POST['gid'], $mid) ); + } $view = 'none'; $refreshParent = true; } else if ( $action == 'delete' ) { diff --git a/web/includes/config.php.in b/web/includes/config.php.in index 2405b0114..3b6209803 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -179,7 +179,7 @@ function loadConfig( $defineConsts=true ) { } require_once( 'logger.php' ); -// For Human-readability, user ZM_SERVER in zm.conf, and convert it here to a ZM_SERVER_ID +// For Human-readability, use ZM_SERVER_HOST or ZM_SERVER_NAME in zm.conf, and convert it here to a ZM_SERVER_ID if ( ! defined('ZM_SERVER_ID') ) { if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) { $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_NAME)); diff --git a/web/includes/functions.php b/web/includes/functions.php index e2a93849b..9f58232ee 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1872,7 +1872,10 @@ function monitorIdsToNames( $ids ) { } } $names = array(); - foreach ( preg_split( '/\s*,\s*/', $ids ) as $id ) { + if ( ! is_array($ids) ) { + $ids = preg_split( '/\s*,\s*/', $ids ); + } + foreach ( $ids as $id ) { if ( visibleMonitor( $id ) ) { if ( isset($mITN_monitors[$id]) ) { $names[] = $mITN_monitors[$id]['Name']; diff --git a/web/skins/classic/views/cycle.php b/web/skins/classic/views/cycle.php index bde0793fe..be5bf154c 100644 --- a/web/skins/classic/views/cycle.php +++ b/web/skins/classic/views/cycle.php @@ -31,52 +31,26 @@ if ( empty($_REQUEST['mode']) ) { } else { $mode = validHtmlStr($_REQUEST['mode']); } +ob_start(); +include('_monitor_filters.php'); +$filterbar = ob_get_contents(); +ob_end_clean(); -$group_id = 0; -if ( isset($_REQUEST['group']) ) { - $group_id = $_REQUEST['group']; -} else if ( isset($_COOKIE['zmGroup'] ) ) { - $group_id = $_COOKIE['zmGroup']; -} - -$subgroup_id = 0; -if ( isset($_REQUEST['subgroup']) ) { - $subgroup_id = $_REQUEST['subgroup']; -} else if ( isset($_COOKIE['zmSubGroup'] ) ) { - $subgroup_id = $_COOKIE['zmSubGroup']; -} -$groupIds = null; -if ( $group_id ) { -$groupIds = array(); - if ( $group = dbFetchOne( 'SELECT MonitorIds FROM Groups WHERE Id = ?', NULL, array($group_id) ) ) - if ( $group['MonitorIds'] ) - $groupIds = explode( ',', $group['MonitorIds'] ); - if ( $subgroup_id ) { - if ( $group = dbFetchOne( 'SELECT MonitorIds FROM Groups WHERE Id = ?', NULL, array($subgroup_id) ) ) - if ( $group['MonitorIds'] ) - $groupIds = array_merge( $groupIds, explode( ',', $group['MonitorIds'] ) ); - } else { - foreach ( dbFetchAll( 'SELECT MonitorIds FROM Groups WHERE ParentId = ?', NULL, array($group_id) ) as $group ) - if ( $group['MonitorIds'] ) - $groupIds = array_merge( $groupIds, explode( ',', $group['MonitorIds'] ) ); - } -} -$groupSql = ''; -if ( $groupIds ) - $groupSql = " and find_in_set( Id, '".implode( ',', $groupIds )."' )"; - -$sql = "SELECT * FROM Monitors WHERE Function != 'None'$groupSql ORDER BY Sequence"; -$monitors = array(); $monIdx = 0; -foreach( dbFetchAll( $sql ) as $row ) { - if ( !visibleMonitor( $row['Id'] ) ) +$monitors = array(); +foreach( $displayMonitors as &$row ) { + if ( $row['Function'] == 'None' ) continue; if ( isset($_REQUEST['mid']) && $row['Id'] == $_REQUEST['mid'] ) $monIdx = count($monitors); + $row['ScaledWidth'] = reScale( $row['Width'], $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); $row['ScaledHeight'] = reScale( $row['Height'], $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); + $row['PopupScale'] = reScale( SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); + + $row['connKey'] = generateConnKey(); $monitors[] = new Monitor( $row ); -} +} # end foreach Monitor if ( $monitors ) { $monitor = $monitors[$monIdx]; @@ -103,22 +77,7 @@ xhtmlHeaders(__FILE__, translate('CycleWatch') );

    - -'All'); - foreach ( Group::find_all( array('ParentId'=>null) ) as $Group ) { - $groups[$Group->Id()] = $Group->Name(); - } - echo htmlSelect( 'group', $groups, $group_id, 'changeGroup(this);' ); - $groups = array(0=>'All'); - if ( $group_id ) { - foreach ( Group::find_all( array('ParentId'=>$group_id) ) as $Group ) { - $groups[$Group->Id()] = $Group->Name(); - } - } - echo htmlSelect( 'subgroup', $groups, $subgroup_id, 'changeSubGroup(this);' ); -?> +
    diff --git a/web/skins/classic/views/group.php b/web/skins/classic/views/group.php index 0e71040c7..a010d75e7 100644 --- a/web/skins/classic/views/group.php +++ b/web/skins/classic/views/group.php @@ -104,7 +104,7 @@ echo htmlSelect( 'newGroup[ParentId]', $options, $newGroup->ParentId(), array('o