May-12 API Update

This commit is contained in:
Pavel Dmitriev 2022-05-12 16:59:48 +03:00
parent a12a40c8b1
commit 797f5a4d8e
51 changed files with 274 additions and 160 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
/temp-test
/composer.lock
/vendor
.scannerwork

View File

@ -1,4 +1,4 @@
FROM mxfox.ru/chimera/fox-web-basic:latest AS prepare
FROM mxfox.ru/mxfox/fox-web-basic:latest AS prepare
RUN apt-get update \
&& apt-get install curl -y \
@ -31,6 +31,6 @@ RUN rm -f composer.* \
&& rm -rf docker-build
FROM mxfox.ru/chimera/fox-web-basic:latest as build
FROM mxfox.ru/mxfox/fox-web-basic:latest as build
COPY --from=prepare /var/www/html /var/www/html
COPY docker-build/rootfs /

View File

@ -105,7 +105,6 @@ try {
print(json_encode(["error"=>["errCode"=>500,"message"=>"Internal server error", "xCode"=>"ERR"]]));
header('HTTP/1.0 500 Internal server error', true, 500);
throw($e);
exit;
}
exit;

View File

@ -26,16 +26,14 @@ $m = fox\modules::list();
$initUser=empty(config::get("INIT_USERNAME"))?"admin":config::get("INIT_USERNAME");
$initPass=empty(config::get("INIT_PASSWORD"))?"chimera":config::get("INIT_PASSWORD");
//if (moduleInfo::getCount() ==0) {
// installing modules
foreach ($m as $mod) {
if (array_key_exists($mod->name, modules::pseudoModules) && !$mod->getInstances()) {
print "Install module ".$mod->name."...";
$mod->save();
print "OK\n";
}
// installing modules
foreach ($m as $mod) {
if (array_key_exists($mod->name, modules::pseudoModules) && !$mod->getInstances()) {
print "Install module ".$mod->name."...";
$mod->save();
print "OK\n";
}
//}
}
if (company::getCount()==0) {

56
cli/installPackages.php Normal file
View File

@ -0,0 +1,56 @@
<?php
use fox\modules;
use fox\config;
include(__DIR__."/../Autoloader.php");
if (strtolower(config::get("Environment"))=="development") {
print "Development mode. Package installation skipped.\n";
exit;
}
print "Packages installer started\n";
foreach (scandir(modules::packagesDir) as $file) {
if (!preg_match("/\.zip$/", $file)) {
continue;
}
if (! is_file(modules::packagesDir . "/" . $file)) {
continue;
}
// read config
$newModInfo=json_decode(file_get_contents("zip://".modules::packagesDir . "/" . $file."#module.json"));
// check if module present
$modInstalled=false;
$needUpdate=false;
if (file_exists(modules::modulesDir."/".$newModInfo->name) && file_exists(modules::modulesDir."/".$newModInfo->name."/module.json")) {
$exModInfo=json_decode(file_get_contents(modules::modulesDir."/".$newModInfo->name."/module.json"));
if (!empty($exModInfo)) {
$modInstalled=true;
$needUpdate=(property_exists($exModInfo, "hash") && $exModInfo->hash!=$newModInfo->hash);
}
}
print "Module ".$newModInfo->name.": ".($modInstalled?"Installed, ".($needUpdate?"Outdated":"Actual"):"Not installed").".\n";
if ($modInstalled && $needUpdate) {
system("rm -rf \"".modules::modulesDir."/".$newModInfo->name."\"");
}
if (!$modInstalled || $needUpdate) {
print "Install...";
$zip = new \ZipArchive();
$zip->open(modules::packagesDir . "/" . $file);
mkdir (modules::modulesDir."/".$newModInfo->name);
$zip->extractTo(modules::modulesDir."/".$newModInfo->name);
if (file_exists(modules::modulesDir."/".$newModInfo->name."/fox-start.d")) {
system("chmod a+x \"".modules::modulesDir."/".$newModInfo->name."/fox-start.d/\"*");
}
print "OK\n";
}
}
print "Packages installer completed\n";

View File

@ -2,6 +2,7 @@
"require" : {
"aws/aws-sdk-php" : "^3.208",
"html2text/html2text" : "^4.3",
"phpmailer/phpmailer" : "~6.5.3"
"phpmailer/phpmailer" : "~6.5.3",
"firebase/php-jwt" : "~6.1"
}
}

View File

@ -38,7 +38,7 @@ class UID extends baseClass implements stringExportable, stringImportable
public function __get($key)
{
if (! empty($this->id) && $this->__loaded == false) {
if (empty($this->id) && $this->__loaded) {
$this->fill($this->id);
}
@ -80,8 +80,7 @@ class UID extends baseClass implements stringExportable, stringImportable
$sum2 = substr($code, 0, 1) + substr($code, 2, 1) + substr($code, 4, 1) + substr($code, 6, 1) + substr($code, 8, 1);
$sum = $sum1 + $sum2;
$ceil = ceil(($sum / 10)) * 10;
$delta = $ceil - $sum;
return $delta;
return $ceil - $sum;
}
public static $sqlTable = "tblRegistry";

View File

@ -20,8 +20,7 @@ class auth extends baseClass implements noSqlMigration
$sql = sql::getConnection();
$res = $sql->quickExec1Line("select * from `" . user::$sqlTable . "` where `login` = '" . common::clearInput($login) . "' and `secret` = '" . xcrypt::hash($password) . "'");
if ($res) {
$u = new user($res);
return $u;
return new user($res);
} else {
return false;
}

View File

@ -17,6 +17,7 @@ use fox\foxException;
use fox\auth;
use fox\authToken;
use fox\request;
use fox\foxRequestResult;
class login implements externalCallable
{
@ -26,7 +27,7 @@ class login implements externalCallable
switch ($request->method) {
case "POST":
if ($request->authOK) {
return;
throw new foxRequestResult("OK",200);
}
if (! (gettype($request->requestBody) == "object" && property_exists($request->requestBody, "login") && property_exists($request->requestBody, "password"))) {
throw new foxException("Bad request", 400);

View File

@ -49,7 +49,7 @@ class oauth implements externalCallable
case "POST":
$profile = oAuthProfile::getByHash(common::clearInput($request->requestBody->hash));
$oac = $profile->getClient(config::get("SITEPREFIX")."/auth/oauth");
$xTokens=$oac->getTokenByCode(common::clearInput($request->requestBody->code));
$oac->getTokenByCode(common::clearInput($request->requestBody->code));
$userInfo=$oac->getUserInfo();
$userRefId=$profile->id.":".$userInfo->sub;
$u=user::getByRefID("oauth",$userRefId);
@ -64,8 +64,9 @@ class oauth implements externalCallable
"expire" => $t->expireStamp->isNull() ? "Never" : $t->expireStamp
];
return $u;
break;
default:
throw new foxException("Method not implemented",405);
}
}
}

View File

@ -49,7 +49,6 @@ class register implements externalCallable {
if (!$u) {
$u = new user();
// $u->login=uniqid();
$u->eMail=$eMail;
$u->authType="oauth";
$u->authRefId=$userRefId;

View File

@ -16,6 +16,7 @@ use fox\externalCallable;
use fox\foxException;
use fox\request;
use fox\modules;
use fox\foxRequestResult;
class session implements externalCallable
{
@ -29,13 +30,13 @@ class session implements externalCallable
}
$request->token->delete();
return;
foxRequestResult::throw(200,"Deleted");
case "GET":
if ($request->authOK) {
$modules=[];
$i = 0;
foreach (modules::listInstalled() as $mod) {
if (array_search("menu", $mod->features) !== false && $request->user->checkAccess($mod->globalAccessKey, $mod->name) && ! empty($mod->menuItem)) {
if ($request->user->checkAccess($mod->globalAccessKey, $mod->name)) {
$i ++;
$modules[($mod->modPriority * 100) + $i] = [
"name" => $mod->name,
@ -54,9 +55,11 @@ class session implements externalCallable
"modules" => $modules
];
}
;
throw new foxException("Unauthorized", 401);
break;
default:
throw new foxException("Method not allowe", 405);
break;
}
}
}

View File

@ -155,9 +155,7 @@ class authToken extends baseClass
} else if ($t->token1 == $token1 && $t->token2 != $token2 && $token2 != $t->token2b) {
// token2 failed - token are compromised
trigger_error("Token #".$t->id." are compromised!");
// $t->delete();
} else {
$t->delete();
}
$sql->quickExec("COMMIT");
@ -189,7 +187,6 @@ class authToken extends baseClass
$t->expireStamp = empty($expireInDays) ? (new time()) : (new time(time() + ($expireInDays * 86400)));
$t->renewStamp = empty($renewTTL) ? (new time()) : (new time(time() + ($renewTTL * 60)));
;
for ($i = 0; $i < 32; $i ++) {
$token = substr(preg_replace("/[-_+=\\/]/", "", base64_encode(random_bytes(64))), 0, 64);
@ -217,7 +214,6 @@ class authToken extends baseClass
$renewTTL = config::get("TOKEN_RENEW_" . $this->type) === null ? static::tokenTypes[$this->type]["renewTTL"] : config::get("TOKEN_RENEW_" . $this->type);
$this->renewStamp = empty($renewTTL) ? (new time()) : (new time(time() + ($renewTTL * 60)));
;
$this->token2b = $this->token2;
$this->token2 = substr(preg_replace("/[-_+=\\/]/", "", base64_encode(random_bytes(64))), 0, 64);

View File

@ -100,7 +100,7 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
case "object":
if (! empty($val::$SQLType)) {
$type = $val::$SQLType;
} elseif (($val instanceof stringExportable)) {
} elseif ($val instanceof stringExportable) {
$type = "VARCHAR(255)";
} elseif ($val instanceof \JsonSerializable) {
$type = "VARCHAR(255)";
@ -134,18 +134,19 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
if ($this->sql === null) {
$this->sql = sql::getConnection();
}
return;
}
protected function __xConstruct()
{}
{
return true;
}
public function __construct($id = null, ?namespace\sql $sql = null, $prefix = null, $settings = null)
{
# How to call from child template:
# parent::__construct($id, $sql, $prefix, $settings);
$this->__settings = $settings;
if (empty($this::$sqlSelectTemplate) and ! empty($this::$sqlTable)) {
if (empty($this::$sqlSelectTemplate) && ! empty($this::$sqlTable)) {
$this->__sqlSelectTemplate = "select * from `" . $this::$sqlTable . "` as `i`";
} else {
$this->__sqlSelectTemplate = $this::$sqlSelectTemplate;
@ -442,7 +443,6 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
}
} else {
throw new \Exception("property $key not availiable for read in class " . get_class($this), 595);
break;
}
}
}
@ -504,7 +504,7 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
if (($this->__get($key)) instanceof \JsonSerializable) {
$rv[$key] = $this->__get($key);
} elseif (($this->__get($key)) instanceof stringExportable) {
if (($this->__get($key)->isNull())) {
if ($this->__get($key)->isNull()) {
$rv[$key] = null;
} else {
$rv[$key] = (string) ($this->__get($key));
@ -558,7 +558,7 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
break;
default:
continue 2;
break;
}
}
@ -583,9 +583,10 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
$sqlQueryString=$ref->sqlSelectTemplate.(empty($join)?"":" ".$join).(empty($where)?"":" WHERE ".$where).(empty($limit)?"":" ".$limit);
$res=$sql->quickExec($sqlQueryString);
$rv=[];
$rv=new searchResult();
$rv->setIndexByPage($page, $pageSize);
while ($row=mysqli_fetch_assoc($res)) {
$rv[]=new static($row);
$rv->push(new static($row));
}
return $rv;
}

View File

@ -14,10 +14,19 @@ namespace fox;
class baseModule
{
/**
* @deprecated - moved into module.json
*/
public static $title = "Basic Module Template";
/**
* @deprecated - moved into module.json
*/
public static $desc = "Базовый класс модуля-заглушки";
/**
* @deprecated - moved into module.json
*/
public static $version = "0.0.0";
public static $type = "fake";
@ -71,8 +80,6 @@ class baseModule
public static function getModInfo(): moduleInfo
{
$mi = new moduleInfo();
$mi->title = static::$title;
$mi->modVersion = static::$version;
$mi->name = $mi->namespace = substr(static::class, 0, strrpos(static::class, '\\'));
$mi->features = static::$features;
$mi->isTemplate = true;

View File

@ -41,7 +41,7 @@ class cache
if (empty($port)) {
$port = config::get("cachePort");
}
;
if (empty($port)) {
$port = 11211;
}
@ -104,8 +104,7 @@ class cache
if ($xval==null || $xval=="null") { return null; }
$rv= json_decode($this->mcd->get($this->prefix . "." . $key), $array);
if ($rv !== null) { return $rv; }
$rv= json_decode(xcrypt::decrypt($this->mcd->get($this->prefix . "." . $key)), $array);
return $rv;
return json_decode(xcrypt::decrypt($this->mcd->get($this->prefix . "." . $key)), $array);
}
public function del($key) {

View File

@ -42,7 +42,6 @@ class common
if (isset($_GET[$name])) {
$val = preg_replace('![^' . $regex . ']+!', '', $_GET[$name]);
}
;
}
} else {
@ -52,10 +51,8 @@ class common
} else {
$val = "";
}
if ($val == "") {
if (isset($_GET[$name])) {
$val = preg_replace("![\'\"]+!", '\"', $_GET[$name]);
}
if ($val == "" && isset($_GET[$name])) {
$val = preg_replace("![\'\"]+!", '\"', $_GET[$name]);
}
} else {
if (isset($_POST[$name])) {
@ -98,17 +95,13 @@ class common
mt_srand((double) microtime() * 10000); // optional for php 4.2.0 and up.
$charid = strtoupper(md5(uniqid(rand(), true)));
$hyphen = chr(45); // "-"
$uuid = substr($charid, 0, 8) . $hyphen . substr($charid, 8, 4) . $hyphen . substr($charid, 12, 4) . $hyphen . substr($charid, 16, 4) . $hyphen . substr($charid, 20, 12);
return substr($charid, 0, 8) . $hyphen . substr($charid, 8, 4) . $hyphen . substr($charid, 12, 4) . $hyphen . substr($charid, 16, 4) . $hyphen . substr($charid, 20, 12);
return $uuid;
}
static function getGUID()
{
$cUuid = getGUIDc();
$uuid = chr(123) . // "{"
$cUuid . chr(125); // "}"
return $uuid;
return chr(123) . getGUIDc() . chr(125);
}
static function fullname2qname($first, $mid, $last)
@ -119,7 +112,6 @@ class common
static function text2html($src)
{
$src = preg_replace("/[\n]/", "</br>", $src);
// $src=preg_replace("/[\cr\<\>]/"," ",$src);
return $src;
}

View File

@ -68,7 +68,8 @@ class config extends dbStoredBase
"FOX_DEFAULT_LANGUAGE",
"FOX_DEFAULT_MODULE",
"FOX_SESSION_RENEW_SEC",
"FOX_ALLOW_REGISTER"
"FOX_ALLOW_REGISTER",
"FOX_ENVIRONMENT",
];
static function get($key, $module = "core")

View File

@ -74,7 +74,7 @@ class confirmCode extends baseClass {
protected function validateSave()
{
if ($this->issueStamp->isNull()) {$this->issueStamp=time::current();};
if ($this->issueStamp->isNull()) {$this->issueStamp=time::current();}
if ($this->expireStamp->isNull()) {$this->expireStamp=time::current()->addSec(static::defaultTTL);}
if ($this->code==null) { $this->code=(common::genPasswd(4,[0,1,2,3,4,5,6,7,8,9]));}
if (empty($this->instance) || empty($this->class) || empty($this->operation) || empty($this->reference)) {

View File

@ -60,7 +60,7 @@ class cronDb {
}
$rv=[];
while ($row = $r->fetchArray(SQLITE3_ASSOC)) {
array_push($rv, $row);;
array_push($rv, $row);
}
return $rv;

View File

@ -46,11 +46,6 @@ class errorPage
exit();
}
$mod_name = config::get("defaultLoginModule");
if (! $mod_name) {
$mod_name = "core";
}
static::show($error_code, $error_desc, true);
exit();
}

View File

@ -16,7 +16,7 @@ use Exception;
class fileConverter
{
public static function convert($src, $dst = null, $type, $url = null)
public static function convert($src, $dst, $type, $url = null)
{
if (empty($url)) {
$url = config::get("converterURL");
@ -29,8 +29,6 @@ class fileConverter
throw new \Exception("Invalid source file $src");
}
$boundary = '------------chimera---' . substr(md5(rand(0, 32000) . '---fox-----'), 0, 10);
// "засечка" :P
$boundary = md5(rand(0, 32000));

View File

@ -81,8 +81,8 @@ class mailAccount extends baseClass implements externalCallable {
case "password": return xcrypt::decrypt($this->__password);
case "rxLogin": return $this->login;
case "rxPassword": return xcrypt::decrypt($this->__password);
case "rxLogin": return $this->login;
case "rxPassword": return xcrypt::decrypt($this->__password);
case "txLogin": return $this->login;
case "txPassword": return xcrypt::decrypt($this->__password);
default: return parent::__get($key);
}
}
@ -161,7 +161,7 @@ class mailAccount extends baseClass implements externalCallable {
throw new foxException("Forbidden", 403);
}
return static::search();
return static::search()->result;
}
public static function API_DELETE(request $request) {

View File

@ -23,7 +23,7 @@ class mailAddress {
$this->__set("address", $address);
} else {
$res=[];
$name = preg_replace('!^[\"\ \t]*|[\"\ \t]*$!', '', $name);
$name = preg_replace('!(^[\"\ \t]*)|([\"\ \t]*$)!', '', $name);
if(preg_match("/([^\<\>]*) \<([^\<\>]*)\>/", $name, $res)) {
$this->address=$res[2];
$this->name=$res[1];
@ -36,7 +36,7 @@ class mailAddress {
}
}
$this->name = preg_replace('!^[\"]*|[\"]*$!', '', $this->name);
$this->name = preg_replace('!(^[\"]*)|([\"]*$)!', '', $this->name);
}

View File

@ -185,17 +185,17 @@ class mailMessage extends baseClass {
$ref = explode(" ", $val);
$this->refIds=[];
foreach ($ref as $ref_item) {
$ref_item=preg_replace("!^\<|\>$!", '', $ref_item);
$ref_item=preg_replace("!(^\<)|(\>$)!", '', $ref_item);
array_push($this->refIds, $ref_item);
}
break;
case "inReplyTo":
$this->inReptyTo=preg_replace("!^\<|\>$!", '', $val);
$this->inReptyTo=preg_replace("!(^\<)|(\>$)!", '', $val);
break;
case "messageId":
$this->messageId=preg_replace("!^\<|\>$!", '', $val);
$this->messageId=preg_replace("!(^\<)|(\>$)!", '', $val);
break;
case "subject":
$this->__subject=base64_encode($val);
@ -301,7 +301,6 @@ class mailMessage extends baseClass {
if ($this->account->txProto!='smtp' || $this->account->txServer == null) {
throw new \Exception("This account can't send messages");
return false;
}
$mail = new PHPMailer(true);
$mail->CharSet = PHPMailer::CHARSET_UTF8;

View File

@ -23,7 +23,7 @@ class settings implements externalCallable
{
public static function APICall(request $request)
{
$profiles = oAuthProfile::search();
$profiles = oAuthProfile::search()->result;
$oauth=[];
foreach ($profiles as $p) {
if ($p->enabled) {

View File

@ -226,6 +226,17 @@ class moduleInfo extends baseClass implements externalCallable
return $modsInstalled[$modInstanceName];
}
public static function getByFeature(string $feature) {
$rv=[];
foreach(moduleInfo::getAll() as $mod) {
if (array_search($feature, $mod->features) !==false) {
$rv[]=$mod;
}
}
return $rv;
}
public function getInstances()
{
if (! $this->isTemplate) {
@ -245,6 +256,11 @@ class moduleInfo extends baseClass implements externalCallable
public static function load(string $modName)
{}
public function newClass() {
$ref=$this->namespace.'\module';
return new $ref();
}
public function export() {
$rv=parent::export();
if ($this->isTemplate) {
@ -362,7 +378,7 @@ class moduleInfo extends baseClass implements externalCallable
$mod->features[]=common::clearInput($request->requestBody->feature);
$mod->features=array_values($mod->features);
$mod->save();
static::log($request->instance,__FUNCTION__, "feature ".common::clearInput($request->requestBody->feature)." set for module ".$mod->name,$request->user,"module",$user->id,null,logEntry::sevInfo);
static::log($request->instance,__FUNCTION__, "feature ".common::clearInput($request->requestBody->feature)." set for module ".$mod->name,$request->user,"module",$mod->id,null,logEntry::sevInfo);
foxRequestResult::throw(201, "Created");
}
foxRequestResult::throw(201, "Created");
@ -370,7 +386,7 @@ class moduleInfo extends baseClass implements externalCallable
case "config":
config::set(common::clearInput($request->requestBody->key), $request->requestBody->value, $mod->name);
static::log($request->instance,__FUNCTION__, "config key ".common::clearInput($request->requestBody->key)." set for module ".$mod->name,$request->user,"module",$user->id,null,logEntry::sevInfo);
static::log($request->instance,__FUNCTION__, "config key ".common::clearInput($request->requestBody->key)." set for module ".$mod->name,$request->user,"module",$mod->id,null,logEntry::sevInfo);
foxRequestResult::throw(201, "Created");
break;
default:

View File

@ -13,7 +13,9 @@ namespace fox;
*/
class modules implements externalCallable
{
public const modulesDir=__DIR__ . "/../../modules/";
public const packagesDir=__DIR__ . "/../../packages/";
// not implemented yet
public const pseudoModules = [
"core" => [
@ -138,8 +140,18 @@ class modules implements externalCallable
if (array_key_exists($modName, static::pseudoModules)) {
$modInfo = new moduleInfo(static::pseudoModules[$modName]);
} else {
$modClass = $modName . "\module";
$modInfo = (new $modClass())::getModInfo();
try {
$modClass = $modName . "\module";
$modInfo = (new $modClass())::getModInfo();
$modDesc=json_decode(file_get_contents(static::modulesDir . "/" . $modName . "/module.json"));
if (empty($modDesc)) { throw new foxException("Unable to read module.json"); }
if ($modDesc->name != $modInfo->name) { throw new foxException("Module name mismatch for ".$modInfo->name); }
$modInfo->title=$modDesc->title;
$modInfo->modVersion=$modDesc->version;
} catch (\Exception $e) {
trigger_error($e->getMessage());
continue;
}
}
$rv[$modInfo->name] = $modInfo;
}
@ -152,20 +164,27 @@ class modules implements externalCallable
return moduleInfo::getAll();
}
public static function getByFeature(string $feature) {
return moduleInfo::getByFeature($feature);
}
public static function scan()
{
$rv = [];
foreach (static::pseudoModules as $key => $val) {
$rv[] = $key;
}
foreach (scandir(__DIR__ . "/../../modules/") as $dir) {
foreach (scandir(static::modulesDir) as $dir) {
if (preg_match("/^[.]/", $dir)) {
continue;
}
if (! is_dir(__DIR__ . "/../../modules/" . $dir)) {
if (! is_dir(static::modulesDir . $dir)) {
continue;
}
if (! file_exists(__DIR__ . "/../../modules/" . $dir . "/module.php") && ! file_exists(__DIR__ . "/../../modules/" . $dir . "/Autoloader.php")) {
if (! file_exists(static::modulesDir . $dir . "/module.json")) {
continue;
}
if (! file_exists(static::modulesDir . $dir . "/module.php") && ! file_exists(static::modulesDir . $dir . "/Autoloader.php")) {
continue;
}
$rv[] = $dir;
@ -226,7 +245,7 @@ class modules implements externalCallable
$mod->modPriority=$modPriority;
}
$mod->save();
static::log($request->instance,__FUNCTION__, "Module ".$mod->name." installed",$request->user,"module",$mod->id,null,logEntry::sevInfo);
logEntry::add($request->instance, static::class, __FUNCTION__, null, "Module ".$mod->name." installed", "INFO", $request->user, "module", $mod->id);
foxRequestResult::throw(201, "Created", $mod);
}

View File

@ -145,7 +145,6 @@ class oAuthClient {
} else {
trigger_error(json_encode($res));
throw new foxException("Invalid token");
exit;
}
}

View File

@ -75,7 +75,7 @@ class oAuthProfile extends baseClass implements externalCallable {
if (! $request->user->checkAccess("adminAuthMethods", "core")) {
throw new foxException("Forbidden", 403);
}
return static::search();
return static::search()->result;
}
public static function APIX_GET_disable(request $request) {

31
core/fox/searchResult.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace fox;
class searchResult {
public $page=0;
public $pages=0;
public $result=[];
public $count=0;
protected $idx=1;
public function setIndex($idx) {
$this->idx = $idx;
}
public function push($val) {
$this->result[$this->idx] = $val;
$this->idx++;
$this->count++;
}
public function __construct($idx=null) {
if (isset($idx)) {
$this->idx = $idx;
}
}
public function setIndexByPage($page=null, $pagesize=null) {
if ($page===null) { $page = $this->page; }
$this->idx = (($page-1)*$pagesize)+1;
}
}
?>

View File

@ -109,7 +109,6 @@ class sql
mysqli_set_charset($this->mysqli, "utf8");
if (! $this->mysqli) { // Если дескриптор равен 0 соединение не установлено
throw new Exception("SQL Connection to $this->server failed");
exit();
}
$this->connected = true;
}
@ -135,7 +134,6 @@ class sql
}
;
return null;
exit();
}
return $result;
}
@ -194,7 +192,6 @@ class sql
if (! $this->stmt) {
$err = 'ERR:EXEC 1P' . mysqli_errno($this->mysqli) . ' ' . mysqli_error($this->mysqli);
throw new Exception($err, mysqli_errno($this->mysqli));
exit();
}
call_user_func_array(array(
$this->stmt,
@ -205,7 +202,6 @@ class sql
$errNo = mysqli_errno($this->mysqli);
$this->stmt->close();
throw new Exception($err, $errNo);
exit();
}
return true;
}
@ -218,7 +214,6 @@ class sql
$this->stmt->close();
}
throw new \Exception('ERR:EXEC 3P' . mysqli_errno($this->mysqli) . ' ' . mysqli_error($this->mysqli), mysqli_errno($this->mysqli));
exit();
}
$this->stmt->close();
return true;
@ -232,9 +227,9 @@ class sql
function paramAdd($sqlParamName, $paramValue, $setNull = false)
{
if ($this->queryType == "insert") {
return $this->paramAddInsert($sqlParamName, $paramValue, $setNull);
$this->paramAddInsert($sqlParamName, $paramValue, $setNull);
} elseif ($this->queryType == "update") {
return $this->paramAddUpdate($sqlParamName, $paramValue, $setNull);
$this->paramAddUpdate($sqlParamName, $paramValue, $setNull);
}
}

View File

@ -305,7 +305,7 @@ class user extends baseClass implements externalCallable
if (! $request->user->checkAccess("adminUsers", "core")) {
throw new foxException("Forbidden", 403);
}
return static::search();
return static::search()->result;
}
public static function API_POST_search(request $request) {
@ -315,7 +315,7 @@ class user extends baseClass implements externalCallable
$pageSize=common::clearInput($request->requestBody->pageSize,"0-9");
if (empty($pageSize)) { $pageSize=$request->user->settings["pageSize"];}
return static::search(common::clearInput($request->requestBody->pattern), $pageSize);
return static::search(common::clearInput($request->requestBody->pattern), $pageSize)->result;
}
@ -330,7 +330,7 @@ class user extends baseClass implements externalCallable
public static function API_GET_sendEMailConfirmation(request $request) {
$request->user->sendEMailConfirmation();
static::log($request->instance,__FUNCTION__, "mailConfirmation sent for user ".$user->login,$request->user,"user",$user->id,null,logEntry::sevInfo);
static::log($request->instance,__FUNCTION__, "mailConfirmation sent for user ".$request->user->login,$request->user,"user",$request->user->id,null,logEntry::sevInfo);
}
public static function API_POST_validateEMailCode(request $request) {
@ -341,21 +341,6 @@ class user extends baseClass implements externalCallable
foxException::throw("ERR", "Validation failed", 400);
}
}
public static function APICall(request $request) {
if (!empty($request->function)) {
throw new foxException("Method not allowed",405);
}
switch ($request->method) {
default:
throw new foxException("Method not allowed",405);
}
}
}
?>

View File

@ -163,7 +163,7 @@ class userGroup extends baseClass implements externalCallable
if (! $request->user->checkAccess("adminUserGroups", "core")) {
throw new foxException("Forbidden", 403);
}
return static::search();
return static::search()->result;
}
public static function API_POST_members(request $request)
@ -223,7 +223,7 @@ class userGroup extends baseClass implements externalCallable
case "PUT":
$grName=common::clearInput($request->requestBody->name);
$groups = userGroup::search($grName);
$groups = userGroup::search($grName)->result;
foreach ($groups as $group) {
if (trim(strtolower($group->name))==trim(strtolower($grName))) {
foxException::throw("ERR", "Already exists", 409, "GAX");

View File

@ -158,7 +158,7 @@ class userGroupMembership extends baseClass implements externalCallable
$res=static::search(null,$pageSize,$page,[
"user"=>$user,
"group"=>$group,
]);
])->result;
$rv=[];
foreach ($res as $ugm) {

View File

@ -118,7 +118,7 @@ class userInvitation extends baseClass implements externalCallable {
}
public static function API_GET_list(request $request) {
return static::search();
return static::search()->result;
}
public static function APIX_GET_reSend(request $request) {

View File

@ -1,3 +1,3 @@
FROM mxfox.ru/chimera/fox-web-basic:latest
FROM mxfox.ru/mxfox/fox-web-basic:latest
COPY . /var/www/html
COPY docker-build/rootfs /

View File

@ -35,10 +35,11 @@ if (array_search($req[1], $jsons)!==false) {
}
?>
<!DOCTYPE>
<html lang="en" class=login>
<head><title>Error <?php print $code; ?></title></head>
<html class=login>
<body class=login style="
<body class=login style="
background-image: url(/static/theme/chimera/img/chimera_logo.svg);
min-width: inherit;
min-height: 100%;
@ -48,7 +49,7 @@ if (array_search($req[1], $jsons)!==false) {
background-position-y: center;
background-color: #150007;">
<div style=' text-align: center; width: 100%; heigth: 100%;'>
<div style=' text-align: center; width: 100%; height: 100%;'>
</div>
@ -56,7 +57,6 @@ if (array_search($req[1], $jsons)!==false) {
border: 2px solid red;
background-color: rgba(0, 0, 0, 0.85);
margin: 0;
padding: 70 0 0 0;
position: absolute;
left: calc(50% - 250px);
@ -68,7 +68,9 @@ if (array_search($req[1], $jsons)!==false) {
vertical-align: center;
font-family: 'Fira Mono', monospace;
font-weight: 200;
font-size: 32px; color: #FF3500; margin: 1%">
font-size: 32px;
color: #FF3500;
margin: 1%">
<?php
print "ERROR: $code<br/>";
if (array_key_exists($code, $codes)) {

View File

@ -14,7 +14,6 @@
if (php_sapi_name() != 'cli') {
throw new Exception("This script can be run via CLI only");
exit;
}
use fox\moduleInfo;
@ -134,7 +133,7 @@ while(($xpid=pcntl_wait($status,WNOHANG)) >=0) {
foreach($pids as $pid=>&$ttl) {
if ($ttl < $ttlc) {
print "Kill PID ".$pid." by TTL\n";
var_dump(posix_kill($pid, SIGKILL));
posix_kill($pid, SIGKILL);
$ttl+=5;
}
}

View File

@ -1,4 +1,5 @@
#!/bin/bash
php /var/www/html/cli/installPackages.php
php /var/www/html/cli/migration.php
php /var/www/html/cli/initialize.php

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>Chimera Fox</title>
<meta name="viewport" content="width=device-width, initial-scale=1">

View File

@ -1,4 +1,4 @@
# Modules Folder
This folder must be empty and don't used for any data except modules subfolders.
Modules must compliant FoxAPImk2.
Modules may be added as mapped-folder for docker containers or prebuilt in it.
Modules may be prebuilt in docker image or placed as packages into `packages` folder - it will be unpacked automatically while startup procedure.

4
packages/README.md Normal file
View File

@ -0,0 +1,4 @@
# Packages Folder
This folder must be empty and don't used for any data except modules packages.
Modules must compliant FoxAPImk2.
Modules will be unpacked automatically while startup procedure.

View File

@ -2,7 +2,7 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule . - [S=5]
RewriteRule . - [S=6]
RewriteRule "^theme/([^/]+)/main.css" "/modules/$1/theme/main.css" [L]
RewriteRule "^theme/([^/]+)/img/([0-9a-zA-Z\.\-\_]*)" "/modules/$1/theme/img/$2" [L]
@ -10,3 +10,4 @@ RewriteRule "^theme/([^/]+)/font/([0-9a-zA-Z\.\-\_]*)" "/modules/$1/theme/font/$
RewriteRule "^theme/([^/]+)/css/([0-9a-zA-Z\.\-\_]*\.css)" "/modules/$1/theme/css/$2" [L]
RewriteRule "^js/([^/]+)/([0-9a-zA-Z\.\-\_]*\.js)" "/modules/$1/js/$2" [L]
RewriteRule "^css/([^/]+)/([0-9a-zA-Z\.\-\_]*\.css)" "/modules/$1/css/$2" [L]

View File

@ -106,7 +106,7 @@ function moduleInstall_Click(el) {
UI.createDialog(
UI.addFieldGroup([
UI.addField({item: "mod_name", title: langPack.core.iface.title, type: "input", reqx: "true", reqx: "true", regx: "^[A-Za-z0-9_-]*$", val: $(el.target).closest("tr").attr("modName")}),
UI.addField({item: "mod_name", title: langPack.core.iface.title, type: "input", reqx: "true", regx: "^[A-Za-z0-9_-]*$", val: $(el.target).closest("tr").attr("modName")}),
UI.addField({item: "mod_priority", title: langPack.core.iface.modules.priority, type: "input", reqx: "true", regx: "^[0-9]*$", val: 100}),
]),
langPack.core.iface.dialodAddButton,

View File

@ -3,6 +3,7 @@ export var langItem={
modName: "core",
err0: "Ошибка загрузки данных",
err1: "Ошибка",
emptyTableText: "Здесь ничего нет. Совсем ничего. Если это ошибка - попробуйте добавить вручную.",
errReqx: "Обязательное поле",
errRegx: "Несовпадение формата",
ok0: "Операция завершена успешно",
@ -34,7 +35,6 @@ export var langItem={
add: "Добавить",
open: "Открыть",
dialodAddButton: "Добавить",
dialodDelButton: "Удалить",
dialodCreateButton: "Создать",
dialodInfoTitle: "Информация",
dialogRegisterButton: "Регистрация",
@ -109,7 +109,6 @@ export var langItem={
allTitle: "Пользователи",
regCode: "Код регистрации",
invitesTitle: "Приглашения",
regCode: "Код приглашения",
inviteButtonTitle: "Пригласить",
allowMultiUseTitle: "Разрешить множественное использование",
allowMultiUseQTitle: "Множеств.",

View File

@ -79,7 +79,11 @@ export function boot(xlite) {
}
export function smartClick(ref) {
UI.setLocation($(ref.currentTarget).prop("href"));
if(typeof(ref)=="string") {
UI.setLocation(ref);
} else {
UI.setLocation($(ref.currentTarget).prop("href"));
}
boot(true);
return false;
}

View File

@ -13,7 +13,7 @@ var codes={
};
export function showError(code, message) {
if (message==undefined) { message=codes[code]};
if (message==undefined) { message=codes[code]}
if (message==undefined) { message="Internal server error"}
$("<div>", { class: "error blanker bggray"}).appendTo("body");
$("<div>", { class: "error error_banner",html: "ERROR "+code+"<br/>"+message}).appendTo("body");
@ -28,9 +28,9 @@ export function click (ref) {
}
export function addButton(id, icon, title, color, onClick, cssClass, style) {
var opts;
if (typeof(id)=='object') {
var opts=id;
opts=id;
id=opts.id;
icon=opts.icon;
title=opts.title;
@ -191,7 +191,7 @@ export function createTabsPanel(panels,ref) {
$.each(panels,function (index,panel) {
if (panel.id==undefined) {panel.id=index;}
$("<li>",{append: $("<a>",{href: "#tab-"+panel.id, text: panel.title})})
$("<li>",{append: $("<a>",{href: "#tab-"+panel.id, id: "a-tab-"+panel.id, text: panel.title})})
.appendTo("#item_tabs_ul_list");
$("<div>", {
id: "tab-"+panel.id,
@ -262,12 +262,13 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
let item_id=ref.item;
let args = ref.args;
if (ref.args==undefined) {
if (ref.args==undefined && ref.val != undefined) {
args = ref.val;
} else {
args={};
}
switch(type) {
case 'password':
case 'password':
item = $("<input>", {class: "i", id: ref.item, name: name, type:'password',width: 'calc(100% - 44px)'})
.add($("<div>",{class: "button short", style: "width: 25px; margin-right: 0; margin-left: 2; padding: 0; padding-top: 1; font-size: 13px;",append: $("<i>",{class: 'far fa-eye'})
})
@ -282,6 +283,13 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
}));
break
case 'passwordNew':
if (args.chars==undefined) {
switch(args.type) {
case "num":
args.chars="0123456789";
}
}
item = $("<input>", {class: "i", id: ref.item, name: name, width: 'calc(100% - 44px)'})
.add($("<div>",{
class: "button short",
@ -292,7 +300,7 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
if ($("#"+item_id).prop("disabled")) {
return;
}
let password=genPassword();
let password=genPassword(args.chars, args.length);
$("#"+item_id).val(password);
$("#"+item_id).change();
if (typeof(ref.newPasswdGenCallback)=="function") {
@ -594,6 +602,16 @@ export function collectForm(formid, getall, withIDS, withREF, validate)
} catch {
}
data.getVals=function() {
let rv={};
$.each(data,function(idx,val) {
if (typeof(val) =='object') {
rv[idx]=val.val;
}
});
return rv;
}
return data;
}
@ -661,13 +679,13 @@ export function on_valChanged(t_this) {
export function progressBar_init()
{
$('.progressbar').each(function() {
el = $(this);
val = parseInt(el.attr('value'));
el.progressbar({
value: 0
});
progressBar_update(el, val);
let el = $(this);
let val = parseInt(el.attr('value'));
el.progressbar({
value: 0
});
progressBar_update(el, val);
});
}
@ -990,7 +1008,7 @@ Number.prototype.pad = function(size) {
}
if (mode=='getPage') {
return sessionStorage.getItem($(this).prop('foxPager_prefix')+"pager");c
return sessionStorage.getItem($(this).prop('foxPager_prefix')+"pager");
}
this.each(function(rid,ref) {
@ -1070,7 +1088,7 @@ Number.prototype.pad = function(size) {
}).appendTo(ref);
} else if (mode=='update') {
prefix = $(ref).prop("foxPager_prefix");
let prefix = $(ref).prop("foxPager_prefix");
sessionStorage.setItem(prefix+"pager", options.page);
$(ref).prop('foxPager_page',options.page);
$(ref).prop('foxPager_pages',options.pages);

View File

@ -37,7 +37,6 @@ function doValidation(code) {
code: code,
},
method: "core/user/validateEMailCode",
errDict: langPack.core.iface.users.errors,
onSuccess: function(json) {
UI.closeDialog('addgrp');

View File

@ -78,7 +78,7 @@ a.ui-button,
a:link.ui-button,
a:visited.ui-button,
.ui-button {
color: #aaaaa;
color: #aaaaaa;
text-decoration: none;
}
.ui-state-hover,

View File

@ -243,7 +243,6 @@ div.header_user
border: 2px solid red;
background-color: rgba(0, 0, 0, 0.85);
margin: 0;
padding: 70px 0 0 0;
position: absolute;
left: calc(50% - 250px);
@ -254,7 +253,9 @@ div.header_user
height: 150px;
font-family: 'Fira Mono', monospace;
font-weight: 200;
font-size: 32px; color: #FF3500; margin: 1%;
font-size: 32px;
color: #FF3500;
margin: 1%;
z-index: 200;
}
@ -403,7 +404,6 @@ div.t_navy_main, div.t_main
{
text-align: left;
vertical-align: top;
padding-top: 0px;
padding: 10px 7px 10px 7px;
}
@ -674,7 +674,7 @@ li.xmenu {
margin-left: 14px;
}
t_navy_main .xmenu p {
.xmenu p {
margin-left: 5px;
}
@ -1437,7 +1437,6 @@ div.alert, p.alert
top: 50%;
margin-left: -350px;
margin-top: -25px;
font-weight: bold;
vertical-align: middle;
padding-top: 10px;
color: white;
@ -1590,7 +1589,6 @@ div.chevron.first .button
div.login
{
width: 300px;
position: absolute;
left: 50%;
top: 50%;
@ -1672,7 +1670,7 @@ div.dialog textarea
table.datatable td.code
{
font-family: 'Share Tech Mono';
font-family: Share Tech Mono, sans-serif;
font-size: 16px;
color: rgba(255,255,255,0.8);
}
@ -1743,7 +1741,6 @@ div.accordion .ui-accordion-header.ui-state-active {
/* accordion and tabs */
div.accordion .ui-accordion-header {
border-radius: 5px;
background-color: rgba(var(--mxs-bg-rgba-prefix),0.7);
font-family: 'Fira Sans Extra Condensed', sans-serif;
font-weight: normal;