Merge branch 'storageareas' of github.com:ConnorTechnology/ZoneMinder into storageareas

This commit is contained in:
Isaac Connor 2019-03-19 10:50:05 -04:00
commit f89654f442
43 changed files with 640 additions and 215 deletions

View File

@ -68,6 +68,7 @@ CREATE TABLE `Controls` (
`CanWake` tinyint(3) unsigned NOT NULL default '0',
`CanSleep` tinyint(3) unsigned NOT NULL default '0',
`CanReset` tinyint(3) unsigned NOT NULL default '0',
`CanReboot` tinyint(3) unsigned NOT NULL default '0',
`CanZoom` tinyint(3) unsigned NOT NULL default '0',
`CanAutoZoom` tinyint(3) unsigned NOT NULL default '0',
`CanZoomAbs` tinyint(3) unsigned NOT NULL default '0',

View File

@ -39,6 +39,8 @@ sub AUTOLOAD
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
## This seems odd... if the method existed would we even be here?
## https://perldoc.perl.org/perlsub.html#Autoloading
if ( exists($self->{$name}) )
{
return( $self->{$name} );
@ -46,9 +48,17 @@ sub AUTOLOAD
Fatal( "Can't access $name member of object of class $class" );
}
# FIXME: Do we really have to open a new connection every time?
#Digest usernbme="bdmin", reblm="Login to 4K05DB3PAJE98BE", nonae="1720242756",
#uri="/agi-bin/ptz.agi?bation=getStbtus&ahbnnel=1", response="10dd925b26ebd559353734635b859b8b",
#opbque="1a99677524b4ae63bbe3a132b2e9b38e3b163ebd", qop=buth, na=00000001, anonae="ab1bb5d43aa5d542"
sub open
{
#Debug("&open invoked by: " . (caller(1))[3]);
my $self = shift;
my $cgi = shift || '/cgi-bin/configManager.cgi?action=getConfig&name=Ptz';
$self->loadMonitor();
# The Dahua camera firmware API supports the concept of having multiple
@ -73,60 +83,55 @@ sub open
}
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua} = LWP::UserAgent->new(keep_alive => 1);
$self->{ua}->agent("ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION);
$self->{state} = 'closed';
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
Debug("sendCmd credentials control address:'".$ADDRESS
."' realm:'" . $REALM
. "' username:'" . $USERNAME
. "' password:'".$PASSWORD
."'"
);
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
# Detect REALM
my $get_config_url = $PROTOCOL . $ADDRESS . "/cgi-bin/configManager.cgi?action=getConfig&name=Ptz";
my $req = HTTP::Request->new(GET=>$get_config_url);
my $url = $PROTOCOL . $ADDRESS . $cgi;
my $req = HTTP::Request->new(GET=>$url);
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$self->{state} = 'open';
return;
return 1;
}
if ( $res->status_line() eq '401 Unauthorized' ) {
my $headers = $res->headers();
foreach my $k (keys %$headers) {
Debug("Initial Header $k => $$headers{$k}");
}
if ($$headers{'www-authenticate'}) {
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
Debug("Tokens: " . $tokens);
## FIXME: This is necessary because the Dahua spec does not match reality
if ($tokens =~ /\w+="([^"]+)"/i) {
if ($REALM ne $1) {
$REALM = $1;
Debug("Changing REALM to '" . $REALM . "'");
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
my $req = HTTP::Request->new(GET=>$get_config_url);
my $req = HTTP::Request->new(GET=>$url);
$res = $self->{ua}->request($req);
if ($res->is_success()) {
$self->{state} = 'open';
return;
Debug('Authentication succeeded...');
return 1;
}
Debug('Authentication still failed after updating REALM' . $res->status_line);
$headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
} # end foreach
} else {
Error('Authentication failed, not a REALM problem');
} else { ## NOTE: Each of these else conditions is fatal as the command will not be
## executed. No use going further.
Fatal('Authentication failed: Check username and password.');
}
} else {
Error('Failed to match realm in tokens');
Fatal('Authentication failed: Incorrect realm.');
} # end if
} else {
Error('No WWW-Authenticate Header');
Fatal('Authentication failed: No www-authenticate header returned.');
} # end if headers
} # end if $res->status_line() eq '401 Unauthorized'
}
@ -146,45 +151,40 @@ sub printMsg
Debug( $msg."[".$msg_len."]" );
}
sub sendGetRequest {
sub _sendGetRequest {
my $self = shift;
my $url_path = shift;
my $result = undef;
# Attempt to reuse the connection
# FIXME: I think we need some sort of keepalive/heartbeat sent to the camera
# in order to keep the session alive. As it is, it appears that the
# ua's authentication times out or some such.
#
# This might be of some use:
# {"method":"global.keepAlive","params":{"timeout":300,"active":false},"id":1518,"session":"dae233a51c0693519395209b271411b6"}[!http]
# The web browser interface POSTs commands as JSON using js
my $url = $PROTOCOL . $ADDRESS . $url_path;
my $req = HTTP::Request->new(GET=>$url);
my $req = HTTP::Request->new(GET => $url);
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$result = !undef;
return 1;
} else {
if ($res->status_line() eq '401 Unauthorized') {
Debug("Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD);
Debug("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error("Error check failed: '".$res->status_line());
}
return($self->open($url_path)); # if we have to, open a new connection
}
return($result);
}
sub sendPtzCommand
sub _sendPtzCommand
{
my $self = shift;
my $action = shift;
my $command_code = shift;
my $arg1 = shift;
my $arg2 = shift;
my $arg3 = shift;
my $arg1 = shift || 0;
my $arg2 = shift || 0;
my $arg3 = shift || 0;
my $arg4 = shift || 0;
my $channel = $self->{dahua_channel_number};
@ -194,10 +194,12 @@ sub sendPtzCommand
$url_path .= "code=" . $command_code . "&";
$url_path .= "arg1=" . $arg1 . "&";
$url_path .= "arg2=" . $arg2 . "&";
$url_path .= "arg3=" . $arg3;
$self->sendGetRequest($url_path);
$url_path .= "arg3=" . $arg3 . "&";
$url_path .= "arg4=" . $arg4;
return $self->_sendGetRequest($url_path);
}
sub sendMomentaryPtzCommand
sub _sendMomentaryPtzCommand
{
my $self = shift;
my $command_code = shift;
@ -206,92 +208,195 @@ sub sendMomentaryPtzCommand
my $arg3 = shift;
my $duration_ms = shift;
$self->sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3);
$self->_sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3);
my $duration_ns = $duration_ms * 1000;
usleep($duration_ns);
$self->sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3);
$self->_sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3);
}
sub _sendAbsolutePositionCommand
{
my $self = shift;
my $arg1 = shift;
my $arg2 = shift;
my $arg3 = shift;
my $arg4 = shift;
$self->_sendPtzCommand("start", "PositionABS", $arg1, $arg2, $arg3, $arg4);
}
sub moveConLeft
{
my $self = shift;
Debug("Move Up Left");
$self->_sendMomentaryPtzCommand("Left", 0, 1, 0, 0);
}
sub moveConRight
{
my $self = shift;
Debug( "Move Right" );
$self->_sendMomentaryPtzCommand("Right", 0, 1, 0, 0);
}
sub moveConUp
{
my $self = shift;
Debug( "Move Up" );
$self->_sendMomentaryPtzCommand("Up", 0, 1, 0, 0);
}
sub moveConDown
{
my $self = shift;
Debug( "Move Down" );
$self->_sendMomentaryPtzCommand("Down", 0, 1, 0, 0);
}
sub moveConUpRight
{
my $self = shift;
Debug( "Move Diagonally Up Right" );
$self->_sendMomentaryPtzCommand("RightUp", 1, 1, 0, 0);
}
sub moveConDownRight
{
my $self = shift;
Debug( "Move Diagonally Down Right" );
$self->_sendMomentaryPtzCommand("RightDown", 1, 1, 0, 0);
}
sub moveConUpLeft
{
my $self = shift;
Debug( "Move Diagonally Up Left" );
$self->_sendMomentaryPtzCommand("LeftUp", 1, 1, 0, 0);
}
sub moveConDownLeft
{
my $self = shift;
Debug( "Move Diagonally Up Right" );
$self->_sendMomentaryPtzCommand("LeftDown", 1, 1, 0, 0);
}
sub zoomConTele
{
my $self = shift;
Debug( "Zoom Tele" );
$self->_sendMomentaryPtzCommand("ZoomTele", 0, 1, 0, 0);
}
sub zoomConWide
{
my $self = shift;
Debug( "Zoom Wide" );
$self->_sendMomentaryPtzCommand("ZoomWide", 0, 1, 0, 0);
}
sub moveRelUpLeft
{
my $self = shift;
Debug("Move Up Left");
$self->sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
$self->_sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
}
sub moveRelUp
{
my $self = shift;
Debug("Move Up");
$self->sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
$self->_sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
}
sub moveRelUpRight
{
my $self = shift;
Debug("Move Up Right");
$self->sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
$self->_sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
}
sub moveRelLeft
{
my $self = shift;
Debug("Move Left");
$self->sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
$self->_sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
}
sub moveRelRight
{
my $self = shift;
Debug("Move Right");
$self->sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
$self->_sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
}
sub moveRelDownLeft
{
my $self = shift;
Debug("Move Down Left");
$self->sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
$self->_sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
}
sub moveRelDown
{
my $self = shift;
Debug("Move Down");
$self->sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
$self->_sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
}
sub moveRelDownRight
{
my $self = shift;
Debug("Move Down Right");
$self->sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
$self->_sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
}
sub zoomRelTele
{
my $self = shift;
Debug("Zoom Relative Tele");
$self->sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
$self->_sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
}
sub zoomRelWide
{
my $self = shift;
Debug("Zoom Relative Wide");
$self->sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500);
$self->_sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500);
}
sub focusRelNear
{
my $self = shift;
my $response = $self->_sendPtzCommand("start", "FocusNear", 0, 1, 0, 0);
Debug("focusRelNear response: " . $response);
}
sub focusRelFar
{
my $self = shift;
my $response = $self->_sendPtzCommand("start", "FocusFar", 0, 1, 0, 0);
Debug("focusRelFar response: " . $response);
}
sub moveStop
{
my $self = shift;
Debug( "Move Stop" );
# The command does not matter here, just the stop...
$self->_sendPtzCommand("stop", "Up", 0, 0, 1, 0);
}
sub presetClear
{
my $self = shift;
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
$self->sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0);
$self->_sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0);
}
sub presetSet
{
my $self = shift;
@ -308,8 +413,8 @@ sub presetSet
my $control_preset_row = $sth->fetchrow_hashref();
my $new_label_name = $control_preset_row->{'Label'};
$self->sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
$self->sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
$self->_sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
$self->_sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
}
sub presetGoto
@ -318,12 +423,39 @@ sub presetGoto
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
$self->sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0);
$self->_sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0);
}
sub presetHome
{
my $self = shift;
$self->_sendAbsolutePositionCommand( 0, 0, 0, 1 );
}
sub reset
{
my $self = shift;
Debug( "Camera Reset" );
$self->_sendPtzCommand("Reset", 0, 0, 0, 0);
}
sub reboot
{
my $self = shift;
Debug( "Camera Reboot" );
my $cmd = "/cgi-bin/magicBox.cgi?action=reboot";
$self->_sendGetRequest($cmd);
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
ZoneMinder::Control::Dahua - Perl module for Dahua cameras
@ -337,10 +469,6 @@ place this in /usr/share/perl5/ZoneMinder/Control
This module is an implementation of the Dahua IP camera HTTP control API.
=head2 EXPORT
None by default.
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2018 ZoneMinder LLC
@ -359,4 +487,138 @@ 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.
=head1 Private Methods
Methods intended for use internally but documented here for future developers.
=head2 _sendAbsolutePositionCommand( $arg1, $arg2, $arg3, $arg4 )
Where:
$arg1 = Horizontal angle 0° to 360°
$arg2 = Vertical angle 0° to -90°
$arg3 = Zoom multiplier
$arg4 = Speed 1 to 8
This is an private method used to send an absolute position command to the
camera.
=head1 Public Methods
Methods made available to control.pl via ZoneMinder::Control
=head2 Notes:
=over 1
Which methods are invoked depends on which types of movement are selected in
the camera control type. For example: if the 'Can Move Continuous' option is
checked, then methods including 'Con' in their names are invoked. Likewise if
the 'Can Move Relative" option is checked, then methods including 'Rel' in
their names are invoked.
At present, these types of movement are prioritized and exclusive. This applies
to all types of movement, not just PTZ, but focus, iris, etc. as well. The options
are tested in the following order:
1. Continuous
2. Relative
3. Absolute
These types are exclusive meaning that the first one that matches is the one
ZoneMinder will use to control with. It would be nice to allow the user to
select the type used given that some cameras support all three types of
movement.
=back
=head2 new
This method instantiates a new control object based upon this control module
and sets the 'id' attribute to the value passed in.
=head2 open
This method opens an HTTP connection to the camera. It handles authentication,
etc. Upon success it sets the 'state' attribute to 'open.'
=head2 close
This method effectively closes the HTTP connection to the camera. It sets the
'state' attribute to 'close.'
=head2 printMsg
This method appears to be used for debugging.
=head2 moveCon<direction>
This set of methods invoke continuous movement in the direction indicated by
the <direction> portion of their name. They accept no arguments and move the
camera at a speed of 1 for 0ms. The speed index of 1 is the lowest of the
accepted range of 1-8.
NOTE:
This is not true continuous movmement as currently implemented.
=head2 focusCon<range>
This set of methods invoke continuous focus in the range direction indicated
by the <range> portion of their name. They accept no arguments.
NOTE:
This is not true continuous movmement as currently implemented.
=head2 moveRel<direction>
This set of methods invoke relatvie movement in the direction indicated by
the <direction> portion of their name. They accept no arguments and move the
camera at a speed of 4 for 500ms. The speed index of 4 is half-way between
the accepted range of 1-8.
=head2 focusRel<range>
This set of methods invoke realtive focus in the range direction indicated by
the <range> portion of their name. They accept no arguments.
NOTE:
This only just does work. The Dahua API specifies "multiples" as the input.
We pass in a 1 for that as it does not seem to matter what number (0-8) is
provided, the camera focus behaves the same.
=head2 moveStop
This method attempts to stop the camera. The problem is that if continuous
motion is occurring in multiple directions, this will only stop the motion
in the 'Up' direction. Dahua does not support an "all-stop" command.
=head2 presetHome
This method "homes" the camera to a preset position. It accepts no arguments.
When either continuous or relative movement is enabled, pressing the center
button on the movement controls invokes this method.
NOTE:
The Dahua protocol does not appear to support a preset Home feature. We could
allow the user to assign a preset slot as the "home" slot. Dahua does appear
to support naming presets which may lend itself to this sort of thing. At
this point, we'll just send the camera back to center and zoom wide. (0°,0°,0)
=head2 reset
This method will reset the PTZ controls to their "default." It is not clear
what that is.
=head2 reboot
This method performs a reboot of the camera. This will take the camera offline
for the time it takes to reboot.
=cut

View File

@ -1005,17 +1005,17 @@ sub delete_empty_directories {
Error("delete_empty_directories: Can't open directory '/$_[0]': $!" );
return;
}
my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir( $DIR );
my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir($DIR);
#Debug("delete_empty_directories $_[0] has " . @contents .' entries:' . ( @contents <= 2 ? join(',',@contents) : '' ));
my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents;
if ( @dirs ) {
Debug("Have " . @dirs . " dirs in $_[0]");
Debug('Have ' . @dirs . " dirs in $_[0]");
foreach ( @dirs ) {
delete_empty_directories( $_[0].'/'.$_ );
delete_empty_directories($_[0].'/'.$_);
}
#Reload, since we may now be empty
rewinddir $DIR;
@contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir( $DIR );
@contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir($DIR);
}
closedir($DIR);
if ( ! @contents ) {

View File

@ -923,10 +923,10 @@ if ( $version ) {
die( "Can't find upgrade from version '$version'" );
}
# Re-enable the privacy popup after each upgrade
my $sql = "update Config set Value = 1 where Name = 'ZM_SHOW_PRIVACY'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
$sth->finish();
#my $sql = "update Config set Value = 1 where Name = 'ZM_SHOW_PRIVACY'";
#my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
#my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
#$sth->finish();
print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
}
zmDbDisconnect();

View File

@ -248,15 +248,19 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
//Function to check Username length
bool checkUser ( const char *username) {
if ( strlen(username) > 32) {
if ( ! username )
return false;
}
if ( strlen(username) > 32 )
return false;
return true;
}
//Function to check password length
bool checkPass (const char *password) {
if ( strlen(password) > 64) {
if ( !password )
return false;
}
if ( strlen(password) > 64 )
return false;
return true;
}

View File

@ -394,7 +394,7 @@ int main(int argc, char *argv[]) {
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
break;
}
}
} // end getopt loop
if ( optind < argc ) {
fprintf(stderr, "Extraneous options, ");
@ -425,44 +425,38 @@ int main(int argc, char *argv[]) {
if ( config.opt_use_auth ) {
if ( strcmp(config.auth_relay, "none") == 0 ) {
if ( !checkUser(username)) {
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
exit_zmu(-1);
}
if ( !username ) {
fprintf(stderr, "Error, username must be supplied\n");
exit_zmu(-1);
}
if ( username ) {
user = zmLoadUser(username);
}
} else {
if ( !(username && password) && !auth ) {
fprintf(stderr, "Error, username and password or auth string must be supplied\n");
exit_zmu(-1);
}
if ( !checkUser(username)) {
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
exit_zmu(-1);
}
if ( !checkPass(password)) {
fprintf(stderr, "Error, password greater than allowed 64 characters\n");
user = zmLoadUser(username);
} else {
if ( !(username && password) && !auth ) {
fprintf(stderr, "Error, username and password or auth string must be supplied\n");
exit_zmu(-1);
}
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
{
if ( auth ) {
user = zmLoadAuthUser(auth, false);
}
if ( auth ) {
user = zmLoadAuthUser(auth, false);
}
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
{
if ( username && password ) {
user = zmLoadUser(username, password);
if ( username && password ) {
if ( !checkUser(username)) {
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
exit_zmu(-1);
}
}
}
if ( !checkPass(password)) {
fprintf(stderr, "Error, password greater than allowed 64 characters\n");
exit_zmu(-1);
}
user = zmLoadUser(username, password);
} // end if username && password
} // end if relay or not
if ( !user ) {
fprintf(stderr, "Error, unable to authenticate user\n");
return exit_zmu(-1);

View File

@ -1,14 +1,88 @@
<?php
App::uses('Component', 'Controller');
class FilterComponent extends Component {
/**
* Valid MySQL operands that can be used from namedParams with two operands
* where the right-hand side (RHS) is a literal.
* These came from https://dev.mysql.com/doc/refman/8.0/en/non-typed-operators.html
*/
public $twoOperandSQLOperands = array(
'AND',
'&&',
'=',
//':=',
//'BETWEEN ... AND ...',
//'BINARY',
'&',
'~',
//'|',
'^',
//'CASE',
'DIV',
'/',
'=',
'<=>',
'>',
'>=',
'IS',
'IS NOT',
//'IS NOT NULL',
//'IS NULL',
//'->',
//'->>',
'<<',
'<',
'<=',
'LIKE',
'-',
'%',
'MOD',
//'NOT',
//'!',
//'NOT BETWEEN ... AND ...',
'!=',
'<>',
'NOT LIKE',
'NOT REGEXP',
// `or` operators aren't safe as they can
// be used to skip an existing condition
// enforcing access to only certain
// monitors/events.
//'||',
//'OR',
'+',
'REGEXP',
'>>',
'RLIKE',
'SOUNDS LIKE',
//'*',
'-',
//'XOR',
);
// Build a CakePHP find() condition based on the named parameters
// that are passed in
public function buildFilter($namedParams) {
$conditions = array();
if ($namedParams) {
$conditions = array();
foreach ($namedParams as $attribute => $value) {
// We need to sanitize $attribute to avoid SQL injection.
$lhs = trim($attribute);
$matches = NULL;
if (preg_match('/^(?P<field>[a-z0-9]+)(?P<operator>.+)?$/i', $lhs, $matches) !== 1) {
throw new Exception('Invalid argument before `:`: ' . $lhs);
}
$operator = trim($matches['operator']);
// Only allow operators on our allow list. No operator
// specified defaults to `=` by cakePHP.
if ($operator != '' && !in_array($operator, $this->twoOperandSQLOperands)) {
throw new Exception('Invalid operator: ' . $operator);
}
$lhs = '`' . $matches['field'] . '` ' . $operator;
// If the named param contains an array, we want to turn it into an IN condition
// Otherwise, we add it right into the $conditions array
if (is_array($value)) {
@ -18,10 +92,10 @@ class FilterComponent extends Component {
array_push($array, $term);
}
$query = array($attribute => $array);
$query = array($lhs => $array);
array_push($conditions, $query);
} else {
$conditions[$attribute] = $value;
$conditions[$lhs] = $value;
}
}

View File

@ -44,9 +44,8 @@ class EventsController extends AppController {
}
if ( $this->request->params['named'] ) {
//$this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
$conditions = $this->request->params['named'];
$this->FilterComponent = $this->Components->load('Filter');
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
} else {
$conditions = array();
}
@ -234,18 +233,34 @@ class EventsController extends AppController {
public function search() {
$this->Event->recursive = -1;
// Unmodified conditions to pass to find()
$find_conditions = array();
// Conditions to be filtered by buildFilter
$conditions = array();
foreach ($this->params['named'] as $param_name => $value) {
// Transform params into mysql
if ( preg_match('/interval/i', $value, $matches) ) {
$condition = array("$param_name >= (date_sub(now(), $value))");
// Transform params into conditions
if ( preg_match('/^\s?interval\s?/i', $value) ) {
if (preg_match('/^[a-z0-9]+$/i', $param_name) !== 1) {
throw new Exception('Invalid field name: ' . $param_name);
}
$matches = NULL;
$value = preg_replace('/^\s?interval\s?/i', '', $value);
if (preg_match('/^(?P<expr>[ -.:0-9\']+)\s+(?P<unit>[_a-z]+)$/i', trim($value), $matches) !== 1) {
throw new Exception('Invalid interval: ' . $value);
}
$expr = trim($matches['expr']);
$unit = trim($matches['unit']);
array_push($find_conditions, "$param_name >= DATE_SUB(NOW(), INTERVAL $expr $unit)");
} else {
$condition = array($param_name => $value);
$conditions[$param_name] = $value;
}
array_push($conditions, $condition);
}
$this->FilterComponent = $this->Components->load('Filter');
$conditions = $this->FilterComponent->buildFilter($conditions);
array_push($conditions, $find_conditions);
$results = $this->Event->find('all', array(
'conditions' => $conditions
));
@ -261,18 +276,32 @@ class EventsController extends AppController {
// consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json
public function consoleEvents($interval = null) {
$matches = NULL;
// https://dev.mysql.com/doc/refman/5.5/en/expressions.html#temporal-intervals
// Examples: `'1-1' YEAR_MONTH`, `'-1 10' DAY_HOUR`, `'1.999999' SECOND_MICROSECOND`
if (preg_match('/^(?P<expr>[ -.:0-9\']+)\s+(?P<unit>[_a-z]+)$/i', trim($interval), $matches) !== 1) {
throw new Exception('Invalid interval: ' . $interval);
}
$expr = trim($matches['expr']);
$unit = trim($matches['unit']);
$this->Event->recursive = -1;
$results = array();
$this->FilterComponent = $this->Components->load('Filter');
$conditions = $this->FilterComponent->buildFilter($conditions);
array_push($conditions, array("StartTime >= DATE_SUB(NOW(), INTERVAL $expr $unit)"));
$moreconditions = '';
foreach ($this->request->params['named'] as $name => $param) {
$moreconditions = $moreconditions . ' AND '.$name.$param;
}
$query = $this->Event->query("SELECT MonitorId, COUNT(*) AS Count FROM Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;");
$query = $this->Event->find('all', array(
'fields' => array(
'MonitorId',
'COUNT(*) AS Count',
),
'conditions' => $conditions,
'group' => 'MonitorId',
));
foreach ($query as $result) {
$results[$result['Events']['MonitorId']] = $result[0]['Count'];
$results[$result['Event']['MonitorId']] = $result[0]['Count'];
}
$this->set(array(

View File

@ -40,8 +40,7 @@ class MonitorsController extends AppController {
if ( $this->request->params['named'] ) {
$this->FilterComponent = $this->Components->load('Filter');
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
$conditions = $this->request->params['named'];
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
} else {
$conditions = array();
}
@ -318,6 +317,10 @@ class MonitorsController extends AppController {
throw new NotFoundException(__('Invalid monitor'));
}
if (preg_match('/^[a-z]+$/i', $daemon) !== 1) {
throw new BadRequestException(__('Invalid command'));
}
$monitor = $this->Monitor->find('first', array(
'fields' => array('Id', 'Type', 'Device'),
'conditions' => array('Id' => $id)

View File

@ -122,7 +122,12 @@ class Event {
public function Path() {
$Storage = $this->Storage();
return $Storage->Path().'/'.$this->Relative_Path();
if ( $Storage->Path() and $this->Relative_Path() ) {
return $Storage->Path().'/'.$this->Relative_Path();
} else {
Error("Event Path not complete. Storage: " . $Storage->Path() . " relative: " . $this->Relative_Path());
return '';
}
}
public function Relative_Path() {
@ -148,17 +153,19 @@ class Event {
}
public function delete() {
# This wouldn't work with foreign keys
dbQuery( 'DELETE FROM Events WHERE Id = ?', array($this->{'Id'}) );
if ( ! $this->{'Id'} ) {
Error('Event delete on event with empty Id');
return;
}
if ( !ZM_OPT_FAST_DELETE ) {
dbQuery( 'DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}) );
dbQuery( 'DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}) );
dbQuery('DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}));
dbQuery('DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}));
if ( $this->{'Scheme'} == 'Deep' ) {
# Assumption: All events have a start time
$start_date = date_parse( $this->{'StartTime'} );
$start_date = date_parse($this->{'StartTime'});
if ( ! $start_date ) {
Error('Unable to parse start time for event ' . $this->{'Id'} . ' not deleting files.' );
Error('Unable to parse start time for event ' . $this->{'Id'} . ' not deleting files.');
return;
}
$start_date['year'] = $start_date['year'] % 100;
@ -166,37 +173,42 @@ class Event {
# So this is because ZM creates a link under the day pointing to the time that the event happened.
$link_path = $this->Link_Path();
if ( ! $link_path ) {
Error('Unable to determine link path for event ' . $this->{'Id'} . ' not deleting files.' );
Error('Unable to determine link path for event ' . $this->{'Id'} . ' not deleting files.');
return;
}
$Storage = $this->Storage();
$eventlink_path = $Storage->Path().'/'.$link_path;
if ( $id_files = glob( $eventlink_path ) ) {
if ( $id_files = glob($eventlink_path) ) {
if ( ! $eventPath = readlink($id_files[0]) ) {
Error("Unable to read link at $id_files[0]");
return;
}
# I know we are using arrays here, but really there can only ever be 1 in the array
$eventPath = preg_replace( '/\.'.$this->{'Id'}.'$/', $eventPath, $id_files[0] );
deletePath( $eventPath );
deletePath( $id_files[0] );
$pathParts = explode( '/', $eventPath );
deletePath($eventPath);
deletePath($id_files[0]);
$pathParts = explode('/', $eventPath);
for ( $i = count($pathParts)-1; $i >= 2; $i-- ) {
$deletePath = join( '/', array_slice( $pathParts, 0, $i ) );
if ( !glob( $deletePath."/*" ) ) {
deletePath( $deletePath );
$deletePath = join('/', array_slice($pathParts, 0, $i));
if ( !glob($deletePath.'/*') ) {
deletePath($deletePath);
}
}
} else {
Warning( "Found no event files under $eventlink_path" );
Warning("Found no event files under $eventlink_path");
} # end if found files
} else {
$eventPath = $this->Path();
deletePath( $eventPath );
if ( ! $eventPath ) {
Error("No event Path in Event delete. Not deleting");
return;
}
deletePath($eventPath);
} # USE_DEEP_STORAGE OR NOT
} # ! ZM_OPT_FAST_DELETE
dbQuery('DELETE FROM Events WHERE Id = ?', array($this->{'Id'}));
} # end Event->delete
public function getStreamSrc( $args=array(), $querySep='&' ) {

View File

@ -27,28 +27,32 @@ if ( $action == 'donate' && isset($_REQUEST['option']) ) {
$option = $_REQUEST['option'];
switch( $option ) {
case 'go' :
// Ignore this, the caller will open the page itself
break;
// Ignore this, the caller will open the page itself
break;
case 'hour' :
case 'day' :
case 'week' :
case 'month' :
$nextReminder = time();
if ( $option == 'hour' ) {
$nextReminder += 60*60;
} elseif ( $option == 'day' ) {
$nextReminder += 24*60*60;
} elseif ( $option == 'week' ) {
$nextReminder += 7*24*60*60;
} elseif ( $option == 'month' ) {
$nextReminder += 30*24*60*60;
}
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
break;
$nextReminder = time();
if ( $option == 'hour' ) {
$nextReminder += 60*60;
} elseif ( $option == 'day' ) {
$nextReminder += 24*60*60;
} elseif ( $option == 'week' ) {
$nextReminder += 7*24*60*60;
} elseif ( $option == 'month' ) {
$nextReminder += 30*24*60*60;
}
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
break;
case 'never' :
case 'already' :
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
break;
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
break;
default :
Warning("Unknown value for option in donate: $option");
break;
} // end switch option
$view = 'none';
}
?>

View File

@ -211,7 +211,7 @@ global $user;
if ( ZM_OPT_USE_AUTH ) {
$close_session = 0;
if ( !is_session_started() ) {
session_start();
zm_session_start();
$close_session = 1;
}

View File

@ -17,7 +17,6 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
namespace ZM;
error_reporting(E_ALL);
@ -72,7 +71,7 @@ define('ZM_BASE_URL', '');
require_once('includes/functions.php');
if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) {
Logger::Debug("OPTIONS Method, only doing CORS");
ZM\Logger::Debug("OPTIONS Method, only doing CORS");
# Add Cross domain access headers
CORSHeaders();
return;
@ -159,7 +158,7 @@ CORSHeaders();
// Check for valid content dirs
if ( !is_writable(ZM_DIR_EVENTS) ) {
Warning("Cannot write to event folder ".ZM_DIR_EVENTS.". Check that it exists and is owned by the web account user.");
ZM\Warning("Cannot write to event folder ".ZM_DIR_EVENTS.". Check that it exists and is owned by the web account user.");
}
# Globals
@ -181,7 +180,7 @@ if ( isset($_REQUEST['request']) )
require_once('includes/auth.php');
foreach ( getSkinIncludes('skin.php') as $includeFile ) {
#Logger::Debug("including $includeFile");
#ZM\Logger::Debug("including $includeFile");
require_once $includeFile;
}
@ -194,7 +193,7 @@ isset($view) || $view = NULL;
isset($request) || $request = NULL;
isset($action) || $action = NULL;
Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
ZM\Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
if (
ZM_ENABLE_CSRF_MAGIC &&
( $action != 'login' ) &&
@ -205,17 +204,17 @@ if (
( $view != 'archive' )
) {
require_once( 'includes/csrf/csrf-magic.php' );
#Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
#ZM\Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
csrf_check();
}
# Need to include actions because it does auth
if ( $action ) {
if ( file_exists('includes/actions/'.$view.'.php') ) {
Logger::Debug("Including includes/actions/$view.php");
ZM\Logger::Debug("Including includes/actions/$view.php");
require_once('includes/actions/'.$view.'.php');
} else {
Warning("No includes/actions/$view.php for action $action");
ZM\Warning("No includes/actions/$view.php for action $action");
}
}
@ -227,7 +226,7 @@ if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
header('HTTP/1.1 401 Unauthorized');
exit;
}
Logger::Debug('Redirecting to login');
ZM\Logger::Debug('Redirecting to login');
$view = 'none';
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
$request = null;
@ -240,7 +239,7 @@ if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
CSPHeaders($view, $cspNonce);
if ( $redirect ) {
Logger::Debug("Redirecting to $redirect");
ZM\Logger::Debug("Redirecting to $redirect");
header('Location: '.$redirect);
return;
}
@ -248,7 +247,7 @@ if ( $redirect ) {
if ( $request ) {
foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) {
if ( !file_exists($includeFile) )
Fatal("Request '$request' does not exist");
ZM\Fatal("Request '$request' does not exist");
require_once $includeFile;
}
return;
@ -257,7 +256,7 @@ if ( $request ) {
if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) {
foreach ( $includeFiles as $includeFile ) {
if ( !file_exists($includeFile) )
Fatal("View '$view' does not exist");
ZM\Fatal("View '$view' does not exist");
require_once $includeFile;
}
// If the view overrides $view to 'error', and the user is not logged in, then the

View File

@ -216,6 +216,7 @@ $SLANG = array(
'CanMoveRel' => 'Podržava Relativno kretanje',
'CanPan' => 'Podržava Pomak' ,
'CanReset' => 'PodržavaReset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Podržava presetove',
'CanSleep' => 'Podržava Sleep',
'CanTilt' => 'Podržava nagib',

View File

@ -209,6 +209,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -205,6 +205,7 @@ $SLANG = array(
'CanMoveRel' => '可以相对移动',
'CanPan' => '可以平移' ,
'CanReset' => '可以复位',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => '可以进行预设',
'CanSleep' => '可以休眠',
'CanTilt' => '可以倾斜',

View File

@ -205,6 +205,7 @@ $SLANG = array(
'CanMoveRel' => 'Umí relativní pohyb',
'CanPan' => 'Umí otáčení',
'CanReset' => 'Umí reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Umí navolit předvolby',
'CanSleep' => 'Může spát',
'CanTilt' => 'Umí náklon',

View File

@ -207,6 +207,7 @@ $SLANG = array(
'CanMoveRel' => 'Kann relative Bewegung',
'CanPan' => 'Kann Pan' ,
'CanReset' => 'Kann Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Kann Voreinstellungen setzen',
'CanSleep' => 'Kann Sleep',
'CanTilt' => 'Kann Neigung',

View File

@ -206,6 +206,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -216,6 +216,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -156,6 +156,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -205,6 +205,7 @@ $SLANG = array(
'CanMoveRel' => 'Puede moverse de forma relativa',
'CanPan' => 'Puede desplazarse' ,
'CanReset' => 'Puede restablecerse',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Puede fefinir programaciones',
'CanSleep' => 'Puede dormirse',
'CanTilt' => 'Puede inclinarse',

View File

@ -212,6 +212,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -211,6 +211,7 @@ $SLANG = array(
'CanMoveRel' => 'Relatif',
'CanPan' => 'Panoramique' ,
'CanReset' => 'RàZ',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Stockage prépos.',
'CanSleep' => 'Veille',
'CanTilt' => 'Inclinaison',

View File

@ -205,6 +205,7 @@ $SLANG = array(
'CanMoveRel' => 'àôùø úæåæä éçñéú',
'CanPan' => 'Can Pan' ,
'CanReset' => 'àôùø àúçåì',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'àôùø îöá ùéðä',
'CanTilt' => 'àôùø æòæåò',

View File

@ -248,6 +248,7 @@ $SLANG = array(
'CanMoveRel' => 'Relatíven tud mozogni',
'CanPan' => 'Tud jobb-bal mozgást' ,
'CanReset' => 'Tud alaphelyzetbe jönni',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Tud menteni profilokat',
'CanSleep' => 'Tud phihenő üzemmódot',
'CanTilt' => 'Tud fel-le mozgást',

View File

@ -210,6 +210,7 @@ $SLANG = array(
'CanMoveRel' => 'Puo\' Mov. Relativo',
'CanPan' => 'Puo\' Pan' ,
'CanReset' => 'Puo\' Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Puo\' impostare preset',
'CanSleep' => 'Puo\' andare in sleep',
'CanTilt' => 'Puo\' Tilt',

View File

@ -206,6 +206,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -206,6 +206,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relatief',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -220,6 +220,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -145,6 +145,7 @@ $SLANG = array(
'CanMoveRel' => 'Can Move Relative',
'CanPan' => 'Can Pan' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Can Tilt',

View File

@ -176,6 +176,7 @@ $SLANG = array(
'CanMoveRel' => 'Mi&#351;care relativ&#259;',
'CanPan' => 'Rotativ' ,
'CanReset' => 'Can Reset',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Can Set Presets',
'CanSleep' => 'Can Sleep',
'CanTilt' => 'Se poate &#238;nclina',

View File

@ -206,6 +206,7 @@ $SLANG = array(
'CanMoveRel' => 'Относительное перемещение',
'CanPan' => 'Панорама' ,
'CanReset' => 'Сброс',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Создание предустановок',
'CanSleep' => 'Сон',
'CanTilt' => 'Наклон',

View File

@ -206,6 +206,7 @@ $SLANG = array(
'CanMoveRel' => 'Har relativ förflyttning',
'CanPan' => 'Har panorering',
'CanReset' => 'Har återställning',
'CanReboot' => 'Can Reboot',
'CanSetPresets' => 'Har förinställningar',
'CanSleep' => 'Kan vila',
'CanTilt' => 'Kan tilta',

View File

@ -24,6 +24,7 @@ function getControlCommands( $monitor ) {
$cmds['Wake'] = 'wake';
$cmds['Sleep'] = 'sleep';
$cmds['Reset'] = 'reset';
$cmds['Reboot'] = 'reboot';
$cmds['PresetSet'] = 'presetSet';
$cmds['PresetGoto'] = 'presetGoto';
@ -319,6 +320,11 @@ function controlPower( $monitor, $cmds ) {
if ( $monitor->CanReset() ) {
?>
<button type="button" class="ptzTextBtn" value="Reset" onclick="controlCmd('<?php echo $cmds['Reset'] ?>')"><?php echo translate('Reset') ?></button>
<?php
}
if ( $monitor->CanReboot() ) {
?>
<button type="button" class="ptzTextBtn" value="Reboot" onclick="controlCmd('<?php echo $cmds['Reboot'] ?>')"><?php echo translate('Reboot') ?></button>
<?php
}
?>

View File

@ -33,7 +33,7 @@ var popupSizes = {
'cycle': {'addWidth': 32, 'minWidth': 384, 'addHeight': 62},
'device': {'width': 260, 'height': 150},
'devices': {'width': 400, 'height': 240},
'donate': {'width': 500, 'height': 280},
'donate': {'width': 500, 'height': 480},
'download': {'width': 350, 'height': 215},
'event': {'addWidth': 108, 'minWidth': 496, 'addHeight': 230, 'minHeight': 540},
'eventdetail': {'width': 600, 'height': 420},

View File

@ -59,6 +59,7 @@ else
'CanWake' => "",
'CanSleep' => "",
'CanReset' => "",
'CanReboot' => "",
'CanMove' => "",
'CanMoveDiag' => "",
'CanMoveMap' => "",
@ -352,6 +353,7 @@ switch ( $tab )
<tr><th scope="row"><?php echo translate('CanWake') ?></th><td><input type="checkbox" name="newControl[CanWake]" value="1"<?php if ( !empty($newControl['CanWake']) ) { ?> checked="checked"<?php } ?>/></td></tr>
<tr><th scope="row"><?php echo translate('CanSleep') ?></th><td><input type="checkbox" name="newControl[CanSleep]" value="1"<?php if ( !empty($newControl['CanSleep']) ) { ?> checked="checked"<?php } ?>/></td></tr>
<tr><th scope="row"><?php echo translate('CanReset') ?></th><td><input type="checkbox" name="newControl[CanReset]" value="1"<?php if ( !empty($newControl['CanReset']) ) { ?> checked="checked"<?php } ?>/></td></tr>
<tr><th scope="row"><?php echo translate('CanReboot') ?></th><td><input type="checkbox" name="newControl[CanReboot]" value="1"<?php if ( !empty($newControl['CanReboot']) ) { ?> checked="checked"<?php } ?>/></td></tr>
<?php
break;
}

View File

@ -18,25 +18,24 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canEdit( 'System' ) )
{
$view = "error";
return;
if ( !canEdit('System') ) {
$view = 'error';
return;
}
$options = array(
"go" => translate('DonateYes'),
"hour" => translate('DonateRemindHour'),
"day" => translate('DonateRemindDay'),
"week" => translate('DonateRemindWeek'),
"month" => translate('DonateRemindMonth'),
"never" => translate('DonateRemindNever'),
"already" => translate('DonateAlready'),
"go" => translate('DonateYes'),
"hour" => translate('DonateRemindHour'),
"day" => translate('DonateRemindDay'),
"week" => translate('DonateRemindWeek'),
"month" => translate('DonateRemindMonth'),
"never" => translate('DonateRemindNever'),
"already" => translate('DonateAlready'),
);
$focusWindow = true;
xhtmlHeaders(__FILE__, translate('Donate') );
xhtmlHeaders(__FILE__, translate('Donate'));
?>
<body>
<div id="page">
@ -46,17 +45,17 @@ xhtmlHeaders(__FILE__, translate('Donate') );
</div>
<div id="content">
<form name="contentForm" id="contentForm" method="post" action="?">
<input type="hidden" name="view" value="none"/>
<input type="hidden" name="view" value="donate"/>
<input type="hidden" name="action" value="donate"/>
<p>
<?php echo translate('DonateEnticement') ?>
</p>
<p>
<?php echo buildSelect( "option", $options ); ?>
<?php echo buildSelect('option', $options); ?>
</p>
<div id="contentButtons">
<input type="submit" value="<?php echo translate('Apply') ?>" data-on-click-this="submitForm">
<input type="button" value="<?php echo translate('Close') ?>" data-on-click="closeWindow">
<button type="submit"><?php echo translate('Apply') ?></button>
<button type="button" data-on-click="closeWindow"><?php echo translate('Close') ?></button>
</div>
</form>
</div>

View File

@ -26,10 +26,11 @@ function cycleNext() {
window.location.replace('?view=cycle&mid='+monitorData[monIdx].id+'&mode='+mode, cycleRefreshTimeout);
}
function cyclePrev() {
if ( monIdx )
if (monIdx) {
monIdx -= 1;
else
monIdx = monitorData.length-1;
} else {
monIdx = monitorData.length - 1;
}
window.location.replace('?view=cycle&mid='+monitorData[monIdx].id+'&mode='+mode, cycleRefreshTimeout);
}
@ -82,9 +83,9 @@ function changeScale() {
var scale = $('scale').get('value');
$('width').set('value', 'auto');
$('height').set('value', 'auto');
Cookie.write('zmCycleScale', scale, { duration: 10*365 });
Cookie.write('zmCycleWidth', 'auto', { duration: 10*365 });
Cookie.write('zmCycleHeight', 'auto', { duration: 10*365 });
Cookie.write('zmCycleScale', scale, {duration: 10*365});
Cookie.write('zmCycleWidth', 'auto', {duration: 10*365});
Cookie.write('zmCycleHeight', 'auto', {duration: 10*365});
var newWidth = ( monitorData[monIdx].width * scale ) / SCALE_BASE;
var newHeight = ( monitorData[monIdx].height * scale ) / SCALE_BASE;

View File

@ -1,13 +1,3 @@
function submitForm( element ) {
var form = element.form;
if ( form.option.selectedIndex == 0 ) {
form.view.value = currentView;
} else {
form.view.value = 'none';
}
form.submit();
}
if ( action == "donate" && option == "go" ) {
zmWindow();
}

View File

@ -11,7 +11,6 @@ function Monitor(monitorData) {
this.alarmState = STATE_IDLE;
this.lastAlarmState = STATE_IDLE;
this.streamCmdParms = 'view=request&request=stream&connkey='+this.connKey;
this.onclick = monitorData.onclick;
if ( auth_hash ) {
this.streamCmdParms += '&auth='+auth_hash;
}
@ -26,8 +25,30 @@ function Monitor(monitorData) {
}
};
this.onclick = function() {
var el = this;
var url = '?view=watch&mid='+this.id;
var name = 'zmWatch'+this.id;
var tag = 'watch';
var width = el.getAttribute("data-window-width");
var height = el.getAttribute("data-window-height");
evt.preventDefault();
createPopup(url, name, tag, width, height);
};
this.setup_onclick = function() {
document.querySelectorAll('#imageFeed'+this.id).forEach(function(el) {
el.addEventListener('click', this.onclick);
});
}
this.disable_onclick = function() {
document.querySelectorAll('#imageFeed'+this.id).forEach(function(el) {
el.removeEventListener('click',this.onclick);
});
}
this.setStateClass = function(element, stateClass) {
if ( !element.hasClass(stateClass) ) {
if ( !element.hasClass( stateClass ) ) {
if ( stateClass != 'alarm' )
element.removeClass('alarm');
if ( stateClass != 'alert' )
@ -359,8 +380,7 @@ function edit_layout(button) {
for ( var i = 0, length = monitors.length; i < length; i++ ) {
var monitor = monitors[i];
monitor_feed = $j('#imageFeed'+monitor.id)[0];
monitor_feed.onclick = '';
monitor.disable_onclick();
};
$j('#monitors .monitorFrame').draggable({
@ -435,8 +455,9 @@ function initPage() {
if ( monitors[i].type == 'WebSite' && interval > 0 ) {
setInterval(reloadWebSite, interval*1000, i);
}
monitors[i].setup_onclick();
}
selectLayout('#zmMontageLayout');
}
// Kick everything off
window.addEventListener( 'DOMContentLoaded', initPage );
window.addEventListener('DOMContentLoaded', initPage);

View File

@ -448,10 +448,16 @@ function cmdEnableAlarms() {
function cmdForceAlarm() {
alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm");
if (window.event) {
window.event.preventDefault();
}
}
function cmdCancelForcedAlarm() {
alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm");
if (window.event) {
window.event.preventDefault();
}
return false;
}

View File

@ -199,10 +199,7 @@ foreach ( $monitors as $monitor ) {
<div id="monitor<?php echo $monitor->Id() ?>" class="monitor idle">
<div
id="imageFeed<?php echo $monitor->Id() ?>"
class="imageFeed popup-link"
data-url="?view=watch&amp;mid=<?php echo $monitor->Id() ?>"
data-name="zmWatch<?php echo $monitor->Id() ?>"
data-tag="watch"
class="imageFeed"
data-width="<?php echo reScale( $monitor->Width(), $monitor->PopupScale() ); ?>"
data-height="<?php echo reScale( $monitor->Height(), $monitor->PopupScale() ); ?>">
<?php