Merge branch 'master' of github.com:zoneminder/ZoneMinder
commit
5c17de8e18
|
@ -3,6 +3,7 @@
|
||||||
# For more information and installation, see the INSTALL file
|
# For more information and installation, see the INSTALL file
|
||||||
#
|
#
|
||||||
cmake_minimum_required (VERSION 2.8.7)
|
cmake_minimum_required (VERSION 2.8.7)
|
||||||
|
cmake_policy(SET CMP0054 NEW)
|
||||||
project (zoneminder)
|
project (zoneminder)
|
||||||
file (STRINGS "version" zoneminder_VERSION)
|
file (STRINGS "version" zoneminder_VERSION)
|
||||||
# make API version a minor of ZM version
|
# make API version a minor of ZM version
|
||||||
|
@ -760,6 +761,7 @@ if(HAVE_OPENSSL_MD5_H)
|
||||||
"unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h"
|
"unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h"
|
||||||
HAVE_MD5_OPENSSL)
|
HAVE_MD5_OPENSSL)
|
||||||
endif(HAVE_OPENSSL_MD5_H)
|
endif(HAVE_OPENSSL_MD5_H)
|
||||||
|
|
||||||
if(HAVE_GNUTLS_GNUTLS_H)
|
if(HAVE_GNUTLS_GNUTLS_H)
|
||||||
set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
|
set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
|
||||||
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
|
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
|
||||||
|
@ -768,13 +770,17 @@ if(HAVE_GNUTLS_GNUTLS_H)
|
||||||
"int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h"
|
"int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h"
|
||||||
HAVE_DECL_GNUTLS_FINGERPRINT)
|
HAVE_DECL_GNUTLS_FINGERPRINT)
|
||||||
endif(HAVE_GNUTLS_GNUTLS_H)
|
endif(HAVE_GNUTLS_GNUTLS_H)
|
||||||
|
|
||||||
if(HAVE_MD5_OPENSSL)
|
if(HAVE_MD5_OPENSSL)
|
||||||
set(HAVE_DECL_MD5 1)
|
set(HAVE_DECL_MD5 1)
|
||||||
else(HAVE_MD5_OPENSSL)
|
|
||||||
message(AUTHOR_WARNING
|
|
||||||
"ZoneMinder requires a working MD5 function for hashed authenication but
|
|
||||||
none were found - hashed authenication will not be available")
|
|
||||||
endif(HAVE_MD5_OPENSSL)
|
endif(HAVE_MD5_OPENSSL)
|
||||||
|
|
||||||
|
if((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT))
|
||||||
|
message(AUTHOR_WARNING
|
||||||
|
"ZoneMinder requires a working MD5 function for hashed authentication but
|
||||||
|
none were found - hashed authentication will not be available")
|
||||||
|
endif((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT))
|
||||||
|
|
||||||
# Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available.
|
# Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available.
|
||||||
# This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac
|
# This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac
|
||||||
if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL)
|
if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL)
|
||||||
|
|
|
@ -356,11 +356,16 @@ sub handleMessage {
|
||||||
my $connection = shift;
|
my $connection = shift;
|
||||||
my $message = shift;
|
my $message = shift;
|
||||||
|
|
||||||
|
# CUA - Axis camera send the message quoted with"
|
||||||
|
# CUA - Also Axis camera cannot save the plus sign which
|
||||||
|
$message =~ s/^\"//g;
|
||||||
|
$message =~ s/\"$//g;
|
||||||
|
|
||||||
my ( $id, $action, $score, $cause, $text, $showtext )
|
my ( $id, $action, $score, $cause, $text, $showtext )
|
||||||
= split( /\|/, $message );
|
= split( /\|/, $message );
|
||||||
$score = 0 if ( !defined($score) );
|
$score = 0 if !defined($score);
|
||||||
$cause = '' if ( !defined($cause) );
|
$cause = '' if !defined($cause);
|
||||||
$text = '' if ( !defined($text) );
|
$text = '' if !defined($text);
|
||||||
|
|
||||||
my $monitor = $monitors{$id};
|
my $monitor = $monitors{$id};
|
||||||
if ( !$monitor ) {
|
if ( !$monitor ) {
|
||||||
|
@ -372,7 +377,7 @@ sub handleMessage {
|
||||||
next if !zmMemVerify($monitor);
|
next if !zmMemVerify($monitor);
|
||||||
|
|
||||||
Debug("Handling action '$action'");
|
Debug("Handling action '$action'");
|
||||||
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) {
|
if ( $action =~ /^(enable|disable)(?:[\+ ](\d+))?$/ ) {
|
||||||
my $state = $1;
|
my $state = $1;
|
||||||
my $delay = $2;
|
my $delay = $2;
|
||||||
if ( $state eq 'enable' ) {
|
if ( $state eq 'enable' ) {
|
||||||
|
|
169
src/zm_user.cpp
169
src/zm_user.cpp
|
@ -26,6 +26,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#if HAVE_GNUTLS_GNUTLS_H
|
#if HAVE_GNUTLS_GNUTLS_H
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
#include <gcrypt.h>
|
#include <gcrypt.h>
|
||||||
#elif HAVE_LIBCRYPTO
|
#elif HAVE_LIBCRYPTO
|
||||||
#include <openssl/md5.h>
|
#include <openssl/md5.h>
|
||||||
#endif // HAVE_L || HAVE_LIBCRYPTO
|
#endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO
|
||||||
|
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include "zm_crypt.h"
|
#include "zm_crypt.h"
|
||||||
|
@ -52,7 +53,7 @@ User::User(const MYSQL_ROW &dbrow) {
|
||||||
id = atoi(dbrow[index++]);
|
id = atoi(dbrow[index++]);
|
||||||
strncpy(username, dbrow[index++], sizeof(username)-1);
|
strncpy(username, dbrow[index++], sizeof(username)-1);
|
||||||
strncpy(password, dbrow[index++], sizeof(password)-1);
|
strncpy(password, dbrow[index++], sizeof(password)-1);
|
||||||
enabled = (bool)atoi(dbrow[index++]);
|
enabled = static_cast<bool>(atoi(dbrow[index++]));
|
||||||
stream = (Permission)atoi(dbrow[index++]);
|
stream = (Permission)atoi(dbrow[index++]);
|
||||||
events = (Permission)atoi(dbrow[index++]);
|
events = (Permission)atoi(dbrow[index++]);
|
||||||
control = (Permission)atoi(dbrow[index++]);
|
control = (Permission)atoi(dbrow[index++]);
|
||||||
|
@ -61,7 +62,7 @@ User::User(const MYSQL_ROW &dbrow) {
|
||||||
char *monitor_ids_str = dbrow[index++];
|
char *monitor_ids_str = dbrow[index++];
|
||||||
if ( monitor_ids_str && *monitor_ids_str ) {
|
if ( monitor_ids_str && *monitor_ids_str ) {
|
||||||
StringVector ids = split(monitor_ids_str, ",");
|
StringVector ids = split(monitor_ids_str, ",");
|
||||||
for( StringVector::iterator i = ids.begin(); i < ids.end(); ++i ) {
|
for ( StringVector::iterator i = ids.begin(); i < ids.end(); ++i ) {
|
||||||
monitor_ids.push_back(atoi((*i).c_str()));
|
monitor_ids.push_back(atoi((*i).c_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ User::~User() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void User::Copy(const User &u) {
|
void User::Copy(const User &u) {
|
||||||
id=u.id;
|
id = u.id;
|
||||||
strncpy(username, u.username, sizeof(username)-1);
|
strncpy(username, u.username, sizeof(username)-1);
|
||||||
strncpy(password, u.password, sizeof(password)-1);
|
strncpy(password, u.password, sizeof(password)-1);
|
||||||
enabled = u.enabled;
|
enabled = u.enabled;
|
||||||
|
@ -88,7 +89,8 @@ bool User::canAccess(int monitor_id) {
|
||||||
if ( monitor_ids.empty() )
|
if ( monitor_ids.empty() )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for ( std::vector<int>::iterator i = monitor_ids.begin(); i != monitor_ids.end(); ++i ) {
|
for ( std::vector<int>::iterator i = monitor_ids.begin();
|
||||||
|
i != monitor_ids.end(); ++i ) {
|
||||||
if ( *i == monitor_id ) {
|
if ( *i == monitor_id ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -103,12 +105,16 @@ User *zmLoadUser(const char *username, const char *password) {
|
||||||
int username_length = strlen(username);
|
int username_length = strlen(username);
|
||||||
char *safer_username = new char[(username_length * 2) + 1];
|
char *safer_username = new char[(username_length * 2) + 1];
|
||||||
|
|
||||||
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
// According to docs, size of safer_whatever must be 2*length+1
|
||||||
|
// due to unicode conversions + null terminator.
|
||||||
mysql_real_escape_string(&dbconn, safer_username, username, username_length);
|
mysql_real_escape_string(&dbconn, safer_username, username, username_length);
|
||||||
|
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`"
|
"SELECT `Id`, `Username`, `Password`, `Enabled`,"
|
||||||
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", safer_username);
|
" `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0,"
|
||||||
|
" `MonitorIds`"
|
||||||
|
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1",
|
||||||
|
safer_username);
|
||||||
|
|
||||||
if ( mysql_query(&dbconn, sql) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
|
@ -146,31 +152,34 @@ User *zmLoadUser(const char *username, const char *password) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) {
|
User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr) {
|
||||||
std::string key = config.auth_hash_secret;
|
std::string key = config.auth_hash_secret;
|
||||||
std::string remote_addr = "";
|
std::string remote_addr = "";
|
||||||
|
|
||||||
if (use_remote_addr) {
|
if ( use_remote_addr ) {
|
||||||
remote_addr = std::string(getenv( "REMOTE_ADDR" ));
|
remote_addr = std::string(getenv("REMOTE_ADDR"));
|
||||||
if ( remote_addr == "" ) {
|
if ( remote_addr == "" ) {
|
||||||
Warning( "Can't determine remote address, using null" );
|
Warning("Can't determine remote address, using null");
|
||||||
remote_addr = "";
|
remote_addr = "";
|
||||||
}
|
}
|
||||||
key += remote_addr;
|
key += remote_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug (1,"Inside zmLoadTokenUser, formed key=%s", key.c_str());
|
Debug(1, "Inside zmLoadTokenUser, formed key=%s", key.c_str());
|
||||||
|
|
||||||
std::pair<std::string, unsigned int> ans = verifyToken(jwt_token_str, key);
|
std::pair<std::string, unsigned int> ans = verifyToken(jwt_token_str, key);
|
||||||
std::string username = ans.first;
|
std::string username = ans.first;
|
||||||
unsigned int iat = ans.second;
|
unsigned int iat = ans.second;
|
||||||
Debug (1,"retrieved user '%s' from token", username.c_str());
|
Debug(1, "retrieved user '%s' from token", username.c_str());
|
||||||
|
|
||||||
if (username != "") {
|
if ( username != "" ) {
|
||||||
char sql[ZM_SQL_MED_BUFSIZ] = "";
|
char sql[ZM_SQL_MED_BUFSIZ] = "";
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`"
|
"SELECT `Id`, `Username`, `Password`, `Enabled`,"
|
||||||
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str() );
|
" `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0,"
|
||||||
|
" `MonitorIds`, `TokenMinExpiry`"
|
||||||
|
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1",
|
||||||
|
username.c_str());
|
||||||
|
|
||||||
if ( mysql_query(&dbconn, sql) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
|
@ -192,70 +201,72 @@ User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) {
|
||||||
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
||||||
User *user = new User(dbrow);
|
User *user = new User(dbrow);
|
||||||
unsigned int stored_iat = strtoul(dbrow[10], NULL,0 );
|
unsigned int stored_iat = strtoul(dbrow[10], NULL, 0);
|
||||||
|
|
||||||
if (stored_iat > iat ) { // admin revoked tokens
|
if ( stored_iat > iat ) { // admin revoked tokens
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
Error("Token was revoked for '%s'", username.c_str());
|
Error("Token was revoked for '%s'", username.c_str());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug (1,"Got last token revoke time of: %u",stored_iat);
|
Debug(1, "Authenticated user '%s' via token with last revoke time: %u",
|
||||||
Debug (1,"Authenticated user '%s' via token", username.c_str());
|
username.c_str(), stored_iat);
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
return user;
|
return user;
|
||||||
|
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
} // end User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr)
|
||||||
}
|
|
||||||
|
|
||||||
// Function to validate an authentication string
|
// Function to validate an authentication string
|
||||||
User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
|
||||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
#ifdef HAVE_GCRYPT_H
|
#ifdef HAVE_GCRYPT_H
|
||||||
// Special initialisation for libgcrypt
|
// Special initialisation for libgcrypt
|
||||||
if ( !gcry_check_version( GCRYPT_VERSION ) ) {
|
if ( !gcry_check_version(GCRYPT_VERSION) ) {
|
||||||
Fatal( "Unable to initialise libgcrypt" );
|
Fatal("Unable to initialise libgcrypt");
|
||||||
}
|
}
|
||||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
|
||||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||||
#endif // HAVE_GCRYPT_H
|
#endif // HAVE_GCRYPT_H
|
||||||
|
|
||||||
const char *remote_addr = "";
|
const char *remote_addr = "";
|
||||||
if ( use_remote_addr ) {
|
if ( use_remote_addr ) {
|
||||||
remote_addr = getenv( "REMOTE_ADDR" );
|
remote_addr = getenv("REMOTE_ADDR");
|
||||||
if ( !remote_addr ) {
|
if ( !remote_addr ) {
|
||||||
Warning( "Can't determine remote address, using null" );
|
Warning("Can't determine remote address, using null");
|
||||||
remote_addr = "";
|
remote_addr = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
Debug(1, "Attempting to authenticate user from auth string '%s'", auth);
|
||||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||||
snprintf( sql, sizeof(sql), "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds` FROM `Users` WHERE `Enabled` = 1" );
|
snprintf(sql, sizeof(sql),
|
||||||
|
"SELECT `Id`, `Username`, `Password`, `Enabled`,"
|
||||||
|
" `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0,"
|
||||||
|
" `MonitorIds` FROM `Users` WHERE `Enabled` = 1");
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, sql ) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result(&dbconn);
|
||||||
if ( !result ) {
|
if ( !result ) {
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
int n_users = mysql_num_rows( result );
|
int n_users = mysql_num_rows(result);
|
||||||
|
|
||||||
if ( n_users < 1 ) {
|
if ( n_users < 1 ) {
|
||||||
mysql_free_result( result );
|
mysql_free_result(result);
|
||||||
Warning( "Unable to authenticate user" );
|
Warning("Unable to authenticate user");
|
||||||
return( 0 );
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) {
|
const char * hex = "0123456789abcdef";
|
||||||
|
while ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) {
|
||||||
const char *user = dbrow[1];
|
const char *user = dbrow[1];
|
||||||
const char *pass = dbrow[2];
|
const char *pass = dbrow[2];
|
||||||
|
|
||||||
|
@ -264,20 +275,20 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
||||||
size_t md5len = 16;
|
size_t md5len = 16;
|
||||||
unsigned char md5sum[md5len];
|
unsigned char md5sum[md5len];
|
||||||
|
|
||||||
time_t now = time( 0 );
|
time_t now = time(0);
|
||||||
unsigned int hours = config.auth_hash_ttl;
|
unsigned int hours = config.auth_hash_ttl;
|
||||||
|
|
||||||
if ( ! hours ) {
|
if ( !hours ) {
|
||||||
Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2.");
|
Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2.");
|
||||||
hours = 2;
|
hours = 2;
|
||||||
} else {
|
} else {
|
||||||
Debug( 1, "AUTH_HASH_TTL is %d", hours );
|
Debug(1, "AUTH_HASH_TTL is %d", hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < hours; i++, now -= 3600 ) {
|
for ( unsigned int i = 0; i < hours; i++, now -= 3600 ) {
|
||||||
struct tm *now_tm = localtime( &now );
|
struct tm *now_tm = localtime(&now);
|
||||||
|
|
||||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
snprintf(auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||||
config.auth_hash_secret,
|
config.auth_hash_secret,
|
||||||
user,
|
user,
|
||||||
pass,
|
pass,
|
||||||
|
@ -285,51 +296,57 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
||||||
now_tm->tm_hour,
|
now_tm->tm_hour,
|
||||||
now_tm->tm_mday,
|
now_tm->tm_mday,
|
||||||
now_tm->tm_mon,
|
now_tm->tm_mon,
|
||||||
now_tm->tm_year
|
now_tm->tm_year);
|
||||||
);
|
|
||||||
|
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
MD5((unsigned char *)auth_key, strlen(auth_key), md5sum);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
gnutls_fingerprint(GNUTLS_DIG_MD5, &md5data, md5sum, &md5len);
|
||||||
#endif
|
#endif
|
||||||
auth_md5[0] = '\0';
|
unsigned char *md5sum_ptr = md5sum;
|
||||||
|
char *auth_md5_ptr = auth_md5;
|
||||||
|
|
||||||
for ( unsigned int j = 0; j < md5len; j++ ) {
|
for ( unsigned int j = 0; j < md5len; j++ ) {
|
||||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
*auth_md5_ptr++ = hex[(*md5sum_ptr>>4)&0xf];
|
||||||
|
*auth_md5_ptr++ = hex[(*md5sum_ptr++)&0xf];
|
||||||
}
|
}
|
||||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", auth_key, auth_md5, auth );
|
*auth_md5_ptr = 0;
|
||||||
|
|
||||||
if ( !strcmp( auth, auth_md5 ) ) {
|
Debug(1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'",
|
||||||
|
auth_key, auth_md5, auth);
|
||||||
|
|
||||||
|
if ( !strcmp(auth, auth_md5) ) {
|
||||||
// We have a match
|
// We have a match
|
||||||
User *user = new User( dbrow );
|
User *user = new User(dbrow);
|
||||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
Debug(1, "Authenticated user '%s'", user->getUsername());
|
||||||
mysql_free_result( result );
|
mysql_free_result(result);
|
||||||
return( user );
|
return user;
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "No match for %s", auth );
|
Debug(1, "No match for %s", auth);
|
||||||
}
|
}
|
||||||
}
|
} // end foreach hour
|
||||||
}
|
} // end foreach user
|
||||||
mysql_free_result( result );
|
mysql_free_result(result);
|
||||||
#else // HAVE_DECL_MD5
|
#else // HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
Error("You need to build with gnutls or openssl to use hash based auth");
|
||||||
#endif // HAVE_DECL_MD5
|
#endif // HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
Debug(1, "No user found for auth_key %s", auth );
|
Debug(1, "No user found for auth_key %s", auth);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} // end User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
|
||||||
|
|
||||||
//Function to check Username length
|
// Function to check Username length
|
||||||
bool checkUser ( const char *username) {
|
bool checkUser(const char *username) {
|
||||||
if ( ! username )
|
if ( !username )
|
||||||
return false;
|
return false;
|
||||||
if ( strlen(username) > 32 )
|
if ( strlen(username) > 32 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//Function to check password length
|
|
||||||
bool checkPass (const char *password) {
|
// Function to check password length
|
||||||
|
bool checkPass(const char *password) {
|
||||||
if ( !password )
|
if ( !password )
|
||||||
return false;
|
return false;
|
||||||
if ( strlen(password) > 64 )
|
if ( strlen(password) > 64 )
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
error_reporting(E_ERROR);
|
ini_set('display_errors','0');
|
||||||
|
|
||||||
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
||||||
ajaxError('No event id(s) supplied');
|
ajaxError('No event id(s) supplied');
|
||||||
|
@ -155,5 +155,5 @@ if ( canEdit('Events') ) {
|
||||||
} // end switch action
|
} // end switch action
|
||||||
} // end if canEdit('Events')
|
} // end if canEdit('Events')
|
||||||
|
|
||||||
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
|
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user '.$user['Username']);
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -42,6 +42,8 @@ if ( $action == 'version' && isset($_REQUEST['option']) ) {
|
||||||
$nextReminder += 24*60*60;
|
$nextReminder += 24*60*60;
|
||||||
} elseif ( $option == 'week' ) {
|
} elseif ( $option == 'week' ) {
|
||||||
$nextReminder += 7*24*60*60;
|
$nextReminder += 7*24*60*60;
|
||||||
|
} elseif ( $option == 'month' ) {
|
||||||
|
$nextReminder += 30*24*60*60;
|
||||||
}
|
}
|
||||||
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'");
|
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -193,7 +193,7 @@ if ( ! defined('ZM_SERVER_ID') ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ZM_TIMEZONE )
|
if ( defined('ZM_TIMEZONE') and ZM_TIMEZONE )
|
||||||
ini_set('date.timezone', ZM_TIMEZONE);
|
ini_set('date.timezone', ZM_TIMEZONE);
|
||||||
|
|
||||||
function process_configfile($configFile) {
|
function process_configfile($configFile) {
|
||||||
|
|
|
@ -47,7 +47,7 @@ function CSPHeaders($view, $nonce) {
|
||||||
&& defined('ZM_OPT_GOOG_RECAPTCHA_SITEKEY')
|
&& defined('ZM_OPT_GOOG_RECAPTCHA_SITEKEY')
|
||||||
&& defined('ZM_OPT_GOOG_RECAPTCHA_SECRETKEY')
|
&& defined('ZM_OPT_GOOG_RECAPTCHA_SECRETKEY')
|
||||||
&& ZM_OPT_USE_GOOG_RECAPTCHA && ZM_OPT_GOOG_RECAPTCHA_SITEKEY && ZM_OPT_GOOG_RECAPTCHA_SECRETKEY) {
|
&& ZM_OPT_USE_GOOG_RECAPTCHA && ZM_OPT_GOOG_RECAPTCHA_SITEKEY && ZM_OPT_GOOG_RECAPTCHA_SECRETKEY) {
|
||||||
$additionalScriptSrc = ' https://www.google.com';
|
$additionalScriptSrc .= ' https://www.google.com';
|
||||||
}
|
}
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
|
@ -2194,7 +2194,6 @@ function ajaxError($message, $code=HTTP_STATUS_OK) {
|
||||||
if ( $code == HTTP_STATUS_OK ) {
|
if ( $code == HTTP_STATUS_OK ) {
|
||||||
$response = array('result'=>'Error', 'message'=>$message);
|
$response = array('result'=>'Error', 'message'=>$message);
|
||||||
header('Content-type: application/json');
|
header('Content-type: application/json');
|
||||||
#header('Content-type: text/plain');
|
|
||||||
exit(jsonEncode($response));
|
exit(jsonEncode($response));
|
||||||
}
|
}
|
||||||
header("HTTP/1.0 $code $message");
|
header("HTTP/1.0 $code $message");
|
||||||
|
@ -2211,7 +2210,6 @@ function ajaxResponse($result=false) {
|
||||||
$response['message'] = $result;
|
$response['message'] = $result;
|
||||||
}
|
}
|
||||||
header('Content-type: application/json');
|
header('Content-type: application/json');
|
||||||
#header('Content-type: text/plain');
|
|
||||||
exit(jsonEncode($response));
|
exit(jsonEncode($response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -798,6 +798,7 @@ $SLANG = array(
|
||||||
'VersionRemindHour' => 'Remind again in 1 hour',
|
'VersionRemindHour' => 'Remind again in 1 hour',
|
||||||
'VersionRemindNever' => 'Don\'t remind about new versions',
|
'VersionRemindNever' => 'Don\'t remind about new versions',
|
||||||
'VersionRemindWeek' => 'Remind again in 1 week',
|
'VersionRemindWeek' => 'Remind again in 1 week',
|
||||||
|
'VersionRemindMonth' => 'Remind again in 1 month',
|
||||||
'Version' => 'Version',
|
'Version' => 'Version',
|
||||||
'ViewMatches' => 'View Matches',
|
'ViewMatches' => 'View Matches',
|
||||||
'VideoFormat' => 'Video Format',
|
'VideoFormat' => 'Video Format',
|
||||||
|
|
|
@ -52,3 +52,42 @@ td.monoRow {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.tabs {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
padding: 0;
|
||||||
|
float: left;
|
||||||
|
list-style: none;
|
||||||
|
height: 32px;
|
||||||
|
border-bottom: 1px solid #7f7fb2;
|
||||||
|
border-left: 1px solid #7f7fb2;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
ul.tabs li {
|
||||||
|
float: left;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 31px;
|
||||||
|
line-height: 31px;
|
||||||
|
border: 1px solid #7f7fb2;
|
||||||
|
border-left: none;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
background: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
ul.tabs li a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000;
|
||||||
|
display: block;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding: 0 20px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
ul.tabs li a:hover {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
html ul.tabs li.active, html ul.tabs li.active a:hover {
|
||||||
|
background: #dddddd;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
|
@ -445,14 +445,14 @@ th.table-th-sort span.table-th-sort-span {
|
||||||
float: right;
|
float: right;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
background: url("/skins/classic/graphics/arrow-s-u.png") no-repeat 0 0;
|
background: url("../skins/classic/graphics/arrow-s-u.png") no-repeat 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
th.table-th-sort-rev span.table-th-sort-span {
|
th.table-th-sort-rev span.table-th-sort-span {
|
||||||
float: right;
|
float: right;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
background: url("/skins/classic/graphics/arrow-s-d.png") no-repeat 0 0;
|
background: url("../skins/classic/graphics/arrow-s-d.png") no-repeat 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-tr-odd {
|
.table-tr-odd {
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
|
||||||
font-size:10px;
|
|
||||||
font-weight: normal;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #7f7fb2;
|
|
||||||
text-align: center;
|
|
||||||
padding: 2px 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: #7f7fb2;
|
|
||||||
text-decoration: none
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: #7f7fb2;
|
|
||||||
text-decoration: none
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #666699;
|
|
||||||
text-decoration: underline
|
|
||||||
}
|
|
||||||
|
|
||||||
img.thumb {
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.monoRow {
|
|
||||||
line-height: 200%;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#eventFrames tr.alarm {
|
|
||||||
background-color: #fa8072;
|
|
||||||
}
|
|
||||||
|
|
||||||
#eventFrames tr.bulk {
|
|
||||||
background-color: #cccccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#eventFrames tr.normal {
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
|
||||||
font-size:10px;
|
|
||||||
font-weight: normal;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #7f7fb2;
|
|
||||||
text-align: center;
|
|
||||||
padding: 2px 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: #7f7fb2;
|
|
||||||
text-decoration: none
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: #7f7fb2;
|
|
||||||
text-decoration: none
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #666699;
|
|
||||||
text-decoration: underline
|
|
||||||
}
|
|
||||||
|
|
||||||
img.thumb {
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.monoRow {
|
|
||||||
line-height: 200%;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#eventFrames tr.alarm {
|
|
||||||
background-color: #fa8072;
|
|
||||||
}
|
|
||||||
|
|
||||||
#eventFrames tr.bulk {
|
|
||||||
background-color: #cccccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#eventFrames tr.normal {
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,52 +20,18 @@
|
||||||
|
|
||||||
function exportHeader($title) {
|
function exportHeader($title) {
|
||||||
?>
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title><?php echo $title ?></title>
|
<title><?php echo $title ?></title>
|
||||||
<style type="text/css">
|
<style>
|
||||||
<?php include(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/css/'.ZM_SKIN_NAME.'/export.css'); ?>
|
<?php
|
||||||
|
include(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/css/base/export.css');
|
||||||
ul.tabs {
|
if ( $css != 'base' ) {
|
||||||
margin: 0;
|
include(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/css/'.$css.'/export.css');
|
||||||
margin-bottom: -1px;
|
}
|
||||||
padding: 0;
|
?>
|
||||||
float: left;
|
|
||||||
list-style: none;
|
|
||||||
height: 32px;
|
|
||||||
border-bottom: 1px solid #7f7fb2;
|
|
||||||
border-left: 1px solid #7f7fb2;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
ul.tabs li {
|
|
||||||
float: left;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
height: 31px;
|
|
||||||
line-height: 31px;
|
|
||||||
border: 1px solid #7f7fb2;
|
|
||||||
border-left: none;
|
|
||||||
margin-bottom: -1px;
|
|
||||||
background: #fff;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
ul.tabs li a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #000;
|
|
||||||
display: block;
|
|
||||||
font-size: 1.2em;
|
|
||||||
padding: 0 20px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
ul.tabs li a:hover {
|
|
||||||
background: #ccc;
|
|
||||||
}
|
|
||||||
html ul.tabs li.active, html ul.tabs li.active a:hover {
|
|
||||||
background: #dddddd;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<script src="<?php echo ($title == translate('Images').' Master' ? '' : '../') ?>jquery.js"></script>
|
<script src="<?php echo ($title == translate('Images').' Master' ? '' : '../') ?>jquery.js"></script>
|
||||||
<!--<script type="text/javascript" src="<?php echo ($title == translate('Images').' Master' ? '' : '../') ?>video.js"></script>-->
|
<!--<script type="text/javascript" src="<?php echo ($title == translate('Images').' Master' ? '' : '../') ?>video.js"></script>-->
|
||||||
|
@ -95,21 +61,21 @@ html ul.tabs li.active, html ul.tabs li.active a:hover {
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<?php
|
<?php
|
||||||
}
|
} // end exportHeader
|
||||||
|
|
||||||
function exportEventDetail($event, $exportFrames, $exportImages) {
|
function exportEventDetail($event, $exportFrames, $exportImages) {
|
||||||
ob_start();
|
ob_start();
|
||||||
exportHeader(translate('Event').' '.$event->Id());
|
exportHeader(translate('Event').' '.$event->Id());
|
||||||
|
|
||||||
$otherlinks = '';
|
$otherlinks = '';
|
||||||
if( $exportFrames ) $otherlinks .= '<a href="zmEventFrames.html">'.translate('Frames').'</a>,';
|
if ( $exportFrames ) $otherlinks .= ' <a href="zmEventFrames.html">'.translate('Frames').'</a>,';
|
||||||
if( $exportImages ) $otherlinks .= '<a href="zmEventImages.html">'.translate('Images').'</a>,';
|
if ( $exportImages ) $otherlinks .= ' <a href="zmEventImages.html">'.translate('Images').'</a>,';
|
||||||
$otherlinks = substr($otherlinks,0,-1);
|
$otherlinks = substr($otherlinks, 0, -1);
|
||||||
?>
|
?>
|
||||||
<body>
|
<body>
|
||||||
<div id="page">
|
<div id="page">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<h2><?php echo translate('Event') ?>: <?php echo validHtmlStr($event->Name()) ?><?php if(!empty($otherlinks)) { ?> (<?php echo$otherlinks?>) <?php } ?></h2>
|
<h2><?php echo translate('Event') .': '.validHtmlStr($event->Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.') ' : '' ) ?></h2>
|
||||||
<table id="eventDetail">
|
<table id="eventDetail">
|
||||||
<tr><th scope="row"><?php echo translate('Id') ?></th><td><?php echo $event->Id() ?></td></tr>
|
<tr><th scope="row"><?php echo translate('Id') ?></th><td><?php echo $event->Id() ?></td></tr>
|
||||||
<tr><th scope="row"><?php echo translate('Name') ?></th><td><?php echo validHtmlStr($event->Name()) ?></td></tr>
|
<tr><th scope="row"><?php echo translate('Name') ?></th><td><?php echo validHtmlStr($event->Name()) ?></td></tr>
|
||||||
|
@ -134,21 +100,21 @@ function exportEventDetail($event, $exportFrames, $exportImages) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportEventFrames($event, $exportDetail, $exportImages) {
|
function exportEventFrames($event, $exportDetail, $exportImages) {
|
||||||
$sql = 'SELECT *, unix_timestamp( TimeStamp ) AS UnixTimeStamp FROM Frames WHERE EventID = ? ORDER BY FrameId';
|
$sql = 'SELECT *, unix_timestamp(TimeStamp) AS UnixTimeStamp FROM Frames WHERE EventID = ? ORDER BY FrameId';
|
||||||
$frames = dbFetchAll($sql, NULL, array($event->Id()));
|
$frames = dbFetchAll($sql, NULL, array($event->Id()));
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
exportHeader(translate('Frames').' '.$event->Id());
|
exportHeader(translate('Frames').' '.$event->Id());
|
||||||
|
|
||||||
$otherlinks = '';
|
$otherlinks = '';
|
||||||
if ( $exportDetail ) $otherlinks .= '<a href="zmEventDetail.html">'.translate('Event').'</a>,';
|
if ( $exportDetail ) $otherlinks .= ' <a href="zmEventDetail.html">'.translate('Event').'</a>,';
|
||||||
if ( $exportImages ) $otherlinks .= '<a href="zmEventImages.html">'.translate('Images').'</a>,';
|
if ( $exportImages ) $otherlinks .= ' <a href="zmEventImages.html">'.translate('Images').'</a>,';
|
||||||
$otherlinks = substr($otherlinks,0,-1);
|
$otherlinks = substr($otherlinks, 0, -1);
|
||||||
?>
|
?>
|
||||||
<body>
|
<body>
|
||||||
<div id="page">
|
<div id="page">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<h2><?php echo translate('Frames') ?>: <?php echo validHtmlStr($event->Name()) ?><?php if(!empty($otherlinks)) { ?> (<?php echo$otherlinks?>) <?php } ?></h2>
|
<h2><?php echo translate('Frames').': '.validHtmlStr($event->Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.')':'') ?></h2>
|
||||||
<table id="eventFrames">
|
<table id="eventFrames">
|
||||||
<tr>
|
<tr>
|
||||||
<th><?php echo translate('FrameId') ?></th>
|
<th><?php echo translate('FrameId') ?></th>
|
||||||
|
@ -208,26 +174,45 @@ function exportEventFrames($event, $exportDetail, $exportImages) {
|
||||||
</html>
|
</html>
|
||||||
<?php
|
<?php
|
||||||
return ob_get_clean();
|
return ob_get_clean();
|
||||||
}
|
} // end function exportEventFrames($event, $exportDetail, $exportImages)
|
||||||
|
|
||||||
function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) {
|
function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) {
|
||||||
ob_start();
|
ob_start();
|
||||||
exportHeader(translate('Images').' '.$event->Id());
|
exportHeader(translate('Images').' '.$event->Id());
|
||||||
|
|
||||||
$otherlinks = '';
|
$otherlinks = '';
|
||||||
if( $exportDetail ) $otherlinks .= '<a href="zmEventDetail.html">'.translate('Event').'</a>,';
|
if ( $exportDetail ) $otherlinks .= ' <a href="zmEventDetail.html">'.translate('Event').'</a>,';
|
||||||
if( $exportFrames ) $otherlinks .= '<a href="zmEventFrames.html">'.translate('Frames').'</a>,';
|
if ( $exportFrames ) $otherlinks .= ' <a href="zmEventFrames.html">'.translate('Frames').'</a>,';
|
||||||
$otherlinks = substr($otherlinks,0,-1);
|
$otherlinks = substr($otherlinks, 0, -1);
|
||||||
|
|
||||||
$filelist = array_keys($myfilelist);
|
$filelist = array_keys($myfilelist);
|
||||||
sort($filelist,SORT_NUMERIC);
|
sort($filelist, SORT_NUMERIC);
|
||||||
$slides = '"'.implode('","',$filelist).'"';
|
$slides = '"'.implode('","',$filelist).'"';
|
||||||
$listcount = count($filelist);
|
$listcount = count($filelist);
|
||||||
?>
|
?>
|
||||||
<body>
|
<body>
|
||||||
<style>
|
<style>
|
||||||
.horizontal_track {background-color: #bbb;width: <?php echo $event->Width()?>px;line-height: 0px;font-size: 0px;text-align: left;padding: 4px;border: 1px solid;border-color: #ddd #999 #999 #ddd;}
|
.horizontal_track {
|
||||||
.horizontal_slider {background-color: #666;width: 16px;height: 8px;position: relative;z-index: 2;line-height: 0;margin: 0;border: 2px solid;border-color: #999 #333 #333 #999;}
|
background-color: #bbb;
|
||||||
|
width: <?php echo $event->Width()?>px;
|
||||||
|
line-height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
text-align: left;
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: #ddd #999 #999 #ddd;
|
||||||
|
}
|
||||||
|
.horizontal_slider {
|
||||||
|
background-color: #666;
|
||||||
|
width: 16px;
|
||||||
|
height: 8px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
line-height: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 2px solid;
|
||||||
|
border-color: #999 #333 #333 #999;
|
||||||
|
}
|
||||||
.horizontal_slit {background-color: #333;width: <?php echo($event->Width()-10)?>px;height: 2px;margin: 4px 4px 2px 4px;line-height: 0;position: absolute;z-index: 1;border: 1px solid;border-color: #999 #ddd #ddd #999;}
|
.horizontal_slit {background-color: #333;width: <?php echo($event->Width()-10)?>px;height: 2px;margin: 4px 4px 2px 4px;line-height: 0;position: absolute;z-index: 1;border: 1px solid;border-color: #999 #ddd #ddd #999;}
|
||||||
.vertical_track {background-color: #bbb;padding: 3px 5px 15px 5px;border: 1px solid;border-color: #ddd #999 #999 #ddd;}
|
.vertical_track {background-color: #bbb;padding: 3px 5px 15px 5px;border: 1px solid;border-color: #ddd #999 #999 #ddd;}
|
||||||
.vertical_slider {background-color: #666;width: 18px;height: 8px;font: 0px;text-align: left;line-height: 0px;position: relative;z-index: 1;border: 2px solid;border-color: #999 #333 #333 #999;}
|
.vertical_slider {background-color: #666;width: 18px;height: 8px;font: 0px;text-align: left;line-height: 0px;position: relative;z-index: 1;border: 2px solid;border-color: #999 #333 #333 #999;}
|
||||||
|
@ -236,7 +221,7 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) {
|
||||||
.value_display {background-color: #bbb;color: #333;width: 30px;margin: 0 2px;text-align: right;font-size: 8pt;font-face: verdana, arial, helvetica, sans-serif;font-weight: bold;line-height: 12px;border: 0;cursor: default;}
|
.value_display {background-color: #bbb;color: #333;width: 30px;margin: 0 2px;text-align: right;font-size: 8pt;font-face: verdana, arial, helvetica, sans-serif;font-weight: bold;line-height: 12px;border: 0;cursor: default;}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h2><?php echo translate('Images') ?>: <?php echo validHtmlStr($event->Name()) ?><?php if(!empty($otherlinks)) { ?> (<?php echo$otherlinks?>) <?php } ?></h2>
|
<h2><?php echo translate('Images').': '.validHtmlStr($event->Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.') ' : '' ) ?></h2>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if ( $event->DefaultVideo() ) {
|
if ( $event->DefaultVideo() ) {
|
||||||
|
@ -247,12 +232,12 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) {
|
||||||
if ( $Monitor->VideoWriter() == '2' ) {
|
if ( $Monitor->VideoWriter() == '2' ) {
|
||||||
# Passthrough
|
# Passthrough
|
||||||
$Rotation = $event->Orientation();
|
$Rotation = $event->Orientation();
|
||||||
if ( in_array($event->Orientation(),array('ROTATE_90','ROTATE_270')) )
|
if ( in_array($event->Orientation(), array('ROTATE_90','ROTATE_270')) )
|
||||||
$Zoom = $event->Height()/$event->Width();
|
$Zoom = $event->Height()/$event->Width();
|
||||||
}
|
} # end if passthrough
|
||||||
?>
|
?>
|
||||||
<div id="videoFeed">
|
<div id="videoFeed">
|
||||||
<video id="videoobj" class="video-js vjs-default-skin" style="transform: matrix(1, 0, 0, 1, 0, 0)"
|
<video id="videoobj" class="video-js vjs-default-skin" style="transform: matrix(1, 0, 0, 1, 0, 0);"
|
||||||
width="<?php echo $event->Width() ?>"
|
width="<?php echo $event->Width() ?>"
|
||||||
height="<?php echo $event->Height() ?>"
|
height="<?php echo $event->Height() ?>"
|
||||||
data-setup='{ "controls": true, "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
|
data-setup='{ "controls": true, "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
|
||||||
|
@ -582,36 +567,45 @@ else if (document.layers) window.onload=start_slider;
|
||||||
</html>
|
</html>
|
||||||
<?php
|
<?php
|
||||||
return ob_get_clean();
|
return ob_get_clean();
|
||||||
}
|
} # end function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist)
|
||||||
|
|
||||||
function eventlist_html($Event) {
|
function eventlist_html($Event, $exportDetail, $exportFrames) {
|
||||||
?>
|
$html = '<div class="event">
|
||||||
<div class="event">
|
';
|
||||||
<?php
|
|
||||||
if ( $Event->SaveJPEGs() ) {
|
if ( $Event->SaveJPEGs() ) {
|
||||||
?>
|
$html .= '<a href="#" onclick="switchevent(\''.$Event->Id().'/zmEventImages.html\');return false;">
|
||||||
<a href="#" onclick="switchevent('<?php echo $Event->Id(); ?>/zmEventImages.html');return false;">
|
';
|
||||||
<?php if ( ZM_WEB_LIST_THUMBS ) { ?>
|
if ( ZM_WEB_LIST_THUMBS ) {
|
||||||
<img width="<?php echo ZM_WEB_LIST_THUMB_WIDTH ?>" src="<?php echo $Event->Id(); ?>/snapshot.jpg" alt="<?php echo $Event->Id()?>"/>
|
$html .= '<img width="'.ZM_WEB_LIST_THUMB_WIDTH.'" src="'. $Event->Id().'/snapshot.jpg" alt="'.$Event->Id().'"/>
|
||||||
<?php } else { echo $Event->Id(); } ?>
|
';
|
||||||
</a>
|
} else {
|
||||||
<?php
|
$html .= $Event->Id();
|
||||||
|
}
|
||||||
|
$html .= '</a><br/>
|
||||||
|
';
|
||||||
} # end if has jpegs
|
} # end if has jpegs
|
||||||
if ( $Event->DefaultVideo() ) {
|
if ( $Event->DefaultVideo() ) {
|
||||||
if ( ZM_WEB_LIST_THUMBS ) {
|
if ( ZM_WEB_LIST_THUMBS ) {
|
||||||
?>
|
$html .= '<a href="'.$Event->Id().'/'.$Event->DefaultVideo() .'">';
|
||||||
<a href="<?php echo $Event->Id().'/'.$Event->DefaultVideo() ?>">
|
$html .= '<img width="'.ZM_WEB_LIST_THUMB_WIDTH.'" src="'. $Event->Id().'/snapshot.jpg" alt="'.$Event->Id().'"/>';
|
||||||
<img width="<?php echo ZM_WEB_LIST_THUMB_WIDTH ?>" src="<?php echo $Event->Id(); ?>/snapshot.jpg" alt="<?php echo $Event->Id()?>"/>
|
$html .= '</a><br/>
|
||||||
</a>
|
';
|
||||||
<?php
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
if ( $exportDetail ) {
|
||||||
</div><!--event-->
|
$html .= '<a href="#" onclick="switchevent(\''.$Event->Id().'/zmEventDetail.html\');return false;">Detail</a>
|
||||||
<?php
|
';
|
||||||
}
|
}
|
||||||
|
if ( $exportFrames ) {
|
||||||
|
$html .= '<a href="#" onclick="switchevent(\''.$Event->Id().'/zmEventFrames.html\');return false;">Frames</a>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
$html .= '</div><!--event-->
|
||||||
|
';
|
||||||
|
return $html;
|
||||||
|
} // end function eventlist_html
|
||||||
|
|
||||||
function exportEventImagesMaster($eids) {
|
function exportEventImagesMaster($eids, $exportDetail, $exportFrames) {
|
||||||
ob_start();
|
ob_start();
|
||||||
exportHeader(translate('Images').' Master');
|
exportHeader(translate('Images').' Master');
|
||||||
?>
|
?>
|
||||||
|
@ -620,10 +614,10 @@ function exportEventImagesMaster($eids) {
|
||||||
<?php
|
<?php
|
||||||
$events = ZM\Event::find(array('Id'=>$eids));
|
$events = ZM\Event::find(array('Id'=>$eids));
|
||||||
|
|
||||||
foreach ($events as $event) {
|
foreach ( $events as $event ) {
|
||||||
//get monitor id and event id
|
//get monitor id and event id
|
||||||
$eventMonitorId[$eid] = $event->MonitorId();
|
$eventMonitorId[$event->Id()] = $event->MonitorId();
|
||||||
$eventPath[$eid] = $event->Relative_Path();
|
$eventPath[$event->Id()] = $event->Relative_Path();
|
||||||
}
|
}
|
||||||
|
|
||||||
$monitors = array_values(array_flip(array_flip($eventMonitorId))); //unique monitors and reindex the array
|
$monitors = array_values(array_flip(array_flip($eventMonitorId))); //unique monitors and reindex the array
|
||||||
|
@ -631,11 +625,10 @@ function exportEventImagesMaster($eids) {
|
||||||
|
|
||||||
//*
|
//*
|
||||||
if ( !empty($monitors) ) {
|
if ( !empty($monitors) ) {
|
||||||
$tmp = dbFetchAll('SELECT Id,Name FROM Monitors WHERE Id IN ('.implode(',', $monitors).') ');
|
$tmp = dbFetchAll('SELECT Id, Name FROM Monitors WHERE Id IN ('.implode(',', $monitors).') ');
|
||||||
foreach ( $tmp as $row ) { $monitorNames[$row['Id']] = $row['Name']; }
|
foreach ( $tmp as $row ) { $monitorNames[$row['Id']] = $row['Name']; }
|
||||||
}
|
}
|
||||||
//*/
|
//*/
|
||||||
//trigger_error(print_r($monitorNames,1));
|
|
||||||
?>
|
?>
|
||||||
<div id="tabs">
|
<div id="tabs">
|
||||||
<ul class="tabs">
|
<ul class="tabs">
|
||||||
|
@ -647,31 +640,32 @@ function exportEventImagesMaster($eids) {
|
||||||
?>
|
?>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<table width="100%" height="100%">
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td valign="top" bgcolor="#dddddd" style="padding:10px;">
|
<td valign="top" bgcolor="#dddddd" style="padding:10px;">
|
||||||
<div class="tab_content" id="all">
|
<div class="tab_content" id="all">
|
||||||
<h2> All </h2>
|
<h2> All </h2>
|
||||||
<?php
|
<?php
|
||||||
foreach($events as $event) {
|
foreach($events as $event) {
|
||||||
eventlist_html($event);
|
echo eventlist_html($event, $exportDetail, $exportFrames);
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
foreach ($monitors as $monitor_id) {
|
foreach ( $monitors as $monitor_id ) {
|
||||||
echo "<div class=\"tab_content\" id=\"tab$monitor_id\">";
|
echo '<div class="tab_content" id="tab'.$monitor_id.'">';
|
||||||
echo '<h2>Monitor: ' . $monitorNames[$monitor_id] . ' </h2>';
|
echo '<h2>Monitor: '.$monitorNames[$monitor_id].'</h2>';
|
||||||
foreach ( $events as $event ) {
|
foreach ( $events as $event ) {
|
||||||
if ( $event->MonitorId() == $monitor_id ) {
|
if ( $event->MonitorId() == $monitor_id ) {
|
||||||
eventlist_html($event);
|
echo eventlist_html($event, $exportDetail, $exportFrames);
|
||||||
} # end if its the right monitor
|
} # end if its the right monitor
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
?>
|
?>
|
||||||
</td><td>
|
|
||||||
<iframe id="myframe" onload="resizeCaller();" name="myframe" src="#"
|
</td><td valign="top">
|
||||||
|
<iframe id="myframe" onload="resizeCaller();" name="myframe" src="about:blank"
|
||||||
scrolling="no" marginwidth="0" marginheight="0" frameborder="0"
|
scrolling="no" marginwidth="0" marginheight="0" frameborder="0"
|
||||||
vspace="0" hspace="0" style="overflow:visible; width:100%; display:none">
|
vspace="0" hspace="0" style="overflow:visible; width:100%; display:none">
|
||||||
</iframe>
|
</iframe>
|
||||||
|
@ -681,7 +675,7 @@ function exportEventImagesMaster($eids) {
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function switchevent(src) {
|
function switchevent(src) {
|
||||||
$('#myframe').attr('src',src);
|
$('#myframe').attr('src', src);
|
||||||
$('#myframe').show();
|
$('#myframe').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,20 +724,38 @@ function resizeIframe(frameid) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function readjustIframe(loadevt) {
|
function readjustIframe(loadevt) {
|
||||||
var crossevt = window.event ? event : loadevt;
|
var crossevt = window.event ? event : loadevt;
|
||||||
var iframeroot = crossevt.currentTarget ? crossevt.currentTarget : crossevt.srcElement;
|
var iframeroot = crossevt.currentTarget ? crossevt.currentTarget : crossevt.srcElement;
|
||||||
if (iframeroot) resizeIframe(iframeroot.id);
|
if (iframeroot) resizeIframe(iframeroot.id);
|
||||||
|
$('#myframe').load($('#myframe').src);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadintoIframe(iframeid, url) {
|
function loadintoIframe(iframeid, url) {
|
||||||
if (document.getElementById) document.getElementById(iframeid).src=url;
|
if (document.getElementById) document.getElementById(iframeid).src=url;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (window.addEventListener) window.addEventListener("load", resizeCaller, false)
|
/*==========[tab code]==========*/
|
||||||
//else if (window.attachEvent) window.attachEvent("onload", resizeCaller)
|
$(document).ready(function() {
|
||||||
//else window.onload=resizeCaller
|
|
||||||
|
//When page loads...
|
||||||
|
$(".tab_content").hide(); //Hide all content
|
||||||
|
$("ul.tabs li:first").addClass("active").show(); //Activate first tab
|
||||||
|
$(".tab_content:first").show(); //Show first tab content
|
||||||
|
|
||||||
|
//On Click Event
|
||||||
|
$("ul.tabs li").click(function() {
|
||||||
|
|
||||||
|
$("ul.tabs li").removeClass("active"); //Remove any "active" class
|
||||||
|
$(this).addClass("active"); //Add "active" class to selected tab
|
||||||
|
$(".tab_content").hide(); //Hide all tab content
|
||||||
|
|
||||||
|
var activeTab = $(this).find("a").attr("href"); //Find the href attribute value to identify the active tab + content
|
||||||
|
$(activeTab).fadeIn(); //Fade in the active ID content
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -751,7 +763,14 @@ function loadintoIframe(iframeid, url) {
|
||||||
return ob_get_clean();
|
return ob_get_clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc) {
|
function exportFileList(
|
||||||
|
$event,
|
||||||
|
$exportDetail,
|
||||||
|
$exportFrames,
|
||||||
|
$exportImages,
|
||||||
|
$exportVideo,
|
||||||
|
$exportMisc
|
||||||
|
) {
|
||||||
|
|
||||||
if ( !canView('Events') or !$event ) {
|
if ( !canView('Events') or !$event ) {
|
||||||
return;
|
return;
|
||||||
|
@ -773,28 +792,31 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex
|
||||||
|
|
||||||
if ( $exportDetail ) {
|
if ( $exportDetail ) {
|
||||||
$file = 'zmEventDetail.html';
|
$file = 'zmEventDetail.html';
|
||||||
if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) {
|
if ( $fp = fopen($eventPath.'/'.$file, 'w') ) {
|
||||||
ZM\Fatal("Can't open event detail export file '$file'");
|
|
||||||
}
|
|
||||||
fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages));
|
fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages));
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
$exportFileList[$file] = $file;
|
$exportFileList[$file] = $file;
|
||||||
|
} else {
|
||||||
|
ZM\Error("Can't open event detail export file '$file'");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( $exportFrames ) {
|
if ( $exportFrames ) {
|
||||||
$file = 'zmEventFrames.html';
|
$file = 'zmEventFrames.html';
|
||||||
if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) {
|
if ( $fp = fopen($eventPath.'/'.$file, 'w') ) {
|
||||||
ZM\Fatal("Can't open event frames export file '$file'");
|
|
||||||
}
|
|
||||||
fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages));
|
fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages));
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
$exportFileList[$file] = $file;
|
$exportFileList[$file] = $file;
|
||||||
|
} else {
|
||||||
|
ZM\Error("Can't open event frames export file '$file'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $exportImages ) {
|
if ( $exportImages ) {
|
||||||
$filesLeft = array();
|
$filesLeft = array();
|
||||||
$myfilelist = array();
|
$myfilelist = array();
|
||||||
foreach ( $files as $file ) {
|
foreach ( $files as $file ) {
|
||||||
if ( preg_match('/-(?:capture|analyse).jpg$/', $file ) ) {
|
if ( preg_match('/-(?:capture|analyse).jpg$/', $file) ) {
|
||||||
$myfilelist[$file] = $exportFileList[$file] = $file;
|
$myfilelist[$file] = $exportFileList[$file] = $file;
|
||||||
} else {
|
} else {
|
||||||
$filesLeft[$file] = $file;
|
$filesLeft[$file] = $file;
|
||||||
|
@ -805,11 +827,13 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex
|
||||||
// create an image slider
|
// create an image slider
|
||||||
if ( !empty($myfilelist) ) {
|
if ( !empty($myfilelist) ) {
|
||||||
$file = 'zmEventImages.html';
|
$file = 'zmEventImages.html';
|
||||||
if ( !($fp = fopen($file, 'w')) )
|
if ( $fp = fopen($eventPath.'/'.$file, 'w') ) {
|
||||||
ZM\Fatal("Can't open event images export file '$file'");
|
|
||||||
fwrite($fp, exportEventImages($event, $exportDetail, $exportFrames, $myfilelist));
|
fwrite($fp, exportEventImages($event, $exportDetail, $exportFrames, $myfilelist));
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
$exportFileList[$file] = $file;
|
$exportFileList[$file] = $file;
|
||||||
|
} else {
|
||||||
|
ZM\Error("Can't open event images export file '$file'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} # end if exportImages
|
} # end if exportImages
|
||||||
|
|
||||||
|
@ -869,13 +893,14 @@ function exportEvents(
|
||||||
|
|
||||||
# Ensure that we are going to be able to do this.
|
# Ensure that we are going to be able to do this.
|
||||||
if ( ! ( mkdir($export_dir) or file_exists($export_dir) ) ) {
|
if ( ! ( mkdir($export_dir) or file_exists($export_dir) ) ) {
|
||||||
ZM\Fatal("Can't create exports dir at '$export_dir'");
|
ZM\Error("Can't create exports dir at '$export_dir'");
|
||||||
} else {
|
return false;
|
||||||
ZM\Logger::Debug("Successfully created dir '$export_dir'");
|
|
||||||
}
|
}
|
||||||
|
ZM\Logger::Debug("Successfully created dir '$export_dir'");
|
||||||
chmod($export_dir, 0700);
|
chmod($export_dir, 0700);
|
||||||
if ( !chdir($export_dir) ) {
|
if ( !chdir($export_dir) ) {
|
||||||
ZM\Fatal("Can't chdir to $export_dir");
|
ZM\Error("Can't chdir to $export_dir");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$export_root = 'zmExport';
|
$export_root = 'zmExport';
|
||||||
|
@ -886,44 +911,38 @@ function exportEvents(
|
||||||
if ( !is_array($eids) ) {
|
if ( !is_array($eids) ) {
|
||||||
$eids = array($eids);
|
$eids = array($eids);
|
||||||
}
|
}
|
||||||
ZM\Logger::Debug('Eids: ' . print_r($eids,true));
|
|
||||||
foreach ( $eids as $eid ) {
|
foreach ( $eids as $eid ) {
|
||||||
$event = new ZM\Event($eid);
|
$event = new ZM\Event($eid);
|
||||||
$event_dir = $export_dir.'/'.$event->Id();
|
$event_dir = $export_dir.'/'.$event->Id();
|
||||||
if ( !(mkdir($event_dir) or file_exists($event_dir) ) ) {
|
if ( !(mkdir($event_dir) or file_exists($event_dir)) ) {
|
||||||
ZM\Error("Can't mkdir $event_dir");
|
ZM\Error("Can't mkdir $event_dir");
|
||||||
}
|
}
|
||||||
$event_exportFileList = exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc);
|
$event_exportFileList = exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc);
|
||||||
ZM\Logger::Debug("File list for event $eid " . print_r($event_exportFileList, true));
|
$exportFileList = array_merge($exportFileList, $event_exportFileList);
|
||||||
$exportFileList = array_merge($exportFileList,$event_exportFileList);
|
|
||||||
foreach ( $event_exportFileList as $file ) {
|
foreach ( $event_exportFileList as $file ) {
|
||||||
if ( preg_match('/\.html$/', $file) )
|
#if ( preg_match('/\.html$/', $file) )
|
||||||
continue;
|
#continue;
|
||||||
#exec('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file, $output, $return);
|
|
||||||
$cmd = 'cp -as '.$event->Path().'/'.$file.' '.$export_dir.'/'.$event->Id().'/'.$file. ' 2>&1';
|
$cmd = 'cp -as '.$event->Path().'/'.$file.' '.$export_dir.'/'.$event->Id().'/'.$file. ' 2>&1';
|
||||||
exec($cmd, $output, $return);
|
exec($cmd, $output, $return);
|
||||||
ZM\Logger::Debug($cmd.' return code: '.$return.' output: '.print_r($output,true));
|
ZM\Logger::Debug($cmd.' return code: '.$return.' output: '.print_r($output,true));
|
||||||
}
|
} # end foreach event_exportFile
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
|
|
||||||
// create an master image
|
|
||||||
if ( $exportImages ) {
|
|
||||||
if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/jquery.js') )
|
if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/jquery.js') )
|
||||||
ZM\Error('Failed linking jquery.js');
|
ZM\Error('Failed linking jquery.js');
|
||||||
//if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/video.js', $export_dir.'/video.js') )
|
//if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/video.js', $export_dir.'/video.js') )
|
||||||
//Error("Failed linking video.js");
|
//Error("Failed linking video.js");
|
||||||
|
|
||||||
$html_eventMaster_file = 'zmEventImagesMaster_'.date('Ymd_His'). '.html';
|
$html_eventMaster_file = 'zmEventImagesMaster.html';
|
||||||
$html_eventMaster_path = $export_dir.'/'.$html_eventMaster_file;
|
$html_eventMaster_path = $export_dir.'/'.$html_eventMaster_file;
|
||||||
|
|
||||||
if ( ($fp = fopen($html_eventMaster_path, 'w')) ) {
|
if ( ($fp = fopen($html_eventMaster_path, 'w')) ) {
|
||||||
fwrite($fp, exportEventImagesMaster($eids));
|
fwrite($fp, exportEventImagesMaster($eids, $exportDetail, $exportFrames));
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
$exportFileList[] = $html_eventMaster_file;
|
$exportFileList[] = $html_eventMaster_file;
|
||||||
} else {
|
} else {
|
||||||
ZM\Error("Can't open event images export file '$html_eventMaster_path'");
|
ZM\Error("Can't open event images export file '$html_eventMaster_path'");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$listFile = $export_dir.'/'.$export_listFile;
|
$listFile = $export_dir.'/'.$export_listFile;
|
||||||
if ( !($fp = fopen($listFile, 'w')) ) {
|
if ( !($fp = fopen($listFile, 'w')) ) {
|
||||||
|
@ -949,10 +968,10 @@ function exportEvents(
|
||||||
$exportFormat .= '.gz';
|
$exportFormat .= '.gz';
|
||||||
}
|
}
|
||||||
if ( $exportStructure == 'flat' ) {
|
if ( $exportStructure == 'flat' ) {
|
||||||
if (preg_match("/BSD/i", $version)) {
|
if ( preg_match('/BSD/i', $version) ) {
|
||||||
$command .= " -s '#^.*/##'";
|
$command .= ' -s \'#^.*/##\'';
|
||||||
} else {
|
} else {
|
||||||
$command .= " --xform='s#^.+/##x'";
|
$command .= ' --xform=\'s#^.+/##x\'';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$command .= ' --file='.escapeshellarg($archive);
|
$command .= ' --file='.escapeshellarg($archive);
|
||||||
|
@ -974,10 +993,10 @@ function exportEvents(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//clean up temporary files
|
// clean up temporary files
|
||||||
if ( !empty($html_eventMaster) ) {
|
if ( !empty($html_eventMaster) ) {
|
||||||
unlink($monitorPath.'/'.$html_eventMaster);
|
unlink($monitorPath.'/'.$html_eventMaster);
|
||||||
}
|
}
|
||||||
|
|
||||||
return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey;
|
return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey;
|
||||||
}
|
} // end function exportEvents
|
||||||
|
|
|
@ -244,8 +244,6 @@ while ( $event_row = dbFetchNext($results) ) {
|
||||||
</div><!--row-->
|
</div><!--row-->
|
||||||
<button type="button" id="exportButton" name="exportButton" value="Export" disabled="disabled"><?php echo translate('Export') ?></button>
|
<button type="button" id="exportButton" name="exportButton" value="Export" disabled="disabled"><?php echo translate('Export') ?></button>
|
||||||
</div><!--container-->
|
</div><!--container-->
|
||||||
<?php
|
|
||||||
?>
|
|
||||||
<h2 id="exportProgress" class="<?php
|
<h2 id="exportProgress" class="<?php
|
||||||
if ( isset($_REQUEST['generated']) ) {
|
if ( isset($_REQUEST['generated']) ) {
|
||||||
if ( $_REQUEST['generated'] )
|
if ( $_REQUEST['generated'] )
|
||||||
|
@ -255,7 +253,7 @@ while ( $event_row = dbFetchNext($results) ) {
|
||||||
} else {
|
} else {
|
||||||
echo 'hidden warnText';
|
echo 'hidden warnText';
|
||||||
}
|
}
|
||||||
?>">
|
?>">
|
||||||
<span id="exportProgressText">
|
<span id="exportProgressText">
|
||||||
<?php
|
<?php
|
||||||
if ( isset($_REQUEST['generated']) ) {
|
if ( isset($_REQUEST['generated']) ) {
|
||||||
|
@ -267,7 +265,7 @@ while ( $event_row = dbFetchNext($results) ) {
|
||||||
?></span>
|
?></span>
|
||||||
<span id="exportProgressTicker"></span>
|
<span id="exportProgressTicker"></span>
|
||||||
</h2>
|
</h2>
|
||||||
<button type="button" data-on-click-this="startDownload" <?php echo empty($_REQUEST['generated'])? ' class="hidden"' : '' ?>><?php echo translate('Download') ?></button>
|
<button type="button" data-on-click-this="startDownload"<?php echo empty($_REQUEST['generated'])? ' class="hidden"' : '' ?>><?php echo translate('Download') ?></button>
|
||||||
<input type="hidden" name="exportFile" value="<?php echo isset($_REQUEST['exportFile']) ? validHtmlStr($_REQUEST['exportFile']) : '' ?>"/>
|
<input type="hidden" name="exportFile" value="<?php echo isset($_REQUEST['exportFile']) ? validHtmlStr($_REQUEST['exportFile']) : '' ?>"/>
|
||||||
<input type="hidden" name="generated" value="<?php echo isset($_REQUEST['generated']) ? validHtmlStr($_REQUEST['generated']) : '' ?>"/>
|
<input type="hidden" name="generated" value="<?php echo isset($_REQUEST['generated']) ? validHtmlStr($_REQUEST['generated']) : '' ?>"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -69,7 +69,7 @@ function group_line( $Group ) {
|
||||||
$html .= str_repeat('<td class="colName"> </td>', $Group->depth());
|
$html .= str_repeat('<td class="colName"> </td>', $Group->depth());
|
||||||
$html .= '<td class="colName" colspan="'.($max_depth-($Group->depth()-1)).'">';
|
$html .= '<td class="colName" colspan="'.($max_depth-($Group->depth()-1)).'">';
|
||||||
if ( canEdit('Groups') ) {
|
if ( canEdit('Groups') ) {
|
||||||
$html .= '<a href="#" onclick="editGroup('.$Group->Id().');">'. validHtmlStr($Group->Id() . ' ' . $Group->Name()).'</a>';
|
$html .= '<a href="#" data-on-click-this="editGroup" data-group-id="'.$Group->Id().'">'.validHtmlStr($Group->Id().' '.$Group->Name()).'</a>';
|
||||||
} else {
|
} else {
|
||||||
$html .= validHtmlStr($Group->Name());
|
$html .= validHtmlStr($Group->Name());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
var consoleRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_MAIN ?>;
|
var consoleRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_MAIN ?>;
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if ( ZM_CHECK_FOR_UPDATES && canEdit('System') && ZM_DYN_LAST_VERSION && ( verNum(ZM_VERSION) < verNum(ZM_DYN_LAST_VERSION) ) && ( verNum(ZM_DYN_CURR_VERSION) < verNum(ZM_DYN_LAST_VERSION) ) && ( ZM_DYN_NEXT_REMINDER < time() ) ) {
|
if ( canEdit('System') ) {
|
||||||
|
if ( ZM_CHECK_FOR_UPDATES && ZM_DYN_LAST_VERSION && ( verNum(ZM_VERSION) < verNum(ZM_DYN_LAST_VERSION) ) && ( verNum(ZM_DYN_CURR_VERSION) < verNum(ZM_DYN_LAST_VERSION) ) && ( ZM_DYN_NEXT_REMINDER < time() ) ) {
|
||||||
$showVersionPopup = true;
|
$showVersionPopup = true;
|
||||||
} elseif ( ZM_DYN_SHOW_DONATE_REMINDER ) {
|
$nextReminder = time() + 60*60;
|
||||||
if ( canEdit('System') ) {
|
// limit popups to one per hour instead of on every console refresh.
|
||||||
|
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'");
|
||||||
|
|
||||||
|
} else if ( ZM_DYN_SHOW_DONATE_REMINDER ) {
|
||||||
if ( ZM_DYN_DONATE_REMINDER_TIME > 0 ) {
|
if ( ZM_DYN_DONATE_REMINDER_TIME > 0 ) {
|
||||||
if ( ZM_DYN_DONATE_REMINDER_TIME < time() ) {
|
if ( ZM_DYN_DONATE_REMINDER_TIME < time() ) {
|
||||||
$showDonatePopup = true;
|
$showDonatePopup = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$nextReminder = time() + 30*24*60*60;
|
$nextReminder = time() + 30*24*60*60;
|
||||||
dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" );
|
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end if canEdit('System')
|
||||||
?>
|
?>
|
||||||
var showVersionPopup = <?php echo isset($showVersionPopup )?'true':'false' ?>;
|
var showVersionPopup = <?php echo isset($showVersionPopup )?'true':'false' ?>;
|
||||||
var showDonatePopup = <?php echo isset($showDonatePopup )?'true':'false' ?>;
|
var showDonatePopup = <?php echo isset($showDonatePopup )?'true':'false' ?>;
|
||||||
|
|
|
@ -65,7 +65,7 @@ function exportResponse(respObj, respText) {
|
||||||
//window.location.replace( thisUrl+'?view='+currentView+'&'+eids.join('&')+'&exportFile='+respObj.exportFile+'&generated='+((respObj.result=='Ok')?1:0) );
|
//window.location.replace( thisUrl+'?view='+currentView+'&'+eids.join('&')+'&exportFile='+respObj.exportFile+'&generated='+((respObj.result=='Ok')?1:0) );
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportEvent( ) {
|
function exportEvents( ) {
|
||||||
var parms = 'view=event&request=event&action=export';
|
var parms = 'view=event&request=event&action=export';
|
||||||
parms += '&'+$('contentForm').toQueryString();
|
parms += '&'+$('contentForm').toQueryString();
|
||||||
var query = new Request.JSON( {
|
var query = new Request.JSON( {
|
||||||
|
@ -87,7 +87,7 @@ function initPage() {
|
||||||
if ( exportReady ) {
|
if ( exportReady ) {
|
||||||
startDownload.pass(exportFile).delay(1500);
|
startDownload.pass(exportFile).delay(1500);
|
||||||
}
|
}
|
||||||
document.getElementById('exportButton').addEventListener('click', exportEvent);
|
document.getElementById('exportButton').addEventListener('click', exportEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', initPage);
|
window.addEventListener('DOMContentLoaded', initPage);
|
||||||
|
|
|
@ -8,8 +8,13 @@ function setGroup( element ) {
|
||||||
form.submit();
|
form.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function editGroup( gid ) {
|
function editGroup( element ) {
|
||||||
createPopup( '?view=group&gid='+gid, 'zmGroup', 'group' );
|
var gid = element.getAttribute('data-group-id');
|
||||||
|
if ( !gid ) {
|
||||||
|
console.log('No group id found in editGroup');
|
||||||
|
} else {
|
||||||
|
createPopup('?view=group&gid='+gid, 'zmGroup'+gid, 'group');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteGroup( element ) {
|
function deleteGroup( element ) {
|
||||||
|
|
|
@ -175,7 +175,7 @@ function clearLog() {
|
||||||
var clearReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse});
|
var clearReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse});
|
||||||
var tbody = $(logTable).getElement('tbody');
|
var tbody = $(logTable).getElement('tbody');
|
||||||
var rows = tbody.getElements('tr');
|
var rows = tbody.getElements('tr');
|
||||||
if ( rows ) {
|
if ( rows && rows.length ) {
|
||||||
var minTime = rows[0].getElement('td').get('text');
|
var minTime = rows[0].getElement('td').get('text');
|
||||||
clearParms += "&minTime="+encodeURIComponent(minTime);
|
clearParms += "&minTime="+encodeURIComponent(minTime);
|
||||||
var maxTime = rows[rows.length-1].getElement('td').get('text');
|
var maxTime = rows[rows.length-1].getElement('td').get('text');
|
||||||
|
|
|
@ -173,19 +173,19 @@ if ( $showZones ) {
|
||||||
|
|
||||||
<span id="widthControl">
|
<span id="widthControl">
|
||||||
<label><?php echo translate('Width') ?></label>
|
<label><?php echo translate('Width') ?></label>
|
||||||
<?php echo htmlSelect('width', $widths, $options['width'], 'changeSize(this);'); ?>
|
<?php echo htmlSelect('width', $widths, $options['width'], array('data-on-change-this'=>'changeSize')); ?>
|
||||||
</span>
|
</span>
|
||||||
<span id="heightControl">
|
<span id="heightControl">
|
||||||
<label><?php echo translate('Height') ?></label>
|
<label><?php echo translate('Height') ?></label>
|
||||||
<?php echo htmlSelect('height', $heights, $options['height'], 'changeSize(this);'); ?>
|
<?php echo htmlSelect('height', $heights, $options['height'], array('data-on-change-this'=>'changeSize')); ?>
|
||||||
</span>
|
</span>
|
||||||
<span id="scaleControl">
|
<span id="scaleControl">
|
||||||
<label><?php echo translate('Scale') ?></label>
|
<label><?php echo translate('Scale') ?></label>
|
||||||
<?php echo htmlSelect('scale', $scales, $scale, 'changeScale(this);'); ?>
|
<?php echo htmlSelect('scale', $scales, $scale, array('data-on-change-this'=>'changeScale')); ?>
|
||||||
</span>
|
</span>
|
||||||
<span id="layoutControl">
|
<span id="layoutControl">
|
||||||
<label for="layout"><?php echo translate('Layout') ?></label>
|
<label for="layout"><?php echo translate('Layout') ?></label>
|
||||||
<?php echo htmlSelect('zmMontageLayout', $layoutsById, $layout_id, array('onchange'=>'selectLayout(this);')); ?>
|
<?php echo htmlSelect('zmMontageLayout', $layoutsById, $layout_id, array('data-on-change-this'=>'selectLayout')); ?>
|
||||||
</span>
|
</span>
|
||||||
<input type="hidden" name="Positions"/>
|
<input type="hidden" name="Positions"/>
|
||||||
<button type="button" id="EditLayout" data-on-click-this="edit_layout"><?php echo translate('EditLayout') ?></button>
|
<button type="button" id="EditLayout" data-on-click-this="edit_layout"><?php echo translate('EditLayout') ?></button>
|
||||||
|
|
|
@ -33,6 +33,7 @@ if ( verNum(ZM_DYN_CURR_VERSION) != verNum(ZM_DYN_LAST_VERSION) and canEdit('Sys
|
||||||
'hour' => translate('VersionRemindHour'),
|
'hour' => translate('VersionRemindHour'),
|
||||||
'day' => translate('VersionRemindDay'),
|
'day' => translate('VersionRemindDay'),
|
||||||
'week' => translate('VersionRemindWeek'),
|
'week' => translate('VersionRemindWeek'),
|
||||||
|
'month' => translate('VersionRemindMonth'),
|
||||||
'never' => translate('VersionRemindNever')
|
'never' => translate('VersionRemindNever')
|
||||||
) );
|
) );
|
||||||
}
|
}
|
||||||
|
@ -64,16 +65,16 @@ if ( ZM_DYN_DB_VERSION && (ZM_DYN_DB_VERSION != ZM_VERSION) ) {
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<form name="contentForm" id="contentForm" method="get" action="?">
|
<form name="contentForm" id="contentForm" method="get" action="?">
|
||||||
<input type="hidden" name="view" value="none"/>
|
<input type="hidden" name="view" value="version"/>
|
||||||
<input type="hidden" name="action" value="version"/>
|
<input type="hidden" name="action" value="version"/>
|
||||||
<p><?php echo translate('UpdateAvailable') ?></p>
|
<p><?php echo translate('UpdateAvailable') ?></p>
|
||||||
<p><?php echo sprintf( $CLANG['LatestRelease'], ZM_DYN_LAST_VERSION, ZM_VERSION ) ?></p>
|
<p><?php echo sprintf( $CLANG['LatestRelease'], ZM_DYN_LAST_VERSION, ZM_VERSION ) ?></p>
|
||||||
<p><?php echo buildSelect( "option", $options ); ?></p>
|
<p><?php echo buildSelect('option', $options); ?></p>
|
||||||
<div id="contentButtons">
|
<div id="contentButtons">
|
||||||
<?php
|
<?php
|
||||||
if ( canEdit('System') ) {
|
if ( canEdit('System') ) {
|
||||||
?>
|
?>
|
||||||
<button type="submit" data-on-click-this="submitForm" value="Apply"><?php echo translate('Apply') ?></button>
|
<button type="submit"><?php echo translate('Apply') ?></button>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,13 @@ if ( $archivetype ) {
|
||||||
$filename_path = ZM_DIR_EXPORTS.'/'.$filename;
|
$filename_path = ZM_DIR_EXPORTS.'/'.$filename;
|
||||||
ZM\Logger::Debug("downloading archive from $filename_path");
|
ZM\Logger::Debug("downloading archive from $filename_path");
|
||||||
if ( is_readable($filename_path) ) {
|
if ( is_readable($filename_path) ) {
|
||||||
|
while (ob_get_level()) {
|
||||||
|
ZM\Logger::Debug('Clearing ob');
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
header("Content-type: application/$mimetype" );
|
header("Content-type: application/$mimetype" );
|
||||||
header("Content-Disposition: inline; filename=$filename");
|
header("Content-Disposition: inline; filename=$filename");
|
||||||
header('Content-Length: ' . filesize($filename_path) );
|
header('Content-Length: '.filesize($filename_path));
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
if ( ! @readfile($filename_path) ) {
|
if ( ! @readfile($filename_path) ) {
|
||||||
ZM\Error("Error sending $filename_path");
|
ZM\Error("Error sending $filename_path");
|
||||||
|
@ -61,11 +65,9 @@ if ( $archivetype ) {
|
||||||
ZM\Error("$filename_path does not exist or is not readable.");
|
ZM\Error("$filename_path does not exist or is not readable.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZM\Error("Unsupported archive type specified. Supported archives are tar and zip");
|
ZM\Error('Unsupported archive type specified. Supported archives are tar and zip');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZM\Error("No archive type given to archive.php. Please specify a tar or zip archive.");
|
ZM\Error('No archive type given to archive.php. Please specify a tar or zip archive.');
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue