diff --git a/db/zm_update-1.35.21.sql b/db/zm_update-1.35.21.sql new file mode 100644 index 000000000..d3d80e08c --- /dev/null +++ b/db/zm_update-1.35.21.sql @@ -0,0 +1,56 @@ +-- +-- Add HomeView to Users +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Users' + AND column_name = 'HomeView' + ) > 0, +"SELECT 'Column HomeView already exists in Users'", +"ALTER TABLE `Users` ADD `HomeView` varchar(64) NOT NULL DEFAULT '' AFTER `APIEnabled`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.TABLES + WHERE table_name = 'Snapshots' + AND table_schema = DATABASE() + ) > 0, + "SELECT 'Snapshots table exists'", + "CREATE TABLE Snapshots ( + `Id` int(10) unsigned NOT NULL auto_increment, + `Name` VARCHAR(64), + `Description` TEXT, + `CreatedBy` int(10), + `CreatedOn` datetime default NULL, + PRIMARY KEY(Id) +) ENGINE=InnoDB;" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.TABLES + WHERE table_name = 'Snapshot_Events' + AND table_schema = DATABASE() + ) > 0, + "SELECT 'Snapshot_Events table exists'", + "CREATE TABLE Snapshot_Events ( + `Id` int(10) unsigned NOT NULL auto_increment, + `SnapshotId` int(10) unsigned NOT NULL, + FOREIGN KEY (`SnapshotId`) REFERENCES `Snapshots` (`Id`) ON DELETE CASCADE, + `EventId` bigint unsigned NOT NULL, + FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE, + KEY `Snapshot_Events_SnapshotId_idx` (`SnapshotId`), + PRIMARY KEY(Id) +) ENGINE=InnoDB;" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/web/ajax/snapshots.php b/web/ajax/snapshots.php new file mode 100644 index 000000000..e3da25ae3 --- /dev/null +++ b/web/ajax/snapshots.php @@ -0,0 +1,227 @@ + "search text" pairs +// Bootstrap table sends json_ecoded array, which we must decode +$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array(); + +// Sort specifies the name of the column to sort on +$sort = 'Id'; +if ( isset($_REQUEST['sort']) ) { + $sort = $_REQUEST['sort']; +} + +// 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 +// Set the default to 0 for events view, to prevent an issue with ALL pagination +$limit = 0; +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']; + } +} + +// +// MAIN LOOP +// + +switch ( $task ) { + case 'delete' : + if ( !canEdit('Events') ) { + ajaxError('Insufficient permissions for user '.$user['Username']); + return; + } + + foreach ( $ids as $id ) $data[] = deleteRequest($id); + break; + case 'query' : + $data = queryRequest($search, $advsearch, $sort, $offset, $order, $limit); + break; + default : + ZM\Fatal("Unrecognised task '$task'"); +} // end switch task + +ajaxResponse($data); + +// +// FUNCTION DEFINITIONS +// + +function deleteRequest($id) { + $message = array(); + $snapshot = new ZM\Snapshot($id); + if ( !$snapshot->Id() ) { + $message[] = array($id=>'Snapshot not found.'); + //} else if ( $snapshot->Archived() ) { + //$message[] = array($id=>'Event is archived, cannot delete it.'); + } else { + $snapshot->delete(); + } + + return $message; +} + +function queryRequest($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) + ); + + // Put server pagination code here + // The table we want our data from + $table = 'Snapshots'; + + // The names of the dB columns in the events table we are interested in + $columns = array('Id', 'Name', 'Description', 'CreatedDateTime'); + + if ( !in_array($sort, array_merge($columns)) ) { + ZM\Error('Invalid sort field: ' . $sort); + $sort = 'Id'; + } + + $values = array(); + $likes = array(); + $where = ''; + + $col_str = '*'; + $sql = 'SELECT ' .$col_str. ' FROM `Snapshots`'.$where.' ORDER BY '.$sort.' '.$order; + + $unfiltered_rows = array(); + $event_ids = array(); + + ZM\Debug('Calling the following sql query: ' .$sql); + $query = dbQuery($sql, $values); + if ( $query ) { + while ( $row = dbFetchNext($query) ) { + $snapshot = new ZM\Snapshot($row); + $snapshot->remove_from_cache(); + $snapshot_ids[] = $snapshot->Id(); + $unfiltered_rows[] = $row; + } # end foreach row + } + + ZM\Debug('Have ' . count($unfiltered_rows) . ' snapshots matching base filter.'); + + $filtered_rows = null; + + if ( count($advsearch) or $search != '' ) { + $search_filter = new ZM\Filter(); + + $search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$event_ids)); + + // 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) ) { + $terms = array(); + foreach ( $advsearch as $col=>$text ) { + $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); + } # end foreach col in advsearch + $terms[0]['obr'] = 1; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter->addTerms($terms); + } else if ( $search != '' ) { + $search = '%' .$search. '%'; + $terms = array(); + foreach ( $columns as $col ) { + $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); + } + $terms[0]['obr'] = 1; + $terms[0]['cnj'] = 'and'; + $terms[count($terms)-1]['cbr'] = 1; + $search_filter = $search_filter->addTerms($terms, array('obr'=>1, 'cbr'=>1, 'op'=>'OR')); + } # end if search + + $sql = 'SELECT ' .$col_str. ' FROM `Snapshots` WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order; + ZM\Debug('Calling the following sql query: ' .$sql); + $filtered_rows = dbFetchAll($sql); + ZM\Debug('Have ' . count($filtered_rows) . ' snapshots matching search filter.'); + } else { + $filtered_rows = $unfiltered_rows; + } # end if search_filter->terms() > 1 + + $returned_rows = array(); + foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { + $snapshot = new ZM\Snapshot($row); + + //$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'), '&'); + + // Modify the row data as needed + //$row['imgHtml'] = 'Event '.$event->Id().''; + $row['Name'] = validHtmlStr($row['Name']); + $row['Description'] = validHtmlStr($row['Description']); + //$row['Archived'] = $row['Archived'] ? translate('Yes') : translate('No'); + //$row['Emailed'] = $row['Emailed'] ? translate('Yes') : translate('No'); + //$row['Cause'] = validHtmlStr($row['Cause']); + $row['CreatedOn'] = strftime(STRF_FMT_DATETIME_SHORTER, strtotime($row['CreatedOn'])); + //$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'] = nl2br(htmlspecialchars($row['Notes'])); + //$row['DiskSpace'] = human_filesize($event->DiskSpace()); + $returned_rows[] = $row; + } # end foreach row matching search + + $data['rows'] = $returned_rows; + + # totalNotFiltered must equal total, except when either search bar has been used + $data['totalNotFiltered'] = count($unfiltered_rows); + if ( $search != '' || count($advsearch) ) { + $data['total'] = count($filtered_rows); + } else { + $data['total'] = $data['totalNotFiltered']; + } + + return $data; +} +?> diff --git a/web/includes/Snapshot.php b/web/includes/Snapshot.php new file mode 100644 index 000000000..2c36306f5 --- /dev/null +++ b/web/includes/Snapshot.php @@ -0,0 +1,65 @@ + null, + 'CreatedBy' => null, + 'CreatedOn' => 'NOW()', + 'Name' => '', + 'Description' => '', + ); + + public static function find( $parameters = array(), $options = array() ) { + return ZM_Object::_find(get_class(), $parameters, $options); + } + + public static function find_one( $parameters = array(), $options = array() ) { + return ZM_Object::_find_one(get_class(), $parameters, $options); + } + + public function Event() { + return new Event( $this->{'EventId'} ); + } + + public function delete() { + if ( property_exists($this, 'Id') ) { + dbQuery('DELETE FROM `Snapshot_Events` WHERE `SnapshotId`=?', array($this->{'Id'})); + } + } + + public function EventIds( ) { + if ( ! property_exists($this, 'EventIds') ) { + $this->{'EventIds'} = dbFetchAll('SELECT `EventId` FROM `Snapshot_Events` WHERE `SnapshotId`=?', 'EventId', array($this->{'Id'})); + } + return $this->{'EventIds'}; + } + public function Events() { + if ( ! property_exists($this, 'Events') ) { + $this->{'Events'} = Event::find(array('Id'=>$this->EventIds())); + } + return $this->{'Events'}; + } + +} # end class Snapshot + +class Snapshot_Event extends ZM_Object { + protected static $table = 'Snapshot_Events'; + protected $defaults = array( + 'Id' => null, + 'EventId' => 0, + 'SnapshotId' => 0, + ); + public static function find( $parameters = array(), $options = array() ) { + return ZM_Object::_find(get_class(), $parameters, $options); + } + + public static function find_one( $parameters = array(), $options = array() ) { + return ZM_Object::_find_one(get_class(), $parameters, $options); + } +} # end class Snapshot_Event +?> diff --git a/web/includes/actions/snapshot.php b/web/includes/actions/snapshot.php new file mode 100644 index 000000000..11edcb5a6 --- /dev/null +++ b/web/includes/actions/snapshot.php @@ -0,0 +1,73 @@ + 0 ) ) { + ZM\Error('No monitor ids given in snapshot creation request'); + return; + } + $snapshot = new ZM\Snapshot(); + $snapshot->save(array('CreatedBy'=>$user['Id'])); + + foreach ( $_REQUEST['monitor_ids'] as $monitor_id ) { + $snapshot_event = new ZM\Snapshot_Event(); + + $monitor = new ZM\Monitor($monitor_id); + $event_id = $monitor->TriggerOn(); + ZM\Debug("Have event $event_id for monitor $monitor_id"); + if ( $event_id ) { + $snapshot_event->save(array( + 'SnapshotId'=>$snapshot->Id(), + 'EventId'=>$event_id + )); + } + } # end foreach monitor + foreach ( $_REQUEST['monitor_ids'] as $monitor_id ) { + $monitor = new ZM\Monitor($monitor_id); + $monitor->TriggerOff(); + } + $redirect = '?view=snapshot&id='.$snapshot->Id(); + return; +} + +// Event scope actions, view permissions only required +if ( isset($_REQUEST['id']) ) { + $snapshot = new ZM\Snapshot($_REQUEST['id']); + if ( ($action == 'save') ) { + if ( canEdit('Events') or $snapshot->CreatedBy() == $user['Id'] ) { + + $changes = $snapshot->changes($_REQUEST['snapshot']); + if ( count($changes) ) { + $snapshot->save($changes); + } + $redirect = '?view=snapshots'; + } + } else if ( $action == 'delete' ) { + if ( canEdit('Events') ) { + $snapshot->delete(); + $redirect = '?view=snapshots'; + } + } +} // end if canEdit(Events) +?> diff --git a/web/skins/classic/css/base/views/snapshot.css b/web/skins/classic/css/base/views/snapshot.css new file mode 100644 index 000000000..b54036859 --- /dev/null +++ b/web/skins/classic/css/base/views/snapshot.css @@ -0,0 +1,17 @@ +#content { + margin: 0 15px; +} + +.Name { + width: 600px; +} +.Name input { + width: 100%; +} + +.Description { + width: 600px; +} +.Description textarea { + width: 100%; +} diff --git a/web/skins/classic/views/js/snapshot.js b/web/skins/classic/views/js/snapshot.js new file mode 100644 index 000000000..9db096277 --- /dev/null +++ b/web/skins/classic/views/js/snapshot.js @@ -0,0 +1,115 @@ +var backBtn = $j('#backBtn'); +var saveBtn = $j('#saveBtn'); +var deleteBtn = $j('#deleteBtn'); + +// Manage the DELETE CONFIRMATION modal button +function manageDelConfirmModalBtns() { + document.getElementById('delConfirmBtn').addEventListener('click', function onDelConfirmClick(evt) { + if ( !canEdit.Events ) { + enoperm(); + return; + } + + evt.preventDefault(); + /* + $j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+eventData.Id) + .done(function(data) { + streamNext(true); + }) + .fail(logAjaxFail); + */ + }); + + // Manage the CANCEL modal button + document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) { + $j('#deleteConfirm').modal('hide'); + }); +} + +function initPage() { + + // enable or disable buttons based on current selection and user rights + /* + renameBtn.prop('disabled', !canEdit.Events); + archiveBtn.prop('disabled', !(!eventData.Archived && canEdit.Events)); + unarchiveBtn.prop('disabled', !(eventData.Archived && canEdit.Events)); + */ + saveBtn.prop('disabled', !(canEdit.Events || (snapshot.CreatedBy == user.Id) )); + /* + exportBtn.prop('disabled', !canView.Events); + downloadBtn.prop('disabled', !canView.Events); + */ + deleteBtn.prop('disabled', !canEdit.Events); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Manage the BACK button + bindButton('#backBtn', 'click', null, function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + bindButton('#refreshBtn', 'click', null, function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); + + // Manage the EDIT button + bindButton('#saveBtn', 'click', null, function onSaveClick(evt) { + /* + if ( ! canEdit.Events ) { + enoperm(); + return; + } + */ + console.log(evt); + evt.target.form.submit(); + }); + + /* + // Manage the EXPORT button + bindButton('#exportBtn', 'click', null, function onExportClick(evt) { + evt.preventDefault(); + window.location.assign('?view=export&eids[]='+eventData.Id); + }); + + // Manage the DOWNLOAD VIDEO button + bindButton('#downloadBtn', 'click', null, function onDownloadClick(evt) { + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=modal&modal=download&eids[]='+eventData.Id) + .done(function(data) { + insertModalHtml('downloadModal', data.html); + $j('#downloadModal').modal('show'); + // Manage the GENERATE DOWNLOAD button + $j('#exportButton').click(exportEvent); + }) + .fail(logAjaxFail); + }); +*/ + // Manage the DELETE button + bindButton('#deleteBtn', 'click', null, function onDeleteClick(evt) { + if ( !canEdit.Events ) { + enoperm(); + return; + } + + evt.preventDefault(); + if ( ! $j('#deleteConfirm').length ) { + // Load the delete confirmation modal into the DOM + $j.getJSON(thisUrl + '?request=modal&modal=delconfirm') + .done(function(data) { + insertModalHtml('deleteConfirm', data.html); + manageDelConfirmModalBtns(); + $j('#deleteConfirm').modal('show'); + }) + .fail(logAjaxFail); + return; + } + $j('#deleteConfirm').modal('show'); + }); +} // end initPage + +// Kick everything off +$j(document).ready(initPage); diff --git a/web/skins/classic/views/js/snapshot.js.php b/web/skins/classic/views/js/snapshot.js.php new file mode 100644 index 000000000..fd97be727 --- /dev/null +++ b/web/skins/classic/views/js/snapshot.js.php @@ -0,0 +1,30 @@ + +var snapshot = ; + +var eventDataStrings = { + Id: '', + Name: '', + MonitorId: '', + MonitorName: '', + Cause: '', + StartDateTimeFmt: '', + Length: '', + Frames: '', + AlarmFrames: '', + TotScore: '', + AvgScore: '', + MaxScore: '', + DiskSpace: '', + Storage: '', + ArchivedStr: '', + EmailedStr: '' +}; + +// Strings +// +var deleteString = ""; +var causeString = ""; +var WEB_LIST_THUMB_WIDTH = ''; +var WEB_LIST_THUMB_HEIGHT = ''; diff --git a/web/skins/classic/views/js/snapshots.js b/web/skins/classic/views/js/snapshots.js new file mode 100644 index 000000000..235fd709f --- /dev/null +++ b/web/skins/classic/views/js/snapshots.js @@ -0,0 +1,312 @@ +var backBtn = $j('#backBtn'); +var exportBtn = $j('#exportBtn'); +var deleteBtn = $j('#deleteBtn'); +var table = $j('#snapshotTable'); + +/* +This is the format of the json object sent by bootstrap-table + +var params = +{ +"type":"get", +"data": + { + "search":"some search text", + "sort":"StartDateTime", + "order":"asc", + "offset":0, + "limit":25 + "filter": + { + "Name":"some advanced search text" + "StartDateTime":"some more advanced search text" + } + }, +"cache":true, +"contentType":"application/json", +"dataType":"json" +}; +*/ + +// Called by bootstrap-table to retrieve zm event data +function ajaxRequest(params) { + if ( params.data && params.data.filter ) { + params.data.advsearch = params.data.filter; + delete params.data.filter; + } + $j.getJSON(thisUrl + '?view=request&request=snapshots&task=query', params.data) + .done(function(data) { + var rows = processRows(data.rows); + // rearrange the result into what bootstrap-table expects + params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows}); + }) + .fail(function(jqXHR) { + logAjaxFail(jqXHR); + table.bootstrapTable('refresh'); + }); +} + +function processRows(rows) { + $j.each(rows, function(ndx, row) { + + var id = row.Id; + row.Id = '' + id + ''; + row.Name = '' + row.Name + ''; + row.Description = '' + row.Description + ''; + + //if ( WEB_LIST_THUMBS ) row.Thumbnail = '' + row.imgHtml + ''; + }); + + return rows; +} + +// Returns the event id's of the selected rows +function getIdSelections() { + var table = $j('#snapshotTable'); + + return $j.map(table.bootstrapTable('getSelections'), function(row) { + return row.Id.replace(/(<([^>]+)>)/gi, ''); // strip the html from the element before sending + }); +} + +// Returns a boolen to indicate at least one selected row is archived +function getArchivedSelections() { + var table = $j('#snapshotTable'); + var selection = $j.map(table.bootstrapTable('getSelections'), function(row) { + return row.Archived; + }); + return selection.includes("Yes"); +} + +// Load the Delete Confirmation Modal HTML via Ajax call +function getDelConfirmModal() { + $j.getJSON(thisUrl + '?request=modal&modal=delconfirm') + .done(function(data) { + insertModalHtml('deleteConfirm', data.html); + manageDelConfirmModalBtns(); + }) + .fail(logAjaxFail); +} + +// Manage the DELETE CONFIRMATION modal button +function manageDelConfirmModalBtns() { + document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) { + if ( ! canEdit.Events ) { + enoperm(); + return; + } + + var selections = getIdSelections(); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+selections.join('&eids[]=')) + .done( function(data) { + $j('#snapshotTable').bootstrapTable('refresh'); + $j('#deleteConfirm').modal('hide'); + }) + .fail( function(jqxhr) { + logAjaxFail(jqxhr); + $j('#snapshotTable').bootstrapTable('refresh'); + $j('#deleteConfirm').modal('hide'); + }); + }); + + // Manage the CANCEL modal button + document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) { + $j('#deleteConfirm').modal('hide'); + }); +} + +function getEventDetailModal(eid) { + $j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eids[]=' + eid) + .done(function(data) { + insertModalHtml('eventDetailModal', data.html); + $j('#eventDetailModal').modal('show'); + // Manage the Save button + $j('#eventDetailSaveBtn').click(function(evt) { + evt.preventDefault(); + $j('#eventDetailForm').submit(); + }); + }) + .fail(logAjaxFail); +} + +function getObjdetectModal(eid) { + $j.getJSON(thisUrl + '?request=modal&modal=objdetect&eid=' + eid) + .done(function(data) { + insertModalHtml('objdetectModal', data.html); + $j('#objdetectModal').modal('show'); + }) + .fail(logAjaxFail); +} + +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(); + + // Init the bootstrap-table + table.bootstrapTable({icons: icons}); + + // Hide these columns on first run when no cookie is saved + if ( !getCookie("zmEventsTable.bs.table.columns") ) { + table.bootstrapTable('hideColumn', 'Archived'); + table.bootstrapTable('hideColumn', 'Emailed'); + } + + // enable or disable buttons based on current selection and user rights + table.on('check.bs.table uncheck.bs.table ' + + 'check-all.bs.table uncheck-all.bs.table', + function() { + selections = table.bootstrapTable('getSelections'); + + viewBtn.prop('disabled', !(selections.length && canView.Events)); + archiveBtn.prop('disabled', !(selections.length && canEdit.Events)); + unarchiveBtn.prop('disabled', !(getArchivedSelections()) && canEdit.Events); + editBtn.prop('disabled', !(selections.length && canEdit.Events)); + exportBtn.prop('disabled', !(selections.length && canView.Events)); + downloadBtn.prop('disabled', !(selections.length && canView.Events)); + deleteBtn.prop('disabled', !(selections.length && canEdit.Events)); + }); + + // Don't enable the back button if there is no previous zm page to go back to + backBtn.prop('disabled', !document.referrer.length); + + // Setup the thumbnail video animation + initThumbAnimation(); + + // Some toolbar events break the thumbnail animation, so re-init eventlistener + table.on('all.bs.table', initThumbAnimation); + + // Manage the BACK button + document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) { + evt.preventDefault(); + window.history.back(); + }); + + // Manage the REFRESH Button + document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) { + evt.preventDefault(); + window.location.reload(true); + }); + +/* + // Manage the ARCHIVE button + document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) { + var selections = getIdSelections(); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=events&task=archive&eids[]='+selections.join('&eids[]=')) + .done( function(data) { + $j('#snapshotTable').bootstrapTable('refresh'); + }) + .fail(logAjaxFail); + }); + + // Manage the UNARCHIVE button + document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) { + if ( ! canEdit.Events ) { + enoperm(); + return; + } + + var selections = getIdSelections(); + //console.log(selections); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+selections.join('&eids[]=')) + .done( function(data) { + $j('#snapshotTable').bootstrapTable('refresh'); + }) + .fail(logAjaxFail); + }); + + // Manage the EDIT button + document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) { + if ( ! canEdit.Events ) { + enoperm(); + return; + } + + var selections = getIdSelections(); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eids[]='+selections.join('&eids[]=')) + .done(function(data) { + insertModalHtml('eventDetailModal', data.html); + $j('#eventDetailModal').modal('show'); + // Manage the Save button + $j('#eventDetailSaveBtn').click(function(evt) { + evt.preventDefault(); + $j('#eventDetailForm').submit(); + }); + }) + .fail(logAjaxFail); + }); + + // Manage the EXPORT button + document.getElementById("exportBtn").addEventListener("click", function onExportClick(evt) { + var selections = getIdSelections(); + + evt.preventDefault(); + window.location.assign('?view=export&eids[]='+selections.join('&eids[]=')); + }); + + // Manage the DOWNLOAD VIDEO button + document.getElementById("downloadBtn").addEventListener("click", function onDownloadClick(evt) { + var selections = getIdSelections(); + + evt.preventDefault(); + $j.getJSON(thisUrl + '?request=modal&modal=download&eids[]='+selections.join('&eids[]=')) + .done(function(data) { + insertModalHtml('downloadModal', data.html); + $j('#downloadModal').modal('show'); + // Manage the GENERATE DOWNLOAD button + $j('#exportButton').click(exportEvent); + }) + .fail(logAjaxFail); + }); +*/ + // Manage the DELETE button + document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) { + if ( ! canEdit.Events ) { + enoperm(); + return; + } + + evt.preventDefault(); + $j('#deleteConfirm').modal('show'); + }); + + // Update table links each time after new data is loaded + table.on('post-body.bs.table', function(data) { + // Manage the Object Detection links in the events list + $j(".objDetectLink").click(function(evt) { + evt.preventDefault(); + var eid = $j(this).data('eid'); + getObjdetectModal(eid); + }); + + // Manage the eventdetail links in the events list + $j(".eDetailLink").click(function(evt) { + evt.preventDefault(); + var eid = $j(this).data('eid'); + getEventDetailModal(eid); + }); + + var thumb_ndx = $j('#snapshotTable tr th').filter(function() { + return $j(this).text().trim() == 'Thumbnail'; + }).index(); + table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass('colThumbnail'); + }); + + table.bootstrapTable('resetSearch'); + // The table is initially given a hidden style, so now that we are done rendering, show it + table.show(); +} + +$j(document).ready(function() { + initPage(); +}); diff --git a/web/skins/classic/views/js/snapshots.js.php b/web/skins/classic/views/js/snapshots.js.php new file mode 100644 index 000000000..487d9cd5d --- /dev/null +++ b/web/skins/classic/views/js/snapshots.js.php @@ -0,0 +1,6 @@ +var confirmDeleteEventsString = ""; +var archivedString = ""; +var emailedString = ""; +var yesString = ""; +var noString = ""; +var WEB_LIST_THUMBS = ; diff --git a/web/skins/classic/views/snapshot.php b/web/skins/classic/views/snapshot.php new file mode 100644 index 000000000..042d4fe41 --- /dev/null +++ b/web/skins/classic/views/snapshot.php @@ -0,0 +1,98 @@ +Id()); +?> + +
+ +Id() ) { + echo '
Snapshot was not found.
'; +} +?> + +
+ +
+
+ + +Id() ) { ?> + + + + +Id ?> +
+ +

Id() ?>

+
+
+ +
+ CreatedOn() ?> +
+
+ +
+
+ +
+
+Id() ) { ?> + +
+Events(); + $width = 100 / ( ( count($events) < 4 ) ? count($events) : 4 ) -1; + foreach ( $snapshot->Events() as $event ) { + $imgSrc = $event->getThumbnailSrc(array(), '&'); + echo ''; + } +?> +
+Id() ?> +
+
+ diff --git a/web/skins/classic/views/snapshots.php b/web/skins/classic/views/snapshots.php new file mode 100644 index 000000000..4fefc2b98 --- /dev/null +++ b/web/skins/classic/views/snapshots.php @@ -0,0 +1,92 @@ + + +
+ +
+ + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+
+