diff --git a/.gitmodules b/.gitmodules index eb0e282a2..9f21bb62b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "web/api/app/Plugin/Crud"] path = web/api/app/Plugin/Crud - url = https://github.com/ZoneMinder/crud.git + url = https://github.com/FriendsOfCake/crud.git branch = 3.0 [submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"] path = web/api/app/Plugin/CakePHP-Enum-Behavior diff --git a/db/triggers.sql b/db/triggers.sql index 87c8465b4..574d34440 100644 --- a/db/triggers.sql +++ b/db/triggers.sql @@ -3,10 +3,10 @@ delimiter // DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour FOR EACH ROW BEGIN - UPDATE Monitors SET + UPDATE Monitor_Status SET HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; END; // @@ -20,10 +20,10 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; ELSE - UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitor_Status.MonitorId=NEW.MonitorId; END IF; END IF; END; @@ -32,10 +32,10 @@ FOR EACH ROW DROP TRIGGER IF EXISTS Events_Day_delete_trigger// CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day FOR EACH ROW BEGIN - UPDATE Monitors SET + UPDATE Monitor_Status SET DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0), DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; END; // @@ -48,10 +48,10 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; ELSE - UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; END IF; END IF; END; @@ -61,10 +61,10 @@ FOR EACH ROW DROP TRIGGER IF EXISTS Events_Week_delete_trigger// CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week FOR EACH ROW BEGIN - UPDATE Monitors SET + UPDATE Monitor_Status SET WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; END; // @@ -77,10 +77,10 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; ELSE - UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; END IF; END IF; END; @@ -89,10 +89,10 @@ FOR EACH ROW DROP TRIGGER IF EXISTS Events_Month_delete_trigger// CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month FOR EACH ROW BEGIN - UPDATE Monitors SET + UPDATE Monitor_Status SET MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; END; // @@ -105,10 +105,10 @@ FOR EACH ROW set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( diff ) THEN IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitor_Status.MonitorId=NEW.MonitorId; ELSE - UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId; + UPDATE Monitor_Status SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; END IF; END IF; END; @@ -126,14 +126,14 @@ BEGIN set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); IF ( NEW.StorageId = OLD.StorageID ) THEN IF ( diff ) THEN - UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId; + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Storage.Id = OLD.StorageId; END IF; ELSE IF ( NEW.DiskSpace ) THEN - UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId; + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Storage.Id = NEW.StorageId; END IF; IF ( OLD.DiskSpace ) THEN - UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId; + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Storage.Id = OLD.StorageId; END IF; END IF; @@ -145,20 +145,20 @@ BEGIN 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 = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; + UPDATE Monitor_Status SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; ELSEIF ( OLD.Archived ) THEN DELETE FROM Events_Archived WHERE EventId=OLD.Id; - UPDATE Monitors + UPDATE Monitor_Status SET ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0), ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; ELSE IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Monitors SET + UPDATE Monitor_Status SET ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; END IF; END IF; ELSEIF ( NEW.Archived AND diff ) THEN @@ -166,10 +166,10 @@ BEGIN END IF; IF ( diff ) THEN - UPDATE Monitors + UPDATE Monitor_Status SET TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; END IF; END; @@ -185,17 +185,17 @@ 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); - UPDATE Monitors SET + INSERT INTO Events_Hour (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + UPDATE Monitor_Status SET HourEvents = COALESCE(HourEvents,0)+1, DayEvents = COALESCE(DayEvents,0)+1, WeekEvents = COALESCE(WeekEvents,0)+1, MonthEvents = COALESCE(MonthEvents,0)+1, TotalEvents = COALESCE(TotalEvents,0)+1 - WHERE Id=NEW.MonitorId; + WHERE Monitor_Status.MonitorId=NEW.MonitorId; END; // @@ -205,7 +205,7 @@ CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events FOR EACH ROW BEGIN IF ( OLD.DiskSpace ) THEN - UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId; + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Storage.Id = OLD.StorageId; END IF; DELETE FROM Events_Hour WHERE EventId=OLD.Id; DELETE FROM Events_Day WHERE EventId=OLD.Id; @@ -213,17 +213,17 @@ BEGIN DELETE FROM Events_Month WHERE EventId=OLD.Id; IF ( OLD.Archived ) THEN DELETE FROM Events_Archived WHERE EventId=OLD.Id; - UPDATE Monitors SET + UPDATE Monitor_Status SET ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0), ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0), TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0), TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; ELSE - UPDATE Monitors SET + UPDATE Monitor_Status SET TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0), TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) - WHERE Id=OLD.MonitorId; + WHERE Monitor_Status.MonitorId=OLD.MonitorId; END IF; END; @@ -233,14 +233,14 @@ 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; + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Monitors.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; + UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Monitors.Id=OLD.MonitorID; END // diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 401dac87c..1b442c221 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -189,8 +189,8 @@ CREATE TABLE `Events` ( `SecondaryStorageId` smallint(5) unsigned default 0, `Name` varchar(64) NOT NULL default '', `Cause` varchar(32) NOT NULL default '', - `StartTime` datetime default NULL, - `EndTime` datetime default NULL, + `StartDateTime` datetime default NULL, + `EndDateTime` datetime default NULL, `Width` smallint(5) unsigned NOT NULL default '0', `Height` smallint(5) unsigned NOT NULL default '0', `Length` decimal(10,2) NOT NULL default '0.00', @@ -216,52 +216,52 @@ CREATE TABLE `Events` ( PRIMARY KEY (`Id`), KEY `Events_MonitorId_idx` (`MonitorId`), KEY `Events_StorageId_idx` (`StorageId`), - KEY `Events_StartTime_idx` (`StartTime`), - KEY `Events_EndTime_DiskSpace` (`EndTime`,`DiskSpace`) + KEY `Events_StartDateTime_idx` (`StartDateTime`), + KEY `Events_EndDateTime_DiskSpace` (`EndDateTime`,`DiskSpace`) ) ENGINE=@ZM_MYSQL_ENGINE@; DROP TABLE IF EXISTS `Events_Hour`; CREATE TABLE `Events_Hour` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, - `StartTime` datetime default NULL, + `StartDateTime` datetime default NULL, `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Hour_MonitorId_idx` (`MonitorId`), - KEY `Events_Hour_StartTime_idx` (`StartTime`) + KEY `Events_Hour_StartDateTime_idx` (`StartDateTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; DROP TABLE IF EXISTS `Events_Day`; CREATE TABLE `Events_Day` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, - `StartTime` datetime default NULL, + `StartDateTime` datetime default NULL, `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Day_MonitorId_idx` (`MonitorId`), - KEY `Events_Day_StartTime_idx` (`StartTime`) + KEY `Events_Day_StartDateTime_idx` (`StartDateTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; DROP TABLE IF EXISTS `Events_Week`; CREATE TABLE `Events_Week` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, - `StartTime` datetime default NULL, + `StartDateTime` datetime default NULL, `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Week_MonitorId_idx` (`MonitorId`), - KEY `Events_Week_StartTime_idx` (`StartTime`) + KEY `Events_Week_StartDateTime_idx` (`StartDateTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; DROP TABLE IF EXISTS `Events_Month`; CREATE TABLE `Events_Month` ( `EventId` BIGINT unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL, - `StartTime` datetime default NULL, + `StartDateTime` datetime default NULL, `DiskSpace` bigint default NULL, PRIMARY KEY (`EventId`), KEY `Events_Month_MonitorId_idx` (`MonitorId`), - KEY `Events_Month_StartTime_idx` (`StartTime`) + KEY `Events_Month_StartDateTime_idx` (`StartDateTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -303,6 +303,7 @@ CREATE TABLE `Filters` ( `UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0', `Background` tinyint(1) unsigned NOT NULL default '0', `Concurrent` tinyint(1) unsigned NOT NULL default '0', + `LockRows` tinyint(1) unsigned NOT NULL default '0', PRIMARY KEY (`Id`), KEY `Name` (`Name`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -315,6 +316,7 @@ DROP TABLE IF EXISTS `Frames`; CREATE TABLE `Frames` ( `Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `EventId` BIGINT UNSIGNED NOT NULL default '0', + FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE, `FrameId` int(10) unsigned NOT NULL default '0', `Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal', `TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, @@ -524,18 +526,6 @@ CREATE TABLE `Monitors` ( `WebColour` varchar(32) NOT NULL default 'red', `Exif` tinyint(1) unsigned NOT NULL default '0', `Sequence` smallint(5) unsigned default NULL, - `TotalEvents` int(10) default NULL, - `TotalEventDiskSpace` bigint default NULL, - `HourEvents` int(10) default NULL, - `HourEventDiskSpace` bigint default NULL, - `DayEvents` int(10) default NULL, - `DayEventDiskSpace` bigint default NULL, - `WeekEvents` int(10) default NULL, - `WeekEventDiskSpace` bigint default NULL, - `MonthEvents` int(10) default NULL, - `MonthEventDiskSpace` bigint default NULL, - `ArchivedEvents` int(10) default NULL, - `ArchivedEventDiskSpace` bigint default NULL, `ZoneCount` TINYINT NOT NULL DEFAULT 0, `Refresh` int(10) unsigned default NULL, `Latitude` DECIMAL(10,8), @@ -552,6 +542,18 @@ CREATE TABLE `Monitor_Status` ( `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, `CaptureBandwidth` INT NOT NULL default 0, + `TotalEvents` int(10) default NULL, + `TotalEventDiskSpace` bigint default NULL, + `HourEvents` int(10) default NULL, + `HourEventDiskSpace` bigint default NULL, + `DayEvents` int(10) default NULL, + `DayEventDiskSpace` bigint default NULL, + `WeekEvents` int(10) default NULL, + `WeekEventDiskSpace` bigint default NULL, + `MonthEvents` int(10) default NULL, + `MonthEventDiskSpace` bigint default NULL, + `ArchivedEvents` int(10) default NULL, + `ArchivedEventDiskSpace` bigint default NULL, PRIMARY KEY (`MonitorId`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- @@ -609,8 +611,11 @@ DROP TABLE IF EXISTS `Stats`; CREATE TABLE `Stats` ( `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `MonitorId` int(10) unsigned NOT NULL default '0', + FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE, `ZoneId` int(10) unsigned NOT NULL default '0', + FOREIGN KEY (`ZoneId`) REFERENCES `Zones` (`Id`) ON DELETE CASCADE, `EventId` BIGINT UNSIGNED NOT NULL, + FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE, `FrameId` int(10) unsigned NOT NULL default '0', `PixelDiff` tinyint(3) unsigned NOT NULL default '0', `AlarmPixels` int(10) unsigned NOT NULL default '0', @@ -705,6 +710,7 @@ DROP TABLE IF EXISTS `Zones`; CREATE TABLE `Zones` ( `Id` int(10) unsigned NOT NULL auto_increment, `MonitorId` int(10) unsigned NOT NULL default '0', + FOREIGN KEY (`MonitorId`) REFERENCES `Monitors` (`Id`) ON DELETE CASCADE, `Name` varchar(64) NOT NULL default '', `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive','Privacy') NOT NULL default 'Active', `Units` enum('Pixels','Percent') NOT NULL default 'Pixels', @@ -799,7 +805,7 @@ INSERT INTO `Filters` VALUES ( 'PurgeWhenFull', - '{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}', + '{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="},{"cnj":"and","obr":"0","attr":"EndDateTime","op":"IS NOT","val":"NULL","cbr":"0"}],"limit":100,"sort_asc":1}', 0/*AutoArchive*/, 0/*AutoVideo*/, 0/*AutoUpload*/, @@ -843,7 +849,7 @@ INSERT INTO `Filters` ) VALUES ( 'Update DiskSpace', - '{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}', + '{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"},{"cnj":"and","obr":"0","attr":"EndDateTime","op":"IS NOT","val":"NULL","cbr":"0"}]}', 0/*AutoArchive*/, 0/*AutoVideo*/, 0/*AutoUpload*/, diff --git a/db/zm_update-1.35.11.sql b/db/zm_update-1.35.11.sql new file mode 100644 index 000000000..5e4338d76 --- /dev/null +++ b/db/zm_update-1.35.11.sql @@ -0,0 +1,92 @@ +/* Change Id type to BIGINT. */ +set @exist := (SELECT count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'Events' AND COLUMN_NAME = 'Id' and DATA_TYPE='bigint'); + +set @sqlstmt := if( @exist = 0, "ALTER TABLE Events MODIFY Id bigint unsigned NOT NULL auto_increment", "SELECT 'Events.Id is already BIGINT'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +/* Add FOREIGN KEYS After deleting lost records */ +set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Frames' and column_name='EventId' and referenced_table_name='Events' and referenced_column_name='Id'); + +set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'"); +set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY EventId in Frames already exists'", @sqlstmt); +set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for EventId to Frames'", @sqlstmt); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Frames'", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; +set @sqlstmt := if( @exist = 0, "DELETE FROM Frames WHERE EventId NOT IN (SELECT Id FROM Events)", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; +set @sqlstmt := if( @exist = 0, "ALTER TABLE Frames ADD FOREIGN KEY (EventId) REFERENCES Events (Id) ON DELETE CASCADE", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + + +set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Stats' and column_name='EventId' and referenced_table_name='Events' and referenced_column_name='Id'); +set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'"); +set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY EventId in Stats already exists'", @sqlstmt); +set @sqlstmt := if( @exist = 0, "SELECT 'Adding FOREIGN KEY for EventId to Stats'", @sqlstmt); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Stats'", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; +set @sqlstmt := if( @exist = 0, "DELETE FROM Stats WHERE EventId NOT IN (SELECT Id FROM Events);", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +set @sqlstmt := if( @exist = 0, "ALTER TABLE Stats ADD FOREIGN KEY (EventId) REFERENCES Events (Id) ON DELETE CASCADE", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + + +set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Stats' and column_name='MonitorId' and referenced_table_name='Monitors' and referenced_column_name='Id'); +set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'"); +set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY for MonitorId in Stats already exists'", @sql_stmt); +set @sqlstmt := if( @exist = 0, "SELECT 'Adding FOREIGN KEY for MonitorId to Stats'", @sqlstmt); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Stats'", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; +set @sqlstmt := if( @exist = 0, "DELETE FROM Stats WHERE MonitorId NOT IN (SELECT Id FROM Monitors);", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +set @sqlstmt := if( @exist = 0, "ALTER TABLE Stats ADD FOREIGN KEY (MonitorId) REFERENCES Monitors (Id) ON DELETE CASCADE", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Stats' and column_name='ZoneId' and referenced_table_name='Zones' and referenced_column_name='Id'); +set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'"); +set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY for ZoneId in Stats already exists'", @sqlstmt); +set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for ZoneId to Stats'", @sqlstmt); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +set @sqlstmt := if( @exist = 0, "SELECT 'Deleting unlinked Stats'", "SELECT 'Ok'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; +set @sqlstmt := if( @exist = 0, "DELETE FROM Stats WHERE ZoneId NOT IN (SELECT Id FROM Zones);", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; +set @sqlstmt := if( @exist = 0, "ALTER TABLE Stats ADD FOREIGN KEY (ZoneId) REFERENCES Zones (Id) ON DELETE CASCADE", "SELECT '.'"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; + +SELECT 'Adding foreign key for MonitorId to Zones'; +set @exist := (select count(*) FROM information_schema.key_column_usage where table_name='Zones' and column_name='MonitorId' and referenced_table_name='Monitors' and referenced_column_name='Id'); +set @sqlstmt := if( @exist > 1, "SELECT 'You have more than 1 FOREIGN KEY. Please do manual cleanup'", "SELECT 'Ok'"); +set @sqlstmt := if( @exist = 1, "SELECT 'FOREIGN KEY for MonitorId in Zones already exists'", @sqlstmnt); +set @sqlstmt := if( @exist = 0, "SELECT 'Adding foreign key for MonitorId in Zones'", @sqlstmnt); + +/*"SELECT 'FOREIGN KEY for MonitorId in Zones does not already exist'");*/ +set @badzones := (select count(*) FROM Zones WHERE MonitorId NOT IN (SELECT Id FROM Monitors)); +set @sqlstmt := if ( @badzones > 0, "SELECT 'You have Zones with no Monitor record in the Monitors table. Please delete them manually'", "ALTER TABLE Zones ADD FOREIGN KEY (MonitorId) REFERENCES Monitors (Id)"); +PREPARE stmt FROM @sqlstmt; +EXECUTE stmt; diff --git a/db/zm_update-1.35.12.sql b/db/zm_update-1.35.12.sql new file mode 100644 index 000000000..b8c54f713 --- /dev/null +++ b/db/zm_update-1.35.12.sql @@ -0,0 +1,18 @@ +-- +-- Update Filters table to have a LockRows Column +-- + +SELECT 'Checking for LockRows in Filters'; +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Filters' + AND table_schema = DATABASE() + AND column_name = 'LockRows' + ) > 0, +"SELECT 'Column LockRows already exists in Filters'", +"ALTER TABLE Filters ADD COLUMN `LockRows` tinyint(1) unsigned NOT NULL default '0' AFTER `Concurrent`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.35.13.sql b/db/zm_update-1.35.13.sql new file mode 100644 index 000000000..2911aaf65 --- /dev/null +++ b/db/zm_update-1.35.13.sql @@ -0,0 +1,101 @@ +/* DateTime is invalid and it being set here will cause warnings because it isn't in the dropdown set of values in Filter edit. */ +UPDATE Config SET Value='StartDateTime' WHERE Name='ZM_WEB_EVENT_SORT_FIELD' AND Value='DateTime'; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Events' + AND column_name = 'StartDateTime' + ) > 0, +"SELECT 'Column StartDateTime already exists in Events'", +"ALTER TABLE Events CHANGE StartTime StartDateTime datetime default NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Events' + AND column_name = 'EndDateTime' + ) > 0, + "SELECT 'Column EndDateTime already exists in Events'", + "ALTER TABLE Events CHANGE EndTime EndDateTime datetime default NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Events_Hour' + AND column_name = 'StartDateTime' + ) > 0, + "SELECT 'Column StartDateTime already exists in Events_Hour'", + "ALTER TABLE Events_Hour CHANGE StartTime StartDateTime datetime default NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Events_Day' + AND column_name = 'StartDateTime' + ) > 0, + "SELECT 'Column StartDateTime already exists in Events_Day'", + "ALTER TABLE Events_Day CHANGE StartTime StartDateTime datetime default NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Events_Week' + AND column_name = 'StartDateTime' + ) > 0, + "SELECT 'Column StartDateTime already exists in Events_Week'", + "ALTER TABLE Events_Week CHANGE StartTime StartDateTime datetime default NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Events_Month' + AND column_name = 'StartDateTime' + ) > 0, + "SELECT 'Column StartDateTime already exists in Events_Month'", + "ALTER TABLE Events_Month CHANGE StartTime StartDateTime datetime default NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +delimiter // + +DROP TRIGGER IF EXISTS event_insert_trigger// + +/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. + * The DiskSpace will get update in the Event Update Trigger + */ +CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events +FOR EACH ROW + BEGIN + + INSERT INTO Events_Hour (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + UPDATE Monitors SET + HourEvents = COALESCE(HourEvents,0)+1, + DayEvents = COALESCE(DayEvents,0)+1, + WeekEvents = COALESCE(WeekEvents,0)+1, + MonthEvents = COALESCE(MonthEvents,0)+1, + TotalEvents = COALESCE(TotalEvents,0)+1 + WHERE Id=NEW.MonitorId; +END; +// + +delimiter ; diff --git a/db/zm_update-1.35.14.sql b/db/zm_update-1.35.14.sql new file mode 100644 index 000000000..daa8239ff --- /dev/null +++ b/db/zm_update-1.35.14.sql @@ -0,0 +1,574 @@ +SELECT 'This update may make changes that require SUPER privileges. If you see an error message saying: + +ERROR 1419 (HY000) at line 298: You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) + +You will have to either run this update as root manually using something like (on ubuntu/debian) + +sudo mysql --defaults-file=/etc/mysql/debian.cnf zm < /usr/share/zoneminder/db/zm_update-1.35.14.sql + +OR + +sudo mysql --defaults-file=/etc/mysql/debian.cnf "set global log_bin_trust_function_creators=1;" +sudo zmupdate.pl +'; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'TotalEvents' + ) > 0, +"SELECT 'Column TotalEvents already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `TotalEvents` INT(10) AFTER `CaptureBandwidth`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'TotalEvents' + ) > 0, +"SELECT 'Column TotalEvents is already removed from Monitors'", +"ALTER TABLE `Monitors` DROP `TotalEvents`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'TotalEventDiskSpace' + ) > 0, +"SELECT 'Column TotalEventDiskSpace already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `TotalEventDiskSpace` BIGINT AFTER `TotalEvents`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'TotalEventDiskSpace' + ) > 0, +"SELECT 'Column TotalEventDiskSpace is already removed from Monitors'", +"ALTER TABLE `Monitors` DROP `TotalEventDiskSpace`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'HourEvents' + ) > 0, +"SELECT 'Column HourEvents already exists in Monitors'", +"ALTER TABLE `Monitor_Status` ADD `HourEvents` INT(10) AFTER `TotalEventDiskSpace`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'HourEvents' + ) > 0, +"ALTER TABLE `Monitors` DROP `HourEvents`", +"SELECT 'Column HourEvents is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'HourEventDiskSpace' + ) > 0, +"SELECT 'Column HourEventDiskSpace already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `HourEventDiskSpace` BIGINT AFTER `HourEvents`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'HourEventDiskSpace' + ) > 0, +"ALTER TABLE `Monitors` DROP `HourEventDiskSpace`", +"SELECT 'Column HourEventDiskSpace is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'DayEvents' + ) > 0, +"SELECT 'Column DayEvents already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `DayEvents` INT(10) AFTER `HourEventDiskSpace`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'DayEvents' + ) > 0, +"ALTER TABLE `Monitors` DROP `DayEvents`", +"SELECT 'Column DayEvents is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'DayEventDiskSpace' + ) > 0, +"SELECT 'Column DayEventDiskSpace already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `DayEventDiskSpace` BIGINT AFTER `DayEvents`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'DayEventDiskSpace' + ) > 0, +"ALTER TABLE `Monitors` DROP `DayEventDiskSpace`", +"SELECT 'Column DayEventDiskSpace is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'WeekEvents' + ) > 0, +"SELECT 'Column WeekEvents already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `WeekEvents` INT(10) AFTER `DayEventDiskSpace`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'WeekEvents' + ) > 0, +"ALTER TABLE `Monitors` DROP `WeekEvents`", +"SELECT 'Column WeekEvents is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'WeekEventDiskSpace' + ) > 0, +"SELECT 'Column WeekEventDiskSpace already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `WeekEventDiskSpace` BIGINT AFTER `WeekEvents`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'WeekEventDiskSpace' + ) > 0, +"ALTER TABLE `Monitors` DROP `WeekEventDiskSpace`", +"SELECT 'Column WeekEventDiskSpace is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'MonthEvents' + ) > 0, +"SELECT 'Column MonthEvents already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `MonthEvents` INT(10) AFTER `WeekEventDiskSpace`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'MonthEvents' + ) > 0, +"ALTER TABLE `Monitors` DROP `MonthEvents`", +"SELECT 'Column MonthEvents is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'MonthEventDiskSpace' + ) > 0, +"SELECT 'Column MonthEventDiskSpace already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `MonthEventDiskSpace` BIGINT AFTER `MonthEvents`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'MonthEventDiskSpace' + ) > 0, +"ALTER TABLE `Monitors` DROP `MonthEventDiskSpace`", +"SELECT 'Column MonthEventDiskSpace is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'ArchivedEvents' + ) > 0, +"SELECT 'Column ArchivedEvents already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `ArchivedEvents` INT(10) AFTER `MonthEventDiskSpace`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'ArchivedEvents' + ) > 0, +"ALTER TABLE `Monitors` DROP `ArchivedEvents`", +"SELECT 'Column ArchivedEvents is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + AND column_name = 'ArchivedEventDiskSpace' + ) > 0, +"SELECT 'Column ArchivedEventDiskSpace already exists in Monitor_Status'", +"ALTER TABLE `Monitor_Status` ADD `ArchivedEventDiskSpace` BIGINT AFTER `ArchivedEvents`" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'ArchivedEventDiskSpace' + ) > 0, +"ALTER TABLE `Monitors` DROP `ArchivedEventDiskSpace`", +"SELECT 'Column ArchivedEventDiskSpace is already removed from Monitors'" +)); +PREPARE stmt FROM @s; +EXECUTE stmt; + +UPDATE Monitor_Status 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(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitor_Status.MonitorId SET + Monitor_Status.TotalEvents = E.TotalEvents, + Monitor_Status.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitor_Status.ArchivedEvents = E.ArchivedEvents, + Monitor_Status.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitor_Status.HourEvents = E.HourEvents, + Monitor_Status.HourEventDiskSpace = E.HourEventDiskSpace, + Monitor_Status.DayEvents = E.DayEvents, + Monitor_Status.DayEventDiskSpace = E.DayEventDiskSpace, + Monitor_Status.WeekEvents = E.WeekEvents, + Monitor_Status.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitor_Status.MonthEvents = E.MonthEvents, + Monitor_Status.MonthEventDiskSpace = E.MonthEventDiskSpace; + + +delimiter // +DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// +CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour +FOR EACH ROW BEGIN + UPDATE Monitor_Status SET + HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), + HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Hour_update_trigger// + +CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitor_Status SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + ELSE + UPDATE Monitor_Status SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitor_Status.MonitorId=NEW.MonitorId; + END IF; + END IF; + END; +// + +DROP TRIGGER IF EXISTS Events_Day_delete_trigger// +CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day +FOR EACH ROW BEGIN + UPDATE Monitor_Status SET + DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0), + DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Day_update_trigger; +CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitor_Status SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + ELSE + UPDATE Monitor_Status SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + END IF; + END IF; + END; + // + + +DROP TRIGGER IF EXISTS Events_Week_delete_trigger// +CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week +FOR EACH ROW BEGIN + UPDATE Monitor_Status SET + WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), + WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Week_update_trigger; +CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitor_Status SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + ELSE + UPDATE Monitor_Status SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + END IF; + END IF; + END; + // + +DROP TRIGGER IF EXISTS Events_Month_delete_trigger// +CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month +FOR EACH ROW BEGIN + UPDATE Monitor_Status SET + MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), + MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS Events_Month_update_trigger; +CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month +FOR EACH ROW + BEGIN + declare diff BIGINT default 0; + + set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( diff ) THEN + IF ( NEW.MonitorID != OLD.MonitorID ) THEN + UPDATE Monitor_Status SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitor_Status.MonitorId=OLD.MonitorId; + UPDATE Monitor_Status SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + ELSE + UPDATE Monitor_Status SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + END IF; + END IF; + END; + // + +drop procedure if exists update_storage_stats// + +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 = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); + IF ( NEW.StorageId = OLD.StorageID ) THEN + IF ( diff ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Storage.Id = OLD.StorageId; + END IF; + ELSE + IF ( NEW.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Storage.Id = NEW.StorageId; + END IF; + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Storage.Id = OLD.StorageId; + 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 Monitor_Status SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Monitor_Status.MonitorId=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitor_Status + SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; + ELSE + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Monitor_Status SET + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; + END IF; + END IF; + ELSEIF ( NEW.Archived AND diff ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + END IF; + + IF ( diff ) THEN + UPDATE Monitor_Status + SET + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; + END IF; + +END; + +// + +DROP TRIGGER IF EXISTS event_insert_trigger// + +/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. + * The DiskSpace will get update in the Event Update Trigger + */ +CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events +FOR EACH ROW + BEGIN + + INSERT INTO Events_Hour (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartDateTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartDateTime,0); + UPDATE Monitor_Status SET + HourEvents = COALESCE(HourEvents,0)+1, + DayEvents = COALESCE(DayEvents,0)+1, + WeekEvents = COALESCE(WeekEvents,0)+1, + MonthEvents = COALESCE(MonthEvents,0)+1, + TotalEvents = COALESCE(TotalEvents,0)+1 + WHERE Monitor_Status.MonitorId=NEW.MonitorId; +END; +// + +DROP TRIGGER IF EXISTS event_delete_trigger// + +CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events +FOR EACH ROW +BEGIN + IF ( OLD.DiskSpace ) THEN + UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Storage.Id = OLD.StorageId; + END IF; + 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 Monitor_Status SET + ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0), + ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0), + TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0), + TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; + ELSE + UPDATE Monitor_Status SET + TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0), + TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) + WHERE Monitor_Status.MonitorId=OLD.MonitorId; + END IF; +END; + +// + +DELIMITER ; + +REPLACE INTO Events_Hour SELECT Id,MonitorId,StartDateTime,DiskSpace FROM Events WHERE StartDateTime > DATE_SUB(NOW(), INTERVAL 1 hour); +REPLACE INTO Events_Day SELECT Id,MonitorId,StartDateTime,DiskSpace FROM Events WHERE StartDateTime > DATE_SUB(NOW(), INTERVAL 1 day); +REPLACE INTO Events_Week SELECT Id,MonitorId,StartDateTime,DiskSpace FROM Events WHERE StartDateTime > DATE_SUB(NOW(), INTERVAL 1 week); +REPLACE INTO Events_Month SELECT Id,MonitorId,StartDateTime,DiskSpace FROM Events WHERE StartDateTime > DATE_SUB(NOW(), INTERVAL 1 month); +REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1; + +UPDATE Monitor_Status 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(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartDateTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitor_Status.MonitorId SET + Monitor_Status.TotalEvents = E.TotalEvents, + Monitor_Status.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitor_Status.ArchivedEvents = E.ArchivedEvents, + Monitor_Status.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitor_Status.HourEvents = E.HourEvents, + Monitor_Status.HourEventDiskSpace = E.HourEventDiskSpace, + Monitor_Status.DayEvents = E.DayEvents, + Monitor_Status.DayEventDiskSpace = E.DayEventDiskSpace, + Monitor_Status.WeekEvents = E.WeekEvents, + Monitor_Status.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitor_Status.MonthEvents = E.MonthEvents, + Monitor_Status.MonthEventDiskSpace = E.MonthEventDiskSpace; diff --git a/distros/beowulf/zoneminder.postinst b/distros/beowulf/zoneminder.postinst index 603786ff6..032595355 100644 --- a/distros/beowulf/zoneminder.postinst +++ b/distros/beowulf/zoneminder.postinst @@ -39,9 +39,9 @@ if [ "$1" = "configure" ]; then exit 1; fi # This creates the user. - echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute, REFERENCES on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql else - echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute, REFERENCES on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi zmupdate.pl --nointeractive diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 5c19dd0c0..16904e725 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -3,7 +3,7 @@ %global zmgid_final apache # Crud is configured as a git submodule -%global crud_version 3.1.0-zm +%global crud_version 3.2.0 # CakePHP-Enum-Behavior is configured as a git submodule %global ceb_version 1.0-zm @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.10 +Version: 1.35.14 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -44,7 +44,7 @@ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz -Source1: https://github.com/ZoneMinder/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz +Source1: https://github.com/FriendOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz %{?rhel:BuildRequires: epel-rpm-macros} diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index e9bd30a17..d026b02b3 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -67,8 +67,8 @@ if [ "$1" = "configure" ]; then # This creates the user. echo "CREATE USER '${ZM_DB_USER}'@localhost IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi - echo "Updating permissions" - echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "Updating permissions for user ${ZM_DB_USER}@localhost" + echo "GRANT LOCK TABLES,ALTER,DROP,SELECT,INSERT,UPDATE,DELETE,CREATE,INDEX,ALTER ROUTINE,CREATE ROUTINE, TRIGGER,EXECUTE,REFERENCES ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql zmupdate.pl --nointeractive zmupdate.pl --nointeractive -f diff --git a/distros/ubuntu2004/zoneminder.postinst b/distros/ubuntu2004/zoneminder.postinst index a8b8eaf51..cd147a0e2 100644 --- a/distros/ubuntu2004/zoneminder.postinst +++ b/distros/ubuntu2004/zoneminder.postinst @@ -25,8 +25,8 @@ create_update_user () { # This creates the user. echo "CREATE USER '${ZM_DB_USER}'@${ZM_DB_HOST} IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi - echo "Updating permissions" - echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "Updating permissions for user ${ZM_DB_USER}@${ZM_DB_HOST}" + echo "GRANT LOCK tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine,trigger,execute,REFERENCES ON ${ZM_DB_NAME}.* TO '${ZM_DB_USER}'@${ZM_DB_HOST};" | mysql --defaults-file=/etc/mysql/debian.cnf mysql } update_db () { diff --git a/docs/faq.rst b/docs/faq.rst index 56b67d082..1db30e05f 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -27,7 +27,7 @@ After you've done that, you changes will automatically be loaded into zmfilter w Check the ``zmfilter.log`` file to make sure it is running as sometimes missing perl modules mean that it never runs but people don't always realize. **Purge By Age** -To delete events that are older than 7 days, create a new filter with "Date" set to "less than" and a value of "-7 days", sort by "date/time" in "asc"ending order, then enable the checkbox "delete all matches". You can also use a value of week or week and days: "-2 week" or "-2 week 4 day" +To delete events that are older than 7 days, create a new filter with "End Date" set to "less than" and a value of "-7 days", sort by "date/time" in "asc"ending order, then enable the checkbox "delete all matches". You can also use a value of week or week and days: "-2 week" or "-2 week 4 day" Save with 'Run Filter In Background' enabled to have it run automatically. Optional skip archived events: click on the plus sign next to -7 days to add another condition. "and" "archive status" equal to "unarchived only". @@ -534,4 +534,4 @@ The GPL license allows you produce systems based on GPL software provided your s I am having issues with zmNinja and/or Event Notification Server ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -zmNinja and the Event Notification Server are 3rd party solutions. The developer maintains exhaustive `documentation and FAQs `__. Please direct your questions there. \ No newline at end of file +zmNinja and the Event Notification Server are 3rd party solutions. The developer maintains exhaustive `documentation and FAQs `__. Please direct your questions there. diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 43fbcb457..98ed7fa69 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -2648,7 +2648,7 @@ our @options = ( }, { name => 'ZM_WEB_EVENT_SORT_FIELD', - default => 'StartDateTime', + default => 'StartTime', description => 'Default field the event lists are sorted by', help => q` Events in lists can be initially ordered in any way you want. @@ -2660,7 +2660,7 @@ our @options = ( `, type => { db_type =>'string', - hint =>'Id|Name|Cause|DiskSpace|MonitorName|StartDateTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore', + hint =>'Id|Name|Cause|DiskSpace|MonitorName|StartTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore', pattern =>qr|.|, format =>q( $1 ) }, diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 0af41b2ca..2664830dd 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -68,8 +68,8 @@ $serial = $primary_key = 'Id'; SecondaryStorageId Name Cause - StartTime - EndTime + StartDateTime + EndDateTime Width Height Length @@ -111,8 +111,8 @@ sub Time { $_[0]{Time} = $_[1]; } if ( ! defined $_[0]{Time} ) { - if ( $_[0]{StartTime} ) { - $_[0]{Time} = Date::Parse::str2time( $_[0]{StartTime} ); + if ( $_[0]{StartDateTime} ) { + $_[0]{Time} = Date::Parse::str2time( $_[0]{StartDateTime} ); } } return $_[0]{Time}; @@ -349,6 +349,10 @@ sub GenerateVideo { return; } # end sub GenerateVideo +# Note about transactions, this function may be called with rows locked and hence in a transaction. +# So we will detect if we are in a transaction, and if not, start one. We will NOT do rollback or +# commits unless we started the transaction. + sub delete { my $event = $_[0]; @@ -360,11 +364,11 @@ sub delete { my $in_zmaudit = ( $0 =~ 'zmaudit.pl$'); if ( ! $in_zmaudit ) { - if ( ! ( $event->{Id} and $event->{MonitorId} and $event->{StartTime} ) ) { + if ( ! ( $event->{Id} and $event->{MonitorId} and $event->{StartDateTime} ) ) { # zmfilter shouldn't delete anything in an odd situation. zmaudit will though. my ( $caller, undef, $line ) = caller; - Warning("$0 Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:". - (defined($event->{StartTime})?$event->{StartTime}:'undef')." from $caller:$line"); + Warning("$0 Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartDateTime:". + (defined($event->{StartDateTime})?$event->{StartDateTime}:'undef')." from $caller:$line"); return; } if ( !($event->Storage()->Path() and -e $event->Storage()->Path()) ) { @@ -375,26 +379,28 @@ sub delete { if ( $$event{Id} ) { # Need to have an event Id if we are to delete from the db. - Info("Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from ".$event->Path()); + Info("Deleting event $event->{Id} from Monitor $event->{MonitorId} StartDateTime:$event->{StartDateTime} from ".$event->Path()); $ZoneMinder::Database::dbh->ping(); - $ZoneMinder::Database::dbh->begin_work(); - #$event->lock_and_load(); + my $in_transaction = $ZoneMinder::Database::dbh->{AutoCommit} ? 0 : 1; - ZoneMinder::Database::zmDbDo('DELETE FROM Frames WHERE EventId=?', $$event{Id}); - if ( $ZoneMinder::Database::dbh->errstr() ) { - $ZoneMinder::Database::dbh->commit(); - return; - } + $ZoneMinder::Database::dbh->begin_work() if ! $in_transaction; + + # Going to delete in order of least value to greatest value. Stats is least and references Frames ZoneMinder::Database::zmDbDo('DELETE FROM Stats WHERE EventId=?', $$event{Id}); if ( $ZoneMinder::Database::dbh->errstr() ) { - $ZoneMinder::Database::dbh->commit(); + $ZoneMinder::Database::dbh->commit() if ! $in_transaction; + return; + } + ZoneMinder::Database::zmDbDo('DELETE FROM Frames WHERE EventId=?', $$event{Id}); + if ( $ZoneMinder::Database::dbh->errstr() ) { + $ZoneMinder::Database::dbh->commit() if ! $in_transaction; return; } # Do it individually to avoid locking up the table for new events ZoneMinder::Database::zmDbDo('DELETE FROM Events WHERE Id=?', $$event{Id}); - $ZoneMinder::Database::dbh->commit(); + $ZoneMinder::Database::dbh->commit() if ! $in_transaction; } if ( ( $in_zmaudit or (!$Config{ZM_OPT_FAST_DELETE})) and $event->Storage()->DoDelete() ) { @@ -814,9 +820,9 @@ sub recover_timestamps { my $duration = $last_timestamp - $first_timestamp; $Event->Length($duration); - $Event->StartTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) ); - $Event->EndTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $last_timestamp) ); - Debug("From capture Jpegs have duration $duration = $last_timestamp - $first_timestamp : $$Event{StartTime} to $$Event{EndTime}"); + $Event->StartDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) ); + $Event->EndDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $last_timestamp) ); + Debug("From capture Jpegs have duration $duration = $last_timestamp - $first_timestamp : $$Event{StartDateTime} to $$Event{EndDateTime}"); $ZoneMinder::Database::dbh->begin_work(); foreach my $jpg ( @capture_jpgs ) { my ( $id ) = $jpg =~ /^(\d+)\-capture\.jpg$/; @@ -852,8 +858,8 @@ sub recover_timestamps { } my $seconds = ($h*60*60)+($m*60)+$s; $Event->Length($seconds.'.'.$u); - $Event->StartTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) ); - $Event->EndTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp+$seconds) ); + $Event->StartDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) ); + $Event->EndDateTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp+$seconds) ); } if ( @mp4_files ) { $Event->DefaultVideo($mp4_files[0]); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 9bb13132c..eb1b36077 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -77,6 +77,7 @@ UpdateDiskSpace UserId Background Concurrent +LockRows ); sub Execute { @@ -103,6 +104,8 @@ sub Execute { $sql =~ s/zmSystemLoad/$load/g; } + $sql .= ' FOR UPDATE' if $$self{LockRows}; + Debug("Filter::Execute SQL ($sql)"); my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) or Fatal("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr()); @@ -146,7 +149,7 @@ sub Sql { my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query_json}); my $sql = 'SELECT E.*, - unix_timestamp(E.StartTime) as Time, + unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName, M.DefaultRate, M.DefaultScale @@ -181,27 +184,25 @@ sub Sql { $self->{Sql} .= $Config{ZM_SERVER_ID}; # StartTime options } elsif ( $term->{attr} eq 'DateTime' ) { - $self->{Sql} .= 'E.StartTime'; - } elsif ( $term->{attr} eq 'StartDateTime' ) { - $self->{Sql} .= 'E.StartTime'; + $self->{Sql} .= 'E.StartDateTime'; } elsif ( $term->{attr} eq 'Date' ) { - $self->{Sql} .= 'to_days( E.StartTime )'; + $self->{Sql} .= 'to_days( E.StartDateTime )'; } elsif ( $term->{attr} eq 'StartDate' ) { - $self->{Sql} .= 'to_days( E.StartTime )'; + $self->{Sql} .= 'to_days( E.StartDateTime )'; } elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) { - $self->{Sql} .= 'extract( hour_second from E.StartTime )'; + $self->{Sql} .= 'extract( hour_second from E.StartDateTime )'; } elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) { - $self->{Sql} .= 'weekday( E.StartTime )'; + $self->{Sql} .= 'weekday( E.StartDateTime )'; # EndTIme options } elsif ( $term->{attr} eq 'EndDateTime' ) { - $self->{Sql} .= 'E.EndTime'; + $self->{Sql} .= 'E.EndDateTime'; } elsif ( $term->{attr} eq 'EndDate' ) { - $self->{Sql} .= 'to_days( E.EndTime )'; - } elsif ( $term->{attr} eq 'EndTime' ) { - $self->{Sql} .= 'extract( hour_second from E.EndTime )'; + $self->{Sql} .= 'to_days( E.EndDateTime )'; + } elsif ( $term->{attr} eq 'EndDateTime' ) { + $self->{Sql} .= 'extract( hour_second from E.EndDateTime )'; } elsif ( $term->{attr} eq 'EndWeekday' ) { - $self->{Sql} .= "weekday( E.EndTime )"; + $self->{Sql} .= "weekday( E.EndDateTime )"; } elsif ( $term->{attr} eq 'ExistsInFileSystem' ) { push @{$self->{PostSQLConditions}}, $term; $self->{Sql} .= 'TRUE /* ExistsInFileSystem */'; @@ -365,7 +366,7 @@ sub Sql { $sql .= ' AND ( '.join(' or ', @auto_terms).' )'; } if ( !$filter_expr->{sort_field} ) { - $filter_expr->{sort_field} = 'StartTime'; + $filter_expr->{sort_field} = 'StartDateTime'; $filter_expr->{sort_asc} = 0; } my $sort_column = ''; @@ -375,10 +376,14 @@ sub Sql { $sort_column = 'M.Name'; } elsif ( $filter_expr->{sort_field} eq 'Name' ) { $sort_column = 'E.Name'; + } elsif ( $filter_expr->{sort_field} eq 'StartDateTime' ) { + $sort_column = 'E.StartDateTime'; } elsif ( $filter_expr->{sort_field} eq 'StartTime' ) { - $sort_column = 'E.StartTime'; + $sort_column = 'E.StartDateTime'; } elsif ( $filter_expr->{sort_field} eq 'EndTime' ) { - $sort_column = 'E.EndTime'; + $sort_column = 'E.EndDateTime'; + } elsif ( $filter_expr->{sort_field} eq 'EndDateTime' ) { + $sort_column = 'E.EndDateTime'; } elsif ( $filter_expr->{sort_field} eq 'Secs' ) { $sort_column = 'E.Length'; } elsif ( $filter_expr->{sort_field} eq 'Frames' ) { @@ -394,7 +399,7 @@ sub Sql { } elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) { $sort_column = 'E.DiskSpace'; } else { - $sort_column = 'E.StartTime'; + $sort_column = 'E.StartDateTime'; } my $sort_order = $filter_expr->{sort_asc} ? 'ASC' : 'DESC'; $sql .= ' ORDER BY '.$sort_column.' '.$sort_order; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/General.pm b/scripts/ZoneMinder/lib/ZoneMinder/General.pm index 0d3784185..d68967fa9 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/General.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/General.pm @@ -262,21 +262,21 @@ sub createEvent { } $frame->{Type} = $frame->{Score}>0?'Alarm':'Normal' unless( $frame->{Type} ); $frame->{Delta} = $lastTimestamp?($frame->{TimeStamp}-$lastTimestamp):0.0; - $event->{StartTime} = $frame->{TimeStamp} unless ( $event->{StartTime} ); + $event->{StartDateTime} = $frame->{TimeStamp} unless ( $event->{StartDateTime} ); $event->{TotScore} += $frame->{Score}; $event->{MaxScore} = $frame->{Score} if ( $frame->{Score} > $event->{MaxScore} ); $event->{AlarmFrames}++ if ( $frame->{Type} eq 'Alarm' ); - $event->{EndTime} = $frame->{TimeStamp}; + $event->{EndDateTime} = $frame->{TimeStamp}; $lastTimestamp = $frame->{TimeStamp}; } $event->{Width} = $event->{monitor}->{Width} unless( $event->{Width} ); $event->{Height} = $event->{monitor}->{Height} unless( $event->{Height} ); $event->{AvgScore} = $event->{TotScore}/int($event->{AlarmFrames}); - $event->{Length} = $event->{EndTime} - $event->{StartTime}; + $event->{Length} = $event->{EndDateTime} - $event->{StartDateTime}; my %formats = ( - StartTime => 'from_unixtime(?)', - EndTime => 'from_unixtime(?)', + StartDateTime => 'from_unixtime(?)', + EndDateTime => 'from_unixtime(?)', ); my ( @fields, @formats, @values ); @@ -297,7 +297,7 @@ sub createEvent { $event->{Id} = $dbh->{mysql_insertid}; Info( "Created event ".$event->{Id} ); - if ( $event->{EndTime} ) { + if ( $event->{EndDateTime} ) { $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} if ( $event->{Name} eq 'New Event' ); my $sql = "update Events set Name = ? where Id = ?"; @@ -383,8 +383,8 @@ sub updateEvent { if ( $event->{Name} eq 'New Event' ); my %formats = ( - StartTime => 'from_unixtime(?)', - EndTime => 'from_unixtime(?)', + StartDateTime => 'from_unixtime(?)', + EndDateTime => 'from_unixtime(?)', ); my ( @values, @sets ); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index 0513f563e..cf5a0b508 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -159,7 +159,7 @@ sub new { ( $this->{fileName} = $0 ) =~ s|^.*/||; $this->{logPath} = $ZoneMinder::Config::Config{ZM_PATH_LOGS}; $this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log'; - ($this->{logFile}) = $this->{logFile} =~ /^([\w\.\/]+)$/; + ($this->{logFile}) = $this->{logFile} =~ /^([_\-\w\.\/]+)$/; $this->{trace} = 0; @@ -504,9 +504,9 @@ sub openFile { $LOGFILE->autoflush() if $this->{autoFlush}; my $webUid = (getpwnam($ZoneMinder::Config::Config{ZM_WEB_USER}))[2]; - Error("Can't get uid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webUid; + Error('Can\'t get uid for '.$ZoneMinder::Config::Config{ZM_WEB_USER}) if ! defined $webUid; my $webGid = (getgrnam($ZoneMinder::Config::Config{ZM_WEB_GROUP}))[2]; - Error("Can't get gid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webGid; + Error('Can\'t get gid for '.$ZoneMinder::Config::Config{ZM_WEB_USER}) if ! defined $webGid; if ( $> == 0 ) { # If we are root, we want to make sure that www-data or whatever owns the file chown($webUid, $webGid, $this->{logFile} ) or @@ -610,6 +610,7 @@ sub logInit( ;@ ) { my %options = @_ ? @_ : (); $logger = ZoneMinder::Logger->new() if !$logger; $logger->initialise(%options); + logSetSignal(); } sub logReinit { @@ -626,12 +627,26 @@ sub logHupHandler { $do_log_rotate = 1; } +sub logUSR1Handler { + $logger->level($logger->level()+1); + Info('Logger - Level changed to '. $logger->level() . '=>'.$codes{$logger->level()}); +} + +sub logUSR2Handler { + $logger->level($logger->level()-1); + Info('Logger - Level changed to '. $logger->level() . '=>'.$codes{$logger->level()}); +} + sub logSetSignal { $SIG{HUP} = \&logHupHandler; + $SIG{USR1} = \&logUSR1Handler; + $SIG{USR2} = \&logUSR2Handler; } sub logClearSignal { $SIG{HUP} = 'DEFAULT'; + $SIG{USR1} = 'DEFAULT'; + $SIG{USR2} = 'DEFAULT'; } sub logLevel { diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm index 55e46e114..80b4fb75a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Monitor.pm @@ -121,18 +121,6 @@ $serial = $primary_key = 'Id'; WebColour Exif Sequence - TotalEvents - TotalEventDiskSpace - HourEvents - HourEventDiskSpace - DayEvents - DayEventDiskSpace - WeekEvents - WeekEventDiskSpace - MonthEvents - MonthEventDiskSpace - ArchivedEvents - ArchivedEventDiskSpace ZoneCount Refresh DefaultCodec @@ -219,18 +207,6 @@ $serial = $primary_key = 'Id'; WebColour => '#ff0000', Exif => 0, Sequence => undef, - TotalEvents => undef, - TotalEventDiskSpace => undef, - HourEvents => undef, - HourEventDiskSpace => undef, - DayEvents => undef, - DayEventDiskSpace => undef, - WeekEvents => undef, - WeekEventDiskSpace => undef, - MonthEvents => undef, - MonthEventDiskSpace => undef, - ArchivedEvents => undef, - ArchivedEventDiskSpace => undef, ZoneCount => 0, Refresh => undef, DefaultCodec => 'auto', diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm b/scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm new file mode 100644 index 000000000..059c38121 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Monitor_Status.pm @@ -0,0 +1,99 @@ +# ========================================================================== +# +# ZoneMinder Monitor_STatus Module +# Copyright (C) 2020 ZoneMinder +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# ========================================================================== +# +# This module contains the common definitions and functions used by the rest +# of the ZoneMinder scripts +# +package ZoneMinder::Monitor_Status; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Object; + +#our @ISA = qw(Exporter ZoneMinder::Base); +use parent qw(ZoneMinder::Object); + +use vars qw/ $table $primary_key %fields $serial %defaults $debug/; +$table = 'Monitor_Status'; +$serial = $primary_key = 'MonitorId'; +%fields = map { $_ => $_ } qw( + MonitorId + TotalEvents + TotalEventDiskSpace + HourEvents + HourEventDiskSpace + DayEvents + DayEventDiskSpace + WeekEvents + WeekEventDiskSpace + MonthEvents + MonthEventDiskSpace + ArchivedEvents + ArchivedEventDiskSpace + ); + +%defaults = ( + TotalEvents => undef, + TotalEventDiskSpace => undef, + HourEvents => undef, + HourEventDiskSpace => undef, + DayEvents => undef, + DayEventDiskSpace => undef, + WeekEvents => undef, + WeekEventDiskSpace => undef, + MonthEvents => undef, + MonthEventDiskSpace => undef, + ArchivedEvents => undef, + ArchivedEventDiskSpace => undef, + ); + +sub Monitor { + return new ZoneMinder::Monitor( $_[0]{MonitorId} ); +} # end sub Monitor + +1; +__END__ + +=head1 NAME + +ZoneMinder::Monitor_Status - Perl Class for Monitor Status + +=head1 SYNOPSIS + +use ZoneMinder::Monitor_Status; + +=head1 AUTHOR + +Isaac Connor, Eisaac@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2017 ZoneMinder LLC + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index 569b3de9d..0f8bcb8ad 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -40,6 +40,10 @@ our @ISA = qw(ZoneMinder::Base); # # ========================================================================== +sub def_or_undef { + return defined($_[0]) ? $_[0] : 'undef'; +} + use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); @@ -370,40 +374,29 @@ sub set { $log->error("$type -> set called with non-hash params from $caller $line"); } - foreach my $field ( keys %fields ) { - if ( $params ) { - $log->debug("field: $field, param: ".$$params{$field}) if $debug; - if ( exists $$params{$field} ) { - $log->debug("field: $field, $$self{$field} =? param: ".$$params{$field}) if $debug; - if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) { -# Only make changes to fields that have changed - if ( defined $fields{$field} ) { - $$self{$field} = $$params{$field} if defined $fields{$field}; - push @set_fields, $fields{$field}, $$params{$field}; #mark for sql updating - } # end if - $log->debug("Running $field with $$params{$field}") if $debug; - if ( my $func = $self->can( $field ) ) { - $func->( $self, $$params{$field} ); - } # end if - } # end if - } # end if - } # end if $params - - if ( defined $fields{$field} ) { - if ( $$self{$field} ) { - $$self{$field} = transform( $type, $field, $$self{$field} ); - } # end if $$self{field} - } - } # end foreach field + if ( $params ) { + foreach my $field ( keys %{$params} ) { + $log->debug("field: $field, ".def_or_undef($$self{$field}).' =? param: '.def_or_undef($$params{$field})) if $debug; + if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) { + # Only make changes to fields that have changed + if ( defined $fields{$field} ) { + $$self{$field} = $$params{$field}; + push @set_fields, $fields{$field}, $$params{$field}; #mark for sql updating + } # end if has a column + $log->debug("Running $field with $$params{$field}") if $debug; + if ( my $func = $self->can( $field ) ) { + $func->( $self, $$params{$field} ); + } # end if has function + } # end if has change + } # end foreach field + } # end if $params foreach my $field ( keys %defaults ) { - if ( ( ! exists $$self{$field} ) or (!defined $$self{$field}) or ( $$self{$field} eq '' ) ) { - $log->debug("Setting default ($field) ($$self{$field}) ($defaults{$field}) ") if $debug; + $log->debug("Setting default ($field) (".def_or_undef($$self{$field}).') ('.def_or_undef($defaults{$field}).') ') if $debug; if ( defined $defaults{$field} ) { - $log->debug("Default $field is defined: $defaults{$field}") if $debug; - if ( $defaults{$field} eq 'NOW()' ) { - $$self{$field} = 'NOW()'; + if ( $defaults{$field} eq '' or $defaults{$field} eq 'NOW()' ) { + $$self{$field} = $defaults{$field}; } else { $$self{$field} = eval($defaults{$field}); $log->error( "Eval error of object default $field default ($defaults{$field}) Reason: " . $@ ) if $@; @@ -411,8 +404,9 @@ sub set { } else { $$self{$field} = $defaults{$field}; } # end if -#$$self{$field} = ( defined $defaults{$field} ) ? eval($defaults{$field}) : $defaults{$field}; - $log->debug("Setting default for ($field) using ($defaults{$field}) to ($$self{$field}) ") if $debug; + $log->debug("Setting default for ($field) using (".def_or_undef($defaults{$field}).') to ('.def_or_undef($$self{$field}).') ') if $debug; + } elsif ( defined $fields{$field} and $$self{$field} ) { + $$self{$field} = transform( $type, $field, $$self{$field} ); } # end if } # end foreach default return @set_fields; diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 80a227e2b..29ff7cab9 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -205,7 +205,7 @@ MAIN: while( $loop ) { my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql ) or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() ); - my $eventSelectSql = 'SELECT `Id`, (unix_timestamp() - unix_timestamp(`StartTime`)) AS Age + my $eventSelectSql = 'SELECT `Id`, (unix_timestamp() - unix_timestamp(`StartDateTime`)) AS Age FROM `Events` WHERE `MonitorId` = ?'.(@Storage_Areas ? ' AND `StorageId` IN ('.join(',',map { '?'} @Storage_Areas).')' : '' ). ' ORDER BY `Id`'; my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql ) or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() ); @@ -397,13 +397,13 @@ MAIN: while( $loop ) { my ( undef, $year, $month, $day ) = split('/', $day_dir); $year += 2000; my ( $hour, $minute, $second ) = split('/', $event_dir); - my $StartTime =sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', $year, $month, $day, $hour, $minute, $second); + my $StartDateTime =sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', $year, $month, $day, $hour, $minute, $second); my $Event = ZoneMinder::Event->find_one( MonitorId=>$monitor_dir, - StartTime=>$StartTime, + StartDateTime=>$StartDateTime, ); if ( $Event ) { - Debug("Found event matching starttime on monitor $monitor_dir at $StartTime: " . $Event->to_string()); + Debug("Found event matching StartDateTime on monitor $monitor_dir at $StartDateTime: " . $Event->to_string()); next; } aud_print("Deleting event directories with no event id information at $day_dir/$event_dir"); @@ -440,7 +440,7 @@ MAIN: while( $loop ) { $Event->Path(); $Event->age(); Debug("Have event $$Event{Id} at $$Event{Path}"); - $Event->StartTime(POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path())))); + $Event->StartDateTime(POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path())))); } # end foreach event } @@ -592,7 +592,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { Warning("Event $$Event{Id} is Archived. Taking no further action on it."); next; } - if ( !$Event->StartTime() ) { + if ( !$Event->StartDateTime() ) { aud_print("Event $$Event{Id} has no start time."); if ( confirm() ) { $Event->delete(); @@ -600,7 +600,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { } next; } - if ( ! $Event->EndTime() ) { + if ( ! $Event->EndDateTime() ) { if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { aud_print("Event $$Event{Id} has no end time and is $age seconds old. Deleting it."); if ( confirm() ) { @@ -639,9 +639,9 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}"); $Event->StorageId($$fs_events{$db_event}->StorageId()); } - if ( ! $Event->StartTime() ) { - Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}"); - $Event->StartTime($$fs_events{$db_event}->StartTime()); + if ( ! $Event->StartDateTime() ) { + Info("Updating StartDateTime on event $$Event{Id} from $$Event{StartDateTime} to $$fs_events{$db_event}{StartDateTime}"); + $Event->StartDateTime($$fs_events{$db_event}->StartDateTime()); } $Event->save(); @@ -683,12 +683,12 @@ if ( $level > 1 ) { # Remove empty events (with no frames) $cleaned = 0; Debug("Checking for Events with no Frames"); - my $selectEmptyEventsSql = 'SELECT `E`.`Id` AS `Id`, `E`.`StartTime`, `F`.`EventId` FROM `Events` AS E LEFT JOIN `Frames` AS F ON (`E`.`Id` = `F`.`EventId`) - WHERE isnull(`F`.`EventId`) AND now() - interval '.$Config{ZM_AUDIT_MIN_AGE}.' second > `E`.`StartTime`'; + my $selectEmptyEventsSql = 'SELECT `E`.`Id` AS `Id`, `E`.`StartDateTime`, `F`.`EventId` FROM `Events` AS E LEFT JOIN `Frames` AS F ON (`E`.`Id` = `F`.`EventId`) + WHERE isnull(`F`.`EventId`) AND now() - interval '.$Config{ZM_AUDIT_MIN_AGE}.' second > `E`.`StartDateTime`'; if ( my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql ) ) { if ( $res = $selectEmptyEventsSth->execute() ) { while( my $event = $selectEmptyEventsSth->fetchrow_hashref() ) { - aud_print("Found empty event with no frame records '$event->{Id}' at $$event{StartTime}"); + aud_print("Found empty event with no frame records '$event->{Id}' at $$event{StartDateTime}"); if ( confirm() ) { if ( $res = $deleteEventSth->execute($event->{Id}) ) { $cleaned = 1; @@ -750,7 +750,7 @@ if ( $level > 1 ) { #"SELECT E.Id, ANY_VALUE(E.MonitorId), # #max(F.TimeStamp) as EndTime, -#unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length, +#unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartDateTime) as Length, #max(F.FrameId) as Frames, #count(if(F.Score>0,1,NULL)) as AlarmFrames, #sum(F.Score) as TotScore, @@ -760,11 +760,11 @@ if ( $level > 1 ) { #WHERE isnull(E.Frames) or isnull(E.EndTime) #GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}.' second)' #; - 'SELECT *, unix_timestamp(`StartTime`) AS `TimeStamp` FROM `Events` WHERE `EndTime` IS NULL AND `StartTime` < (now() - interval '.$Config{ZM_AUDIT_MIN_AGE}.' second)'.($monitor_id?' AND MonitorId=?':''); + 'SELECT *, unix_timestamp(`StartDateTime`) AS `TimeStamp` FROM `Events` WHERE `EndDateTime` IS NULL AND `StartDateTime` < (now() - interval '.$Config{ZM_AUDIT_MIN_AGE}.' second)'.($monitor_id?' AND MonitorId=?':''); my $selectFrameDataSql = ' SELECT - max(`TimeStamp`) AS `EndTime`, + max(`TimeStamp`) AS `EndDateTime`, unix_timestamp(max(`TimeStamp`)) AS `EndTimeStamp`, max(`FrameId`) AS `Frames`, count(if(`Score`>0,1,NULL)) AS `AlarmFrames`, @@ -779,7 +779,7 @@ FROM `Frames` WHERE `EventId`=?'; my $updateUnclosedEventsSql = "UPDATE low_priority `Events` SET `Name` = ?, - `EndTime` = ?, + `EndDateTime` = ?, `Length` = ?, `Frames` = ?, `AlarmFrames` = ?, @@ -794,7 +794,7 @@ FROM `Frames` WHERE `EventId`=?'; $res = $selectUnclosedEventsSth->execute($monitor_id?$monitor_id:()) or Fatal("Can't execute: ".$selectUnclosedEventsSth->errstr()); while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() ) { - aud_print("Found open event '$event->{Id}' on Monitor $event->{MonitorId} at $$event{StartTime}"); + aud_print("Found open event '$event->{Id}' on Monitor $event->{MonitorId} at $$event{StartDateTime}"); if ( confirm('close', 'closing') ) { if ( ! ( $res = $selectFrameDataSth->execute($event->{Id}) ) ) { Error("Can't execute: $selectFrameDataSql:".$selectFrameDataSth->errstr()); @@ -808,7 +808,7 @@ FROM `Frames` WHERE `EventId`=?'; $event->{Id}, RECOVER_TAG ), - $frame->{EndTime}, + $frame->{EndDateTime}, $frame->{EndTimeStamp} - $event->{TimeStamp}, $frame->{Frames}, $frame->{AlarmFrames}, @@ -885,11 +885,11 @@ FROM `Frames` WHERE `EventId`=?'; $loop = $continuous; my $eventcounts_sql = ' - UPDATE `Monitors` SET - `TotalEvents`=(SELECT COUNT(`Id`) FROM `Events` WHERE `MonitorId`=`Monitors`.`Id`), - `TotalEventDiskSpace`=(SELECT SUM(`DiskSpace`) FROM `Events` WHERE `MonitorId`=`Monitors`.`Id` AND `DiskSpace` IS NOT NULL), - `ArchivedEvents`=(SELECT COUNT(`Id`) FROM `Events` WHERE `MonitorId`=`Monitors`.`Id` AND `Archived`=1), - `ArchivedEventDiskSpace`=(SELECT SUM(`DiskSpace`) FROM `Events` WHERE `MonitorId`=`Monitors`.`Id` AND `Archived`=1 AND `DiskSpace` IS NOT NULL) + UPDATE `Monitor_Status` SET + `TotalEvents`=(SELECT COUNT(`Id`) FROM `Events` WHERE `MonitorId`=`Monitor_Status`.`MonitorId`), + `TotalEventDiskSpace`=(SELECT SUM(`DiskSpace`) FROM `Events` WHERE `MonitorId`=`Monitor_Status`.`MonitorId` AND `DiskSpace` IS NOT NULL), + `ArchivedEvents`=(SELECT COUNT(`Id`) FROM `Events` WHERE `MonitorId`=`Monitor_Status`.`MonitorId` AND `Archived`=1), + `ArchivedEventDiskSpace`=(SELECT SUM(`DiskSpace`) FROM `Events` WHERE `MonitorId`=`Monitor_Status`.`MonitorId` AND `Archived`=1 AND `DiskSpace` IS NOT NULL) '; my $eventcounts_sth = $dbh->prepare_cached( $eventcounts_sql ); @@ -897,40 +897,40 @@ FROM `Frames` WHERE `EventId`=?'; $eventcounts_sth->finish(); my $eventcounts_hour_sql = ' - UPDATE `Monitors` INNER JOIN ( + UPDATE `Monitor_Status` INNER JOIN ( SELECT `MonitorId`, COUNT(*) AS `HourEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `HourEventDiskSpace` FROM `Events_Hour` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Monitors`.`Id` SET - `Monitors`.`HourEvents` = `E`.`HourEvents`, - `Monitors`.`HourEventDiskSpace` = `E`.`HourEventDiskSpace` + ) AS `E` ON `E`.`MonitorId`=`Monitor_Status`.`MonitorId` SET + `Monitor_Status`.`HourEvents` = `E`.`HourEvents`, + `Monitor_Status`.`HourEventDiskSpace` = `E`.`HourEventDiskSpace` '; my $eventcounts_day_sql = ' - UPDATE `Monitors` INNER JOIN ( + UPDATE `Monitor_Status` INNER JOIN ( SELECT `MonitorId`, COUNT(*) AS `DayEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `DayEventDiskSpace` FROM `Events_Day` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Monitors`.`Id` SET - `Monitors`.`DayEvents` = `E`.`DayEvents`, - `Monitors`.`DayEventDiskSpace` = `E`.`DayEventDiskSpace` + ) AS `E` ON `E`.`MonitorId`=`Monitor_Status`.`MonitorId` SET + `Monitor_Status`.`DayEvents` = `E`.`DayEvents`, + `Monitor_Status`.`DayEventDiskSpace` = `E`.`DayEventDiskSpace` '; my $eventcounts_week_sql = ' - UPDATE `Monitors` INNER JOIN ( + UPDATE `Monitor_Status` INNER JOIN ( SELECT `MonitorId`, COUNT(*) AS `WeekEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `WeekEventDiskSpace` FROM `Events_Week` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Monitors`.`Id` SET - `Monitors`.`WeekEvents` = `E`.`WeekEvents`, - `Monitors`.`WeekEventDiskSpace` = `E`.`WeekEventDiskSpace` + ) AS `E` ON `E`.`MonitorId`=`Monitor_Status`.`MonitorId` SET + `Monitor_Status`.`WeekEvents` = `E`.`WeekEvents`, + `Monitor_Status`.`WeekEventDiskSpace` = `E`.`WeekEventDiskSpace` '; my $eventcounts_month_sql = ' - UPDATE `Monitors` INNER JOIN ( + UPDATE `Monitor_Status` INNER JOIN ( SELECT `MonitorId`, COUNT(*) AS `MonthEvents`, SUM(COALESCE(`DiskSpace`,0)) AS `MonthEventDiskSpace` FROM `Events_Month` GROUP BY `MonitorId` - ) AS `E` ON `E`.`MonitorId`=`Monitors`.`Id` SET - `Monitors`.`MonthEvents` = `E`.`MonthEvents`, - `Monitors`.`MonthEventDiskSpace` = `E`.`MonthEventDiskSpace` + ) AS `E` ON `E`.`MonitorId`=`Monitor_Status`.`MonitorId` SET + `Monitor_Status`.`MonthEvents` = `E`.`MonthEvents`, + `Monitor_Status`.`MonthEventDiskSpace` = `E`.`MonthEventDiskSpace` '; my $eventcounts_hour_sth = $dbh->prepare_cached($eventcounts_hour_sql); my $eventcounts_day_sth = $dbh->prepare_cached($eventcounts_day_sql); diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 9a93c8953..834da99f8 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -277,6 +277,7 @@ FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) { sub checkFilter { my $filter = shift; + my $in_transaction = ZoneMinder::Database::start_transaction($dbh) if $$filter{LockRows}; my @Events = $filter->Execute(); Debug( join(' ', @@ -396,6 +397,7 @@ sub checkFilter { $ZoneMinder::Database::dbh->commit(); } # end if UpdateDiskSpace } # end foreach event + ZoneMinder::Database::end_transaction($dbh, $in_transaction) if $$filter{LockRows}; } # end sub checkFilter sub generateVideo { @@ -702,7 +704,7 @@ sub substituteTags { $text =~ s/%EN%/$Event->{Name}/g; $text =~ s/%EC%/$Event->{Cause}/g; $text =~ s/%ED%/$Event->{Notes}/g; - $text =~ s/%ET%/$Event->{StartTime}/g; + $text =~ s/%ET%/$Event->{StartDateTime}/g; $text =~ s/%EVF%/$$Event{DefaultVideo}/g; # Event video filename $text =~ s/%EL%/$Event->{Length}/g; $text =~ s/%EF%/$Event->{Frames}/g; diff --git a/scripts/zmrecover.pl.in b/scripts/zmrecover.pl.in index 58647b946..c52537b4e 100644 --- a/scripts/zmrecover.pl.in +++ b/scripts/zmrecover.pl.in @@ -231,7 +231,7 @@ Debug("@Monitors"); $Event->Height( $Monitor->Height() ); $Event->Orientation( $Monitor->Orientation() ); $Event->recover_timestamps(); - if ( $$Event{StartTime} ) { + if ( $$Event{StartDateTime} ) { $Event->save({}, 1); Info("Event resurrected as " . $Event->to_string() ); } else { @@ -294,7 +294,7 @@ Debug("@Monitors"); $Event->StorageId( $Storage->Id() ); $Event->DiskSpace( undef ); $Event->recover_timestamps(); - if ( $$Event{StartTime} ) { + if ( $$Event{StartDateTime} ) { $Event->save({}, 1); Info("Event resurrected as " . $Event->to_string() ); } else { @@ -309,13 +309,13 @@ Debug("@Monitors"); my ( undef, $year, $month, $day ) = split('/', $day_dir); $year += 2000; my ( $hour, $minute, $second ) = split('/', $event_dir); - my $StartTime =sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', $year, $month, $day, $hour, $minute, $second); + my $StartDateTime =sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', $year, $month, $day, $hour, $minute, $second); my $Event = ZoneMinder::Event->find_one( MonitorId=>$monitor_dir, - StartTime=>$StartTime, + StartDateTime=>$StartDateTime, ); if ( $Event ) { - Debug("Found event matching starttime on monitor $monitor_dir at $StartTime: " . $Event->to_string()); + Debug("Found event matching starttime on monitor $monitor_dir at $StartDateTime: " . $Event->to_string()); next; } @@ -358,7 +358,7 @@ Debug("@Monitors"); $Event->Orientation( $Monitor->Orientation() ); $Event->StorageId( $Storage->Id() ); $Event->recover_timestamps(); - if ( $$Event{StartTime} ) { + if ( $$Event{StartDateTime} ) { $Event->save({}, 1); Info("Event resurrected as " . $Event->to_string() ); } else { @@ -400,7 +400,7 @@ Debug("@Monitors"); $Event->Orientation( $Monitor->Orientation() ); $Event->StorageId( $Storage->Id() ); $Event->recover_timestamps(); - if ( $$Event{StartTime} ) { + if ( $$Event{StartDateTime} ) { $Event->save({}, 1); Info("Event resurrected as " . $Event->to_string() ); } else { diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in index 555d9c602..8aef4ddc6 100644 --- a/scripts/zmstats.pl.in +++ b/scripts/zmstats.pl.in @@ -43,10 +43,10 @@ while( 1 ) { } } - $dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr()); - $dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)') or Error($dbh->errstr()); - $dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr()); - $dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr()); + $dbh->do('DELETE FROM Events_Hour WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr()); + $dbh->do('DELETE FROM Events_Day WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 day)') or Error($dbh->errstr()); + $dbh->do('DELETE FROM Events_Week WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr()); + $dbh->do('DELETE FROM Events_Month WHERE StartDateTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr()); # Prune the Logs table if required if ( $Config{ZM_LOG_DATABASE_LIMIT} ) { diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in index 1ee392c1e..acd4ce255 100644 --- a/scripts/zmtrigger.pl.in +++ b/scripts/zmtrigger.pl.in @@ -187,7 +187,7 @@ while (1) { } else { Fatal("Can't select: $!"); } - } # end if select returned activitiy + } # end if select returned activity # Check polled connections foreach my $connection ( @in_poll_connections ) { @@ -266,7 +266,7 @@ while (1) { Debug('Checking for timed actions'); my $now = time(); foreach my $action_time ( sort( grep { $_ < $now } @action_times ) ) { - Info("Found " . scalar @{$actions{$action_time}} . "actions expiring at $action_time"); + Info('Found '.(scalar @{$actions{$action_time}}).'actions expiring at '.$action_time); foreach my $action ( @{$actions{$action_time}} ) { my $connection = $action->{connection}; Info("Found action '$$action{message}'"); @@ -319,7 +319,7 @@ exit; sub loadMonitor { my $monitor = shift; - Debug("Loading monitor $monitor"); + Debug('Loading monitor '.$monitor); zmMemInvalidate($monitor); if ( zmMemVerify($monitor) ) { # This will re-init shared memory @@ -334,7 +334,7 @@ sub loadMonitors { my %new_monitors = (); my $sql = 'SELECT * FROM `Monitors` - WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect\' )'. + WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect,Record\' )'. ( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' ) ; my $sth = $dbh->prepare_cached( $sql ) @@ -372,9 +372,10 @@ sub handleMessage { Warning("Can't find monitor '$id' for message '$message'"); return; } - Debug("Found monitor for id '$id'"); - - next if !zmMemVerify($monitor); + if ( !zmMemVerify($monitor) ) { + Warning("Can't verify monitor '$id' for message '$message'"); + return; + } Debug("Handling action '$action'"); if ( $action =~ /^(enable|disable)(?:[\+ ](\d+))?$/ ) { @@ -387,21 +388,25 @@ sub handleMessage { } # Force a reload $monitor_reload_time = 0; - Info("Set monitor to $state"); + Info('Set monitor to '.$state); if ( $delay ) { - my $action_text = $id.'|'.( ($state eq 'enable') - ? 'disable' - : 'enable' - ); + my $action_text = $id.'|'.(($state eq 'enable') ? 'disable' : 'enable'); handleDelay($delay, $connection, $action_text); } } elsif ( $action =~ /^(on|off)(?:[ \+](\d+))?$/ ) { - next if !$monitor->{Enabled}; + if ( !$monitor->{Enabled} ) { + Info('Motion detection not enabled on monitor '.$id); + return; + } my $trigger = $1; my $delay = $2; my $trigger_data; if ( $trigger eq 'on' ) { + if ( $score <= 0 ) { + Warning('Triggering on with invalid score will have no result.'); + return; + } zmTriggerEventOn($monitor, $score, $cause, $text); zmTriggerShowtext($monitor, $showtext) if defined($showtext); Info("Trigger '$trigger' '$cause'"); diff --git a/scripts/zmvideo.pl.in b/scripts/zmvideo.pl.in index 04cfabb04..c676eb164 100644 --- a/scripts/zmvideo.pl.in +++ b/scripts/zmvideo.pl.in @@ -201,7 +201,7 @@ if ( $event_id ) { my $sql = " SELECT (SELECT max(Delta) FROM Frames WHERE EventId=Events.Id)-(SELECT min(Delta) FROM Frames WHERE EventId=Events.Id) as FullLength, Events.*, - unix_timestamp(Events.StartTime) as Time, + unix_timestamp(Events.StartDateTime) as Time, M.Name as MonitorName, M.Palette FROM Events diff --git a/src/zm_comms.h b/src/zm_comms.h index 46face33c..a5af97dd5 100644 --- a/src/zm_comms.h +++ b/src/zm_comms.h @@ -49,7 +49,7 @@ protected: const int &mWd; protected: - CommsBase( int &rd, int &wd ) : mRd( rd ), mWd( wd ) { + CommsBase(const int &rd, const int &wd) : mRd(rd), mWd(wd) { } virtual ~CommsBase() { } @@ -62,10 +62,10 @@ public: public: int getReadDesc() const { - return( mRd ); + return mRd; } int getWriteDesc() const { - return( mWd ); + return mWd; } int getMaxDesc() const { return( mRd>mWd?mRd:mWd ); diff --git a/src/zm_config.h b/src/zm_config.h index d348f38e9..687ac430c 100644 --- a/src/zm_config.h +++ b/src/zm_config.h @@ -112,8 +112,9 @@ public: double DecimalValue() const; const char *StringValue() const; - ConfigItem &operator=(const ConfigItem item) { - Copy(item);return *this; + ConfigItem &operator=(const ConfigItem &item) { + Copy(item); + return *this; } inline operator bool() const { return BooleanValue(); diff --git a/src/zm_coord.h b/src/zm_coord.h index c6234fb1c..bb49c2f8b 100644 --- a/src/zm_coord.h +++ b/src/zm_coord.h @@ -48,12 +48,12 @@ public: return( result ); } - inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); } - inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); } - inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); } - inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); } - inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); } - inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); } + inline bool operator==( const Coord &coord ) const { return( x == coord.x && y == coord.y ); } + inline bool operator!=( const Coord &coord ) const { return( x != coord.x || y != coord.y ); } + inline bool operator>( const Coord &coord ) const { return( x > coord.x && y > coord.y ); } + inline bool operator>=( const Coord &coord ) const { return( !(operator<(coord)) ); } + inline bool operator<( const Coord &coord ) const { return( x < coord.x && y < coord.y ); } + inline bool operator<=( const Coord &coord ) const { return( !(operator>(coord)) ); } inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); } inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); } diff --git a/src/zm_db.h b/src/zm_db.h index 11fc9faaa..f9f168cd0 100644 --- a/src/zm_db.h +++ b/src/zm_db.h @@ -28,7 +28,7 @@ class zmDbRow { MYSQL_RES *result_set; MYSQL_ROW row; public: - zmDbRow() { result_set = nullptr; row = nullptr; }; + zmDbRow() : result_set(nullptr), row(nullptr) { }; MYSQL_RES *fetch( const char *query ); zmDbRow( MYSQL_RES *, MYSQL_ROW *row ); ~zmDbRow(); diff --git a/src/zm_event.cpp b/src/zm_event.cpp index e8821b1eb..63d1f0b9a 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -39,6 +39,7 @@ //#define USE_PREPARED_SQL 1 const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" }; +#define MAX_DB_FRAMES 120 char frame_insert_sql[ZM_SQL_LGE_BUFSIZ] = "INSERT INTO `Frames` (`EventId`, `FrameId`, `Type`, `TimeStamp`, `Delta`, `Score`) VALUES "; int Event::pre_alarm_count = 0; @@ -69,7 +70,7 @@ Event::Event( start_time = now; } else if ( start_time.tv_sec > now.tv_sec ) { Error( - "StartTime in the future %u.%u > %u.%u", + "StartDateTime in the future %u.%u > %u.%u", start_time.tv_sec, start_time.tv_usec, now.tv_sec, now.tv_usec ); start_time = now; @@ -109,12 +110,10 @@ Event::Event( ); db_mutex.lock(); - if ( mysql_query(&dbconn, sql) ) { + while ( mysql_query(&dbconn, sql) ) { db_mutex.unlock(); Error("Can't insert event: %s. sql was (%s)", mysql_error(&dbconn), sql); - return; - } else { - Debug(2, "Created new event with %s", sql); + db_mutex.lock(); } id = mysql_insert_id(&dbconn); @@ -254,6 +253,11 @@ Event::~Event() { videoStore = nullptr; } + // endtime is set in AddFrame, so SHOULD be set to the value of the last frame timestamp. + if ( !end_time.tv_sec ) { + Warning("Empty endtime for event. Should not happen. Setting to now."); + gettimeofday(&end_time, nullptr); + } struct DeltaTimeval delta_time; DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2); Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec); @@ -284,7 +288,7 @@ Event::~Event() { // Should not be static because we might be multi-threaded char sql[ZM_SQL_LGE_BUFSIZ]; snprintf(sql, sizeof(sql), - "UPDATE Events SET Name='%s%" PRIu64 "', EndTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64 " AND Name='New Event'", + "UPDATE Events SET Name='%s%" PRIu64 "', EndDateTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64 " AND Name='New Event'", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, @@ -300,7 +304,7 @@ Event::~Event() { if ( !mysql_affected_rows(&dbconn) ) { // Name might have been changed during recording, so just do the update without changing the name. snprintf(sql, sizeof(sql), - "UPDATE Events SET EndTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, + "UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, @@ -338,7 +342,7 @@ bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, - bool alarm_frame) { + bool alarm_frame) const { int thisquality = (alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ? @@ -476,7 +480,7 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) { } // void Event::updateNotes(const StringSetMap &newNoteSetMap) void Event::AddFrames(int n_frames, Image **images, struct timeval **timestamps) { - for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { + for ( int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE ) { AddFramesInternal(n_frames, i, images, timestamps); } } @@ -546,6 +550,7 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str } else { Debug(1, "No valid pre-capture frames to add"); } + end_time = *timestamps[n_frames-1]; } // void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, struct timeval **timestamps) void Event::AddPacket(ZMPacket *packet, int score, Image *alarm_image) { @@ -567,6 +572,10 @@ void Event::AddPacket(ZMPacket *packet, int score, Image *alarm_image) { void Event::WriteDbFrames() { char *frame_insert_values_ptr = (char *)&frame_insert_sql + 90; // 90 == strlen(frame_insert_sql); + + /* Each frame needs about 63 chars. So if we buffer too many frames, we will exceed the size of frame_insert_sql; + */ + Debug(1, "Inserting %d frames", frame_data.size()); while ( frame_data.size() ) { Frame *frame = frame_data.front(); frame_data.pop(); @@ -582,7 +591,7 @@ void Event::WriteDbFrames() { frame->score); delete frame; } - *(frame_insert_values_ptr-1) = '\0'; // The -2 is for the extra , added for values above + *(frame_insert_values_ptr-1) = '\0'; // The -1 is for the extra , added for values above db_mutex.lock(); int rc = mysql_query(&dbconn, frame_insert_sql); db_mutex.unlock(); @@ -681,12 +690,20 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a // The idea is to write out 1/sec frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score)); double fps = monitor->get_capture_fps(); - if ( write_to_db or ( fps and (frame_data.size() > fps) ) or frame_type==BULK ) { - Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > capture fps %f or BULK", + if ( write_to_db + or + (frame_data.size() > MAX_DB_FRAMES) + or + (frame_type==BULK) + or + ( fps and (frame_data.size() > fps) ) + ) { + Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK", frame_data.size(), write_to_db, fps); WriteDbFrames(); last_db_frame = frames; + static char sql[ZM_SQL_MED_BUFSIZ]; snprintf(sql, sizeof(sql), "UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, ( delta_time.positive?"":"-" ), @@ -706,6 +723,9 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a db_mutex.lock(); } db_mutex.unlock(); + } else { + Debug(1, "Not Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK", + frame_data.size(), write_to_db, fps); } // end if frame_type == BULK } // end if db_frame diff --git a/src/zm_event.h b/src/zm_event.h index ea6dd53aa..b6544ea0c 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -113,17 +113,16 @@ class Event { ~Event(); uint64_t Id() const { return id; } - const std::string &Cause() { return cause; } + const std::string &Cause() const { return cause; } int Frames() const { return frames; } int AlarmFrames() const { return alarm_frames; } const struct timeval &StartTime() const { return start_time; } const struct timeval &EndTime() const { return end_time; } - struct timeval &StartTime() { return start_time; } - struct timeval &EndTime() { return end_time; } bool SendFrameImage( const Image *image, bool alarm_frame=false ); - bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); + bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ) const; + bool WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow ); void updateNotes( const StringSetMap &stringSetMap ); @@ -147,7 +146,7 @@ class Event { return Event::getSubPath( localtime( time ) ); } - const char* getEventFile(void) { + const char* getEventFile(void) const { return video_file.c_str(); } diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index b8de60e46..2c8ec7c08 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -45,7 +45,7 @@ bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) { static char sql[ZM_SQL_SML_BUFSIZ]; snprintf(sql, sizeof(sql), "SELECT `Id` FROM `Events` WHERE " - "`MonitorId` = %d AND unix_timestamp(`EndTime`) > %ld " + "`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld " "ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time); if ( mysql_query(&dbconn, sql) ) { @@ -116,8 +116,8 @@ bool EventStream::loadEventData(uint64_t event_id) { static char sql[ZM_SQL_MED_BUFSIZ]; snprintf(sql, sizeof(sql), - "SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, " - "unix_timestamp( `EndTime` ) AS EndTimestamp, " + "SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartDateTime` ) AS StartTimestamp, " + "unix_timestamp( `EndDateTime` ) AS EndTimestamp, " "(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, " "`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id); @@ -189,13 +189,13 @@ bool EventStream::loadEventData(uint64_t event_id) { if ( storage_path[0] == '/' ) snprintf(event_data->path, sizeof(event_data->path), - "%s/%d/%02d/%02d/%02d/%02d/%02d/%02d", + "%s/%u/%02d/%02d/%02d/%02d/%02d/%02d", storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec); else snprintf(event_data->path, sizeof(event_data->path), - "%s/%s/%d/%02d/%02d/%02d/%02d/%02d/%02d", + "%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec); @@ -203,23 +203,23 @@ bool EventStream::loadEventData(uint64_t event_id) { struct tm *event_time = localtime(&event_data->start_time); if ( storage_path[0] == '/' ) snprintf(event_data->path, sizeof(event_data->path), - "%s/%d/%04d-%02d-%02d/%" PRIu64, + "%s/%u/%04d-%02d-%02d/%" PRIu64, storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_data->event_id); else snprintf(event_data->path, sizeof(event_data->path), - "%s/%s/%d/%04d-%02d-%02d/%" PRIu64, + "%s/%s/%u/%04d-%02d-%02d/%" PRIu64, staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, - event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, + event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_data->event_id); } else { if ( storage_path[0] == '/' ) - snprintf(event_data->path, sizeof(event_data->path), "%s/%d/%" PRIu64, + snprintf(event_data->path, sizeof(event_data->path), "%s/%u/%" PRIu64, storage_path, event_data->monitor_id, event_data->event_id); else - snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%d/%" PRIu64, + snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%u/%" PRIu64, staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id); } @@ -424,6 +424,7 @@ void EventStream::processCommand(const CmdMsg *msg) { switch ( replay_rate ) { case -1 * ZM_RATE_BASE : replay_rate = -2 * ZM_RATE_BASE; + break; case -2 * ZM_RATE_BASE : replay_rate = -5 * ZM_RATE_BASE; break; @@ -526,7 +527,7 @@ void EventStream::processCommand(const CmdMsg *msg) { if ( offset < 0.0 ) { Warning("Invalid offset, not seeking"); break; - } + } // This should get us close, but not all frames will have the same duration curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration)+1; if ( event_data->frames[curr_frame_id-1].offset > offset ) { @@ -538,10 +539,10 @@ void EventStream::processCommand(const CmdMsg *msg) { } if ( curr_frame_id < 1 ) { curr_frame_id = 1; - } else if ( curr_frame_id > event_data->last_frame_id ) { + } else if ( (unsigned long)curr_frame_id > event_data->last_frame_id ) { curr_frame_id = event_data->last_frame_id; } - + curr_stream_time = event_data->frames[curr_frame_id-1].timestamp; Debug(1, "Got SEEK command, to %f (new current frame id: %d offset %f)", offset, curr_frame_id, event_data->frames[curr_frame_id-1].offset); @@ -794,7 +795,7 @@ bool EventStream::sendFrame(int delta_us) { Error("Unable to get a frame"); return false; } - + Image *send_image = prepareImage(image); static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; int img_buffer_size = 0; @@ -881,7 +882,7 @@ void EventStream::runStream() { // If we are streaming and this frame is due to be sent // frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2 // so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc. - + if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) { send_frame = true; } @@ -963,7 +964,7 @@ void EventStream::runStream() { if ( (mode == MODE_SINGLE) && ( (curr_frame_id < 1 ) || - ((unsigned int)curr_frame_id >= event_data->frame_count) + ((unsigned int)curr_frame_id >= event_data->frame_count) ) ) { Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start"); @@ -1054,9 +1055,8 @@ void EventStream::runStream() { closeComms(); } // end void EventStream::runStream() -bool EventStream::send_file(const char * filepath) { +bool EventStream::send_file(const char *filepath) { static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; - int rc; int img_buffer_size = 0; uint8_t *img_buffer = temp_img_buffer; @@ -1084,7 +1084,7 @@ bool EventStream::send_file(const char * filepath) { Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno)); return false; } - rc = zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size); + int rc = zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size); if ( rc == (int)filestat.st_size ) { // Success fclose(fdj); /* Close the file handle */ diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 5013e5487..39daf23d0 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -76,7 +76,7 @@ class EventStream : public StreamBase { StreamMode mode; bool forceEventChange; - unsigned long curr_frame_id; + long curr_frame_id; double curr_stream_time; bool send_frame; struct timeval start; // clock time when started the event @@ -133,11 +133,11 @@ class EventStream : public StreamBase { void setStreamMode( StreamMode p_mode ) { mode = p_mode; } - void runStream(); + void runStream() override; Image *getImage(); private: - bool send_file( const char *file_path ); - bool send_buffer( uint8_t * buffer, int size ); + bool send_file(const char *filepath); + bool send_buffer(uint8_t * buffer, int size); Storage *storage; FFmpeg_Input *ffmpeg_input; AVCodecContext *input_codec_context; diff --git a/src/zm_exception.h b/src/zm_exception.h index a02653b88..1bceeed8a 100644 --- a/src/zm_exception.h +++ b/src/zm_exception.h @@ -22,8 +22,7 @@ #include -class Exception -{ +class Exception { protected: typedef enum { INFO, WARNING, ERROR, FATAL } Severity; @@ -32,33 +31,28 @@ protected: Severity mSeverity; public: - Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity ) + explicit Exception(const std::string &message, const Severity severity=ERROR) : + mMessage(message), + mSeverity(severity) { } -public: - const std::string &getMessage() const - { - return( mMessage ); + const std::string &getMessage() const { + return mMessage; } - Severity getSeverity() const - { - return( mSeverity ); + Severity getSeverity() const { + return mSeverity; } - bool isInfo() const - { - return( mSeverity == INFO ); + bool isInfo() const { + return mSeverity == INFO; } - bool isWarning() const - { + bool isWarning() const { return( mSeverity == WARNING ); } - bool isError() const - { + bool isError() const { return( mSeverity == ERROR ); } - bool isFatal() const - { + bool isFatal() const { return( mSeverity == FATAL ); } }; diff --git a/src/zm_ffmpeg_input.h b/src/zm_ffmpeg_input.h index 41dc03008..30af56a32 100644 --- a/src/zm_ffmpeg_input.h +++ b/src/zm_ffmpeg_input.h @@ -24,10 +24,10 @@ class FFmpeg_Input { int Close(); AVFrame *get_frame( int stream_id=-1 ); AVFrame *get_frame( int stream_id, double at ); - int get_video_stream_id() { + int get_video_stream_id() const { return video_stream_id; } - int get_audio_stream_id() { + int get_audio_stream_id() const { return audio_stream_id; } diff --git a/src/zm_fifo.h b/src/zm_fifo.h index 065fd569c..abd666891 100644 --- a/src/zm_fifo.h +++ b/src/zm_fifo.h @@ -68,19 +68,26 @@ class FifoStream : public StreamBase { ); protected: - typedef enum { MJPEG, RAW } StreamType; + typedef enum { UNKNOWN, MJPEG, RAW } StreamType; StreamType stream_type; bool sendMJEGFrames(); bool sendRAWFrames(); void processCommand(const CmdMsg *msg) {} public: - FifoStream() {} + FifoStream() : + stream_path(nullptr), + fd(0), + total_read(0), + bytes_read(0), + frame_count(0), + stream_type(UNKNOWN) + {} static void fifo_create_if_missing( const char * path, bool delete_fake_fifo = true); void setStreamStart(const char * path); void setStreamStart(int monitor_id, const char * format); - void runStream(); + void runStream() override; }; #endif // ZM_FIFO_H diff --git a/src/zm_local_camera.h b/src/zm_local_camera.h index c466f72b8..d31fa3e20 100644 --- a/src/zm_local_camera.h +++ b/src/zm_local_camera.h @@ -149,16 +149,17 @@ public: int Palette() const { return( palette ); } int Extras() const { return( extras ); } - int Brightness( int p_brightness=-1 ); - int Hue( int p_hue=-1 ); - int Colour( int p_colour=-1 ); - int Contrast( int p_contrast=-1 ); + int Brightness( int p_brightness=-1 ) override; + int Hue( int p_hue=-1 ) override; + int Colour( int p_colour=-1 ) override; + int Contrast( int p_contrast=-1 ) override; + + int PrimeCapture()override ; + int PreCapture()override ; + int Capture(ZMPacket &p) override; + int PostCapture()override ; + int Close() override { return 0; }; - int PrimeCapture(); - int PreCapture(); - int Capture(ZMPacket &p); - int PostCapture(); - int Close() { return 0; }; static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); AVStream* get_VideoStream(); }; diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index 619cba115..6fad415a7 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -44,18 +44,6 @@ Logger *Logger::smInstance = nullptr; Logger::StringMap Logger::smCodes; Logger::IntMap Logger::smSyslogPriorities; -#if 0 -static void subtractTime( struct timeval * const tp1, struct timeval * const tp2 ) { - tp1->tv_sec -= tp2->tv_sec; - if ( tp1->tv_usec <= tp2->tv_usec ) { - tp1->tv_sec--; - tp1->tv_usec = 1000000 - (tp2->tv_usec - tp1->tv_usec); - } else { - tp1->tv_usec = tp1->tv_usec - tp2->tv_usec; - } -} -#endif - void Logger::usrHandler(int sig) { Logger *logger = fetch(); if ( sig == SIGUSR1 ) diff --git a/src/zm_logger.h b/src/zm_logger.h index 82144a1c9..ad1ac398b 100644 --- a/src/zm_logger.h +++ b/src/zm_logger.h @@ -121,7 +121,7 @@ private: Logger(); ~Logger(); - int limit(int level) { + int limit(const int level) const { if ( level > DEBUG9 ) return DEBUG9; if ( level < NOLOG ) @@ -163,7 +163,7 @@ public: } Level level(Level=NOOPT); - bool debugOn() { + bool debugOn() const { return mEffectiveLevel >= DEBUG1; } diff --git a/src/zm_mem_utils.h b/src/zm_mem_utils.h index 6ef1bdac5..a9e1ab097 100644 --- a/src/zm_mem_utils.h +++ b/src/zm_mem_utils.h @@ -26,7 +26,7 @@ inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) { uint8_t* retptr; #if HAVE_POSIX_MEMALIGN - if ( posix_memalign((void**)&retptr,reqalignment,reqsize) != 0 ) + if ( posix_memalign((void**)&retptr, reqalignment, reqsize) != 0 ) return nullptr; return retptr; @@ -39,7 +39,7 @@ inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) { alloc = retptr + sizeof(void*); - if(((long)alloc % reqalignment) != 0) + if ( ((long)alloc % reqalignment) != 0 ) alloc = alloc + (reqalignment - ((long)alloc % reqalignment)); /* Store a pointer before to the start of the block, just before returned aligned memory */ diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index db23cfa48..25acabdf4 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2465,7 +2465,6 @@ int Monitor::Capture() { } // end if result } // end if deinterlacing - // Icon: I'm not sure these should be here. They have nothing to do with capturing if ( shared_data->action & GET_SETTINGS ) { shared_data->brightness = camera->Brightness(); @@ -2527,23 +2526,10 @@ void Monitor::TimestampImage(Image *ts_image, const struct timeval *ts_time) con bool Monitor::closeEvent() { if ( !event ) - return true; -#if 0 - if ( event_delete_thread ) { - event_delete_thread->join(); - delete event_delete_thread; - event_delete_thread = NULL; - } - event_delete_thread = new std::thread([](Event *event) { - Event * e = event; - event = nullptr; - delete e; - e = nullptr; - }, event); -#else + return false; + delete event; event = nullptr; -#endif video_store_data->recording = (struct timeval){0}; return true; } // end bool Monitor::closeEvent() diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 792bf7c55..e71ddffe2 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -419,7 +419,7 @@ public: inline Function GetFunction() const { return function; } - inline bool Enabled() { + inline bool Enabled() const { if ( function <= MONITOR ) return false; return enabled; @@ -427,7 +427,7 @@ public: inline const char *EventPrefix() const { return event_prefix; } - inline bool Ready() { + inline bool Ready() const { if ( function <= MONITOR ) { Error("Should not be calling Ready if the function doesn't include motion detection"); return false; @@ -435,15 +435,15 @@ public: if ( image_count >= ready_count ) { return true; } - Debug(2, "Not ready because image_count(%d) <= ready_count(%d)", image_count, ready_count ); + Debug(2, "Not ready because image_count(%d) <= ready_count(%d)", image_count, ready_count); return false; } - inline bool Active() { + inline bool Active() const { if ( function <= MONITOR ) return false; return( enabled && shared_data->active ); } - inline bool Exif() { + inline bool Exif() const { return embed_exif; } inline bool RecordAudio() { @@ -474,7 +474,7 @@ public: void SetVideoWriterEventId( uint64_t p_event_id ) { video_store_data->current_event = p_event_id; } struct timeval GetVideoWriterStartTime() const { return video_store_data->recording; } - void SetVideoWriterStartTime(struct timeval &t) { video_store_data->recording = t; } + void SetVideoWriterStartTime(const struct timeval &t) { video_store_data->recording = t; } unsigned int GetPreEventCount() const { return pre_event_count; }; struct timeval GetVideoBufferDuration() const { return video_buffer_duration; }; @@ -502,7 +502,7 @@ public: inline void setStartupTime( time_t p_time ) { shared_data->startup_time = p_time; } void get_ref_image(); - int LabelSize() { return label_size; } + int LabelSize() const { return label_size; } void actionReload(); void actionEnable(); diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index e586a0910..68d3bbc5d 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -292,7 +292,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) { if ( (nbytes = sendto(sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr))) < 0 ) { //if ( errno != EAGAIN ) { - Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); + Error("Can't sendto on sd %d: %s", sd, strerror(errno)); //exit( -1 ); } } @@ -507,7 +507,7 @@ void MonitorStream::runStream() { const int max_swap_len_suffix = 15; int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator - int subfolder1_length = snprintf(nullptr, 0, "/zmswap-m%d", monitor->Id()) + 1; + int subfolder1_length = snprintf(nullptr, 0, "/zmswap-m%u", monitor->Id()) + 1; int subfolder2_length = snprintf(nullptr, 0, "/zmswap-q%06d", connkey) + 1; int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length; @@ -878,7 +878,7 @@ void MonitorStream::SingleImageRaw(int scale) { } fprintf(stdout, - "Content-Length: %d\r\n" + "Content-Length: %u\r\n" "Content-Type: image/x-rgb\r\n\r\n", snap_image->Size()); fwrite(snap_image->Buffer(), snap_image->Size(), 1, stdout); diff --git a/src/zm_monitorstream.h b/src/zm_monitorstream.h index 24655b636..20842fa55 100644 --- a/src/zm_monitorstream.h +++ b/src/zm_monitorstream.h @@ -71,7 +71,7 @@ class MonitorStream : public StreamBase { bool setStreamStart(int monitor_id) { return loadMonitor(monitor_id); } - void runStream(); + void runStream() override; }; #endif // ZM_MONITORSTREAM_H diff --git a/src/zm_signal.cpp b/src/zm_signal.cpp index 81012c07e..2756802a3 100644 --- a/src/zm_signal.cpp +++ b/src/zm_signal.cpp @@ -29,15 +29,13 @@ bool zm_reload = false; bool zm_terminate = false; -RETSIGTYPE zm_hup_handler(int signal) -{ +RETSIGTYPE zm_hup_handler(int signal) { // Shouldn't do complex things in signal handlers, logging is complex and can block due to mutexes. //Info("Got signal %d (%s), reloading", signal, strsignal(signal)); zm_reload = true; } -RETSIGTYPE zm_term_handler(int signal) -{ +RETSIGTYPE zm_term_handler(int signal) { // Shouldn't do complex things in signal handlers, logging is complex and can block due to mutexes. //Info("Got signal %d (%s), exiting", signal, strsignal(signal)); zm_terminate = true; @@ -55,8 +53,7 @@ RETSIGTYPE zm_die_handler(int signal) #if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) void *ip = nullptr; void *cr2 = nullptr; - if (info && context) { - + if ( info && context ) { Debug(1, "Signal information: number %d code %d errno %d pid %d uid %d status %d", signal, info->si_code, info->si_errno, info->si_pid, @@ -79,7 +76,7 @@ RETSIGTYPE zm_die_handler(int signal) #endif // defined(__x86_64__) // Print the signal address and instruction pointer if available - if (ip) { + if ( ip ) { Error("Signal address is %p, from %p", cr2, ip); } else { Error("Signal address is %p, no instruction pointer", cr2); @@ -115,8 +112,7 @@ RETSIGTYPE zm_die_handler(int signal) exit(signal); } -void zmSetHupHandler(SigHandler * handler) -{ +void zmSetHupHandler(SigHandler * handler) { sigset_t block_set; sigemptyset(&block_set); struct sigaction action, old_action; @@ -127,8 +123,7 @@ void zmSetHupHandler(SigHandler * handler) sigaction(SIGHUP, &action, &old_action); } -void zmSetTermHandler(SigHandler * handler) -{ +void zmSetTermHandler(SigHandler * handler) { sigset_t block_set; sigemptyset(&block_set); struct sigaction action, old_action; @@ -141,8 +136,7 @@ void zmSetTermHandler(SigHandler * handler) sigaction(SIGQUIT, &action, &old_action); } -void zmSetDieHandler(SigHandler * handler) -{ +void zmSetDieHandler(SigHandler * handler) { sigset_t block_set; sigemptyset(&block_set); struct sigaction action, old_action; @@ -163,19 +157,16 @@ void zmSetDieHandler(SigHandler * handler) sigaction(SIGFPE, &action, &old_action); } -void zmSetDefaultHupHandler() -{ +void zmSetDefaultHupHandler() { zmSetHupHandler((SigHandler *) zm_hup_handler); } -void zmSetDefaultTermHandler() -{ +void zmSetDefaultTermHandler() { zmSetTermHandler((SigHandler *) zm_term_handler); } -void zmSetDefaultDieHandler() -{ - if (config.dump_cores) { +void zmSetDefaultDieHandler() { + if ( config.dump_cores ) { // Do nothing } else { zmSetDieHandler((SigHandler *) zm_die_handler); diff --git a/src/zm_time.h b/src/zm_time.h index 0f7dbc794..490ca1e2c 100644 --- a/src/zm_time.h +++ b/src/zm_time.h @@ -76,7 +76,9 @@ struct DeltaTimeval #define USEC_PER_SEC 1000000 #define MSEC_PER_SEC 1000 +/* extern struct timeval tv; +*/ inline int tvDiffUsec( struct timeval first, struct timeval last ) { diff --git a/src/zm_user.h b/src/zm_user.h index 42fe07554..519ea13de 100644 --- a/src/zm_user.h +++ b/src/zm_user.h @@ -46,7 +46,7 @@ class User { User(); explicit User(const MYSQL_ROW &dbrow); ~User(); - User(User &u) { Copy(u); } + User(const User &u) { Copy(u); } void Copy(const User &u); User& operator=(const User &u) { Copy(u); return *this; diff --git a/src/zm_video.cpp b/src/zm_video.cpp index d97c75167..b24905ece 100644 --- a/src/zm_video.cpp +++ b/src/zm_video.cpp @@ -216,7 +216,7 @@ int X264MP4Writer::Open() { int X264MP4Writer::Close() { /* Flush all pending frames */ for ( int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) { -Debug(1,"Encoding delayed frame"); + Debug(1, "Encoding delayed frame"); if ( x264encodeloop(true) < 0 ) break; } @@ -227,7 +227,7 @@ Debug(1,"Encoding delayed frame"); /* Close MP4 handle */ MP4Close(mp4h); - Debug(1,"Optimising"); + Debug(1, "Optimising"); /* Required for proper HTTP streaming */ MP4Optimize((path + ".incomplete").c_str(), path.c_str()); diff --git a/src/zmc.cpp b/src/zmc.cpp index 509183bb0..921b5433e 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -248,8 +248,8 @@ int main(int argc, char *argv[]) { time_t now = (time_t)time(nullptr); monitors[i]->setStartupTime(now); - snprintf(sql, sizeof(sql), - "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')", + snprintf(sql, sizeof(sql), + "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Running') ON DUPLICATE KEY UPDATE Status='Running'", monitors[i]->Id()); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); @@ -270,7 +270,7 @@ int main(int argc, char *argv[]) { } for ( int i = 0; i < n_monitors; i++ ) { snprintf(sql, sizeof(sql), - "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')", + "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'", monitors[i]->Id()); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); @@ -391,7 +391,7 @@ int main(int argc, char *argv[]) { for ( int i = 0; i < n_monitors; i++ ) { static char sql[ZM_SQL_SML_BUFSIZ]; snprintf(sql, sizeof(sql), - "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','NotRunning')", + "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='NotRunning'", monitors[i]->Id()); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); diff --git a/src/zms.cpp b/src/zms.cpp index 9b661c564..4e77ea4c1 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -95,91 +95,92 @@ int main(int argc, const char *argv[], char **envp) { } const char *query = getenv("QUERY_STRING"); - if ( query ) { - Debug(1, "Query: %s", query); - - char temp_query[1024]; - strncpy(temp_query, query, sizeof(temp_query)-1); - char *q_ptr = temp_query; - char *parms[16]; // Shouldn't be more than this - int parm_no = 0; - while ( (parm_no < 16) && (parms[parm_no] = strtok(q_ptr, "&")) ) { - parm_no++; - q_ptr = nullptr; - } - - for ( int p = 0; p < parm_no; p++ ) { - char *name = strtok(parms[p], "="); - char const *value = strtok(nullptr, "="); - if ( !value ) - value = ""; - if ( !strcmp(name, "source") ) { - source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR; - if ( !strcmp(value, "fifo") ) - source = ZMS_FIFO; - } else if ( !strcmp(name, "mode") ) { - mode = !strcmp(value, "jpeg")?ZMS_JPEG:ZMS_MPEG; - mode = !strcmp(value, "raw")?ZMS_RAW:mode; - mode = !strcmp(value, "zip")?ZMS_ZIP:mode; - mode = !strcmp(value, "single")?ZMS_SINGLE:mode; - } else if ( !strcmp(name, "format") ) { - strncpy( format, value, sizeof(format) ); - } else if ( !strcmp(name, "monitor") ) { - monitor_id = atoi(value); - if ( source == ZMS_UNKNOWN ) - source = ZMS_MONITOR; - } else if ( !strcmp(name, "time") ) { - event_time = atoi(value); - } else if ( !strcmp(name, "event") ) { - event_id = strtoull(value, nullptr, 10); - source = ZMS_EVENT; - } else if ( !strcmp(name, "frame") ) { - frame_id = strtoull(value, nullptr, 10); - source = ZMS_EVENT; - } else if ( !strcmp(name, "scale") ) { - scale = atoi(value); - } else if ( !strcmp(name, "rate") ) { - rate = atoi(value); - } else if ( !strcmp(name, "maxfps") ) { - maxfps = atof(value); - } else if ( !strcmp(name, "bitrate") ) { - bitrate = atoi(value); - } else if ( !strcmp(name, "ttl") ) { - ttl = atoi(value); - } else if ( !strcmp(name, "replay") ) { - if ( !strcmp(value, "gapless") ) { - replay = EventStream::MODE_ALL_GAPLESS; - } else if ( !strcmp(value, "all") ) { - replay = EventStream::MODE_ALL; - } else if ( !strcmp(value, "none") ) { - replay = EventStream::MODE_NONE; - } else if ( !strcmp(value, "single") ) { - replay = EventStream::MODE_SINGLE; - } else { - Error("Unsupported value %s for replay, defaulting to none", value); - } - } else if ( !strcmp(name, "connkey") ) { - connkey = atoi(value); - } else if ( !strcmp(name, "buffer") ) { - playback_buffer = atoi(value); - } else if ( !strcmp(name, "auth") ) { - strncpy(auth, value, sizeof(auth)-1); - } else if ( !strcmp(name, "token") ) { - jwt_token_str = value; - Debug(1, "ZMS: JWT token found: %s", jwt_token_str.c_str()); - } else if ( !strcmp(name, "user") ) { - username = UriDecode(value); - } else if ( !strcmp(name, "pass") ) { - password = UriDecode(value); - Debug(1, "Have %s for password", password.c_str()); - } else { - Debug(1, "Unknown parameter passed to zms %s=%s", name, value); - } // end if possible parameter names - } // end foreach parm - } else { + if ( query == nullptr ) { Fatal("No query string."); + return 0; } // end if query + Debug(1, "Query: %s", query); + + char temp_query[1024]; + strncpy(temp_query, query, sizeof(temp_query)-1); + char *q_ptr = temp_query; + char *parms[16]; // Shouldn't be more than this + int parm_no = 0; + while ( (parm_no < 16) && (parms[parm_no] = strtok(q_ptr, "&")) ) { + parm_no++; + q_ptr = nullptr; + } + + for ( int p = 0; p < parm_no; p++ ) { + char *name = strtok(parms[p], "="); + char const *value = strtok(nullptr, "="); + if ( !value ) + value = ""; + if ( !strcmp(name, "source") ) { + source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR; + if ( !strcmp(value, "fifo") ) + source = ZMS_FIFO; + } else if ( !strcmp(name, "mode") ) { + mode = !strcmp(value, "jpeg")?ZMS_JPEG:ZMS_MPEG; + mode = !strcmp(value, "raw")?ZMS_RAW:mode; + mode = !strcmp(value, "zip")?ZMS_ZIP:mode; + mode = !strcmp(value, "single")?ZMS_SINGLE:mode; + } else if ( !strcmp(name, "format") ) { + strncpy(format, value, sizeof(format)-1); + } else if ( !strcmp(name, "monitor") ) { + monitor_id = atoi(value); + if ( source == ZMS_UNKNOWN ) + source = ZMS_MONITOR; + } else if ( !strcmp(name, "time") ) { + event_time = atoi(value); + } else if ( !strcmp(name, "event") ) { + event_id = strtoull(value, nullptr, 10); + source = ZMS_EVENT; + } else if ( !strcmp(name, "frame") ) { + frame_id = strtoull(value, nullptr, 10); + source = ZMS_EVENT; + } else if ( !strcmp(name, "scale") ) { + scale = atoi(value); + } else if ( !strcmp(name, "rate") ) { + rate = atoi(value); + } else if ( !strcmp(name, "maxfps") ) { + maxfps = atof(value); + } else if ( !strcmp(name, "bitrate") ) { + bitrate = atoi(value); + } else if ( !strcmp(name, "ttl") ) { + ttl = atoi(value); + } else if ( !strcmp(name, "replay") ) { + if ( !strcmp(value, "gapless") ) { + replay = EventStream::MODE_ALL_GAPLESS; + } else if ( !strcmp(value, "all") ) { + replay = EventStream::MODE_ALL; + } else if ( !strcmp(value, "none") ) { + replay = EventStream::MODE_NONE; + } else if ( !strcmp(value, "single") ) { + replay = EventStream::MODE_SINGLE; + } else { + Error("Unsupported value %s for replay, defaulting to none", value); + } + } else if ( !strcmp(name, "connkey") ) { + connkey = atoi(value); + } else if ( !strcmp(name, "buffer") ) { + playback_buffer = atoi(value); + } else if ( !strcmp(name, "auth") ) { + strncpy(auth, value, sizeof(auth)-1); + } else if ( !strcmp(name, "token") ) { + jwt_token_str = value; + Debug(1, "ZMS: JWT token found: %s", jwt_token_str.c_str()); + } else if ( !strcmp(name, "user") ) { + username = UriDecode(value); + } else if ( !strcmp(name, "pass") ) { + password = UriDecode(value); + Debug(1, "Have %s for password", password.c_str()); + } else { + Debug(1, "Unknown parameter passed to zms %s=%s", name, value); + } // end if possible parameter names + } // end foreach parm + if ( monitor_id ) { snprintf(log_id_string, sizeof(log_id_string), "zms_m%d", monitor_id); } else { diff --git a/src/zmu.cpp b/src/zmu.cpp index bdc467812..30a2a0ce7 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -482,7 +482,7 @@ int main(int argc, char *argv[]) { } // end if ! MONITOR if ( verbose ) { - printf("Monitor %d(%s)\n", monitor->Id(), monitor->Name()); + printf("Monitor %u(%s)\n", monitor->Id(), monitor->Name()); } if ( !monitor->connect() ) { Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name()); @@ -521,19 +521,19 @@ int main(int argc, char *argv[]) { } if ( function & ZMU_READ_IDX ) { if ( verbose ) - printf("Last read index: %d\n", monitor->GetLastReadIndex()); + printf("Last read index: %u\n", monitor->GetLastReadIndex()); else { if ( have_output ) fputc(separator, stdout); - printf("%d", monitor->GetLastReadIndex()); + printf("%u", monitor->GetLastReadIndex()); have_output = true; } } if ( function & ZMU_WRITE_IDX ) { if ( verbose ) { - printf("Last write index: %d\n", monitor->GetLastWriteIndex()); + printf("Last write index: %u\n", monitor->GetLastWriteIndex()); } else { if ( have_output ) fputc(separator, stdout); - printf("%d", monitor->GetLastWriteIndex()); + printf("%u", monitor->GetLastWriteIndex()); have_output = true; } } @@ -558,9 +558,9 @@ int main(int argc, char *argv[]) { if ( function & ZMU_IMAGE ) { if ( verbose ) { if ( image_idx == -1 ) - printf("Dumping last image captured to Monitor%d.jpg", monitor->Id()); + printf("Dumping last image captured to Monitor%u.jpg", monitor->Id()); else - printf("Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id()); + printf("Dumping buffer image %d to Monitor%u.jpg", image_idx, monitor->Id()); if ( scale != -1 ) printf(", scaling by %d%%", scale); printf("\n"); @@ -569,7 +569,7 @@ int main(int argc, char *argv[]) { } if ( function & ZMU_ZONES ) { if ( verbose ) - printf("Dumping zone image to Zones%d.jpg\n", monitor->Id()); + printf("Dumping zone image to Zones%u.jpg\n", monitor->Id()); monitor->DumpZoneImage(zoneString); } if ( function & ZMU_ALARM ) { @@ -735,17 +735,17 @@ int main(int argc, char *argv[]) { Debug(1, "Got %d monitors", mysql_num_rows(result)); printf("%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate"); - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { - int mon_id = atoi(dbrow[0]); - int function = atoi(dbrow[1]); - if ( !user || user->canAccess(mon_id) ) { - if ( function > 1 ) { - Monitor *monitor = Monitor::Load(mon_id, false, Monitor::QUERY); + for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { + int monitor_id = atoi(dbrow[0]); + int monitor_function = atoi(dbrow[1]); + if ( !user || user->canAccess(monitor_id) ) { + if ( monitor_function > 1 ) { + Monitor *monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); if ( monitor && monitor->connect() ) { struct timeval tv = monitor->GetTimestamp(); printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8" PRIu64 "%8.2f\n", monitor->Id(), - function, + monitor_function, monitor->GetState(), monitor->GetTriggerState(), tv.tv_sec, tv.tv_usec/10000, diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 1b2eafd8f..d464089d3 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -94,12 +94,12 @@ commonprep () { # The rpm specfile requires we download each submodule as a tarball then manually move it into place # Might as well do this for Debian as well, rather than git submodule init - CRUDVER="3.1.0-zm" + CRUDVER="3.2.0" if [ -e "build/crud-${CRUDVER}.tar.gz" ]; then echo "Found existing Crud ${CRUDVER} tarball..." else echo "Retrieving Crud ${CRUDVER} submodule..." - curl -L https://github.com/ZoneMinder/crud/archive/v${CRUDVER}.tar.gz > build/crud-${CRUDVER}.tar.gz + curl -L https://github.com/FriendsOfCake/crud/archive/v${CRUDVER}.tar.gz > build/crud-${CRUDVER}.tar.gz if [ $? -ne 0 ]; then echo "ERROR: Crud tarball retreival failed..." exit 1 diff --git a/version b/version index a0b71b46f..31d4ef08d 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.10 +1.35.14 diff --git a/web/ajax/events.php b/web/ajax/events.php index f9a4fc14b..5d58a5a41 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -26,22 +26,22 @@ if ( $message ) { return; } +$filter = isset($_REQUEST['filter']) ? ZM\Filter::parse($_REQUEST['filter']) : new ZM\Filter(); +if ( $user['MonitorIds'] ) { + $filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds'])); +} + // Search contains a user entered string to search on $search = isset($_REQUEST['search']) ? $_REQUEST['search'] : ''; // Advanced search contains an array of "column name" => "search text" pairs // Bootstrap table sends json_ecoded array, which we must decode -$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array(); +$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array(); // Sort specifies the name of the column to sort on -$sort = 'StartTime'; +$sort = 'StartDateTime'; if ( isset($_REQUEST['sort']) ) { - if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) { - ZM\Error('Invalid sort field: ' . $_REQUEST['sort']); - } else { - $sort = $_REQUEST['sort']; - //if ( $sort == 'DateTime' ) $sort = 'TimeKey'; - } + $sort = $_REQUEST['sort']; } // Offset specifies the starting row to return, used for pagination @@ -80,7 +80,7 @@ switch ( $task ) { foreach ( $eids as $eid ) $data[] = deleteRequest($eid); break; case 'query' : - $data = queryRequest($search, $advsearch, $sort, $offset, $order, $limit); + $data = queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit); break; default : ZM\Fatal("Unrecognised task '$task'"); @@ -114,62 +114,79 @@ function deleteRequest($eid) { return $message; } -function queryRequest($search, $advsearch, $sort, $offset, $order, $limit) { +function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) { + $data = array( + 'total' => 0, + 'totalNotFiltered' => 0, + 'rows' => array(), + 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG) + ); + + $failed = !$filter->test_pre_sql_conditions(); + if ( $failed ) { + ZM\Debug('Pre conditions failed, not doing sql'); + return $data; + } + // Put server pagination code here // The table we want our data from $table = 'Events'; - // The names of the dB columns in the log table we are interested in - $columns = array('Id', 'MonitorId', 'StorageId', 'Name', 'Cause', 'StartTime', 'EndTime', 'Length', 'Frames', 'AlarmFrames', 'TotScore', 'AvgScore', 'MaxScore', 'Archived', 'Emailed', 'Notes', 'DiskSpace'); + // The names of the dB columns in the events table we are interested in + $columns = array('Id', 'MonitorId', 'StorageId', 'Name', 'Cause', 'StartDateTime', 'EndDateTime', 'Length', 'Frames', 'AlarmFrames', 'TotScore', 'AvgScore', 'MaxScore', 'Archived', 'Emailed', 'Notes', 'DiskSpace'); - // The names of columns shown in the log view that are NOT dB columns in the database + // The names of columns shown in the event view that are NOT dB columns in the database $col_alt = array('Monitor', 'Storage'); - $col_str = implode(', ', $columns); + if ( !in_array($sort, array_merge($columns, $col_alt)) ) { + ZM\Error('Invalid sort field: ' . $sort); + $sort = 'Id'; + } + $data = array(); $query = array(); $query['values'] = array(); $likes = array(); - $where = ''; + $where = ($filter->sql()?'('.$filter->sql().')' : ''); // There are two search bars in the log view, normal and advanced // Making an exuctive decision to ignore the normal search, when advanced search is in use // Alternatively we could try to do both if ( count($advsearch) ) { foreach ( $advsearch as $col=>$text ) { - if ( !in_array($col, array_merge($columns, $col_alt)) ) { + if ( in_array($col, $columns) ) { + array_push($likes, 'E.'.$col.' LIKE ?'); + array_push($query['values'], $text); + } else if ( in_array($col, $col_alt) ) { + array_push($likes, 'M.'.$col.' LIKE ?'); + array_push($query['values'], $text); + } else { ZM\Error("'$col' is not a sortable column name"); continue; } - $text = '%' .$text. '%'; - array_push($likes, $col.' LIKE ?'); - array_push($query['values'], $text); - } + } # end foreach col in advsearch $wherevalues = $query['values']; - $where = ' WHERE (' .implode(' OR ', $likes). ')'; + $where .= ($where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes); } else if ( $search != '' ) { $search = '%' .$search. '%'; foreach ( $columns as $col ) { - array_push($likes, $col.' LIKE ?'); + array_push($likes, 'E.'.$col.' LIKE ?'); array_push($query['values'], $search); } $wherevalues = $query['values']; - $where = ' WHERE (' .implode(' OR ', $likes). ')'; - } + $where .= ( $where != '') ? ' AND (' .implode(' OR ', $likes). ')' : implode(' OR ', $likes); + } + if ( $where ) + $where = ' WHERE '.$where; - $query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; + $sort = $sort == 'Monitor' ? 'M.Name' : 'E.'.$sort; + $col_str = 'E.*, M.Name AS Monitor'; + $query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; array_push($query['values'], $offset, $limit); - //ZM\Warning('Calling the following sql query: ' .$query['sql']); - - $data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total'); - if ( $search != '' || count($advsearch) ) { - $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues); - } else { - $data['total'] = $data['totalNotFiltered']; - } + //ZM\Debug('Calling the following sql query: ' .$query['sql']); $storage_areas = ZM\Storage::find(); $StorageById = array(); @@ -177,37 +194,41 @@ function queryRequest($search, $advsearch, $sort, $offset, $order, $limit) { $StorageById[$S->Id()] = $S; } - $monitor_names = ZM\Monitor::find(); - $MonitorById = array(); - foreach ( $monitor_names as $S ) { - $MonitorById[$S->Id()] = $S; - } - $rows = array(); foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) { - $event = new ZM\Event($row['Id']); + $event = new ZM\Event($row); + if ( !$filter->test_post_sql_conditions($event) ) { + $event->remove_from_cache(); + continue; + } $scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width()); $imgSrc = $event->getThumbnailSrc(array(),'&'); $streamSrc = $event->getStreamSrc(array( - 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&'); + 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&'); // Modify the row data as needed $row['imgHtml'] = '' .validHtmlStr('Event ' .$event->Id()). ''; $row['Name'] = validHtmlStr($row['Name']); $row['Archived'] = $row['Archived'] ? translate('Yes') : translate('No'); $row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No'); - $row['Monitor'] = ( $row['MonitorId'] and isset($MonitorById[$row['MonitorId']]) ) ? $MonitorById[$row['MonitorId']]->Name() : ''; $row['Cause'] = validHtmlStr($row['Cause']); - $row['StartTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime'])); - $row['EndTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartTime'])); + $row['StartDateTime'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['StartDateTime'])); + $row['EndDateTime'] = $row['EndDateTime'] ? strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['EndDateTime'])) : null; $row['Length'] = gmdate('H:i:s', $row['Length'] ); $row['Storage'] = ( $row['StorageId'] and isset($StorageById[$row['StorageId']]) ) ? $StorageById[$row['StorageId']]->Name() : 'Default'; - $row['Notes'] = htmlspecialchars($row['Notes']); - $row['DiskSpace'] = human_filesize($row['DiskSpace']); + $row['Notes'] = nl2br(htmlspecialchars($row['Notes'])); + $row['DiskSpace'] = human_filesize($event->DiskSpace()); $rows[] = $row; } $data['rows'] = $rows; - $data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG); + + # totalNotFiltered must equal total, except when either search bar has been used + $data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'. ($filter->sql() ? ' WHERE '.$filter->sql():''), 'Total'); + if ( $search != '' || count($advsearch) ) { + $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where , 'Total', $wherevalues); + } else { + $data['total'] = $data['totalNotFiltered']; + } return $data; } diff --git a/web/ajax/log.php b/web/ajax/log.php index 9e6bd9585..60f0c1dc4 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -1,132 +1,186 @@ "search text" pairs -// Bootstrap table sends json_ecoded array, which we must decode -$advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array(); - -// Sort specifies the name of the column to sort on -$sort = 'TimeKey'; -if ( isset($_REQUEST['sort']) ) { - if ( !in_array($_REQUEST['sort'], array_merge($columns, $col_alt)) ) { - ZM\Error('Invalid sort field: ' . $_REQUEST['sort']); - } else { - $sort = $_REQUEST['sort']; - if ( $sort == 'DateTime' ) $sort = 'TimeKey'; - } -} - -// Offset specifies the starting row to return, used for pagination -$offset = 0; -if ( isset($_REQUEST['offset']) ) { - if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { - ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); - } else { - $offset = $_REQUEST['offset']; - } -} - -// Order specifies the sort direction, either asc or desc -$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; - -// Limit specifies the number of rows to return -$limit = 100; -if ( isset($_REQUEST['limit']) ) { - if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { - ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); - } else { - $limit = $_REQUEST['limit']; - } -} - -$col_str = implode(', ', $columns); $data = array(); -$query = array(); -$query['values'] = array(); -$likes = array(); -$where = ''; -// There are two search bars in the log view, normal and advanced -// Making an exuctive decision to ignore the normal search, when advanced search is in use -// Alternatively we could try to do both -if ( count($advsearch) ) { +$message = ''; - foreach ( $advsearch as $col=>$text ) { - if ( !in_array($col, array_merge($columns, $col_alt)) ) { - ZM\Error("'$col' is not a sortable column name"); - continue; - } - $text = '%' .$text. '%'; - array_push($likes, $col.' LIKE ?'); - array_push($query['values'], $text); - } - $wherevalues = $query['values']; - $where = ' WHERE (' .implode(' OR ', $likes). ')'; +// +// INITIALIZE AND CHECK SANITY +// -} else if ( $search != '' ) { +if ( !canView('System') ) + $message = 'Insufficient permissions to view log entries for user '.$user['Username']; - $search = '%' .$search. '%'; - foreach ( $columns as $col ) { - array_push($likes, $col.' LIKE ?'); - array_push($query['values'], $search); - } - $wherevalues = $query['values']; - $where = ' WHERE (' .implode(' OR ', $likes). ')'; -} - -$query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; -array_push($query['values'], $offset, $limit); - -//ZM\Warning('Calling the following sql query: ' .$query['sql']); - -$data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total'); -if ( $search != '' || count($advsearch) ) { - $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues); +// task must be set +if ( !isset($_REQUEST['task']) ) { + $message = 'This request requires a task to be set'; +} else if ( $_REQUEST['task'] != 'query' && $_REQUEST['task'] != 'create' ) { + // Only the query and create tasks are supported at the moment + $message = 'Unrecognised task '.$_REQUEST['task']; } else { - $data['total'] = $data['totalNotFiltered']; + $task = $_REQUEST['task']; } -if ( !$Servers ) - $Servers = ZM\Server::find(); -$servers_by_Id = array(); -# There is probably a better way to do this. -foreach ( $Servers as $server ) { - $servers_by_Id[$server->Id()] = $server; +if ( $message ) { + ajaxError($message); + return; } -$rows = array(); -foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $row ) { - $row['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($row['TimeKey'])); - $row['Server'] = ( $row['ServerId'] and isset($servers_by_Id[$row['ServerId']]) ) ? $servers_by_Id[$row['ServerId']]->Name() : ''; - // First strip out any html tags - // Second strip out all characters that are not ASCII 32-126 (yes, 126) - $row['Message'] = preg_replace('/[^\x20-\x7E]/', '', strip_tags($row['Message'])); - $rows[] = $row; -} -$data['rows'] = $rows; -$data['logstate'] = logState(); -$data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG); +// +// MAIN LOOP +// + +switch ( $task ) { + case 'create' : + createRequest(); + break; + case 'query' : + $data = queryRequest(); + break; + default : + ZM\Fatal('Unrecognised task '.$task); +} // end switch task ajaxResponse($data); +// +// FUNCTION DEFINITIONS +// + +function createRequest() { + if ( !empty($_POST['level']) && !empty($_POST['message']) ) { + ZM\logInit(array('id'=>'web_js')); + + $string = $_POST['message']; + + $file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : ''; + if ( !empty($_POST['line']) ) { + $line = validInt($_POST['line']); + } else { + $line = NULL; + } + + $levels = array_flip(ZM\Logger::$codes); + if ( !isset($levels[$_POST['level']]) ) { + ZM\Panic('Unexpected logger level '.$_POST['level']); + } + $level = $levels[$_POST['level']]; + ZM\Logger::fetch()->logPrint($level, $string, $file, $line); + } else { + ZM\Error('Invalid log create: '.print_r($_POST, true)); + } +} + +function queryRequest() { + + // Offset specifies the starting row to return, used for pagination + $offset = 0; + if ( isset($_REQUEST['offset']) ) { + if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { + ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); + } else { + $offset = $_REQUEST['offset']; + } + } + + // Limit specifies the number of rows to return + $limit = 100; + if ( isset($_REQUEST['limit']) ) { + if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { + ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); + } else { + $limit = $_REQUEST['limit']; + } + } + // The table we want our data from + $table = 'Logs'; + + // The names of the dB columns in the log table we are interested in + $columns = array('TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line'); + + // The names of columns shown in the log view that are NOT dB columns in the database + $col_alt = array('DateTime', 'Server'); + + $sort = 'TimeKey'; + if ( isset($_REQUEST['sort']) ) { + $sort = $_REQUEST['sort']; + if ( $sort == 'DateTime' ) $sort = 'TimeKey'; + } + if ( !in_array($sort, array_merge($columns, $col_alt)) ) { + ZM\Error('Invalid sort field: ' . $sort); + return; + } + + // Order specifies the sort direction, either asc or desc + $order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC'; + + $col_str = implode(', ', $columns); + $data = array(); + $query = array(); + $query['values'] = array(); + $likes = array(); + $where = ''; + // There are two search bars in the log view, normal and advanced + // Making an exuctive decision to ignore the normal search, when advanced search is in use + // Alternatively we could try to do both + // + // Advanced search contains an array of "column name" => "search text" pairs + // Bootstrap table sends json_ecoded array, which we must decode + $advsearch = isset($_REQUEST['filter']) ? json_decode($_REQUEST['filter'], JSON_OBJECT_AS_ARRAY) : array(); + // Search contains a user entered string to search on + $search = isset($_REQUEST['search']) ? $_REQUEST['search'] : ''; + if ( count($advsearch) ) { + + foreach ( $advsearch as $col=>$text ) { + if ( !in_array($col, array_merge($columns, $col_alt)) ) { + ZM\Error("'$col' is not a searchable column name"); + continue; + } + // Don't use wildcards on advanced search + //$text = '%' .$text. '%'; + array_push($likes, $col.' LIKE ?'); + array_push($query['values'], $text); + } + $wherevalues = $query['values']; + $where = ' WHERE (' .implode(' OR ', $likes). ')'; + + } else if ( $search != '' ) { + + $search = '%' .$search. '%'; + foreach ( $columns as $col ) { + array_push($likes, $col.' LIKE ?'); + array_push($query['values'], $search); + } + $wherevalues = $query['values']; + $where = ' WHERE (' .implode(' OR ', $likes). ')'; + } + + $query['sql'] = 'SELECT ' .$col_str. ' FROM `' .$table. '` ' .$where. ' ORDER BY ' .$sort. ' ' .$order. ' LIMIT ?, ?'; + array_push($query['values'], $offset, $limit); + + $data['totalNotFiltered'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table, 'Total'); + if ( $search != '' || count($advsearch) ) { + $data['total'] = dbFetchOne('SELECT count(*) AS Total FROM ' .$table.$where , 'Total', $wherevalues); + } else { + $data['total'] = $data['totalNotFiltered']; + } + + $rows = array(); + $results = dbFetchAll($query['sql'], NULL, $query['values']); + if ( !$results ) { + return $data; + } + + foreach ( $results as $row ) { + $row['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($row['TimeKey'])); + $Server = new ZM\Server($row['ServerId']); + $row['Server'] = $Server->Name(); + // First strip out any html tags + // Second strip out all characters that are not ASCII 32-126 (yes, 126) + $row['Message'] = preg_replace('/[^\x20-\x7E]/', '', strip_tags($row['Message'])); + $rows[] = $row; + } + $data['rows'] = $rows; + $data['logstate'] = logState(); + $data['updated'] = preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG); + + return $data; +} diff --git a/web/ajax/status.php b/web/ajax/status.php index 1fe4fa6a4..41c8a625d 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -6,6 +6,7 @@ if ( $_REQUEST['entity'] == 'navBar' ) { $auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS); if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) { $data['auth'] = $auth_hash; + $data['auth_relay'] = get_auth_relay(); } } // Each widget on the navbar has its own function @@ -108,9 +109,9 @@ $statusData = array( 'Name' => true, 'Cause' => true, 'Notes' => true, - 'StartTime' => true, - 'StartTimeShort' => array( 'sql' => 'date_format( StartTime, \''.MYSQL_FMT_DATETIME_SHORT.'\' )' ), - 'EndTime' => true, + 'StartDateTime' => true, + 'StartTimeShort' => array( 'sql' => 'date_format( StartDateTime, \''.MYSQL_FMT_DATETIME_SHORT.'\' )' ), + 'EndDateTime' => true, 'Width' => true, 'Height' => true, 'Length' => true, @@ -132,9 +133,9 @@ $statusData = array( 'MonitorName' => array('sql' => '(SELECT Monitors.Name FROM Monitors WHERE Monitors.Id = Events.MonitorId)'), 'Name' => true, 'Cause' => true, - 'StartTime' => true, - 'StartTimeShort' => array( 'sql' => 'date_format( StartTime, \''.MYSQL_FMT_DATETIME_SHORT.'\' )' ), - 'EndTime' => true, + 'StartDateTime' => true, + 'StartTimeShort' => array( 'sql' => 'date_format( StartDateTime, \''.MYSQL_FMT_DATETIME_SHORT.'\' )' ), + 'EndDateTime' => true, 'Width' => true, 'Height' => true, 'Length' => true, @@ -179,7 +180,7 @@ $statusData = array( 'EventId' => true, 'Type' => true, 'TimeStamp' => true, - 'TimeStampShort' => array( 'sql' => 'date_format( StartTime, \''.MYSQL_FMT_DATETIME_SHORT.'\' )' ), + 'TimeStampShort' => array( 'sql' => 'date_format( StartDateTime, \''.MYSQL_FMT_DATETIME_SHORT.'\' )' ), 'Delta' => true, 'Score' => true, //'Image' => array( 'postFunc' => 'getFrameImage' ), @@ -416,48 +417,54 @@ function getNearEvents() { $event = dbFetchOne('SELECT * FROM Events WHERE Id=?', NULL, array($eventId)); if ( !$event ) return $NearEvents; - if ( isset($_REQUEST['filter']) ) - parseFilter( $_REQUEST['filter'] ); + $filter = ZM\Filter::parse($_REQUEST['filter']); parseSort(); - - if ( $user['MonitorIds'] ) - $midSql = ' AND MonitorId IN ('.join( ',', preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).')'; - else - $midSql = ''; + if ( $user['MonitorIds'] ) { + $filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds'])); + } # When listing, it may make sense to list them in descending order. But when viewing Prev should timewise earlier and Next should be after. - if ( $sortColumn == 'E.Id' or $sortColumn == 'E.StartTime' ) { + if ( $sortColumn == 'E.Id' or $sortColumn == 'E.StartDateTime' ) { $sortOrder = 'ASC'; } - $sql = 'SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$sortColumn.' '.($sortOrder=='ASC'?'<=':'>=').' \''.$event[$_REQUEST['sort_field']].'\''.$_REQUEST['filter']['sql'].$midSql.' AND E.Id<'.$event['Id'] . ' ORDER BY '.$sortColumn.' '.($sortOrder=='ASC'?'DESC':'ASC'); + $sql = 'SELECT E.Id AS Id, E.StartDateTime AS StartDateTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$sortColumn.' '.($sortOrder=='ASC'?'<=':'>=').' \''.$event[$_REQUEST['sort_field']].'\' AND ('.$filter->sql().') AND E.Id<'.$event['Id'] . ' ORDER BY '.$sortColumn.' '.($sortOrder=='ASC'?'DESC':'ASC'); if ( $sortColumn != 'E.Id' ) { # When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id $sql .= ', E.Id DESC'; } $sql .= ' LIMIT 1'; $result = dbQuery($sql); + if ( !$result ) { + ZM\Error('Failed to load previous event using '.$sql); + return $NearEvents; + } + $prevEvent = dbFetchNext($result); - $sql = 'SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$sortColumn .' '.($sortOrder=='ASC'?'>=':'<=').' \''.$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id>'.$event['Id'] . ' ORDER BY '.$sortColumn.' '.($sortOrder=='ASC'?'ASC':'DESC'); + $sql = 'SELECT E.Id AS Id, E.StartDateTime AS StartDateTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$sortColumn .' '.($sortOrder=='ASC'?'>=':'<=').' \''.$event[$_REQUEST['sort_field']]."' AND (".$filter->sql().') AND E.Id>'.$event['Id'] . ' ORDER BY '.$sortColumn.' '.($sortOrder=='ASC'?'ASC':'DESC'); if ( $sortColumn != 'E.Id' ) { # When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id $sql .= ', E.Id ASC'; } $sql .= ' LIMIT 1'; $result = dbQuery($sql); + if ( !$result ) { + ZM\Error('Failed to load next event using '.$sql); + return $NearEvents; + } $nextEvent = dbFetchNext($result); if ( $prevEvent ) { $NearEvents['PrevEventId'] = $prevEvent['Id']; - $NearEvents['PrevEventStartTime'] = $prevEvent['StartTime']; + $NearEvents['PrevEventStartTime'] = $prevEvent['StartDateTime']; $NearEvents['PrevEventDefVideoPath'] = getEventDefaultVideoPath($prevEvent['Id']); } else { - $NearEvents['PrevEventId'] = $result['PrevEventStartTime'] = $result['PrevEventDefVideoPath'] = 0; + $NearEvents['PrevEventId'] = $NearEvents['PrevEventStartTime'] = $NearEvents['PrevEventDefVideoPath'] = 0; } if ( $nextEvent ) { $NearEvents['NextEventId'] = $nextEvent['Id']; - $NearEvents['NextEventStartTime'] = $nextEvent['StartTime']; + $NearEvents['NextEventStartTime'] = $nextEvent['StartDateTime']; $NearEvents['NextEventDefVideoPath'] = getEventDefaultVideoPath($nextEvent['Id']); } else { $NearEvents['NextEventId'] = $NearEvents['NextEventStartTime'] = $NearEvents['NextEventDefVideoPath'] = 0; diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 2c583ba14..cdb67c0f9 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -128,7 +128,7 @@ if ( sem_acquire($semaphore,1) !== false ) { $data['auth'] = $auth_hash; ZM\Debug("including nw auth hash " . $data['auth']); } else { - ZM\Debug('Not including nw auth hash becase it hashn\'t changed '.$auth_hash); + ZM\Debug('Not including new auth hash becase it hasn\'t changed '.$auth_hash); } } ajaxResponse(array('status'=>$data)); diff --git a/web/api/app/Controller/Component/ImageComponent.php b/web/api/app/Controller/Component/ImageComponent.php index 2033e0d8c..567333a71 100644 --- a/web/api/app/Controller/Component/ImageComponent.php +++ b/web/api/app/Controller/Component/ImageComponent.php @@ -89,11 +89,11 @@ class ImageComponent extends Component { } - // Take the StartTime of an Event and return + // Take the StartDateTime of an Event and return // the path to its location on the filesystem public function getEventPath( $event ) { if ( $config['ZM_USE_DEEP_STORAGE'] == 1 ) - return $event['Event']['MonitorId'].'/'.strftime( "%y/%m/%d/%H/%M/%S", strtotime($event['Event']['StartTime']) ); + return $event['Event']['MonitorId'].'/'.strftime( '%y/%m/%d/%H/%M/%S', strtotime($event['Event']['StartDateTime']) ); else return $event['Event']['MonitorId'].'/'.$event['Event']['Id']; } diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 3b132e932..d18409387 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -48,9 +48,24 @@ class EventsController extends AppController { $mon_options = ''; } - if ( $this->request->params['named'] ) { + $named_params = $this->request->params['named']; + if ( $named_params ) { + # In 1.35.13 we renamed StartTime and EndTime to StartDateTime and EndDateTime. + # This hack renames the query string params + foreach ( $named_params as $k=>$v ) { + if ( false !== strpos($k, 'StartTime') ) { + $new_k = preg_replace('/StartTime/', 'StartDateTime', $k); + $named_params[$new_k] = $named_params[$k]; + unset($named_params[$k]); + } + if ( false !== strpos($k, 'EndTime') ) { + $new_k = preg_replace('/EndTime/', 'EndDateTime', $k); + $named_params[$new_k] = $named_params[$k]; + unset($named_params[$k]); + } + } $this->FilterComponent = $this->Components->load('Filter'); - $conditions = $this->FilterComponent->buildFilter($this->request->params['named']); + $conditions = $this->FilterComponent->buildFilter($named_params); } else { $conditions = array(); } @@ -65,7 +80,7 @@ class EventsController extends AppController { // API 'limit' => '100', - 'order' => array('StartTime'), + 'order' => array('StartDateTime'), 'paramType' => 'querystring', ); if ( isset($conditions['GroupId']) ) { @@ -309,7 +324,7 @@ class EventsController extends AppController { } else { $conditions = array(); } - array_push($conditions, array("StartTime >= DATE_SUB(NOW(), INTERVAL $expr $unit)")); + array_push($conditions, array("StartDateTime >= DATE_SUB(NOW(), INTERVAL $expr $unit)")); $query = $this->Event->find('all', array( 'fields' => array( 'MonitorId', diff --git a/web/api/app/Model/Event.php b/web/api/app/Model/Event.php index 22a97f62f..e54fd125f 100644 --- a/web/api/app/Model/Event.php +++ b/web/api/app/Model/Event.php @@ -31,6 +31,10 @@ class Event extends AppModel { */ public $displayField = 'Name'; + public $virtualFields = array( + 'StartTime' => 'StartDateTime', + 'EndTime' => 'EndDateTime' + ); //The Associations below have been created with all possible keys, those that are not needed can be removed diff --git a/web/includes/Event.php b/web/includes/Event.php index 70eb27d8c..4e218edb2 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -14,8 +14,8 @@ class Event extends ZM_Object { 'StorageId' => null, 'SecondaryStorageId' => null, 'Cause' => '', - 'StartTime' => null, - 'EndTime' => null, + 'StartDateTime' => null, + 'EndDateTime' => null, 'Width' => null, 'Height' => null, 'Length' => null, @@ -93,7 +93,7 @@ class Event extends ZM_Object { public function Time() { if ( ! isset($this->{'Time'}) ) { - $this->{'Time'} = strtotime($this->{'StartTime'}); + $this->{'Time'} = strtotime($this->{'StartDateTime'}); } return $this->{'Time'}; } @@ -153,9 +153,9 @@ class Event extends ZM_Object { if ( $this->{'Scheme'} == 'Deep' ) { # Assumption: All events have a start time - $start_date = date_parse($this->{'StartTime'}); + $start_date = date_parse($this->{'StartDateTime'}); if ( ! $start_date ) { - throw new Exception('Unable to parse start time for event ' . $this->{'Id'} . ' not deleting files.'); + throw new Exception('Unable to parse start date time for event ' . $this->{'Id'} . ' not deleting files.'); } $start_date['year'] = $start_date['year'] % 100; @@ -279,7 +279,7 @@ class Event extends ZM_Object { } if ( (!property_exists($this, 'DiskSpace')) or (null === $this->{'DiskSpace'}) ) { $this->{'DiskSpace'} = folder_size($this->Path()); - if ( $this->{'EndTime'} ) { + if ( $this->{'EndDateTime'} ) { # Finished events shouldn't grow in size much so we can commit it to the db. dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'})); } @@ -606,7 +606,7 @@ class Event extends ZM_Object { if ( $this->Archived() ) { return false; } - if ( !$this->EndTime() ) { + if ( !$this->EndDateTime() ) { return false; } if ( !canEdit('Events') ) { @@ -619,7 +619,7 @@ class Event extends ZM_Object { public function cant_delete_reason() { if ( $this->Archived() ) { return 'You cannot delete an archived event. Unarchive it first.'; - } else if ( ! $this->EndTime() ) { + } else if ( ! $this->EndDateTime() ) { return 'You cannot delete an event while it is being recorded. Wait for it to finish.'; } else if ( ! canEdit('Events') ) { return 'You do not have rights to edit Events.'; diff --git a/web/includes/Filter.php b/web/includes/Filter.php index 86c72692b..c4610de4d 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -1,6 +1,7 @@ 0, 'Concurrent' => 0, 'Query_json' => '', + 'LockRows' => 0, ); protected $_querystring; @@ -40,6 +42,7 @@ class Filter extends ZM_Object { public function sql() { if ( ! isset($this->_sql) ) { + $this->_sql = ''; foreach ( $this->FilterTerms() as $term ) { #if ( ! ($term->is_pre_sql() or $term->is_post_sql()) ) { $this->_sql .= $term->sql(); @@ -51,13 +54,14 @@ class Filter extends ZM_Object { return $this->_sql; } - public function querystring($separator='&') { - if ( (! isset($this->_querystring)) or ( $separator != '&' ) ) { + public function querystring($objectname='filter', $separator='&') { + if ( (! isset($this->_querystring)) or ( $separator != '&' ) or ($objectname != 'filter') ) { + $this->_querystring = ''; foreach ( $this->FilterTerms() as $term ) { - $this->_querystring .= $term->querystring($separator); + $this->_querystring .= $term->querystring($objectname, $separator); } # end foreach term if ( $this->Id() ) { - $this->_querystring .= $separator.'filter[Id]='.$this->Id(); + $this->_querystring .= $separator.$objectname.urlencode('[Id]').'='.$this->Id(); } } return $this->_querystring; @@ -65,6 +69,7 @@ class Filter extends ZM_Object { public function hidden_fields() { if ( ! isset($this->_hidden_fields) ) { + $this->_hidden_fields = ''; foreach ( $this->FilterTerms() as $term ) { $this->_hidden_fields .= $term->hidden_fields(); } # end foreach term @@ -141,6 +146,13 @@ class Filter extends ZM_Object { if ( func_num_args( ) ) { $this->{'Query'} = func_get_arg(0); $this->{'Query_json'} = jsonEncode($this->{'Query'}); + # We have altered the query so need to reset all the calculated results. + unset($this->_querystring); + unset($this->_sql); + unset($this->_hidden_fields); + unset($this->_pre_sql_conditions); + unset($this->_post_sql_conditions); + unset($this->_Terms); } if ( !property_exists($this, 'Query') ) { if ( property_exists($this, 'Query_json') and $this->{'Query_json'} ) { @@ -380,35 +392,35 @@ class Filter extends ZM_Object { break; case 'DateTime': case 'StartDateTime': - $sqlValue = 'E.StartTime'; + $sqlValue = 'E.StartDateTime'; $dtAttr = true; break; case 'Date': case 'StartDate': - $sqlValue = 'to_days(E.StartTime)'; + $sqlValue = 'to_days(E.StartDateTime)'; $dtAttr = true; break; case 'Time': case 'StartTime': - $sqlValue = 'extract(hour_second from E.StartTime)'; + $sqlValue = 'extract(hour_second from E.StartDateTime)'; break; case 'Weekday': case 'StartWeekday': - $sqlValue = 'weekday(E.StartTime)'; + $sqlValue = 'weekday(E.StartDateTime)'; break; case 'EndDateTime': - $sqlValue = 'E.EndTime'; + $sqlValue = 'E.EndDateTime'; $dtAttr = true; break; case 'EndDate': - $sqlValue = 'to_days(E.EndTime)'; + $sqlValue = 'to_days(E.EndDateTime)'; $dtAttr = true; break; case 'EndTime': - $sqlValue = 'extract(hour_second from E.EndTime)'; + $sqlValue = 'extract(hour_second from E.EndDateTime)'; break; case 'EndWeekday': - $sqlValue = 'weekday(E.EndTime)'; + $sqlValue = 'weekday(E.EndDateTime)'; break; case 'Id': case 'Name': @@ -619,6 +631,37 @@ class Filter extends ZM_Object { return array_pop($exprStack); } # end function tree -} # end class Filter + function addTerm($term=false, $position=null) { + if ( !FilterTerm::is_valid_attr($term['attr']) ) { + Error('Unsupported filter attribute ' . $term['attr']); + return $this; + } + + $terms = $this->terms(); + + if ( (!isset($position)) or ($position > count($terms)) ) + $position = count($terms); + else if ( $position < 0 ) + $position = 0; + + if ( $term && ($position == 0) ) { + # if only 1 term, don't need AND or OR + unset($term['cnj']); + } + + array_splice($terms, $position, 0, array($term ? $term : array())); + $this->terms($terms); + + return $this; + } # end function addTerm + + function addTerms($terms, $options=null) { + foreach ( $terms as $term ) { + $this->addTerm($term); + } + return $this; + } + +} # end class Filter ?> diff --git a/web/includes/FilterTerm.php b/web/includes/FilterTerm.php index 1f428fefa..6b41a2e1d 100644 --- a/web/includes/FilterTerm.php +++ b/web/includes/FilterTerm.php @@ -13,6 +13,7 @@ function getFilterQueryConjunctionTypes() { return $validConjunctionTypes; } + class FilterTerm { public $filter; public $index; @@ -35,7 +36,7 @@ class FilterTerm { $this->val = $term['val']; if ( isset($term['cnj']) ) { if ( array_key_exists($term['cnj'], $validConjunctionTypes) ) { - $this->cnj = $term['cnj']; + $this->cnj = $term['cnj']; } else { Warning('Invalid cnj ' . $term['cnj'].' in '.print_r($term, true)); } @@ -65,7 +66,8 @@ class FilterTerm { return $values; } - foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)) as $value ) { + $vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)); + foreach ( $vals as $value ) { switch ( $this->attr ) { @@ -75,7 +77,7 @@ class FilterTerm { case 'ExistsInFileSystem': $value = ''; break; - case 'DiskSpace': + case 'DiskPercent': $value = ''; break; case 'MonitorName': @@ -83,7 +85,7 @@ class FilterTerm { case 'Name': case 'Cause': case 'Notes': - if ( $this->op == 'LIKE' || $this->op == 'NOT LIKE' ) { + if ( strstr($this->op, 'LIKE') and ! strstr($this->val, '%' ) ) { $value = '%'.$value.'%'; } $value = dbEscape($value); @@ -145,7 +147,7 @@ class FilterTerm { case 'AlarmZoneId': return ' EXISTS '; case 'ExistsInFileSystem': - case 'DiskSpace': + case 'DiskPercent': return ''; } @@ -185,7 +187,7 @@ class FilterTerm { } return ' IS NOT '; default: - ZM\Warning('Invalid operator in filter: ' . print_r($this->op, true)); + Warning('Invalid operator in filter: ' . print_r($this->op, true)); } // end switch op } # end public function sql_operator @@ -202,7 +204,7 @@ class FilterTerm { switch ( $this->attr ) { case 'ExistsInFileSystem': - case 'DiskSpace': + case 'DiskPercent': $sql .= 'TRUE /*'.$this->attr.'*/'; break; case 'MonitorName': @@ -220,49 +222,50 @@ class FilterTerm { break; # Unspecified start or end, so assume start, this is to support legacy filters case 'DateTime': - $sql .= 'E.StartTime'; + $sql .= 'E.StartDateTime'; break; case 'Date': - $sql .= 'to_days(E.StartTime)'; + $sql .= 'to_days(E.StartDateTime)'; break; case 'Time': - $sql .= 'extract(hour_second FROM E.StartTime)'; + $sql .= 'extract(hour_second FROM E.StartDateTime)'; break; case 'Weekday': - $sql .= 'weekday(E.StartTime)'; + $sql .= 'weekday(E.StartDateTime)'; break; # Starting Time case 'StartDateTime': - $sql .= 'E.StartTime'; + $sql .= 'E.StartDateTime'; break; case 'FramesEventId': $sql .= 'F.EventId'; break; case 'StartDate': - $sql .= 'to_days(E.StartTime)'; + $sql .= 'to_days(E.StartDateTime)'; break; case 'StartTime': - $sql .= 'extract(hour_second FROM E.StartTime)'; + $sql .= 'extract(hour_second FROM E.StartDateTime)'; break; case 'StartWeekday': - $sql .= 'weekday(E.StartTime)'; + $sql .= 'weekday(E.StartDateTime)'; break; # Ending Time case 'EndDateTime': - $sql .= 'E.EndTime'; + $sql .= 'E.EndDateTime'; break; case 'EndDate': - $sql .= 'to_days(E.EndTime)'; + $sql .= 'to_days(E.EndDateTime)'; break; case 'EndTime': - $sql .= 'extract(hour_second FROM E.EndTime)'; + $sql .= 'extract(hour_second FROM E.EndDateTime)'; break; case 'EndWeekday': - $sql .= 'weekday(E.EndTime)'; + $sql .= 'weekday(E.EndDateTime)'; break; + case 'Emailed': case 'Id': case 'Name': - case 'EventDiskSpace': + case 'DiskSpace': case 'MonitorId': case 'StorageId': case 'SecondaryStorageId': @@ -292,19 +295,19 @@ class FilterTerm { return $sql; } # end public function sql - public function querystring($querySep='&') { + public function querystring($objectname='filter', $querySep='&') { # We don't validate the term parameters here $query = ''; if ( $this->cnj ) - $query .= $querySep.urlencode('filter[Query][terms]['.$this->index.'][cnj]').'='.$this->cnj; + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cnj]').'='.$this->cnj; if ( $this->obr ) - $query .= $querySep.urlencode('filter[Query][terms]['.$this->index.'][obr]').'='.$this->obr; + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][obr]').'='.$this->obr; - $query .= $querySep.urlencode('filter[Query][terms]['.$this->index.'][attr]').'='.urlencode($this->attr); - $query .= $querySep.urlencode('filter[Query][terms]['.$this->index.'][op]').'='.urlencode($this->op); - $query .= $querySep.urlencode('filter[Query][terms]['.$this->index.'][val]').'='.urlencode($this->val); + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][attr]').'='.urlencode($this->attr); + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][op]').'='.urlencode($this->op); + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][val]').'='.urlencode($this->val); if ( $this->cbr ) - $query .= $querySep.urlencode('filter[Query][terms]['.$this->index.'][cbr]').'='.$this->cbr; + $query .= $querySep.urlencode($objectname.'[Query][terms]['.$this->index.'][cbr]').'='.$this->cbr; return $query; } # end public function querystring @@ -400,21 +403,63 @@ class FilterTerm { } public function is_pre_sql() { - if ( $this->attr == 'DiskPercent' ) { + if ( $this->attr == 'DiskPercent' ) + return true; + if ( $this->attr == 'DiskBlocks' ) return true; - } return false; } public function is_post_sql() { if ( $this->attr == 'ExistsInFileSystem' ) { return true; - } else if ( $this->attr == 'DiskPercent' ) { - return true; } return false; } + public static function is_valid_attr($attr) { + $attrs = array( + 'ExistsInFileSystem', + 'Emailed', + 'DiskSpace', + 'DiskPercent', + 'DiskBlocks', + 'MonitorName', + 'ServerId', + 'MonitorServerId', + 'StorageServerId', + 'FilterServerId', + 'DateTime', + 'Date', + 'Time', + 'Weekday', + 'StartDateTime', + 'FramesEventId', + 'StartDate', + 'StartTime', + 'StartWeekday', + 'EndDateTime', + 'EndDate', + 'EndTime', + 'EndWeekday', + 'Id', + 'Name', + 'MonitorId', + 'StorageId', + 'SecondaryStorageId', + 'Length', + 'Frames', + 'AlarmFrames', + 'TotScore', + 'AvgScore', + 'MaxScore', + 'Cause', + 'Notes', + 'StateId', + 'Archived' + ); + return in_array($attr, $attrs); + } } # end class FilterTerm ?> diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index de1a10c2e..f2a0c2b8e 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -114,18 +114,6 @@ class Monitor extends ZM_Object { 'WebColour' => '#ff0000', 'Exif' => array('type'=>'boolean','default'=>0), 'Sequence' => null, - 'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), - 'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), 'ZoneCount' => 0, 'Refresh' => null, 'DefaultCodec' => 'auto', @@ -138,6 +126,18 @@ class Monitor extends ZM_Object { 'AnalysisFPS' => null, 'CaptureFPS' => null, 'CaptureBandwidth' => null, + 'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), + 'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1), ); public function Control() { diff --git a/web/includes/Object.php b/web/includes/Object.php index e8fff930b..aa571bb59 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -11,7 +11,16 @@ class ZM_Object { $row = NULL; if ( $IdOrRow ) { + global $object_cache; + if ( ! isset($object_cache[$class]) ) { + $object_cache[$class] = array(); + } + $cache = &$object_cache[$class]; + if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) { + if ( isset($cache[$IdOrRow]) ) { + return $cache[$IdOrRow]; + } $table = $class::$table; $row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($IdOrRow)); if ( !$row ) { @@ -22,12 +31,6 @@ class ZM_Object { } if ( $row ) { - global $object_cache; - if ( ! isset($object_cache[$class]) ) { - $object_cache[$class] = array(); - } - $cache = &$object_cache[$class]; - foreach ($row as $k => $v) { $this->{$k} = $v; } diff --git a/web/includes/actions/filter.php b/web/includes/actions/filter.php index 63f4af94e..9216ef723 100644 --- a/web/includes/actions/filter.php +++ b/web/includes/actions/filter.php @@ -46,7 +46,6 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) { } } else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) { - $sql = ''; $_REQUEST['filter']['Query']['sort_field'] = validStr($_REQUEST['filter']['Query']['sort_field']); $_REQUEST['filter']['Query']['sort_asc'] = validStr($_REQUEST['filter']['Query']['sort_asc']); $_REQUEST['filter']['Query']['limit'] = validInt($_REQUEST['filter']['Query']['limit']); @@ -95,7 +94,7 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) { } else if ( $filter->Background() ) { $filter->control('start'); } - $redirect = '?view=filter&Id='.$filter->Id(); + $redirect = '?view=filter'.$filter->querystring('filter', '&'); } else if ( $action == 'control' ) { if ( $_REQUEST['command'] == 'start' diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index 140957046..be17578cf 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -121,7 +121,8 @@ if ( $action == 'save' ) { } $saferNewName = basename($_REQUEST['newMonitor']['Name']); $link_path = $NewStorage->Path().'/'.$saferNewName; - if ( !symlink($NewStorage->Path().'/'.$mid, $link_path) ) { + // Use a relative path for the target so the link continues to work from backups or directory changes. + if ( !symlink($mid, $link_path) ) { if ( ! ( file_exists($link_path) and is_link($link_path) ) ) { ZM\Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName); } diff --git a/web/includes/auth.php b/web/includes/auth.php index f7cf7ec82..a4e1e4268 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -248,14 +248,12 @@ function userFromSession() { if ( isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) ) $user = getAuthUser($_SESSION['AuthHash'.$_SESSION['remoteAddr']]); else - ZM\Debug("No auth hash in session, there should have been"); + ZM\Debug('No auth hash in session, there should have been'); } else { # Need to refresh permissions and validate that the user still exists $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?'; $user = dbFetchOne($sql, NULL, array($_SESSION['username'])); } - } else { - ZM\Debug('No username in session'); } return $user; } diff --git a/web/includes/functions.php b/web/includes/functions.php index 7a562ca38..9ac46cd3c 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -996,23 +996,19 @@ function parseSort($saveToSession=false, $querySep='&') { $sortColumn = 'E.Cause'; break; case 'DateTime' : - $sortColumn = 'E.StartTime'; - $_REQUEST['sort_field'] = 'StartTime'; + $sortColumn = 'E.StartDateTime'; + $_REQUEST['sort_field'] = 'StartDateTime'; break; case 'DiskSpace' : $sortColumn = 'E.DiskSpace'; break; case 'StartTime' : - $sortColumn = 'E.StartTime'; - break; case 'StartDateTime' : - $sortColumn = 'E.StartTime'; + $sortColumn = 'E.StartDateTime'; break; case 'EndTime' : - $sortColumn = 'E.EndTime'; - break; case 'EndDateTime' : - $sortColumn = 'E.EndTime'; + $sortColumn = 'E.EndDateTime'; break; case 'Length' : $sortColumn = 'E.Length'; @@ -1048,7 +1044,7 @@ function parseSort($saveToSession=false, $querySep='&') { $sortColumn = 'F.Score'; break; default: - $sortColumn = 'E.StartTime'; + $sortColumn = 'E.StartDateTime'; break; } if ( !isset($_REQUEST['sort_asc']) ) @@ -1077,7 +1073,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $Filter = ZM\Filter::parse($filter, $querySep); $filter['sql'] = $Filter->sql(); - $filter['querystring'] = $Filter->querystring($querySep); + $filter['querystring'] = $Filter->querystring('filter', $querySep); $filter['hidden_fields'] = $Filter->hidden_fields(); $filter['pre_sql_conditions'] = $Filter->pre_sql_conditions(); $filter['post_sql_conditions'] = $Filter->post_sql_conditions(); diff --git a/web/includes/logger.php b/web/includes/logger.php index fb7d58f93..ab59b412b 100644 --- a/web/includes/logger.php +++ b/web/includes/logger.php @@ -408,10 +408,10 @@ class Logger { } } - $message = $code.' ['.$string.']'; if ( $level <= $this->syslogLevel ) - syslog( self::$syslogPriorities[$level], $message ); + syslog(self::$syslogPriorities[$level], $message); + $message = $code.' ['.$string.']'; if ( $level <= $this->databaseLevel ) { try { global $dbConn; diff --git a/web/includes/session.php b/web/includes/session.php index 2358567ba..0190f9897 100644 --- a/web/includes/session.php +++ b/web/includes/session.php @@ -144,8 +144,10 @@ class Session { } public function _gc($max) { // Calculate what is to be deemed old - $old = time() - $max; - $sth = $this->db->prepare('DELETE * FROM Sessions WHERE access < :old'); + $now = time(); + $old = $now - $max; + ZM\Debug('doing session gc ' . $now . '-' . $max. '='.$old); + $sth = $this->db->prepare('DELETE FROM Sessions WHERE access < :old'); $sth->bindParam(':old', $old, PDO::PARAM_INT); return $sth->execute() ? true : false; } diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index b80121bed..0877f7399 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -371,6 +371,7 @@ $SLANG = array( 'FilterUpdateDiskSpace' => 'Update used disk space', 'FilterDeleteEvents' => 'Delete all matches', 'FilterCopyEvents' => 'Copy all matches', + 'FilterLockRows' => 'Lock Rows', 'FilterMoveEvents' => 'Move all matches', 'FilterEmailEvents' => 'Email details of all matches', 'FilterEmailTo' => 'Email To', diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index c6723b3a8..ee7a8a471 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -123,7 +123,7 @@ table th:last-child{ a:link { - color: #3498db; + color: #0fbcf9; text-decoration: none; } diff --git a/web/skins/classic/css/base/views/cycle.css b/web/skins/classic/css/base/views/cycle.css new file mode 100644 index 000000000..9d89bc4b3 --- /dev/null +++ b/web/skins/classic/css/base/views/cycle.css @@ -0,0 +1,3 @@ +#imageFeed { + margin: 0 auto; +} diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index b33d1bb7d..85449927c 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -351,6 +351,11 @@ if ( currentView != 'none' && currentView != 'login' ) { .done(setNavBar) .fail(function(jqxhr, textStatus, error) { console.log("Request Failed: " + textStatus + ", " + error); + if ( ! jqxhr.responseText ) { + console.log("No responseText in jqxhr"); + console.log(jqxhr); + return; + } console.log("Response Text: " + jqxhr.responseText.replace(/(<([^>]+)>)/gi, '')); if ( textStatus != "timeout" ) { // The idea is that this should only fail due to auth, so reload the page @@ -367,10 +372,14 @@ if ( currentView != 'none' && currentView != 'login' ) { } if ( data.auth ) { if ( data.auth != auth_hash ) { + console.log("Update auth_hash to "+data.auth); // Update authentication token. auth_hash = data.auth; } } + if ( data.auth_relay ) { + auth_relay = data.auth_relay; + } // iterate through all the keys then update each element id with the same name for (var key of Object.keys(data)) { if ( key == "auth" ) continue; @@ -752,8 +761,13 @@ function stateStuff(action, runState, newState) { } function logAjaxFail(jqxhr, textStatus, error) { - var responseText = jqxhr.responseText.replace(/(<([^>]+)>)/gi, '').trim(); // strip any html or whitespace from the response console.log("Request Failed: " + textStatus + ", " + error); + if ( ! jqxhr.responseText ) { + console.log("Ajax request failed. No responseText. jqxhr follows:"); + console.log(jqxhr); + return; + } + var responseText = jqxhr.responseText.replace(/(<([^>]+)>)/gi, '').trim(); // strip any html or whitespace from the response if ( responseText ) console.log("Response Text: " + responseText); } diff --git a/web/skins/classic/views/cycle.php b/web/skins/classic/views/cycle.php index ba97d341c..b9f779c50 100644 --- a/web/skins/classic/views/cycle.php +++ b/web/skins/classic/views/cycle.php @@ -182,7 +182,7 @@ xhtmlHeaders(__FILE__, translate('CycleWatch')); ?> -
+
diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index 516db0c53..cea64dd59 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -49,9 +49,6 @@ if ( isset($_REQUEST['rate']) ) { if ( isset($_REQUEST['scale']) ) { $scale = validInt($_REQUEST['scale']); -} else if ( isset($_COOKIE['zmEventScaleAuto']) ) { - // If we're using scale to fit use it on all monitors - $scale = '0'; } else if ( isset($_COOKIE['zmEventScale'.$Event->MonitorId()]) ) { $scale = $_COOKIE['zmEventScale'.$Event->MonitorId()]; } else { @@ -149,7 +146,7 @@ if ( !$Event->Id() ) { Id() ?> Id().' '.validHtmlStr($Monitor->Name()) ?> Cause()) ?> - StartTime())) ?> + StartDateTime())) ?> Length().'s' ?> Frames() ?>/AlarmFrames() ?> TotScore() ?>/AvgScore() ?>/MaxScore() ?> diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index a70fa8d19..86376d6b4 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -42,73 +42,6 @@ if ( isset($_REQUEST['filter'])) { parseSort(); $filterQuery = $filter->querystring(); -ZM\Debug('Filter '.print_r($filter, true)); - -if ( $filter->sql() ) { - $eventsSql .= ' AND ('.$filter->sql().')'; -} else { - ZM\Warning('No filters'); - exit; -} -$eventsSql .= ' ORDER BY '.$sortColumn.' '.$sortOrder; -if ( $sortColumn != 'E.Id' ) $eventsSql .= ',E.Id '.$sortOrder; - -$page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 0; -$limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : $filter['limit']; - -if ( $_POST ) { - // I think this is basically so that a refresh doesn't repost - ZM\Debug('Redirecting to ' . $_SERVER['REQUEST_URI']); - header('Location: ?view=' . $view.htmlspecialchars_decode($filterQuery).htmlspecialchars_decode($sortQuery).$limitQuery.'&page='.$page); - exit(); -} - -$failed = !$filter->test_pre_sql_conditions(); -if ( $failed ) { - ZM\Debug('Pre conditions failed, not doing sql'); -} - -$results = $failed ? null : dbQuery($eventsSql); - -$nEvents = $results ? $results->rowCount() : 0; -if ( ! $results ) { - global $error_message; - $error_message = dbError($eventsSql); -} -ZM\Debug("Pre conditions succeeded sql return $nEvents events"); - -if ( !empty($limit) && ($nEvents > $limit) ) { - $nEvents = $limit; -} -$pages = (int)ceil($nEvents/ZM_WEB_EVENTS_PER_PAGE); -#Debug("Page $page Limit $limit #vents: $nEvents pages: $pages "); -if ( !empty($page) ) { - if ( $page < 0 ) - $page = 1; - else if ( $pages and ( $page > $pages ) ) - $page = $pages; - - $limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE); - if ( empty($limit) ) { - $limitAmount = ZM_WEB_EVENTS_PER_PAGE; - } else { - $limitLeft = $limit - $limitStart; - $limitAmount = ($limitLeft>ZM_WEB_EVENTS_PER_PAGE)?ZM_WEB_EVENTS_PER_PAGE:$limitLeft; - } - $eventsSql .= ' LIMIT '.$limitStart.', '.$limitAmount; -} else if ( !empty($limit) ) { - $eventsSql .= ' LIMIT 0, '.$limit; -} - -$maxShortcuts = 5; - -$focusWindow = true; - -$storage_areas = ZM\Storage::find(); -$StorageById = array(); -foreach ( $storage_areas as $S ) { - $StorageById[$S->Id()] = $S; -} xhtmlHeaders(__FILE__, translate('Events')); getBodyTopHTML(); @@ -136,6 +69,8 @@ getBodyTopHTML(); @@ -168,163 +104,24 @@ getBodyTopHTML(); - - - + + + - - - - - 1 ) { -?> - - + + + + + - - - - - - while ( $event_row = dbFetchNext($results) ) { - $event = new ZM\Event($event_row); - - if ( !$filter->test_post_sql_conditions($event) ) { - $event->remove_from_cache(); - continue; - } - $events[] = $event; - if ( $limit and (count($events) >= $limit) ) { - break; - } - ZM\Debug("Have " . count($events) . " events, limit $limit"); - } - foreach ( $events as $event ) { - - $scale = max(reScale(SCALE_BASE, $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE), SCALE_BASE); -?> - Archived() ? ' class="archived"' : '' ?>> - - - - - - - - - - - - - - - - - - - 1 ) { -?> - - -DiskSpace(); -?> - -'; - $imgSrc = $event->getThumbnailSrc(array(),'&'); - $streamSrc = $event->getStreamSrc(array( - 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&'); - - $imgHtml = ''. validHtmlStr('Event '.$event->Id()) .''; - echo ''.$imgHtml.''; - echo ''; - } // end if ZM_WEB_LIST_THUMBS -?> - - + + - - - - -1 ) { -?> - - - - - - - - - - +
diff --git a/web/skins/classic/views/export.php b/web/skins/classic/views/export.php index 90e269a33..e234402f4 100644 --- a/web/skins/classic/views/export.php +++ b/web/skins/classic/views/export.php @@ -152,8 +152,8 @@ while ( $event_row = dbFetchNext($results) ) { Name()).($event->Archived()?'*':'') ?> MonitorId(), $event->MonitorName(), canEdit( 'Monitors' ) ) ?> Cause()), canEdit( 'Events' ), 'title="' .htmlspecialchars($event->Notes()). '" class="eDetailLink" data-eid=' .$event->Id(). '"') ?> - StartTime())) . -( $event->EndTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndTime()) ) : '' ) ?> + StartDateTime())) . +( $event->EndDateTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndDateTime()) ) : '' ) ?> Length() ) ?> Id(), $event->Frames() ) ?> diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index c19e06ef7..723414f6e 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -35,13 +35,12 @@ $filterNames = array(''=>translate('ChooseFilter')); $filter = NULL; $fid = 0; -if ( isset($_REQUEST['Id']) ) { +if ( isset($_REQUEST['Id']) and $_REQUEST['Id'] ) { $fid = validInt($_REQUEST['Id']); -} else if ( isset($_REQUEST['filter[Id]']) ) { - $fid = validInt($_REQUEST['filter[Id]']); - ZM\Warning("got fid by object id $fid"); +} else if ( isset($_REQUEST['filter']) and isset($_REQUEST['filter']['Id']) ) { + $fid = validInt($_REQUEST['filter']['Id']); } - +$filter = null; foreach ( ZM\Filter::find(null,array('order'=>'lower(Name)')) as $Filter ) { $filterNames[$Filter->Id()] = $Filter->Id() . ' ' . $Filter->Name(); if ( $Filter->Background() ) @@ -53,17 +52,18 @@ foreach ( ZM\Filter::find(null,array('order'=>'lower(Name)')) as $Filter ) { $filter = $Filter; } } -if ( !$filter ) { +if ( !$filter ) { $filter = new ZM\Filter(); - - if ( isset($_REQUEST['filter']) ) { - # Update our filter object with whatever changes we have made before saving - $filter->set($_REQUEST['filter']); - } -} else { - ZM\Debug('filter: ' . print_r($filter,true)); } +ZM\Debug('filter: ' . print_r($filter,true)); +if ( isset($_REQUEST['filter']) ) { + # Update our filter object with whatever changes we have made before saving + $filter->set($_REQUEST['filter']); + ZM\Debug("Setting filter from " . print_r($_REQUEST['filter'], true)); +} +ZM\Debug('filter: ' . print_r($filter,true)); + $conjunctionTypes = ZM\getFilterQueryConjunctionTypes(); $obracketTypes = array(); $cbracketTypes = array(); @@ -89,8 +89,8 @@ $attrTypes = array( 'Cause' => translate('AttrCause'), 'DiskBlocks' => translate('AttrDiskBlocks'), 'DiskPercent' => translate('AttrDiskPercent'), - 'DiskSpace' => translate('AttrDiskSpace'), - 'EventDiskSpace' => translate('AttrEventDiskSpace'), + #'StorageDiskSpace' => translate('AttrStorageDiskSpace'), + 'DiskSpace' => translate('AttrEventDiskSpace'), 'EndDateTime' => translate('AttrEndDateTime'), 'EndDate' => translate('AttrEndDate'), 'EndTime' => translate('AttrEndTime'), @@ -366,6 +366,7 @@ for ( $i=0; $i < count($terms); $i++ ) { translate('AttrId'), 'Name' => translate('AttrName'), @@ -373,7 +374,7 @@ $sort_fields = array( 'DiskSpace' => translate('AttrDiskSpace'), 'Notes' => translate('AttrNotes'), 'MonitorName' => translate('AttrMonitorName'), - 'StartDateTime' => translate('AttrStartDateTime'), + 'StartTime' => translate('AttrStartDateTime'), 'Length' => translate('AttrDuration'), 'Frames' => translate('AttrFrames'), 'AlarmFrames' => translate('AttrAlarmFrames'), @@ -381,7 +382,7 @@ $sort_fields = array( 'AvgScore' => translate('AttrAvgScore'), 'MaxScore' => translate('AttrMaxScore'), ); -echo htmlSelect( 'filter[Query][sort_field]', $sort_fields, $filter->sort_field() ); +echo htmlSelect('filter[Query][sort_field]', $sort_fields, $filter->sort_field()); $sort_dirns = array( '1' => translate('SortAsc'), '0' => translate('SortDesc') @@ -484,6 +485,10 @@ if ( ZM_OPT_MESSAGE ) { Concurrent() ) { ?> checked="checked" data-on-click-this="updateButtons"/>

+

+ + LockRows() ) { ?> checked="checked" data-on-click-this="updateButtons"/> +


diff --git a/web/skins/classic/views/frames.php b/web/skins/classic/views/frames.php index 359703acd..da2f4c2ae 100644 --- a/web/skins/classic/views/frames.php +++ b/web/skins/classic/views/frames.php @@ -181,8 +181,9 @@ if ( count($frames) ) { Id(); + $ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth(); $thmb_width = ZM_WEB_LIST_THUMB_WIDTH ? 'width='.ZM_WEB_LIST_THUMB_WIDTH : ''; - $thmb_height = ZM_WEB_LIST_THUMB_HEIGHT ? 'height='.ZM_WEB_LIST_THUMB_HEIGHT : ''; + $thmb_height = 'height="'.( ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor ) .'"'; $thmb_fn = 'filename=' .$Event->MonitorId(). '_' .$frame['EventId']. '_' .$frame['FrameId']. '.jpg'; $img_src = join('&', array_filter(array($base_img_src, $thmb_width, $thmb_height, $thmb_fn))); $full_img_src = join('&', array_filter(array($base_img_src, $thmb_fn))); diff --git a/web/skins/classic/views/js/cycle.js b/web/skins/classic/views/js/cycle.js index a6fb05482..de958d8d0 100644 --- a/web/skins/classic/views/js/cycle.js +++ b/web/skins/classic/views/js/cycle.js @@ -40,6 +40,8 @@ function cyclePrev() { function initCycle() { periodical_id = nextCycleView.periodical(cycleRefreshTimeout); + var scale = $j('#scale').val(); + if ( scale == '0' || scale == 'auto' ) changeScale(); } function changeSize() { @@ -100,6 +102,8 @@ function changeScale() { } if ( scale != '0' && scale != '' && scale != 'auto' ) { + var newWidth = ( monitorData[monIdx].width * scale ) / SCALE_BASE; + var newHeight = ( monitorData[monIdx].height * scale ) / SCALE_BASE; if ( newWidth ) { monitor_frame.css('width', newWidth+'px'); } @@ -107,8 +111,13 @@ function changeScale() { monitor_frame.css('height', newHeight+'px'); } } else { - monitor_frame.css('width', '100%'); - monitor_frame.css('height', 'auto'); + //var bottomEl = streamMode == 'stills' ? $j('#eventImageNav') : $j('#replayStatus'); + var newSize = scaleToFit(monitorData[monIdx].width, monitorData[monIdx].height, monitor_frame, $j('#buttons')); + newWidth = newSize.width; + newHeight = newSize.height; + autoScale = newSize.autoScale; + monitor_frame.width(newWidth); + monitor_frame.height(newHeight); } /*Stream could be an applet so can't use moo tools*/ @@ -124,6 +133,7 @@ function changeScale() { //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); + // zms doesn't actually use width&height if ( scale != '0' && scale != '' && scale != 'auto' ) { src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 143f51ebc..1ddb111fa 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -16,7 +16,7 @@ function vjsReplay() { var overLaid = $j("#videoobj"); overLaid.append('

No more events

'); } else { - var endTime = (Date.parse(eventData.EndTime)).getTime(); + var endTime = (Date.parse(eventData.EndDateTime)).getTime(); var nextStartTime = nextEventStartTime.getTime(); //nextEventStartTime.getTime() is a mootools workaround, highjacks Date.parse if ( nextStartTime <= endTime ) { streamNext(true); @@ -176,12 +176,7 @@ function changeScale() { } else { alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call } - if ( scale == '0' ) { - Cookie.write('zmEventScaleAuto', 'auto', {duration: 10*365, samesite: 'strict'}); - } else { - Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365, samesite: 'strict'}); - Cookie.dispose('zmEventScaleAuto'); - } + Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365, samesite: 'strict'}); } // end function changeScale function changeReplayMode() { @@ -584,7 +579,7 @@ function getEventResponse(respObj, respText) { $('dataCause').setProperty( 'title', causeString ); } $('dataCause').set( 'text', eventData.Cause ); - $('dataTime').set( 'text', eventData.StartTime ); + $('dataTime').set( 'text', eventData.StartDateTime ); $('dataDuration').set( 'text', eventData.Length ); $('dataFrames').set( 'text', eventData.Frames+"/"+eventData.AlarmFrames ); $('dataScore').set( 'text', eventData.TotScore+"/"+eventData.AvgScore+"/"+eventData.MaxScore ); @@ -606,7 +601,7 @@ function getEventResponse(respObj, respText) { vid.src({type: 'video/mp4', src: CurEventDefVideoPath}); //Currently mp4 is all we use console.log('getEventResponse'); initialAlarmCues(eventData.Id);//ajax and render, new event - addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartTime); + addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartDateTime); CurEventDefVideoPath = null; $j('#modeValue').html('Replay'); $j('#zoomValue').html('1'); @@ -1088,7 +1083,7 @@ function initPage() { //FIXME prevent blocking...not sure what is happening or best way to unblock if ( $j('#videoobj').length ) { vid = videojs('videoobj'); - addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartTime); + addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartDateTime); $j('.vjs-progress-control').append('
');//add a place for videojs only on first load vid.on('ended', vjsReplay); vid.on('play', vjsPlay); diff --git a/web/skins/classic/views/js/event.js.php b/web/skins/classic/views/js/event.js.php index beee0b668..105f47da1 100644 --- a/web/skins/classic/views/js/event.js.php +++ b/web/skins/classic/views/js/event.js.php @@ -44,8 +44,8 @@ var eventData = { Width: 'Width() ?>', Height: 'Height() ?>', Length: 'Length() ?>', - StartTime: 'StartTime() ?>', - EndTime: 'EndTime() ?>', + StartDateTime: 'StartDateTime() ?>', + EndDateTime: 'EndDateTime() ?>', Frames: 'Frames() ?>', MonitorName: 'Name()) ?>' }; diff --git a/web/skins/classic/views/js/events.js b/web/skins/classic/views/js/events.js index f037795e8..7d4c67369 100644 --- a/web/skins/classic/views/js/events.js +++ b/web/skins/classic/views/js/events.js @@ -17,14 +17,14 @@ var params = "data": { "search":"some search text", - "sort":"StartTime", + "sort":"StartDateTime", "order":"asc", "offset":0, "limit":25 "filter": { "Name":"some advanced search text" - "StartTime":"some more advanced search text" + "StartDateTime":"some more advanced search text" } }, "cache":true, @@ -35,7 +35,11 @@ var params = // Called by bootstrap-table to retrieve zm event data function ajaxRequest(params) { - $j.getJSON(thisUrl + '?view=request&request=events&task=query', params.data) + if ( params.data && params.data.filter ) { + params.data.advsearch = params.data.filter; + delete params.data.filter; + } + $j.getJSON(thisUrl + '?view=request&request=events&task=query'+filterQuery, params.data) .done(function(data) { var rows = processRows(data.rows); // rearrange the result into what bootstrap-table expects @@ -57,14 +61,14 @@ function processRows(rows) { if ( canEditMonitors ) row.Monitor = '' + row.Monitor + ''; if ( canEditEvents ) row.Cause = '' + row.Cause + ''; if ( row.Notes.indexOf('detected:') >= 0 ) { - row.Cause = row.Cause + '
' + row.Notes + '
'; + row.Cause = row.Cause + '
' + row.Notes + '
'; } else if ( row.Notes != 'Forced Web: ' ) { row.Cause = row.Cause + '
' + row.Notes + '
'; } row.Frames = '' + row.Frames + ''; row.AlarmFrames = '' + row.AlarmFrames + ''; row.MaxScore = '' + row.MaxScore + ''; - row.Thumbnail = '' + row.imgHtml + ''; + if ( WEB_LIST_THUMBS ) row.Thumbnail = '' + row.imgHtml + ''; }); return rows; @@ -131,7 +135,7 @@ function manageDelConfirmModalBtns() { $j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+selections.join('&eids[]=')) .done( function(data) { $j('#eventTable').bootstrapTable('refresh'); - window.location.reload(true); + $j('#deleteConfirm').modal('hide'); }) .fail(logAjaxFail); }); @@ -157,6 +161,9 @@ function getEventDetailModal(eid) { } function initPage() { + // Remove the thumbnail column from the DOM if thumbnails are off globally + if ( !WEB_LIST_THUMBS ) $j('th[data-field="Thumbnail"]').remove(); + // Load the delete confirmation modal into the DOM getDelConfirmModal(); @@ -234,7 +241,6 @@ function initPage() { $j.getJSON(thisUrl + '?request=events&task=archive&eids[]='+selections.join('&eids[]=')) .done( function(data) { $j('#eventTable').bootstrapTable('refresh'); - window.location.reload(true); }) .fail(logAjaxFail); }); @@ -247,17 +253,14 @@ function initPage() { } var selections = getIdSelections(); - console.log(selections); + //console.log(selections); evt.preventDefault(); $j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+selections.join('&eids[]=')) .done( function(data) { $j('#eventTable').bootstrapTable('refresh'); - window.location.reload(true); }) .fail(logAjaxFail); - - //window.location.reload(true); }); // Manage the EDIT button @@ -317,13 +320,6 @@ function initPage() { $j('#deleteConfirm').modal('show'); }); - // Manage the eventdetail links in the events list - $j(".eDetailLink").click(function(evt) { - evt.preventDefault(); - var eid = $j(this).data('eid'); - getEventDetailModal(eid); - }); - // Update table links each time after new data is loaded table.on('post-body.bs.table', function(data) { // Manage the eventdetail links in the events list @@ -339,6 +335,7 @@ function initPage() { table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail zoom'); }); + table.bootstrapTable('resetSearch'); // The table is initially given a hidden style, so now that we are done rendering, show it table.show(); } diff --git a/web/skins/classic/views/js/events.js.php b/web/skins/classic/views/js/events.js.php index 48f405dd7..207a6745e 100644 --- a/web/skins/classic/views/js/events.js.php +++ b/web/skins/classic/views/js/events.js.php @@ -11,3 +11,4 @@ var archivedString = ""; var emailedString = ""; var yesString = ""; var noString = ""; +var WEB_LIST_THUMBS = ; diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index fd7ee94ca..707fb06e1 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -74,35 +74,32 @@ function validateForm(form) { function updateButtons(element) { var form = element.form; - if ( element.type == 'checkbox' && element.checked ) { - form.elements['executeButton'].disabled = false; - } else { - var canExecute = false; - if ( form.elements['filter[AutoArchive]'] && form.elements['filter[AutoArchive]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoUnarchive]'] && form.elements['filter[AutoUnarchive]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoCopy]'] && form.elements['filter[AutoCopy]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoMove]'] && form.elements['filter[AutoMove]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoVideo]'] && form.elements['filter[AutoVideo]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoUpload]'] && form.elements['filter[AutoUpload]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoEmail]'] && form.elements['filter[AutoEmail]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoMessage]'] && form.elements['filter[AutoMessage]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[AutoExecute]'].checked && form.elements['filter[AutoExecuteCmd]'].value != '' ) { - canExecute = true; - } else if ( form.elements['filter[AutoDelete]'].checked ) { - canExecute = true; - } else if ( form.elements['filter[UpdateDiskSpace]'].checked ) { - canExecute = true; - } - form.elements['executeButton'].disabled = !canExecute; + + var canExecute = false; + if ( form.elements['filter[AutoArchive]'] && form.elements['filter[AutoArchive]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoUnarchive]'] && form.elements['filter[AutoUnarchive]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoCopy]'] && form.elements['filter[AutoCopy]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoMove]'] && form.elements['filter[AutoMove]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoVideo]'] && form.elements['filter[AutoVideo]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoUpload]'] && form.elements['filter[AutoUpload]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoEmail]'] && form.elements['filter[AutoEmail]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoMessage]'] && form.elements['filter[AutoMessage]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[AutoExecute]'].checked && form.elements['filter[AutoExecuteCmd]'].value != '' ) { + canExecute = true; + } else if ( form.elements['filter[AutoDelete]'].checked ) { + canExecute = true; + } else if ( form.elements['filter[UpdateDiskSpace]'].checked ) { + canExecute = true; } + form.elements['executeButton'].disabled = !canExecute; if ( form.elements['filter[Name]'].value ) { form.elements['Save'].disabled = false; form.elements['SaveAs'].disabled = false; diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index d612e806e..75cb89707 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -105,7 +105,6 @@ function initPage() { document.querySelectorAll('input[name="newMonitor[MaxFPS]"]').forEach(function(el) { el.oninput = el.onclick = function(e) { if ( e.target.value ) { - console.log('showing'); $j('#newMonitor\\[MaxFPS\\]').show(); } else { $j('#newMonitor\\[MaxFPS\\]').hide(); @@ -115,7 +114,6 @@ function initPage() { document.querySelectorAll('input[name="newMonitor[AlarmMaxFPS]"]').forEach(function(el) { el.oninput = el.onclick = function(e) { if ( e.target.value ) { - console.log('showing'); $j('#newMonitor\\[AlarmMaxFPS\\]').show(); } else { $j('#newMonitor\\[AlarmMaxFPS\\]').hide(); @@ -209,7 +207,6 @@ function initPage() { var form = document.getElementById('contentForm'); var latitude = form.elements['newMonitor[Latitude]'].value; var longitude = form.elements['newMonitor[Longitude]'].value; - console.log("lat: " + latitude + ', long:'+longitude); map = L.map('LocationMap', { center: L.latLng(latitude, longitude), zoom: 13, @@ -258,13 +255,9 @@ function random_WebColour() { function update_estimated_ram_use() { var buffer_count = document.querySelectorAll('input[name="newMonitor[ImageBufferCount]"]')[0].value; - console.log(buffer_count); var width = document.querySelectorAll('input[name="newMonitor[Width]"]')[0].value; - console.log(width); var height = document.querySelectorAll('input[name="newMonitor[Height]"]')[0].value; - console.log(height); var colours = document.querySelectorAll('select[name="newMonitor[Colours]"]')[0].value; - console.log(colours); document.getElementById('estimated_ram_use').innerHTML = human_filesize(buffer_count * width * height * colours, 0); } diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index cc61270d0..bdcc17554 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -1,4 +1,4 @@ -var ZM_OPT_USE_GEOLOCATION = ''; +var ZM_OPT_USE_GEOLOCATION = '' == '1' ? true : false; 0 '; -// select E.Id,E.Name,UNIX_TIMESTAMP(E.StartTime) as StartTimeSecs,UNIX_TIMESTAMP(max(DATE_ADD(E.StartTime, Interval Delta+0.5 Second))) as CalcEndTimeSecs, E.Length,max(F.FrameId) as Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId +// select E.Id,E.Name,UNIX_TIMESTAMP(E.StartDateTime) as StartTimeSecs,UNIX_TIMESTAMP(max(DATE_ADD(E.StartDateTime, Interval Delta+0.5 Second))) as CalcEndTimeSecs, E.Length,max(F.FrameId) as Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId // from Events as E // inner join Monitors as M on (E.MonitorId = M.Id) // inner join Frames F on F.EventId=E.Id -// where not isnull(E.Frames) and not isnull(StartTime) "; +// where not isnull(E.Frames) and not isnull(StartDateTime) "; // Note that the delta value seems more accurate than the time stamp for some reason. $framesSql = ' @@ -219,14 +219,14 @@ $initialDisplayInterval = 1000; if ( isset($_REQUEST['displayinterval']) ) $initialDisplayInterval = validHtmlStr($_REQUEST['displayinterval']); -#$eventsSql .= ' GROUP BY E.Id,E.Name,E.StartTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId'; +#$eventsSql .= ' GROUP BY E.Id,E.Name,E.StartDateTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId'; $minTimeSecs = $maxTimeSecs = 0; if ( isset($minTime) && isset($maxTime) ) { $minTimeSecs = strtotime($minTime); $maxTimeSecs = strtotime($maxTime); - $eventsSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'"; - $framesSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'"; + $eventsSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'"; + $framesSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'"; $framesSql .= ") AND TimeStamp > '" . $minTime . "' AND TimeStamp < '" . $maxTime . "'"; } else { $framesSql .= ')'; diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index cc8b0b568..2be9e9b52 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -231,6 +231,7 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as Url()), $canEdit, $svr_opt ) ?> PathToIndex()), $canEdit, $svr_opt ) ?> PathToZMS()), $canEdit, $svr_opt ) ?> + PathToAPI()), $canEdit, $svr_opt ) ?> Status()), $canEdit, $svr_opt) ?> Id()]), $canEdit, $svr_opt) ?> @@ -279,17 +280,23 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as 'lower(Name)') ) as $Storage ) { + $filter = new ZM\Filter(); + $filter->addTerm(array('attr'=>'StorageId','op'=>'=','val'=>$Storage->Id())); + if ( $user['MonitorIds'] ) { + $filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds'])); + } + $str_opt = 'class="storageCol" data-sid="'.$Storage->Id().'"'; ?> - Id()), $canEdit, $str_opt ) ?> - Name()), $canEdit, $str_opt ) ?> - Path()), $canEdit, $str_opt ) ?> - Type()), $canEdit, $str_opt ) ?> - Scheme()), $canEdit, $str_opt ) ?> - Server()->Name()), $canEdit, $str_opt ) ?> + Id()), $canEdit, $str_opt) ?> + Name()), $canEdit, $str_opt) ?> + Path()), $canEdit, $str_opt) ?> + Type()), $canEdit, $str_opt) ?> + Scheme()), $canEdit, $str_opt) ?> + Server()->Name()), $canEdit, $str_opt) ?> disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?> - EventCount().' using '.human_filesize($Storage->event_disk_space()) ?> + querystring(), $Storage->EventCount().' using '.human_filesize($Storage->event_disk_space()) ); ?> EventCount() or !$canEdit ) { ?> disabled="disabled"EventCount() ? ' title="Can\'t delete as long as there are events stored here."' : ''?>/> @@ -302,13 +309,12 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as APIs are disabled. To enable, please turn on OPT_USE_API in Options->System
"; - } - else { + $apiEnabled = dbFetchOne('SELECT Value FROM Config WHERE Name=\'ZM_OPT_USE_API\''); + if ( $apiEnabled['Value'] != '1' ) { + echo '
APIs are disabled. To enable, please turn on OPT_USE_API in Options->System
'; + } else { ?>
diff --git a/web/skins/classic/views/report_event_audit.php b/web/skins/classic/views/report_event_audit.php index 46c4dc90d..3dfc08a70 100644 --- a/web/skins/classic/views/report_event_audit.php +++ b/web/skins/classic/views/report_event_audit.php @@ -65,8 +65,8 @@ $filterQuery = $filter['query']; ZM\Debug($filterQuery); $eventsSql = 'SELECT *, - UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs, - UNIX_TIMESTAMP(EndTime) AS EndTimeSecs + UNIX_TIMESTAMP(E.StartDateTime) AS StartTimeSecs, + UNIX_TIMESTAMP(EndDateTime) AS EndTimeSecs FROM Events AS E WHERE 1 > 0 '; @@ -77,7 +77,7 @@ if ( count($selected_monitor_ids) ) { $eventsSql .= ' AND MonitorId IN ('.implode(',', $selected_monitor_ids).')'; } if ( isset($minTime) && isset($maxTime) ) { - $eventsSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'"; + $eventsSql .= " AND EndDateTime > '" . $minTime . "' AND StartDateTime < '" . $maxTime . "'"; } $eventsSql .= ' ORDER BY Id ASC'; @@ -211,8 +211,8 @@ for ( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { Server()->Name())?> Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?> - link_to($FirstEvent->Id().' at '.$FirstEvent->StartTime()) : 'none'?> - link_to($LastEvent->Id().' at '.$LastEvent->StartTime()) : 'none'?> + link_to($FirstEvent->Id().' at '.$FirstEvent->StartDateTime()) : 'none'?> + link_to($LastEvent->Id().' at '.$LastEvent->StartDateTime()) : 'none'?> diff --git a/web/skins/classic/views/timeline.php b/web/skins/classic/views/timeline.php index 264c8128e..c4f11d312 100644 --- a/web/skins/classic/views/timeline.php +++ b/web/skins/classic/views/timeline.php @@ -128,9 +128,9 @@ $chart = array( $monitors = array(); # The as E, and joining with Monitors is required for the filterSQL filters. -$rangeSql = 'SELECT min(E.StartTime) AS MinTime, max(E.EndTime) AS MaxTime FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(E.StartTime) AND NOT isnull(E.EndTime)'; -$eventsSql = 'SELECT E.* FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartTime)'; -$eventIdsSql = 'SELECT E.Id FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartTime)'; +$rangeSql = 'SELECT min(E.StartDateTime) AS MinTime, max(E.EndDateTime) AS MaxTime FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(E.StartDateTime) AND NOT isnull(E.EndDateTime)'; +$eventsSql = 'SELECT E.* FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartDateTime)'; +$eventIdsSql = 'SELECT E.Id FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartDateTime)'; $eventsValues = array(); if ( !empty($user['MonitorIds']) ) { @@ -277,8 +277,8 @@ $midTimeT = $minTimeT + $halfRange; $midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT); if ( isset($minTime) && isset($maxTime) ) { - $eventsSql .= " AND EndTime >= '$minTime' AND StartTime <= '$maxTime'"; - $eventIdsSql .= " AND EndTime >= '$minTime' AND StartTime <= '$maxTime'"; + $eventsSql .= " AND EndDateTime >= '$minTime' AND StartDateTime <= '$maxTime'"; + $eventIdsSql .= " AND EndDateTime >= '$minTime' AND StartDateTime <= '$maxTime'"; } if ( 0 ) { @@ -332,13 +332,13 @@ while( $event = $events_result->fetch(PDO::FETCH_ASSOC) ) { $currEventSlots = &$monEventSlots[$event['MonitorId']]; $currFrameSlots = &$monFrameSlots[$event['MonitorId']]; - $startTimeT = strtotime($event['StartTime']); + $startTimeT = strtotime($event['StartDateTime']); $startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); if ( $startIndex < 0 ) $startIndex = 0; - if ( isset($event['EndTime']) ) - $endTimeT = strtotime($event['EndTime']); + if ( isset($event['EndDateTime']) ) + $endTimeT = strtotime($event['EndDateTime']); else $endTimeT = time(); $endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); diff --git a/web/views/image.php b/web/views/image.php index 4d4788094..768c3c346 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -242,17 +242,21 @@ if ( empty($_REQUEST['path']) ) { header('HTTP/1.0 404 Not Found'); ZM\Fatal("Can't create frame images from video because there is no video file for this event at (".$Event->Path().'/'.$Event->DefaultVideo() ); } - $command = ZM_PATH_FFMPEG.' -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -frames:v 1 '.$path; + $command = ZM_PATH_FFMPEG.' -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -frames:v 1 '.$path . ' 2>&1'; #$command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path; #$command ='ffmpeg -v 0 -i '.$Storage->Path().'/'.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path; ZM\Debug("Running $command"); $output = array(); $retval = 0; - exec( $command, $output, $retval ); + exec($command, $output, $retval); ZM\Debug("Command: $command, retval: $retval, output: " . implode("\n", $output)); - if ( ! file_exists( $path ) ) { + if ( ! file_exists($path) ) { header('HTTP/1.0 404 Not Found'); - ZM\Fatal('Can\'t create frame images from video for this event '.$Event->DefaultVideo() ); + ZM\Fatal('Can\'t create frame images from video for this event '.$Event->DefaultVideo().' + +Command was: '.$command.' + +Output was: '.implode(PHP_EOL,$output) ); } # Generating an image file will use up more disk space, so update the Event record. $Event->DiskSpace(null);