Merge branch 'storageareas' into zma_to_thread

This commit is contained in:
Isaac Connor 2018-01-01 16:38:54 -05:00
commit 834dfbfe0c
23 changed files with 447 additions and 288 deletions

3
.gitmodules vendored
View File

@ -2,3 +2,6 @@
path = web/api/app/Plugin/Crud path = web/api/app/Plugin/Crud
url = https://github.com/FriendsOfCake/crud.git url = https://github.com/FriendsOfCake/crud.git
branch = 3.0 branch = 3.0
[submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"]
path = web/api/app/Plugin/CakePHP-Enum-Behavior
url = https://github.com/asper/CakePHP-Enum-Behavior.git

View File

@ -213,7 +213,8 @@ CREATE TABLE `Events` (
KEY `MonitorId` (`MonitorId`), KEY `MonitorId` (`MonitorId`),
KEY `StartTime` (`StartTime`), KEY `StartTime` (`StartTime`),
KEY `Frames` (`Frames`), KEY `Frames` (`Frames`),
KEY `Archived` (`Archived`) KEY `Archived` (`Archived`),
KEY `EndTime_DiskSpace` (`EndTime`,`DiskSpace`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
-- --
@ -277,7 +278,7 @@ CREATE TABLE `Groups` (
--Table structure for table `Groups_Monitors` --Table structure for table `Groups_Monitors`
-- --
DROP TABLE IF EXISTS `Groups_Monitors` DROP TABLE IF EXISTS `Groups_Monitors`;
CREATE TABLE `Groups_Monitors` ( CREATE TABLE `Groups_Monitors` (
`Id` INT(10) unsigned NOT NULL auto_increment, `Id` INT(10) unsigned NOT NULL auto_increment,
`GroupId` int(10) unsigned NOT NULL, `GroupId` int(10) unsigned NOT NULL,
@ -640,6 +641,7 @@ CREATE TABLE `Storage` (
`Type` enum('local','s3fs') NOT NULL default 'local', `Type` enum('local','s3fs') NOT NULL default 'local',
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
`Scheme enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `Scheme enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
`ServerId` int(10) unsigned,
PRIMARY KEY (`Id`) PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
@ -668,7 +670,25 @@ insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edi
-- --
-- Add a sample filter to purge the oldest 100 events when the disk is 95% full -- Add a sample filter to purge the oldest 100 events when the disk is 95% full
-- --
insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0,0,0,0,0,0,'',1,0,1,0); `Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
`Query` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoVideo` tinyint(3) unsigned NOT NULL default '0',
`AutoUpload` tinyint(3) unsigned NOT NULL default '0',
`AutoEmail` tinyint(3) unsigned NOT NULL default '0',
`AutoMessage` tinyint(3) unsigned NOT NULL default '0',
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
`AutoExecuteCmd` tinytext,
`AutoDelete` tinyint(3) unsigned NOT NULL default '0',
`AutoMove` tinyint(3) unsigned NOT NULL default '0',
`AutoMoveTo` smallint(5) unsigned NOT NULL default 0,
`UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0',
`Background` tinyint(1) unsigned NOT NULL default '0',
`Concurrent` tinyint(1) unsigned NOT NULL default '0',
insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0/*AutoArchive*/,0/*AutoVideo*/,0/*AutoUpload*/,0/*AutoEmail*/,0/*AutoMessage*/,0/*AutoExecute*/,'',1/*AutoDelete*/,0/*AutoMove*/,0/*MoveTo*/,0/*UpdateDiskSpace*/,1/*Background*/,0/*Concurrent*/);
insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}',0,0,0,0,0,0,'',0,0,0,1,1,0);
-- --
-- Add in some sample control protocol definitions -- Add in some sample control protocol definitions

23
db/zm_update-1.31.18.sql Normal file
View File

@ -0,0 +1,23 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Storage'
AND column_name = 'ServerId'
) > 0,
"SELECT 'Column ServerId already exists in Storage'",
"ALTER TABLE Storage ADD `ServerId` int(10) unsigned AFTER `Scheme`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM Filters WHERE Name = 'Update DiskSpace'
AND Query = '{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}'
) > 0,
"SELECT 'Update Disk Space Filter already exists.'",
"INSERT INTO Filters (Name,Query,UpdateDiskSpace,Background) values ('Update DiskSpace','{\"terms\":[{\"attr\":\"DiskSpace\",\"op\":\"IS\",\"val\":\"NULL\"}]}',1,1)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -154,11 +154,21 @@ sub Path {
if ( ! $$event{Path} ) { if ( ! $$event{Path} ) {
my $Storage = $event->Storage(); my $Storage = $event->Storage();
$$event{Path} = join('/', $Storage->Path(), $event->RelativePath() );
}
return $$event{Path};
}
sub RelativePath {
my $event = shift;
if ( @_ ) {
$$event{RelativePath} = $_[0];
}
if ( ! $$event{RelativePath} ) {
if ( $$event{Scheme} eq 'Deep' ) { if ( $$event{Scheme} eq 'Deep' ) {
if ( $event->Time() ) { if ( $event->Time() ) {
$$event{Path} = join('/', $$event{RelativePath} = join('/',
$Storage->Path(),
$event->{MonitorId}, $event->{MonitorId},
strftime( '%y/%m/%d/%H/%M/%S', strftime( '%y/%m/%d/%H/%M/%S',
localtime($event->Time()) localtime($event->Time())
@ -166,30 +176,28 @@ sub Path {
); );
} else { } else {
Error("Event $$event{Id} has no value for Time(), unable to determine path"); Error("Event $$event{Id} has no value for Time(), unable to determine path");
$$event{Path} = ''; $$event{RelativePath} = '';
} }
} elsif ( $$event{Scheme} eq 'Medium' ) { } elsif ( $$event{Scheme} eq 'Medium' ) {
if ( $event->Time() ) { if ( $event->Time() ) {
$$event{Path} = join('/', $$event{RelativePath} = join('/',
$Storage->Path(),
$event->{MonitorId}, $event->{MonitorId},
strftime( '%Y-%m-%d', localtime($event->Time())), strftime( '%Y-%m-%d', localtime($event->Time())),
$event->{Id}, $event->{Id},
); );
} else { } else {
Error("Event $$event{Id} has no value for Time(), unable to determine path"); Error("Event $$event{Id} has no value for Time(), unable to determine path");
$$event{Path} = ''; $$event{RelativePath} = '';
} }
} else { # Shallow } else { # Shallow
$$event{Path} = join('/', $$event{RelativePath} = join('/',
$Storage->Path(),
$event->{MonitorId}, $event->{MonitorId},
$event->{Id}, $event->{Id},
); );
} # end if Scheme } # end if Scheme
} # end if ! Path } # end if ! Path
return $$event{Path}; return $$event{RelativePath};
} }
sub GenerateVideo { sub GenerateVideo {
@ -337,7 +345,7 @@ sub delete_files {
return; return;
} }
Debug("Deleting files for Event $$event{Id} from $storage_path."); Debug("Deleting files for Event $$event{Id} from $storage_path.");
my $link_path = $$event{MonitorId}."/*/*/*/.".$$event{Id}; my $link_path = $$event{MonitorId}.'/*/*/*/.'.$$event{Id};
#Debug( "LP1:$link_path" ); #Debug( "LP1:$link_path" );
my @links = glob($link_path); my @links = glob($link_path);
#Debug( "L:".$links[0].": $!" ); #Debug( "L:".$links[0].": $!" );
@ -371,7 +379,7 @@ sub delete_files {
} }
} # end if links } # end if links
} else { } else {
my $command = "/bin/rm -rf ". $event->Path(); my $command = '/bin/rm -rf '. $storage_path . '/'. $event->RelativePath();
ZoneMinder::General::executeShellCommand( $command ); ZoneMinder::General::executeShellCommand( $command );
} }
} # end sub delete_files } # end sub delete_files

View File

@ -238,8 +238,13 @@ MAIN: while( $loop ) {
} elsif ( $$Storage{Scheme} eq 'Medium' ) { } elsif ( $$Storage{Scheme} eq 'Medium' ) {
foreach my $event_dir ( glob("$monitor_dir/*/*") ) { foreach my $event_dir ( glob("$monitor_dir/*/*") ) {
next if ! -d $event_dir; next if ! -d $event_dir;
my $Event = $fs_events->{$event} = new ZoneMinder::Event(); my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d\+)$/;
$$Event{Id} = $event; if ( ! $event_id ) {
Debug("Unable to parse date/event_id from $event_dir");
next;
}
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
$$Event{Id} = $event_id;
$$Event{Path} = $event_dir; $$Event{Path} = $event_dir;
$Event->MonitorId( $monitor_dir ); $Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() ); $Event->StorageId( $Storage->Id() );

View File

@ -1 +1 @@
1.31.17 1.31.18

View File

@ -70,6 +70,7 @@ Cache::config('default', array('engine' => 'Apc'));
* *
*/ */
CakePlugin::load('Crud'); CakePlugin::load('Crud');
CakePlugin::load('CakePHP-Enum-Behavior');
/** /**
* You can attach event listeners to the request lifecycle as Dispatcher Filter. By default CakePHP bundles two filters: * You can attach event listeners to the request lifecycle as Dispatcher Filter. By default CakePHP bundles two filters:
@ -142,6 +143,27 @@ foreach( $configvals as $key => $value) {
Configure::write( $key, $value ); Configure::write( $key, $value );
} }
// For Human-readability, use ZM_SERVER_HOST or ZM_SERVER_NAME in zm.conf, and convert it here to a ZM_SERVER_ID
if ( ! defined('ZM_SERVER_ID') ) {
App::uses('ClassRegistry', 'Utility');
$ServerModel = ClassRegistry::init('Server');
if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) {
$Server = $ServerModel->find( 'first', array( 'conditions'=>array('Name'=>ZM_SERVER_NAME) ) );
if ( ! $Server ) {
Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.');
} else {
define( 'ZM_SERVER_ID', $Server['Server']['Id'] );
}
} else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) {
$Server = $ServerModel->find( 'first', array( 'conditions'=>array('Name'=>ZM_SERVER_HOST) ) );
if ( ! $Server ) {
Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.');
} else {
define( 'ZM_SERVER_ID', $Server['Server']['Id'] );
}
}
}
function process_configfile($configFile) { function process_configfile($configFile) {
if ( is_readable( $configFile ) ) { if ( is_readable( $configFile ) ) {
$configvals = array(); $configvals = array();

View File

@ -13,12 +13,15 @@ class MonitorsController extends AppController {
* *
* @var array * @var array
*/ */
public $components = array('Paginator', 'RequestHandler'); public $components = array('Paginator', 'RequestHandler');
public function beforeRender() {
$this->set($this->Monitor->enumValues());
}
public function beforeFilter() { public function beforeFilter() {
parent::beforeFilter(); parent::beforeFilter();
$canView = $this->Session->Read('monitorPermission'); $canView = $this->Session->Read('monitorPermission');
if ($canView =='None') { if ($canView == 'None') {
throw new UnauthorizedException(__('Insufficient Privileges')); throw new UnauthorizedException(__('Insufficient Privileges'));
return; return;
} }
@ -29,21 +32,21 @@ class MonitorsController extends AppController {
* *
* @return void * @return void
*/ */
public function index() { public function index() {
$this->Monitor->recursive = 0; $this->Monitor->recursive = 0;
if ($this->request->params['named']) { if ($this->request->params['named']) {
$this->FilterComponent = $this->Components->load('Filter'); $this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']); //$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
$conditions = $this->request->params['named']; $conditions = $this->request->params['named'];
} else { } else {
$conditions = array(); $conditions = array();
} }
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) { if (!empty($allowedMonitors)) {
$conditions['Monitor.Id' ] = $allowedMonitors; $conditions['Monitor.Id' ] = $allowedMonitors;
} }
$find_array = array('conditions'=>$conditions,'contain'=>array('Group')); $find_array = array('conditions'=>$conditions,'contain'=>array('Group'));
if ( isset( $conditions['GroupId'] ) ) { if ( isset( $conditions['GroupId'] ) ) {
@ -70,7 +73,7 @@ class MonitorsController extends AppController {
'monitors' => $monitors, 'monitors' => $monitors,
'_serialize' => array('monitors') '_serialize' => array('monitors')
)); ));
} }
/** /**
* view method * view method
@ -79,51 +82,57 @@ class MonitorsController extends AppController {
* @param string $id * @param string $id
* @return void * @return void
*/ */
public function view($id = null) { public function view($id = null) {
$this->Monitor->recursive = 0; $this->Monitor->recursive = 0;
if (!$this->Monitor->exists($id)) { if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor')); throw new NotFoundException(__('Invalid monitor'));
} }
$allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY);
if (!empty($allowedMonitors)) { if (!empty($allowedMonitors)) {
$restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors); $restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors);
} else { } else {
$restricted = ''; $restricted = '';
} }
$options = array('conditions' => array( $options = array('conditions' => array(
array('Monitor.' . $this->Monitor->primaryKey => $id), array('Monitor.' . $this->Monitor->primaryKey => $id),
$restricted $restricted
) )
); );
$monitor = $this->Monitor->find('first', $options); $monitor = $this->Monitor->find('first', $options);
$this->set(array( $this->set(array(
'monitor' => $monitor, 'monitor' => $monitor,
'_serialize' => array('monitor') '_serialize' => array('monitor')
)); ));
} }
/** /**
* add method * add method
* *
* @return void * @return void
*/ */
public function add() { public function add() {
if ($this->request->is('post')) { if ( $this->request->is('post') ) {
if ($this->Session->Read('systemPermission') != 'Edit') if ( $this->Session->Read('systemPermission') != 'Edit' ) {
{ throw new UnauthorizedException(__('Insufficient privileges'));
throw new UnauthorizedException(__('Insufficient privileges')); return;
return; }
}
$this->Monitor->create(); $this->Monitor->create();
if ($this->Monitor->save($this->request->data)) { if ($this->Monitor->save($this->request->data)) {
$this->daemonControl($this->Monitor->id, 'start'); $this->daemonControl($this->Monitor->id, 'start');
return $this->flash(__('The monitor has been saved.'), array('action' => 'index')); //return $this->flash(__('The monitor has been saved.'), array('action' => 'index'));
} $message = 'Saved';
} } else {
} $message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
}
/** /**
* edit method * edit method
@ -132,40 +141,38 @@ class MonitorsController extends AppController {
* @param string $id * @param string $id
* @return void * @return void
*/ */
public function edit($id = null) { public function edit($id = null) {
$this->Monitor->id = $id; $this->Monitor->id = $id;
if (!$this->Monitor->exists($id)) { if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor')); throw new NotFoundException(__('Invalid monitor'));
} }
if ($this->Session->Read('monitorPermission') != 'Edit') if ($this->Session->Read('monitorPermission') != 'Edit') {
{ throw new UnauthorizedException(__('Insufficient privileges'));
throw new UnauthorizedException(__('Insufficient privileges')); return;
return; }
} if ($this->Monitor->save($this->request->data)) {
if ($this->Monitor->save($this->request->data)) { $message = 'Saved';
$message = 'Saved'; } else {
} else { $message = 'Error';
$message = 'Error'; }
}
$this->set(array( $this->set(array(
'message' => $message, 'message' => $message,
'_serialize' => array('message') '_serialize' => array('message')
)); ));
// - restart or stop this monitor after change // - restart or stop this monitor after change
$func = $this->Monitor->find('first', array( $func = $this->Monitor->find('first', array(
'fields' => array('Function'), 'fields' => array('Function'),
'conditions' => array('Id' => $id) 'conditions' => array('Id' => $id)
))['Monitor']['Function']; ))['Monitor']['Function'];
// We don't pass the request data as the monitor object because it may be a subset of the full monitor array // We don't pass the request data as the monitor object because it may be a subset of the full monitor array
if ( $func == 'None' ) { $this->daemonControl( $this->Monitor->id, 'stop' );
$this->daemonControl( $this->Monitor->id, 'stop' ); if ( ( $func != 'None' ) and ( $this->Monitor->ServerId == ZM_SERVER_ID ) ) {
} else { $this->daemonControl( $this->Monitor->id, 'start' );
$this->daemonControl( $this->Monitor->id, 'restart' );
} }
} } // end function edit
/** /**
* delete method * delete method
@ -174,196 +181,183 @@ class MonitorsController extends AppController {
* @param string $id * @param string $id
* @return void * @return void
*/ */
public function delete($id = null) { public function delete($id = null) {
$this->Monitor->id = $id; $this->Monitor->id = $id;
if (!$this->Monitor->exists()) { if (!$this->Monitor->exists()) {
throw new NotFoundException(__('Invalid monitor')); throw new NotFoundException(__('Invalid monitor'));
} }
if ($this->Session->Read('systemPermission') != 'Edit') if ($this->Session->Read('systemPermission') != 'Edit') {
{ throw new UnauthorizedException(__('Insufficient privileges'));
throw new UnauthorizedException(__('Insufficient privileges')); return;
return; }
} $this->request->allowMethod('post', 'delete');
$this->request->allowMethod('post', 'delete');
$this->daemonControl($this->Monitor->id, 'stop'); $this->daemonControl($this->Monitor->id, 'stop');
if ($this->Monitor->delete()) { if ($this->Monitor->delete()) {
return $this->flash(__('The monitor has been deleted.'), array('action' => 'index')); return $this->flash(__('The monitor has been deleted.'), array('action' => 'index'));
} else { } else {
return $this->flash(__('The monitor could not be deleted. Please, try again.'), array('action' => 'index')); return $this->flash(__('The monitor could not be deleted. Please, try again.'), array('action' => 'index'));
} }
} }
public function sourceTypes() { public function sourceTypes() {
$sourceTypes = $this->Monitor->query("describe Monitors Type;"); $sourceTypes = $this->Monitor->query("describe Monitors Type;");
preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches); preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches);
foreach( explode(',', $matches[1]) as $value ) { foreach( explode(',', $matches[1]) as $value ) {
$enum[] = trim( $value, "'" ); $enum[] = trim( $value, "'" );
} }
$this->set(array( $this->set(array(
'sourceTypes' => $enum, 'sourceTypes' => $enum,
'_serialize' => array('sourceTypes') '_serialize' => array('sourceTypes')
)); ));
} }
// arm/disarm alarms // arm/disarm alarms
// expected format: http(s):/portal-api-url/monitors/alarm/id:M/command:C.json // expected format: http(s):/portal-api-url/monitors/alarm/id:M/command:C.json
// where M=monitorId // where M=monitorId
// where C=on|off|status // where C=on|off|status
public function alarm() public function alarm() {
{ $id = $this->request->params['named']['id'];
$id = $this->request->params['named']['id']; $cmd = strtolower($this->request->params['named']['command']);
$cmd = strtolower($this->request->params['named']['command']); if (!$this->Monitor->exists($id)) {
if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor'));
throw new NotFoundException(__('Invalid monitor')); }
} if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status' ) {
if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status') throw new BadRequestException(__('Invalid command'));
{ }
throw new BadRequestException(__('Invalid command')); $zm_path_bin = Configure::read('ZM_PATH_BIN');
}
$zm_path_bin = Configure::read('ZM_PATH_BIN');
switch ($cmd) switch ($cmd) {
{ case 'on':
case "on": $q = '-a';
$q = '-a'; $verbose = '-v';
$verbose = "-v"; break;
break; case 'off':
case "off": $q = '-c';
$q = "-c"; $verbose = '-v';
$verbose = "-v"; break;
break; case 'status':
case "status": $verbose = ''; // zmu has a bug - gives incorrect verbose output in this case
$verbose = ""; // zmu has a bug - gives incorrect verbose output in this case $q = '-s';
$q = "-s"; break;
break; }
}
// form auth key based on auth credentials // form auth key based on auth credentials
$this->loadModel('Config'); $this->loadModel('Config');
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')); $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
$config = $this->Config->find('first', $options); $config = $this->Config->find('first', $options);
$zmOptAuth = $config['Config']['Value']; $zmOptAuth = $config['Config']['Value'];
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')); $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY'));
$config = $this->Config->find('first', $options); $config = $this->Config->find('first', $options);
$zmAuthRelay = $config['Config']['Value']; $zmAuthRelay = $config['Config']['Value'];
$auth=""; $auth='';
if ($zmOptAuth) if ( $zmOptAuth ) {
{ if ( $zmAuthRelay == 'hashed' ) {
if ($zmAuthRelay == 'hashed') $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET'));
{ $config = $this->Config->find('first', $options);
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET')); $zmAuthHashSecret = $config['Config']['Value'];
$config = $this->Config->find('first', $options);
$zmAuthHashSecret = $config['Config']['Value'];
$time = localtime(); $time = localtime();
$ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5]; $ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5];
$ak = md5($ak); $ak = md5($ak);
$auth = " -A ".$ak; $auth = ' -A '.$ak;
} } else if ( $zmAuthRelay == 'plain' ) {
elseif ($zmAuthRelay == 'plain') $auth = ' -U ' .$this->Session->Read('username').' -P '.$this->Session->Read('password');
{
$auth = " -U " .$this->Session->Read('username')." -P ".$this->Session->Read('password'); } else if ( $zmAuthRelay == 'none' ) {
$auth = ' -U ' .$this->Session->Read('username');
} }
elseif ($zmAuthRelay == 'none') }
{
$auth = " -U " .$this->Session->Read('username'); $shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth");
} $status = exec ($shellcmd);
}
$shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth");
$status = exec ($shellcmd);
$this->set(array( $this->set(array(
'status' => $status, 'status' => $status,
'_serialize' => array('status'), '_serialize' => array('status'),
)); ));
}
// Check if a daemon is running for the monitor id
} public function daemonStatus() {
$id = $this->request->params['named']['id'];
$daemon = $this->request->params['named']['daemon'];
// Check if a daemon is running for the monitor id if (!$this->Monitor->exists($id)) {
public function daemonStatus() { throw new NotFoundException(__('Invalid monitor'));
$id = $this->request->params['named']['id']; }
$daemon = $this->request->params['named']['daemon'];
if (!$this->Monitor->exists($id)) { $monitor = $this->Monitor->find('first', array(
throw new NotFoundException(__('Invalid monitor')); 'fields' => array('Id', 'Type', 'Device'),
} 'conditions' => array('Id' => $id)
));
$monitor = $this->Monitor->find('first', array( // Clean up the returned array
'fields' => array('Id', 'Type', 'Device'), $monitor = Set::extract('/Monitor/.', $monitor);
'conditions' => array('Id' => $id)
));
// Clean up the returned array // Pass -d for local, otherwise -m
$monitor = Set::extract('/Monitor/.', $monitor); if ($monitor[0]['Type'] == 'Local') {
$args = '-d '. $monitor[0]['Device'];
} else {
$args = '-m '. $monitor[0]['Id'];
}
// Pass -d for local, otherwise -m // Build the command, and execute it
if ($monitor[0]['Type'] == 'Local') { $zm_path_bin = Configure::read('ZM_PATH_BIN');
$args = "-d ". $monitor[0]['Device']; $command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args");
} else { $status = exec( $command );
$args = "-m ". $monitor[0]['Id'];
}
// Build the command, and execute it // If 'not' is present, the daemon is not running, so return false
$zm_path_bin = Configure::read('ZM_PATH_BIN'); // https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075
$command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args"); // Also sending back the status text so we can check if the monitor is in pending
$status = exec( $command ); // state which means there may be an error
$statustext = $status;
$status = (strpos($status, 'not')) ? false : true;
// If 'not' is present, the daemon is not running, so return false $this->set(array(
// https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075 'status' => $status,
// Also sending back the status text so we can check if the monitor is in pending 'statustext' => $statustext,
// state which means there may be an error '_serialize' => array('status','statustext'),
$statustext = $status; ));
$status = (strpos($status, 'not')) ? false : true; }
$this->set(array( public function daemonControl($id, $command, $monitor=null, $daemon=null) {
'status' => $status, $args = '';
'statustext' => $statustext, $daemons = array();
'_serialize' => array('status','statustext'),
));
}
public function daemonControl($id, $command, $monitor=null, $daemon=null) { if (!$monitor) {
$args = ''; // Need to see if it is local or remote
$daemons = array(); $monitor = $this->Monitor->find('first', array(
'fields' => array('Type', 'Function'),
'conditions' => array('Id' => $id)
));
$monitor = $monitor['Monitor'];
}
if (!$monitor) { if ($monitor['Type'] == 'Local') {
// Need to see if it is local or remote $args = '-d ' . $monitor['Device'];
$monitor = $this->Monitor->find('first', array( } else {
'fields' => array('Type', 'Function'), $args = '-m ' . $id;
'conditions' => array('Id' => $id) }
));
$monitor = $monitor['Monitor'];
}
if ($monitor['Type'] == 'Local') { if ($monitor['Function'] == 'Monitor') {
$args = "-d " . $monitor['Device']; array_push($daemons, 'zmc');
} else { } else {
$args = "-m " . $id; array_push($daemons, 'zmc', 'zma');
} }
$zm_path_bin = Configure::read('ZM_PATH_BIN');
if ($monitor['Function'] == 'Monitor') { foreach ($daemons as $daemon) {
array_push($daemons, 'zmc'); $shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
} else { $status = exec( $shellcmd );
array_push($daemons, 'zmc', 'zma'); }
} }
$zm_path_bin = Configure::read('ZM_PATH_BIN');
foreach ($daemons as $daemon) {
$shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
$status = exec( $shellcmd );
}
}
}
} // end class MonitorsController

View File

@ -86,10 +86,10 @@ class Monitor extends AppModel {
); );
/** /**
* * hasMany associations * hasMany associations
* * *
* * @var array * @var array
* */ */
public $hasAndBelongsToMany = array( public $hasAndBelongsToMany = array(
'Group' => array( 'Group' => array(
'className' => 'Group', 'className' => 'Group',
@ -108,5 +108,16 @@ class Monitor extends AppModel {
'counterQuery' => '' 'counterQuery' => ''
), ),
); );
public $actsAs = array(
'CakePHP-Enum-Behavior.Enum' => array(
'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL'),
'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'),
'Orientation' => array('0','90','180','270','hori','vert'),
'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'),
'OutputContainer' => array('auto','mp4','mkv'),
'DefaultView' => array('Events','Control'),
'Status' => array('Unknown','NotRunning','Running','NoSignal','Signal'),
)
);
} }

@ -0,0 +1 @@
Subproject commit 7108489f218c54d36d235d3af91d6da2f8311237

View File

@ -0,0 +1,2 @@
echo json_encode($message);
echo json_encode($monitor);

View File

@ -0,0 +1,2 @@
$xml = Xml::fromArray(array('response' => $message));
echo $xml->asXML();

View File

@ -160,7 +160,16 @@ class Event {
public function getStreamSrc( $args=array(), $querySep='&' ) { public function getStreamSrc( $args=array(), $querySep='&' ) {
if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) { if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) {
return ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'}; $streamSrc = ZM_BASE_PROTOCOL.'://';
$Monitor = $this->Monitor();
if ( $Monitor->ServerId() ) {
$Server = $Monitor->Server();
$streamSrc .= $Server->Hostname();
} else {
$streamSrc .= $_SERVER['HTTP_HOST'];
}
$streamSrc .= ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'};
return $streamSrc;
} }
$streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS;

View File

@ -295,8 +295,8 @@ private $control_fields = array(
} }
return $filters; return $filters;
} }
public function save( $new_values = null ) {
public function save( $new_values = null ) {
if ( $new_values ) { if ( $new_values ) {
foreach ( $new_values as $k=>$v ) { foreach ( $new_values as $k=>$v ) {
@ -304,8 +304,10 @@ private $control_fields = array(
} }
} }
$sql = 'UPDATE Monitors SET '.implode(', ', array_map( function($field) {return $field.'=?';}, array_keys( $this->defaults ) ) ) . ' WHERE Id=?'; $fields = array_keys( $this->defaults );
$values = array_map( function($field){return $this->{$field};}, $this->fields );
$sql = 'UPDATE Monitors SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?';
$values = array_map( function($field){return $this->{$field};}, $fields );
$values[] = $this->{'Id'}; $values[] = $this->{'Id'};
dbQuery( $sql, $values ); dbQuery( $sql, $values );
} // end function save } // end function save

View File

@ -70,15 +70,38 @@ class Storage {
Warning( "Unknown function call Storage->$fn from $file:$line" ); Warning( "Unknown function call Storage->$fn from $file:$line" );
} }
} }
public static function find_all() { public static function find_all( $parameters = null, $options = null ) {
$storage_areas = array(); $filters = array();
$result = dbQuery( 'SELECT * FROM Storage ORDER BY Name'); $sql = 'SELECT * FROM Storage ';
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage' ); $values = array();
foreach ( $results as $row => $obj ) {
$storage_areas[] = $obj; if ( $parameters ) {
$storage_cache[$obj->Id()] = $obj; $fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
} }
return $storage_areas; if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
} }
public function disk_usage_percent() { public function disk_usage_percent() {
$path = $this->Path(); $path = $this->Path();
@ -95,11 +118,8 @@ class Storage {
Error("disk_total_space returned false for " . $path ); Error("disk_total_space returned false for " . $path );
return 0; return 0;
} }
$free = disk_free_space( $path ); $used = $this->disk_used_space();
if ( ! $free ) { $usage = round( ($used / $total) * 100);
Error("disk_free_space returned false for " . $path );
}
$usage = round(($total - $free) / $total * 100);
return $usage; return $usage;
} }
public function disk_total_space() { public function disk_total_space() {

View File

@ -1280,6 +1280,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) {
$filter['sql'] .= ' not regexp '.$value; $filter['sql'] .= ' not regexp '.$value;
break; break;
case '=[]' : case '=[]' :
case 'IN' :
$filter['sql'] .= ' in ('.join( ',', $valueList ).')'; $filter['sql'] .= ' in ('.join( ',', $valueList ).')';
break; break;
case '![]' : case '![]' :

View File

@ -320,7 +320,10 @@ if ($reload == 'reload') ob_start();
} }
$func = function($S){ return '<span title="'.human_filesize($S->disk_used_space()) . ' of ' . human_filesize($S->disk_total_space()).'">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; }; $func = function($S){ return '<span title="'.human_filesize($S->disk_used_space()) . ' of ' . human_filesize($S->disk_total_space()).'">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; };
#$func = function($S){ return '<span title="">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; }; #$func = function($S){ return '<span title="">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; };
echo implode( ', ', array_map ( $func, $storage_areas ) ); if ( count($storage_areas) >= 4 )
$storage_areas = Storage::find_all( array('ServerId'=>null) );
if ( count($storage_areas) < 4 )
echo implode( ', ', array_map ( $func, $storage_areas ) );
echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%'; echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%';
?></li> ?></li>
</ul> </ul>

View File

@ -37,7 +37,7 @@ $eventCounts = array(
'filter' => array( 'filter' => array(
'Query' => array( 'Query' => array(
'terms' => array( 'terms' => array(
array( 'attr' => 'DateTime', 'op' => '>=', 'val' => '-1 hour' ), array( 'attr' => 'StartDateTime', 'op' => '>=', 'val' => '-1 hour' ),
) )
) )
), ),

View File

@ -55,7 +55,17 @@ function SetImageSource( monId, time ) {
if ( eMonId[i] == monId && time >= eStartSecs[i] && time <= eEndSecs[i] ) { if ( eMonId[i] == monId && time >= eStartSecs[i] && time <= eEndSecs[i] ) {
var duration = eEndSecs[i]-eStartSecs[i]; var duration = eEndSecs[i]-eStartSecs[i];
var frame = parseInt((time - eStartSecs[i])/(duration)*eventFrames[i])+1; var frame = parseInt((time - eStartSecs[i])/(duration)*eventFrames[i])+1;
console.log("SetImageSource: " + time + " duration: " + duration + " frame: " + frame ); var storage = Storage[eStorageId[i]];
if ( storage.ServerId ) {
var server = Servers[storage.ServerId];
if ( server ) {
console.log( server.Hostname + " for event " + eId[i] );
return location.protocol + '//' + server.Hostname + '/index.php?view=image&eid=' + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
} else {
console.log("No server found for " + storage.ServerId );
}
}
console.log("No storage found for " + eStorageId[i] );
return "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height; return "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
} }
} // end for } // end for
@ -465,7 +475,6 @@ function setSpeed( speed_index ) {
currentSpeed = parseFloat(speeds[speed_index]); currentSpeed = parseFloat(speeds[speed_index]);
speedIndex = speed_index; speedIndex = speed_index;
playSecsperInterval = Math.floor( 1000 * currentSpeed * currentDisplayInterval ) / 1000000; playSecsperInterval = Math.floor( 1000 * currentSpeed * currentDisplayInterval ) / 1000000;
console.log("playSecsPerInterval: " + playSecsperInterval + " = currentspeed:" + currentSpeed + " * " + currentDisplayInterval + " /1000");
showSpeed(speed_index); showSpeed(speed_index);
if ( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update if ( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update
} }

View File

@ -23,6 +23,7 @@ var imageLoadTimesNeeded=15; // and how many we need
var timeLabelsFractOfRow = 0.9; var timeLabelsFractOfRow = 0.9;
var eMonId = []; var eMonId = [];
var eId = []; var eId = [];
var eStorageId = [];
var eStartSecs = []; var eStartSecs = [];
var eEndSecs = []; var eEndSecs = [];
var eventFrames = []; // this is going to presume all frames equal durationlength var eventFrames = []; // this is going to presume all frames equal durationlength
@ -43,7 +44,6 @@ $index = 0;
$anyAlarms = false; $anyAlarms = false;
if ( ! $initialModeIsLive ) { if ( ! $initialModeIsLive ) {
Warning($eventsSql);
$result = dbQuery( $eventsSql ); $result = dbQuery( $eventsSql );
if ( ! $result ) { if ( ! $result ) {
Fatal('SQL-ERR'); Fatal('SQL-ERR');
@ -56,6 +56,7 @@ Warning($eventsSql);
if ( $maxTimeSecs < $event['CalcEndTimeSecs'] ) $maxTimeSecs = $event['CalcEndTimeSecs']; if ( $maxTimeSecs < $event['CalcEndTimeSecs'] ) $maxTimeSecs = $event['CalcEndTimeSecs'];
echo " echo "
eMonId[$index]=" . $event['MonitorId'] . "; eMonId[$index]=" . $event['MonitorId'] . ";
eStorageId[$index]=".$event['StorageId'] . ";
eId[$index]=" . $event['Id'] . "; eId[$index]=" . $event['Id'] . ";
eStartSecs[$index]=" . $event['StartTimeSecs'] . "; eStartSecs[$index]=" . $event['StartTimeSecs'] . ";
eEndSecs[$index]=" . $event['CalcEndTimeSecs'] . "; eEndSecs[$index]=" . $event['CalcEndTimeSecs'] . ";
@ -146,7 +147,17 @@ if ( $mId > 0 ) {
echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms. echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms.
} // end if initialmodeislive } // end if initialmodeislive
echo "var monitorName = [];\n";
echo "var Storage = []\n";
foreach ( Storage::find_all() as $Storage ) {
echo 'Storage[' . $Storage->Id() . '] = ' . json_encode($Storage). ";\n";
}
echo "var Servers = []\n";
foreach ( Server::find_all() as $Server ) {
echo 'Servers[' . $Server->Id() . '] = ' . json_encode($Server). ";\n";
}
echo "
var monitorName = [];\n";
echo "var monitorLoading = [];\n"; echo "var monitorLoading = [];\n";
echo "var monitorImageObject = [];\n"; echo "var monitorImageObject = [];\n";
echo "var monitorImageURL = [];\n"; echo "var monitorImageURL = [];\n";

View File

@ -94,7 +94,7 @@ if (isset($_REQUEST['minTime']) && isset($_REQUEST['maxTime']) && count($display
// Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly. // Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly.
$eventsSql = ' $eventsSql = '
SELECT E.Id,E.Name,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs, SELECT E.Id,E.Name,E.StorageId,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
CASE WHEN E.EndTime IS NULL THEN (SELECT UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval max(Delta)+0.5 Second)) FROM Frames F WHERE F.EventId=E.Id) CASE WHEN E.EndTime IS NULL THEN (SELECT UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval max(Delta)+0.5 Second)) FROM Frames F WHERE F.EventId=E.Id)
ELSE UNIX_TIMESTAMP(E.EndTime) ELSE UNIX_TIMESTAMP(E.EndTime)
END AS CalcEndTimeSecs, E.Length, END AS CalcEndTimeSecs, E.Length,

View File

@ -194,7 +194,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
</div> </div>
</form> </form>
<?php <?php
} else if ( $tab == "servers" ) { ?> } else if ( $tab == 'servers' ) { ?>
<form name="serversForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>"> <form name="serversForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/> <input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/> <input type="hidden" name="tab" value="<?php echo $tab ?>"/>
@ -249,6 +249,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<th class="colPath"><?php echo translate('path') ?></th> <th class="colPath"><?php echo translate('path') ?></th>
<th class="colType"><?php echo translate('Type') ?></th> <th class="colType"><?php echo translate('Type') ?></th>
<th class="colScheme"><?php echo translate('StorageScheme') ?></th> <th class="colScheme"><?php echo translate('StorageScheme') ?></th>
<th class="colServer"><?php echo translate('Server') ?></th>
<th class="colMark"><?php echo translate('Mark') ?></th> <th class="colMark"><?php echo translate('Mark') ?></th>
</tr> </tr>
</thead> </thead>
@ -260,6 +261,9 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<td class="colPath"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Path']), $canEdit ) ?></td> <td class="colPath"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Path']), $canEdit ) ?></td>
<td class="colType"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Type']), $canEdit ) ?></td> <td class="colType"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Type']), $canEdit ) ?></td>
<td class="colScheme"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Scheme']), $canEdit ) ?></td> <td class="colScheme"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Scheme']), $canEdit ) ?></td>
<td class="colServer"><?php
$Server = new Server($row['ServerId']);
echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($Server->Name()), $canEdit ) ?></td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton(this);"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td> <td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton(this);"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
</tr> </tr>
<?php } #end foreach Server ?> <?php } #end foreach Server ?>

View File

@ -43,6 +43,11 @@ $scheme_options = array(
'Shallow' => translate('Shallow'), 'Shallow' => translate('Shallow'),
); );
$servers = Server::find_all();
$ServersById = array();
foreach ( $servers as $S ) {
$ServersById[$S->Id()] = $S;
}
$focusWindow = true; $focusWindow = true;
xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] ); xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
@ -67,6 +72,10 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
<th scope="row"><?php echo translate('Path') ?></th> <th scope="row"><?php echo translate('Path') ?></th>
<td><input type="text" name="newStorage[Path]" value="<?php echo $newStorage['Path'] ?>"/></td> <td><input type="text" name="newStorage[Path]" value="<?php echo $newStorage['Path'] ?>"/></td>
</tr> </tr>
<tr>
<th scope="row"><?php echo translate('Server') ?></th>
<td><?php echo htmlSelect( 'newStorage[ServerId]', array('','Remote') + $ServersById, $newStorage['ServerId'] ); ?></td>
</tr>
<tr> <tr>
<th scope="row"><?php echo translate('Type') ?></th> <th scope="row"><?php echo translate('Type') ?></th>
<td><?php echo htmlSelect( 'newStorage[Type]', $type_options, $newStorage['Type'] ); ?></td> <td><?php echo htmlSelect( 'newStorage[Type]', $type_options, $newStorage['Type'] ); ?></td>