Merge branch 'storageareas' into zma_to_thread

Conflicts:
	src/zm_event.cpp
	src/zm_monitor.cpp
This commit is contained in:
Isaac Connor 2018-01-16 13:36:57 -05:00
commit a2211bf438
23 changed files with 480 additions and 65 deletions

View File

@ -209,6 +209,7 @@ CREATE TABLE `Events` (
`StateId` int(10) unsigned NOT NULL, `StateId` int(10) unsigned NOT NULL,
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
PRIMARY KEY (`Id`), PRIMARY KEY (`Id`),
KEY `Events_MonitorId_idx` (`MonitorId`), KEY `Events_MonitorId_idx` (`MonitorId`),
KEY `Events_StorageId_idx` (`StorageId`), KEY `Events_StorageId_idx` (`StorageId`),
@ -267,6 +268,110 @@ CREATE TABLE `Events_Archived` (
KEY `Events_Month_MonitorId_idx` (`MonitorId`) KEY `Events_Month_MonitorId_idx` (`MonitorId`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
drop trigger if exists event_update_trigger;
delimiter //
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = NEW.DiskSpace - OLD.DiskSpace;
IF ( NEW.StorageId = OLD.StorageID ) THEN
IF ( diff ) THEN
call update_storage_stats(OLD.StorageId, diff);
END IF;
ELSE
IF ( NEW.DiskSpace ) THEN
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
END IF;
IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
END IF;
END IF;
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
IF ( NEW.Archived != OLD.Archived ) THEN
IF ( NEW.Archived ) THEN
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
UPDATE Monitors SET ArchivedEvents = ArchivedEvents+1, ArchivedEventDiskSpace = ArchivedEventDiskSpace + NEW.DiskSpace WHERE Id=NEW.MonitorId;
ELSEIF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET ArchivedEvents =ArchivedEvents-1, ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace WHERE Id=OLD.MonitorId;
ELSE
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Monitors SET
ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace + NEW.DiskSpace
WHERE Id=OLD.MonitorId;
END IF;
END IF;
END IF;
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Monitors SET TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace + NEW.DiskSpace WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
DROP TRIGGER IF EXISTS event_insert_trigger;
delimiter //
create trigger event_insert_trigger after insert on Events
for each row
begin
INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
end;
//
delimiter ;
drop trigger if exists event_delete_trigger;
delimiter //
CREATE TRIGGER event_delete_trigger BEFORE DELETe on Events
FOR EACH ROW
BEGIN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
DELETE FROM Events_Day WHERE EventId=OLD.Id;
DELETE FROM Events_Week WHERE EventId=OLD.Id;
DELETE FROM Events_Month WHERE EventId=OLD.Id;
IF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET
ArchivedEvents = ArchivedEvents - 1,
ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace,
TotalEvents = TotalEvents - 1,
TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace
WHERE Id=OLD.MonitorId;
ELSE
UPDATE Monitors SET
TotalEvents = TotalEvents-1,
TotalEventDiskSpace=TotalEventDiskSpace-OLD.DiskSpace
WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
-- --
-- Table structure for table `Filters` -- Table structure for table `Filters`
-- --
@ -686,6 +791,24 @@ CREATE TABLE `Zones` (
KEY `MonitorId` (`MonitorId`) KEY `MonitorId` (`MonitorId`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
DELIMITER //
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
END
//
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
END
//
DELIMITER ;
DROP TABLE IF EXISTS `Storage`; DROP TABLE IF EXISTS `Storage`;
CREATE TABLE `Storage` ( CREATE TABLE `Storage` (
`Id` smallint(5) unsigned NOT NULL auto_increment, `Id` smallint(5) unsigned NOT NULL auto_increment,

150
db/zm_update-1.31.21.sql Normal file
View File

@ -0,0 +1,150 @@
delimiter //
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
END
//
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
END
//
UPDATE Monitors SET ZoneCount=(SELECT COUNT(Id) FROM Zones WHERE MonitorId=Monitors.Id)//
drop trigger if exists event_update_trigger//
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = NEW.DiskSpace - OLD.DiskSpace;
IF ( NEW.StorageId = OLD.StorageID ) THEN
IF ( diff ) THEN
call update_storage_stats(OLD.StorageId, diff);
END IF;
ELSE
IF ( NEW.DiskSpace ) THEN
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
END IF;
IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
END IF;
END IF;
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
IF ( NEW.Archived != OLD.Archived ) THEN
IF ( NEW.Archived ) THEN
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
UPDATE Monitors SET ArchivedEvents = ArchivedEvents+1, ArchivedEventDiskSpace = ArchivedEventDiskSpace + NEW.DiskSpace WHERE Id=NEW.MonitorId;
ELSEIF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET ArchivedEvents =ArchivedEvents-1, ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace WHERE Id=OLD.MonitorId;
ELSE
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Monitors SET
ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace + NEW.DiskSpace
WHERE Id=OLD.MonitorId;
END IF;
END IF;
END IF;
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Monitors SET TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace + NEW.DiskSpace WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
DROP TRIGGER IF EXISTS event_insert_trigger;
delimiter //
create trigger event_insert_trigger after insert on Events
for each row
begin
INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
end;
//
delimiter ;
drop trigger if exists event_delete_trigger;
delimiter //
CREATE TRIGGER event_delete_trigger BEFORE DELETe on Events
FOR EACH ROW
BEGIN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
DELETE FROM Events_Day WHERE EventId=OLD.Id;
DELETE FROM Events_Week WHERE EventId=OLD.Id;
DELETE FROM Events_Month WHERE EventId=OLD.Id;
IF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET
ArchivedEvents = ArchivedEvents - 1,
ArchivedEventDiskSpace = ArchivedEventDiskSpace - OLD.DiskSpace,
TotalEvents = TotalEvents - 1,
TotalEventDiskSpace = TotalEventDiskSpace - OLD.DiskSpace
WHERE Id=OLD.MonitorId;
ELSE
UPDATE Monitors SET
TotalEvents = TotalEvents-1,
TotalEventDiskSpace=TotalEventDiskSpace-OLD.DiskSpace
WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
UPDATE Monitors INNER JOIN (
SELECT MonitorId,
COUNT(Id) AS TotalEvents,
SUM(DiskSpace) AS TotalEventDiskSpace,
SUM(IF(Archived,1,0)) AS ArchivedEvents,
SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
FROM Events GROUP BY MonitorId
) AS E ON E.MonitorId=Monitors.Id SET
Monitors.TotalEvents = E.TotalEvents,
Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
Monitors.ArchivedEvents = E.ArchivedEvents,
Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
Monitors.HourEvents = E.HourEvents,
Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
Monitors.DayEvents = E.DayEvents,
Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
Monitors.WeekEvents = E.WeekEvents,
Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
Monitors.MonthEvents = E.MonthEvents,
Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace;

19
db/zm_update-1.31.22.sql Normal file
View File

@ -0,0 +1,19 @@
delimiter //
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
END
//
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
END
//
UPDATE Monitors SET ZoneCount=(SELECT COUNT(Id) FROM Zones WHERE MonitorId=Monitors.Id)//
UPDATE Storage SET DiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE StorageId=Storage.Id);

View File

@ -359,7 +359,7 @@ sub delete_files {
my $storage_path = $Storage->Path(); my $storage_path = $Storage->Path();
if ( ! $storage_path ) { if ( ! $storage_path ) {
Fatal("Empty storage path when deleting files for event $$event{Id} with storage id $$event{StorageId} "); Error("Empty storage path when deleting files for event $$event{Id} with storage id $$event{StorageId} ");
return; return;
} }

View File

@ -297,7 +297,7 @@ sub run {
while( 1 ) { while( 1 ) {
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
if ( $secs_count % 60 ) { if ( ! ( $secs_count % 60 ) ) {
$dbh = zmDbConnect() if ! $dbh->ping(); $dbh = zmDbConnect() if ! $dbh->ping();
my @cpuload = Sys::CpuLoad::load(); my @cpuload = Sys::CpuLoad::load();
dPrint( ZoneMinder::Logger::INFO, 'Updating Server record' ); dPrint( ZoneMinder::Logger::INFO, 'Updating Server record' );

View File

@ -191,16 +191,23 @@ if ( $command =~ /^(?:start|restart)$/ ) {
zmMemTidy(); zmMemTidy();
runCommand('zmdc.pl startup'); runCommand('zmdc.pl startup');
my $Server = undef;
my $sql;
my @values;
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
require ZoneMinder::Server;
Info("Multi-server configuration detected. Starting up services for server $Config{ZM_SERVER_ID}\n"); Info("Multi-server configuration detected. Starting up services for server $Config{ZM_SERVER_ID}\n");
$Server = new ZoneMinder::Server( $Config{ZM_SERVER_ID} );
$sql = 'SELECT * FROM Monitors WHERE ServerId=?';
@values = ( $Config{ZM_SERVER_ID} );
} else { } else {
Info('Single server configuration detected. Starting up services.'); Info('Single server configuration detected. Starting up services.');
$sql = 'SELECT * FROM Monitors';
} }
my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors';
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () ) my $res = $sth->execute( @values )
or Fatal( "Can't execute: ".$sth->errstr() ); or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() ) { while( my $monitor = $sth->fetchrow_hashref() ) {
if ( $monitor->{Function} ne 'None' ) { if ( $monitor->{Function} ne 'None' ) {
@ -223,10 +230,18 @@ if ( $command =~ /^(?:start|restart)$/ ) {
# This is now started unconditionally # This is now started unconditionally
runCommand('zmdc.pl start zmfilter.pl'); runCommand('zmdc.pl start zmfilter.pl');
if ( $Config{ZM_RUN_AUDIT} ) { if ( $Config{ZM_RUN_AUDIT} ) {
runCommand('zmdc.pl start zmaudit.pl -c'); if ( $Server and exists $$Server{'zmaudit.pl'} and ! $$Server{'zmaudit.pl'} ) {
Debug("Not running zmaudit.pl because it is turned off for this server.");
} else {
runCommand('zmdc.pl start zmaudit.pl -c');
}
} }
if ( $Config{ZM_OPT_TRIGGERS} ) { if ( $Config{ZM_OPT_TRIGGERS} ) {
runCommand('zmdc.pl start zmtrigger.pl'); if ( $Server and exists $$Server{'zmtrigger.pl'} and ! $$Server{'zmtrigger.pl'} ) {
Debug("Not running zmtrigger.pl because it is turned off for this server.");
} else {
runCommand('zmdc.pl start zmtrigger.pl');
}
} }
if ( $Config{ZM_OPT_X10} ) { if ( $Config{ZM_OPT_X10} ) {
runCommand('zmdc.pl start zmx10.pl -c start'); runCommand('zmdc.pl start zmx10.pl -c start');
@ -238,6 +253,11 @@ if ( $command =~ /^(?:start|restart)$/ ) {
if ( $Config{ZM_TELEMETRY_DATA} ) { if ( $Config{ZM_TELEMETRY_DATA} ) {
runCommand('zmdc.pl start zmtelemetry.pl'); runCommand('zmdc.pl start zmtelemetry.pl');
} }
if ( $Server and exists $$Server{'zmstats.pl'} and ! $$Server{'zmstats.pl'} ) {
Debug("Not running zmstats.pl because it is turned off for this server.");
} else {
runCommand('zmdc.pl start zmstats.pl');
}
} else { } else {
$retval = 1; $retval = 1;
} }

View File

@ -164,7 +164,7 @@ while( 1 ) {
$eventcounts_week_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); $eventcounts_week_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() );
$eventcounts_month_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); $eventcounts_month_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() );
sleep( $Config{ZM_STATS_CHECK_INTERVAL} ); sleep( $Config{ZM_STATS_UPDATE_INTERVAL} );
} # end while (1) } # end while (1)
Info( "Stats Daemon exiting\n" ); Info( "Stats Daemon exiting\n" );

View File

@ -399,7 +399,10 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
static char event_file[PATH_MAX]; static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames ); snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames );
if ( ! ( monitor->GetOptSaveJPEGs() & 3 ) ) { if ( monitor->GetOptSaveJPEGs() & 1 ) {
Debug( 1, "Writing pre-capture frame %d", frames );
WriteFrameImage( images[i], *(timestamps[i]), event_file );
} else {
//If this is the first frame, we should add a thumbnail to the event directory //If this is the first frame, we should add a thumbnail to the event directory
// ICON: We are working through the pre-event frames so this snapshot won't // ICON: We are working through the pre-event frames so this snapshot won't
// neccessarily be of the motion. But some events are less than 10 frames, // neccessarily be of the motion. But some events are less than 10 frames,
@ -409,9 +412,8 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
WriteFrameImage( images[i], *(timestamps[i]), snapshot_file.c_str() ); WriteFrameImage( images[i], *(timestamps[i]), snapshot_file.c_str() );
} }
} }
if ( monitor->GetOptSaveJPEGs() & 1 ) { if ( videowriter != NULL ) {
Debug( 1, "Writing pre-capture frame %d", frames ); WriteFrameVideo( images[i], *(timestamps[i]), videowriter );
WriteFrameImage( images[i], *(timestamps[i]), event_file );
} }
struct DeltaTimeval delta_time; struct DeltaTimeval delta_time;
@ -466,20 +468,18 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
static char event_file[PATH_MAX]; static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames ); snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames );
if ( ! ( monitor->GetOptSaveJPEGs() & 3 ) ) {
//if ( monitor->GetOptSaveJPEGs() & 4 ) {
// Only snapshots
//If this is the first frame, we should add a thumbnail to the event directory
if ( frames == 10 || frames == 1 ) {
std::string snapshot_file = std::string(path) + "/snapshot.jpg";
WriteFrameImage( image, timestamp, snapshot_file.c_str() );
}
}
if ( monitor->GetOptSaveJPEGs() & 1 ) { if ( monitor->GetOptSaveJPEGs() & 1 ) {
Debug( 1, "Writing capture frame %d to %s", frames, event_file ); Debug( 1, "Writing capture frame %d to %s", frames, event_file );
if ( ! WriteFrameImage( image, timestamp, event_file ) ) { if ( ! WriteFrameImage( image, timestamp, event_file ) ) {
Error("Failed to write frame image"); Error("Failed to write frame image");
} }
} else {
//If this is the first frame, we should add a thumbnail to the event directory
if ( frames == 1 ) {
char snapshot_file[PATH_MAX];
snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path );
WriteFrameImage( image, timestamp, snapshot_file );
}
} }
struct DeltaTimeval delta_time; struct DeltaTimeval delta_time;

View File

@ -1588,8 +1588,20 @@ void Monitor::Reload() {
function = (Function)atoi(dbrow[index++]); function = (Function)atoi(dbrow[index++]);
enabled = atoi(dbrow[index++]); enabled = atoi(dbrow[index++]);
const char *p_linked_monitors = dbrow[index++]; const char *p_linked_monitors = dbrow[index++];
strncpy( event_prefix, dbrow[index++], sizeof(event_prefix)-1 );
strncpy( label_format, dbrow[index++], sizeof(label_format)-1 ); if ( dbrow[index] ) {
strncpy( event_prefix, dbrow[index++], sizeof(event_prefix)-1 );
} else {
event_prefix[0] = 0;
index++;
}
if ( dbrow[index] ) {
strncpy( label_format, dbrow[index++], sizeof(label_format)-1 );
} else {
label_format[0] = 0;
index++;
}
label_coord = Coord( atoi(dbrow[index]), atoi(dbrow[index+1]) ); index += 2; label_coord = Coord( atoi(dbrow[index]), atoi(dbrow[index+1]) ); index += 2;
label_size = atoi(dbrow[index++]); label_size = atoi(dbrow[index++]);
warmup_count = atoi(dbrow[index++]); warmup_count = atoi(dbrow[index++]);

View File

@ -1 +1 @@
1.31.20 1.31.22

View File

@ -135,6 +135,61 @@ public $defaults = array(
return $this->{'MonitorIds'}; return $this->{'MonitorIds'};
} }
public static function get_group_dropdown() {
session_start();
$selected_group_id = 0;
if ( isset($_REQUEST['groups']) ) {
$selected_group_id = $group_id = $_SESSION['groups'] = $_REQUEST['groups'];
} else if ( isset( $_SESSION['groups'] ) ) {
$selected_group_id = $group_id = $_SESSION['groups'];
} else if ( isset($_REQUEST['filtering']) ) {
unset($_SESSION['groups']);
}
session_write_close();
$Groups = array();
foreach ( Group::find_all( ) as $Group ) {
$Groups[$Group->Id()] = $Group;
}
# This array is indexed by parent_id
global $children;
$children = array();
foreach ( $Groups as $id=>$Group ) {
if ( $Group->ParentId() != null ) {
if ( ! isset( $children[$Group->ParentId()] ) )
$children[$Group->ParentId()] = array();
$children[$Group->ParentId()][] = $Group;
}
}
function get_options( $Group ) {
global $children;
$options = array( $Group->Id() => str_repeat('   ', $Group->depth() ) . $Group->Name() );
if ( isset($children[$Group->Id()]) ) {
foreach ( $children[$Group->Id()] as $child ) {
$options += get_options( $child );
}
}
return $options;
}
$group_options = array();
foreach ( $Groups as $id=>$Group ) {
if ( ! $Group->ParentId() ) {
$group_options += get_options( $Group );
}
}
return htmlSelect( 'Group[]', $group_options, isset($_SESSION['Group'])?$_SESSION['Group']:null, array(
'onchange' => 'this.form.submit();',
'class'=>'chosen',
'multiple'=>'multiple',
'data-placeholder'=>'All',
) );
} # end public static function get_group_dropdown
public static function get_group_dropdowns() { public static function get_group_dropdowns() {
# This will end up with the group_id of the deepest selection # This will end up with the group_id of the deepest selection
$group_id = 0; $group_id = 0;
@ -142,6 +197,8 @@ public $defaults = array(
$groups = array(); $groups = array();
$parent_group_ids = null; $parent_group_ids = null;
session_start(); session_start();
$group_options = array(0=>'All');
while(1) { while(1) {
$Groups = Group::find_all( array('ParentId'=>$parent_group_ids) ); $Groups = Group::find_all( array('ParentId'=>$parent_group_ids) );
if ( ! count( $Groups ) ) if ( ! count( $Groups ) )
@ -161,15 +218,17 @@ public $defaults = array(
if ( ! isset( $groups[$depth] ) ) { if ( ! isset( $groups[$depth] ) ) {
$groups[$depth] = array(0=>'All'); $groups[$depth] = array(0=>'All');
} }
$group_options[$Group->Id()] = str_repeat( ' ', $depth ) . $Group->Name();
$groups[$depth][$Group->Id()] = $Group->Name(); $groups[$depth][$Group->Id()] = $Group->Name();
if ( $selected_group_id and ( $selected_group_id == $Group->Id() ) ) if ( $selected_group_id and ( $selected_group_id == $Group->Id() ) )
$parent_group_ids[] = $Group->Id(); $parent_group_ids[] = $Group->Id();
} }
echo htmlSelect( 'group'.$depth, $groups[$depth], $selected_group_id, "this.form.submit();" ); //echo htmlSelect( 'group'.$depth, $groups[$depth], $selected_group_id, "this.form.submit();" );
if ( ! count($parent_group_ids) ) break; if ( ! count($parent_group_ids) ) break;
$depth += 1; $depth += 1;
} }
echo htmlSelect( 'groups', $group_options, $selected_group_id, 'this.form.submit();' );
session_write_close(); session_write_close();
return $group_id; return $group_id;
@ -179,9 +238,17 @@ public $defaults = array(
public static function get_group_sql( $group_id ) { public static function get_group_sql( $group_id ) {
$groupSql = ''; $groupSql = '';
if ( $group_id ) { if ( $group_id ) {
$MonitorIds = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($group_id) ); if ( is_array( $group_id ) ) {
$group_id_sql_part = ' IN ('.implode(',', array_map(function(){return '?';}, $group_id ) ).')';
$MonitorIds = array_merge( $MonitorIds, dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId = ?)', 'MonitorId', array($group_id) ) ); $MonitorIds = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId'.$group_id_sql_part, 'MonitorId', $group_id );
$MonitorIds = array_merge( $MonitorIds, dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId'.$group_id_sql_part.')', 'MonitorId', $group_id ) );
} else {
$MonitorIds = dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($group_id) );
$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 )."' )"; $groupSql = " find_in_set( Id, '".implode( ',', $MonitorIds )."' )";
} }
return $groupSql; return $groupSql;

View File

@ -23,15 +23,39 @@ class Server {
$this->{'Hostname'} = ''; $this->{'Hostname'} = '';
} }
} }
public static function find_all() { public static function find_all( $parameters = null, $options = null ) {
$servers = array(); $filters = array();
$result = dbQuery( 'SELECT * FROM Servers ORDER BY Name'); $sql = 'SELECT * FROM Servers ';
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server' ); $values = array();
foreach ( $results as $row => $server_obj ) {
$servers[] = $server_obj; if ( $parameters ) {
} $fields = array();
return $servers; $sql .= 'WHERE ';
} foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
}
public function Url() { public function Url() {
if ( $this->Id() ) { if ( $this->Id() ) {

View File

@ -120,6 +120,7 @@ public static function find_all( $parameters = null, $options = null ) {
} }
$used = $this->disk_used_space(); $used = $this->disk_used_space();
$usage = round( ($used / $total) * 100); $usage = round( ($used / $total) * 100);
Warning("Used $usage = round( ( $used / $total ) * 100 )");
return $usage; return $usage;
} }
public function disk_total_space() { public function disk_total_space() {
@ -130,7 +131,7 @@ public static function find_all( $parameters = null, $options = null ) {
} }
public function disk_used_space() { public function disk_used_space() {
# This isn't a function like this in php, so we have to add up the space used in each event. # This isn't a function like this in php, so we have to add up the space used in each event.
if ( ! array_key_exists( 'DiskSpace', $this ) ) { if ( (! array_key_exists( 'DiskSpace', $this )) or (!$this->{'DiskSpace'}) ) {
$used = 0; $used = 0;
if ( $this->{'Type'} == 's3fs' ) { if ( $this->{'Type'} == 's3fs' ) {
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) ); $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) );

View File

@ -379,7 +379,6 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
dbQuery( 'UPDATE Zones SET '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zid) ); dbQuery( 'UPDATE Zones SET '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zid) );
} else { } else {
dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) ); dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) );
dbQuery( 'UPDATE Monitors SET ZoneCount=(SELECT COUNT(Id) FROM Zones WHERE MonitorId=Monitors.Id) WHERE Id=?', array($mid));
} }
//if ( $cookies ) session_write_close(); //if ( $cookies ) session_write_close();
if ( daemonCheck() ) { if ( daemonCheck() ) {
@ -432,7 +431,6 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
foreach( $_REQUEST['markZids'] as $markZid ) { foreach( $_REQUEST['markZids'] as $markZid ) {
$zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) ); $zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) );
dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) ); dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) );
dbQuery( 'UPDATE Monitors SET ZoneCount=(SELECT COUNT(Id) FROM Zones WHERE MonitorId=Monitors.Id) WHERE Id=?', array($mid));
$deletedZid = 1; $deletedZid = 1;
} }
if ( $deletedZid ) { if ( $deletedZid ) {

View File

@ -636,10 +636,10 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
{ {
if ( is_array( $newValues[$key] ) ) { if ( is_array( $newValues[$key] ) ) {
if ( join(',',$newValues[$key]) != $values[$key] ) { if ( join(',',$newValues[$key]) != $values[$key] ) {
$changes[$key] = "$key = ".dbEscape(join(',',$newValues[$key])); $changes[$key] = "`$key` = ".dbEscape(join(',',$newValues[$key]));
} }
} elseif ( $values[$key] ) { } elseif ( $values[$key] ) {
$changes[$key] = "$key = ''"; $changes[$key] = "`$key` = ''";
} }
break; break;
} }
@ -696,9 +696,9 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
{ {
if ( !isset($values[$key]) || ($values[$key] != $value) ) { if ( !isset($values[$key]) || ($values[$key] != $value) ) {
if ( ! isset($value) || $value == '' ) { if ( ! isset($value) || $value == '' ) {
$changes[$key] = "$key = NULL"; $changes[$key] = "`$key` = NULL";
} else { } else {
$changes[$key] = $key . ' = '.dbEscape(trim($value)); $changes[$key] = "`$key` = ".dbEscape(trim($value));
} }
} }
break; break;

View File

@ -82,9 +82,11 @@ echo output_link_if_exists( array(
'/js/dateTimePicker/jquery-ui-timepicker-addon.css', '/js/dateTimePicker/jquery-ui-timepicker-addon.css',
'/js/jquery-ui-structure.css', '/js/jquery-ui-structure.css',
'/css/'.$css.'/jquery-ui-theme.css', '/css/'.$css.'/jquery-ui-theme.css',
'/js/chosen/chosen.min.css',
) )
); );
?>
<link rel="stylesheet" href="skins/classic/js/chosen/chosen.min.css" type="text/css"/>
<?php
if ($basename == 'watch') { if ($basename == 'watch') {
echo output_link_if_exists( array( echo output_link_if_exists( array(
'/css/base/views/control.css', '/css/base/views/control.css',

View File

@ -18,13 +18,13 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
$servers = Server::find_all(); $servers = Server::find_all( null, array('order'=>'lower(Name)'));
$ServersById = array(); $ServersById = array();
foreach ( $servers as $S ) { foreach ( $servers as $S ) {
$ServersById[$S->Id()] = $S; $ServersById[$S->Id()] = $S;
} }
session_start(); session_start();
foreach ( array('ServerId','StorageId','Status','MonitorId') as $var ) { foreach ( array('Group', 'ServerId','StorageId','Status','MonitorId') as $var ) {
if ( isset( $_REQUEST[$var] ) ) { if ( isset( $_REQUEST[$var] ) ) {
if ( $_REQUEST[$var] != '' ) { if ( $_REQUEST[$var] != '' ) {
$_SESSION[$var] = $_REQUEST[$var]; $_SESSION[$var] = $_REQUEST[$var];
@ -49,7 +49,8 @@ foreach ( $storage_areas as $S ) {
<span id="groupControl"><label><?php echo translate('Group') ?>:</label> <span id="groupControl"><label><?php echo translate('Group') ?>:</label>
<?php <?php
# This will end up with the group_id of the deepest selection # This will end up with the group_id of the deepest selection
$group_id = Group::get_group_dropdowns(); $group_id = isset($_SESSION['Group']) ? $_SESSION['Group'] : null;
echo Group::get_group_dropdown();
$groupSql = Group::get_group_sql( $group_id ); $groupSql = Group::get_group_sql( $group_id );
?> ?>
</span> </span>

View File

@ -92,7 +92,8 @@ if ( $newGroup->Id() )
$sql = 'SELECT Id,Name from Groups'.(count($kids)?' WHERE Id NOT IN ('.implode(',',array_map(function(){return '?';}, $kids )).')' : '').' ORDER BY Name'; $sql = 'SELECT Id,Name from Groups'.(count($kids)?' WHERE Id NOT IN ('.implode(',',array_map(function(){return '?';}, $kids )).')' : '').' ORDER BY Name';
$options = array(''=>'None'); $options = array(''=>'None');
foreach ( dbFetchAll( $sql, null, $kids ) as $option ) { foreach ( dbFetchAll( $sql, null, $kids ) as $option ) {
$options[$option['Id']] = $option['Name'];
$options[$option['Id']] = str_repeat('&nbsp;&nbsp;', $Groups[$option['Id']]->depth() ) . $option['Name'];
} }
echo htmlSelect( 'newGroup[ParentId]', $options, $newGroup->ParentId(), array('onchange'=>'configureButtons(this);' )); echo htmlSelect( 'newGroup[ParentId]', $options, $newGroup->ParentId(), array('onchange'=>'configureButtons(this);' ));
?> ?>

View File

@ -18,7 +18,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( !canView( 'Groups' ) ) { if ( !canView('Groups') ) {
$view = 'error'; $view = 'error';
return; return;
} }
@ -64,8 +64,7 @@ function group_line( $Group ) {
global $children; global $children;
global $max_depth; global $max_depth;
$html = '<tr>'; $html = '<tr>';
for ( $i = 1; $i<$Group->depth(); $i+=1 ) $html .= str_repeat( '<td class="colName">&nbsp;</td>', $Group->depth() );
$html .= '<td class="colName">&nbsp;</td>';
$html .= '<td class="colName" colspan="'.($max_depth-($Group->depth()-1)).'">'; $html .= '<td class="colName" colspan="'.($max_depth-($Group->depth()-1)).'">';
if ( canEdit('Groups') ) { if ( canEdit('Groups') ) {
$html .= '<a href="#" onclick="editGroup('.$Group->Id().');">'. validHtmlStr($Group->Id() . ' ' . $Group->Name()).'</a>'; $html .= '<a href="#" onclick="editGroup('.$Group->Id().');">'. validHtmlStr($Group->Id() . ' ' . $Group->Name()).'</a>';

View File

@ -526,7 +526,7 @@ function clicknav(minSecs,maxSecs,live) {// we use the current time if we can
if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1 if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1
zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2); zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2);
var uri = "?view=" + currentView + '&fit='+(fitMode==1?'1':'0') + groupStr + minStr + maxStr + currentStr + intervalStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value]; var uri = "?view=" + currentView + '&fit='+(fitMode==1?'1':'0') + minStr + maxStr + currentStr + intervalStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value];
window.location = uri; window.location = uri;
} // end function clicknav } // end function clicknav
@ -745,7 +745,7 @@ function changeDateTime(e) {
if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1 if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1
zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2); zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2);
var uri = "?view=" + currentView + fitStr + groupStr + minStr + maxStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value]; var uri = "?view=" + currentView + fitStr + minStr + maxStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value];
window.location = uri; window.location = uri;
} }

View File

@ -27,7 +27,6 @@ var eStorageId = [];
var eStartSecs = []; var eStartSecs = [];
var eEndSecs = []; var eEndSecs = [];
var eventFrames = []; // this is going to presume all frames equal durationlength var eventFrames = []; // this is going to presume all frames equal durationlength
var groupStr=<?php echo $group_id ? "'&group=$group_id'" : '""'; ?>;
<?php <?php

View File

@ -463,7 +463,6 @@ $savejpegopts = array(
'Frames only' => 1, 'Frames only' => 1,
'Analysis images only (if available)' => 2, 'Analysis images only (if available)' => 2,
'Frames + Analysis images (if available)' => 3, 'Frames + Analysis images (if available)' => 3,
'Snapshot Only' => 4
); );
$videowriteropts = array( $videowriteropts = array(

View File

@ -19,20 +19,20 @@
// //
if ( !canEdit( 'System' ) ) { if ( !canEdit( 'System' ) ) {
$view = 'error'; $view = 'error';
return; return;
} }
if ( $_REQUEST['id'] ) { if ( $_REQUEST['id'] ) {
if ( !($newStorage = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, ARRAY($_REQUEST['id'])) ) ) { if ( !($newStorage = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, ARRAY($_REQUEST['id'])) ) ) {
$view = 'error'; $view = 'error';
return; return;
$newStorage['ServerId'] = ''; $newStorage['ServerId'] = '';
} }
} else { } else {
$newStorage = array(); $newStorage = array();
$newStorage['Name'] = translate('NewStorage'); $newStorage['Name'] = translate('NewStorage');
$newStorage['Path'] = ''; $newStorage['Path'] = '';
$newStorage['Type'] = 'local'; $newStorage['Type'] = 'local';
$newStorage['Scheme'] = 'Medium'; $newStorage['Scheme'] = 'Medium';
} }
@ -44,7 +44,7 @@ $scheme_options = array(
'Shallow' => translate('Shallow'), 'Shallow' => translate('Shallow'),
); );
$servers = Server::find_all(); $servers = Server::find_all( null, array('order'=>'lower(Name)') );
$ServersById = array(); $ServersById = array();
foreach ( $servers as $S ) { foreach ( $servers as $S ) {
$ServersById[$S->Id()] = $S; $ServersById[$S->Id()] = $S;
@ -75,7 +75,7 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('Server') ?></th> <th scope="row"><?php echo translate('Server') ?></th>
<td><?php echo htmlSelect( 'newStorage[ServerId]', array('','Remote') + $ServersById, $newStorage['ServerId'] ); ?></td> <td><?php echo htmlSelect( 'newStorage[ServerId]', array(''=>'Remote / No Specific Server') + $ServersById, $newStorage['ServerId'] ); ?></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('Type') ?></th> <th scope="row"><?php echo translate('Type') ?></th>