Weekly update 2022-05-29

This commit is contained in:
Pavel Dmitriev 2022-05-29 15:14:34 +03:00
parent 797f5a4d8e
commit 68db950971
21 changed files with 559 additions and 92 deletions

View File

@ -6,8 +6,7 @@ use Exception;
/** /**
* *
* Class fox\baseClass * Class fox\baseClass
* * @desc FOX Base Class
* @desc baseClass mk 2 class
* @copyright MX STAR LLC 2021 * @copyright MX STAR LLC 2021
* @version 4.0.0 * @version 4.0.0
* @author Pavel Dmitriev * @author Pavel Dmitriev
@ -17,6 +16,7 @@ use Exception;
* *
**/ **/
class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportable class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportable
{ {
@ -33,7 +33,7 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
// id for xConstruct; // id for xConstruct;
# if null - generated automatically # if null - generated automatically
public static $sqlSelectTemplate = null; public static $baseSqlSelectTemplate = null;
# primary index field. Default is id # primary index field. Default is id
public static $sqlIdx = "id"; public static $sqlIdx = "id";
@ -146,10 +146,10 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
# How to call from child template: # How to call from child template:
# parent::__construct($id, $sql, $prefix, $settings); # parent::__construct($id, $sql, $prefix, $settings);
$this->__settings = $settings; $this->__settings = $settings;
if (empty($this::$sqlSelectTemplate) && ! empty($this::$sqlTable)) { if (empty($this::$baseSqlSelectTemplate) && ! empty($this::$sqlTable)) {
$this->__sqlSelectTemplate = "select * from `" . $this::$sqlTable . "` as `i`"; $this->__sqlSelectTemplate = "select * from `" . $this::$sqlTable . "` as `i`";
} else { } else {
$this->__sqlSelectTemplate = $this::$sqlSelectTemplate; $this->__sqlSelectTemplate = $this::$baseSqlSelectTemplate;
} }
if (isset($sql)) { if (isset($sql)) {
@ -208,6 +208,8 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
protected function fillFromRow($row) protected function fillFromRow($row)
{ {
foreach ($row as $key => $val) { foreach ($row as $key => $val) {
if (! empty($this->fillPrefix)) { if (! empty($this->fillPrefix)) {
if (! preg_match("/^" . $this->fillPrefix . "/", $key)) { if (! preg_match("/^" . $this->fillPrefix . "/", $key)) {
continue; continue;
@ -218,8 +220,8 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
if (property_exists($this, $key) || property_exists($this, "__" . $key)) { if (property_exists($this, $key) || property_exists($this, "__" . $key)) {
if (property_exists($this, "__" . $key)) { if (property_exists($this, "__" . $key)) {
$key = "__" . $key; $key = "__" . $key;
} }
if (gettype($this->{$key}) == 'boolean') { if (gettype($this->{$key}) == 'boolean') {
$this->{$key} = $val == 1; $this->{$key} = $val == 1;
} elseif ((($this->{$key}) instanceof jsonImportable) || (($this->{$key}) instanceof stringImportable)) { } elseif ((($this->{$key}) instanceof jsonImportable) || (($this->{$key}) instanceof stringImportable)) {
@ -237,8 +239,8 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
} else { } else {
throw new Exception("Invalid type " . gettype($val) . " for " . $key . " in " . get_class($this)); throw new Exception("Invalid type " . gettype($val) . " for " . $key . " in " . get_class($this));
} }
} else { } else {
$this->{$key} = $val; $this->{$key} = $val;
} }
} }
} }
@ -447,6 +449,16 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
} }
} }
public static function qGetSql() : sql
{
return (new static())->getSql();
}
public static function qGetSqlSelectTemplate() : string
{
return (new static())->__sqlSelectTemplate;
}
public function getSql() : sql public function getSql() : sql
{ {
$this->checkSql(); $this->checkSql();
@ -553,7 +565,7 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
$where.=(empty($where)?"":" OR ")."`$key` like '%".common::clearInput($pattern)."'"; $where.=(empty($where)?"":" OR ")."`$key` like '%".common::clearInput($pattern)."'";
break; break;
case "invCode": case "invcode":
$where.=(empty($where)?"":" OR ")."`$key`='".UID::clear($pattern)."'"; $where.=(empty($where)?"":" OR ")."`$key`='".UID::clear($pattern)."'";
break; break;

View File

@ -38,10 +38,8 @@ class common
} else { } else {
$val = ""; $val = "";
} }
if ($val == "") { if ($val == "" && isset($_GET[$name])) {
if (isset($_GET[$name])) { $val = preg_replace('![^' . $regex . ']+!', '', $_GET[$name]);
$val = preg_replace('![^' . $regex . ']+!', '', $_GET[$name]);
}
} }
} else { } else {

View File

@ -11,7 +11,7 @@ namespace fox;
* @license GPLv3 * @license GPLv3
* *
*/ */
class company extends baseClass class company extends baseClass implements externalCallable
{ {
protected $id; protected $id;
@ -28,6 +28,13 @@ class company extends baseClass
public bool $deleted = false; public bool $deleted = false;
public const types=[
"company",
"client",
"supplier",
"partner"
];
public static $sqlTable = "tblCompany"; public static $sqlTable = "tblCompany";
public static $deletedFieldName = "deleted"; public static $deletedFieldName = "deleted";
@ -35,18 +42,26 @@ class company extends baseClass
public static $sqlColumns = [ public static $sqlColumns = [
"name" => [ "name" => [
"type" => "VARCHAR(255)", "type" => "VARCHAR(255)",
"nullable" => false "nullable" => false,
"search"=>"like"
], ],
"qName" => [ "qName" => [
"type" => "VARCHAR(255)", "type" => "VARCHAR(255)",
"nullable" => false "nullable" => false,
"search"=>"like"
], ],
"description" => [ "description" => [
"type" => "VARCHAR(255)" "type" => "VARCHAR(255)",
"search"=>"like"
], ],
"type" => [ "type" => [
"type" => "VARCHAR(255)", "type" => "VARCHAR(255)",
"nullable" => false "nullable" => false
],
"invCode"=> [
"type"=>"CHAR(10)",
"nullable"=>false,
"search"=>"invCode"
] ]
]; ];
@ -57,10 +72,98 @@ class company extends baseClass
protected function validateSave() protected function validateSave()
{ {
if ($this->invCode->isNull()) { if (empty($this->name)) { foxException::throw(foxException::STATUS_ERR, "Empty name not allowed", 406, "ENNA"); }
if (empty($this->qName)) { foxException::throw(foxException::STATUS_ERR, "Empty qName not allowed", 406, "EQNA"); }
if (array_search($this->type, $this::types)===false) { foxException::throw(foxException::STATUS_ERR, "Invalid type", 406, "ITNA"); }
if ($this->invCode->isNull()) {
$this->invCode->issue("core", get_class($this)); $this->invCode->issue("core", get_class($this));
} }
return true; return true;
} }
### REST API
public static function API_GET(request $request) {
if (!empty($request->parameters)) {
throw new foxException("Invalid request",400);
}
$request->blockIfNoAccess("viewCompanies");
return new static(common::clearInput($request->function));
}
public static function API_DELETE(request $request) {
if (!empty($request->parameters)) {
throw new foxException("Invalid request",400);
}
$request->blockIfNoAccess("adminCompanies");
$c = new static(common::clearInput($request->function));
$c->delete();
foxRequestResult::throw("201", "Deleted");
}
public static function API_GET_list(request $request) {
if (!empty($request->parameters)) {
throw new foxException("Invalid request",400);
}
$request->blockIfNoAccess("viewCompanies");
return static::search();
}
public static function API_GET_types(request $request) {
if (!empty($request->parameters)) {
throw new foxException("Invalid request",400);
}
$request->blockIfNoAccess("viewCompanies");
return static::types;
}
public static function API_PUT(request $request) {
if (!empty($request->parameters)) {
throw new foxException("Invalid request",400);
}
$request->blockIfNoAccess("adminCompanies");
$c=new static();
$c->name=$request->requestBody->name;
$c->qName=$request->requestBody->qName;
$c->description=$request->requestBody->description;
$c->type=$request->requestBody->type;
$c->save();
return $c;
}
public static function API_PATCH(request $request) {
if (!empty($request->parameters)) {
throw new foxException("Invalid request",400);
}
$request->blockIfNoAccess("adminCompanies");
$c=new static(common::clearInput($request->function,"0-9"));
$c->name=$request->requestBody->name;
$c->qName=$request->requestBody->qName;
$c->description=$request->requestBody->description;
$c->type=$request->requestBody->type;
$c->save();
return $c;
}
public static function API_POST_search(request $request) {
$request->blockIfNoAccess("viewCompanies");
$opts=[];
$pattern=null;
if (property_exists($request->requestBody, "pattern")) {
$pattern=$request->requestBody->pattern;
}
$pagesize=null;
if (property_exists($request->requestBody, "pageSize")) {
$pagesize=common::clearInput($request->requestBody->pageSize,"0-9");
}
$page=1;
if (property_exists($request->requestBody, "page")) {
$page=common::clearInput($request->requestBody->page,"0-9");
}
return static::search($pattern, $pagesize, $page, $opts);
}
} }
?> ?>

View File

@ -19,13 +19,73 @@ class foxException extends \Exception
protected $status = "ERR"; protected $status = "ERR";
public const STATUS_ERR = "ERR"; public const STATUS_ERR = "ERR";
public const STATUS_WARN = "WARN"; public const STATUS_WARN = "WARN";
public const STATUS_ALERT = "ALERT"; public const STATUS_ALERT = "ALERT";
public const STATUS_INFO = "INFO"; public const STATUS_INFO = "INFO";
public const HTTPCodeNames = [
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-status',
208 => 'Already Reported',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Switch Proxy',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Time-out',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Large',
415 => 'Unsupported Media Type',
416 => 'Requested range not satisfiable',
417 => 'Expectation Failed',
418 => 'I\'m a teapot',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Unordered Collection',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
451 => 'Unavailable For Legal Reasons',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Time-out',
505 => 'HTTP Version not supported',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
510 => 'Not Extended',
511 => 'Network Authentication Required',
];
public static function throw($status, $message, $code, $xCode=null, $prev = null) public static function throw($status, $message, $code, $xCode=null, $prev = null)
{ {
$e = new self($message, $code, $prev); $e = new self($message, $code, $prev);

View File

@ -13,7 +13,7 @@ class langPack {
public static function get($key, $lang=null) { public static function get($key, $lang=null) {
$ref=explode(".",$key); $ref=explode(".",$key);
if (empty($lang)) { $lang = config::get("DEFAULT_LANGUAGE"); }; if (empty($lang)) { $lang = config::get("DEFAULT_LANGUAGE"); }
if (empty($lang)) { $lang = "ru"; } if (empty($lang)) { $lang = "ru"; }
$mod=moduleInfo::getByInstance($ref[0]); $mod=moduleInfo::getByInstance($ref[0]);

View File

@ -177,8 +177,7 @@ class mailAccount extends baseClass implements externalCallable {
if (! $request->user->checkAccess("adminMailAccounts", "core")) { if (! $request->user->checkAccess("adminMailAccounts", "core")) {
throw new foxException("Forbidden", 403); throw new foxException("Forbidden", 403);
} }
$m=new static(common::clearInput($request->function,"0-9")); return new static(common::clearInput($request->function,"0-9"));
return $m;
} }
public static function APIX_GET_setDefault(request $request) { public static function APIX_GET_setDefault(request $request) {

View File

@ -41,6 +41,8 @@ class modules implements externalCallable
"adminUserGroups"=>"Manage userGroups", "adminUserGroups"=>"Manage userGroups",
"adminMailAccounts"=>"Manage system mail accounts", "adminMailAccounts"=>"Manage system mail accounts",
"adminAuthMethods"=>"Manage auth methods", "adminAuthMethods"=>"Manage auth methods",
"viewCompanies"=>"View companies",
"adminCompanies"=>"Manage companies",
], ],
"configKeys"=> [ "configKeys"=> [
"converterURL"=>"FoxConverter URL prefix", "converterURL"=>"FoxConverter URL prefix",
@ -80,6 +82,14 @@ class modules implements externalCallable
"function" => "groups", "function" => "groups",
"pageKey" => "adminGrous" "pageKey" => "adminGrous"
], ],
[
"title" => [
"ru" => "Компании",
"en" => "Companies"
],
"function" => "comps",
"pageKey" => "adminComps"
],
[ [
"title" => [ "title" => [
"ru" => "Учетные записи почты", "ru" => "Учетные записи почты",

View File

@ -173,5 +173,13 @@ class request extends baseClass implements noSqlMigration
} }
return $__foxRequest; return $__foxRequest;
} }
public function blockIfNoAccess(string $rule, string $modInstance=null) {
if ($modInstance==null) { $modInstance=$this->instance; }
if (! $this->user->checkAccess($rule, $modInstance)) {
throw new foxException("Forbidden", 403);
}
}
} }
?> ?>

View File

@ -16,44 +16,23 @@ class user extends baseClass implements externalCallable
{ {
protected $id; protected $id;
public $login; public $login;
protected UID $invCode; protected UID $invCode;
protected $__secret; protected $__secret;
public $authType; public $authType;
public $authRefId; public $authRefId;
public int $offlineAuthCtr = 0; public int $offlineAuthCtr = 0;
public $fullName; public $fullName;
protected bool $active = true; protected bool $active = true;
protected bool $deleted = false; protected bool $deleted = false;
public string $uiTheme = "default";
public $companyId; public $companyId;
public $eMail; public $eMail;
public bool $eMailConfirmed = false; public bool $eMailConfirmed = false;
protected array $config = [];
protected array $settings = [
"pagesize" => 30
];
protected ?company $__company = null; protected ?company $__company = null;
protected static $excludeProps = ["authRefId"];
protected static $excludeProps = ["settings","authRefId"];
public static $sqlTable = "tblUsers"; public static $sqlTable = "tblUsers";
public static $deletedFieldName = "deleted"; public static $deletedFieldName = "deleted";
public static $sqlColumns = [ public static $sqlColumns = [
@ -300,6 +279,13 @@ class user extends baseClass implements externalCallable
return true; return true;
} }
public function export() {
$rv=parent::export();
$rv["config"]=(object)$this->config;
return $rv;
}
### REST API
public static function API_GET_list(request $request) public static function API_GET_list(request $request)
{ {
if (! $request->user->checkAccess("adminUsers", "core")) { if (! $request->user->checkAccess("adminUsers", "core")) {
@ -313,7 +299,7 @@ class user extends baseClass implements externalCallable
throw new foxException("Forbidden", 403); throw new foxException("Forbidden", 403);
} }
$pageSize=common::clearInput($request->requestBody->pageSize,"0-9"); $pageSize=common::clearInput($request->requestBody->pageSize,"0-9");
if (empty($pageSize)) { $pageSize=$request->user->settings["pageSize"];} if (empty($pageSize)) { $pageSize=$request->user->config["pageSize"];}
return static::search(common::clearInput($request->requestBody->pattern), $pageSize)->result; return static::search(common::clearInput($request->requestBody->pattern), $pageSize)->result;

View File

@ -135,6 +135,15 @@ class userGroup extends baseClass implements externalCallable
$user->flushACRCache(); $user->flushACRCache();
return true; return true;
} }
public function isMember(user $user) {
foreach (userGroupMembership::getUsersInGroup($this, $this->sql) as $ugm) {
if ($ugm->user->id == $user->id) {
return true;
}
}
return false;
}
public function addAccessRule(string $rule, string $modInstance = "<all>") public function addAccessRule(string $rule, string $modInstance = "<all>")
{ {

View File

@ -135,7 +135,7 @@ class userGroupMembership extends baseClass implements externalCallable
if (empty($page) || !(is_numeric($page))) {$page=0;} if (empty($page) || !(is_numeric($page))) {$page=0;}
@$pageSize=common::clearInput($request->requestBody->pageSize); @$pageSize=common::clearInput($request->requestBody->pageSize);
if (empty($pageSize) || !(is_numeric($pageSize))) {$pageSize=$request->user->settings["pageSize"];} if (empty($pageSize) || !(is_numeric($pageSize))) {$pageSize=$request->user->config["pageSize"];}
if (!empty($request->requestBody->userId)) { if (!empty($request->requestBody->userId)) {
$user = new user($userId=common::clearInput($request->requestBody->userId,"0-9")); $user = new user($userId=common::clearInput($request->requestBody->userId,"0-9"));

View File

@ -12,5 +12,7 @@
<script src="/static/js/core/fox-common.js"></script> <script src="/static/js/core/fox-common.js"></script>
<script type="module" src="/static/js/core/main.js"></script> <script type="module" src="/static/js/core/main.js"></script>
</head> </head>
<body></body> <body>
<div style="margin-left: auto; margin-right: auto; margin-top: 20px; padding: 20px; width: 500px; border-radius: 10px; border: 1px solid #FF350040; color: #FF350040; font-size: 30px; font-family: monospace, monospace; mono" id="jsNotStartedStub">JavaScript is not supported in your browser or has not been loaded yet. Wait a few minutes and if it doesn't help, try another browser.</div>
</body>
</html> </html>

View File

@ -40,7 +40,7 @@ export function exec(requestType, method , data, onSuccess,noblank,onError,versi
data: JSON.stringify(data), data: JSON.stringify(data),
type: requestType, type: requestType,
headers: headers, headers: headers,
async: ref.async==undefined?true:ref.async,
complete: function(data,textStatus,request) { complete: function(data,textStatus,request) {
if (data.getResponseHeader('X-Fox-Token-Renew') !=null) { if (data.getResponseHeader('X-Fox-Token-Renew') !=null) {
session.updateToken(data.getResponseHeader('X-Fox-Token-Renew')); session.updateToken(data.getResponseHeader('X-Fox-Token-Renew'));
@ -164,6 +164,19 @@ export class auth {
} }
export class session { export class session {
static getConfigItem(item) {
let user=this.get("user");
if (user && user.config && user.config[item]) {
return user.config[item];
} else {
return settings.get(item);
}
}
static getLang() {
return this.getConfigItem("language");
}
static close() { static close() {
localStorage.removeItem("token"); localStorage.removeItem("token");
localStorage.removeItem("tokenExpire"); localStorage.removeItem("tokenExpire");
@ -203,6 +216,14 @@ export class session {
return session.get("menu"); return session.get("menu");
} }
} }
static checkAccess(rule, module) {
let acls=this.get("acls");
if (module==undefined) { module="core"; }
if (acls[module]!=undefined && Object.values(acls[module]).includes(rule)) { return true; }
else if (acls["<all>"]!=undefined && acls["<all>"].includes(rule)) { return true; }
else if (acls["<all>"]!=undefined && acls["<all>"].includes("isRoot")) { return true; }
else { return false; }
}
static getModInstances() { static getModInstances() {
if (session.get("modInstances") == undefined) { if (session.get("modInstances") == undefined) {

167
static/js/core/coreComps.js Normal file
View File

@ -0,0 +1,167 @@
export async function load() {
UI.breadcrumbsUpdate($("<span>",{text: langPack.core.breadcrumbs.modTitle+" / "+langPack.core.iface.companies}));
UI.createTabsPanel([
{id: "comps", title: langPack.core.iface.companies , preLoad: reloadCompanies, buttons: UI.addButton({id: "btnCompAdd", icon: "fas fa-plus", title: langPack.core.iface.add, onClick: btnCmpAdd_Click})}
]);
}
async function reloadCompanies() {
await API.exec("GET", "core/company/list", {}, function(json,_textStatus) {
$("#comps").empty();
let dt=$("<table>",{class: "datatable sel"}).appendTo("#comps");
let trh=$("<tr>").appendTo(dt);
$("<th>",{class: "idx", text: "#"}).appendTo(trh);
$("<th>",{class: "code", text: langPack.core.iface.uid}).appendTo(trh);
$("<th>",{text: langPack.core.iface.type}).appendTo(trh);
$("<th>",{text: langPack.core.iface.title}).appendTo(trh);
$("<th>",{text: langPack.core.iface.comps.qName}).appendTo(trh);
$("<th>",{text: langPack.core.iface.desc}).appendTo(trh);
$("<th>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})}).appendTo(trh);
$.each(json.data.result,function(key,val) {
let trd=$("<tr>").appendTo(dt).bind('contextmenu', usmContextMenuOpen).addClass("contextMenu").attr("compId",val.id).attr("compUid",val.invCode);
$("<td>",{class: "idx", text: key}).appendTo(trd);
$("<td>",{class: "code", text: UI.formatInvCode(val.invCode)}).appendTo(trd);
$("<td>",{text: langPack.core.iface.comps[val.type]}).appendTo(trd);
$("<td>",{text: val.name}).appendTo(trd);
$("<td>",{text: val.qName}).appendTo(trd);
$("<td>",{text: val.description}).appendTo(trd);
$("<td>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})})
.click(usmContextMenuOpen)
.appendTo(trd);
});
},false);
}
function usmContextMenuOpen(el) {
let uid=$(el.target).closest("tr").attr("compUid");
UI.contextMenuOpen(el,[
{title: langPack.core.iface.edit, onClick: function() {
compEdit_Click(el);
}},
{title: langPack.core.iface.delete, onClick: function() {
compDel_Click(el);
}},
],"#"+UI.formatInvCode(uid));
return false;
}
function compDel_Click(ref) {
var id=($(ref.target).closest("tr").attr("compId"));
var uid=($(ref.target).closest("tr").attr("compUid"));
var buttons={};
buttons[langPack.core.iface.dialodDelButton]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "DELETE",
method: "core/company/"+id,
onSuccess: function(json) {
reloadCompanies();
},
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.groups.delButtonTitle+" #"+uid+" "+$(ref.target).closest("tr").attr("xname"),langPack.core.iface.groups.delButtonTitle,buttons);
}
async function compEdit_Click(el) {
let id=$(el.currentTarget).closest("tr").attr("compId");
var buttons={};
buttons[langPack.core.iface.dialodAddButton]=function() {
let data=UI.collectForm("ItemAdd", true,false, false,true);
if (data.validateErrCount > 0) {
return;
}
let vals=data.getVals();
API.exec("PATCH",UI.parceURL().module+"/company/"+id,vals, function onAjaxSuccess(json,_textStatus) {
UI.closeDialog('ItemAdd');
reloadCompanies();
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { UI.closeDialog('ItemAdd'); }
UI.createDialog(
UI.addFieldGroup([
UI.addField({title: langPack.core.iface.title, item: "cao_name", type: "input",reqx: true}),
UI.addField({title: langPack.core.iface.comps.qName, item: "cao_qName", type: "input",reqx: true}),
UI.addField({title: langPack.core.iface.desc, item: "cao_description", type: "input"}),
UI.addField({title: langPack.core.iface.type, item: "cao_type", type: "select", reqx: true, args: [
{val:"",title: ""},
{val:"company",title: langPack.core.iface.comps.company},
{val:"client",title: langPack.core.iface.comps.client},
{val:"supplier",title: langPack.core.iface.comps.supplier},
{val:"parner",title: langPack.core.iface.comps.partner},
]}),
]),
langPack.core.iface.edit,
buttons,
350,1,'ItemAdd'
);
await API.exec("GET",UI.parceURL().module+"/company/"+id,{}, function onAjaxSuccess(json,_textStatus) {
$("#cao_name").val(json.data.name);
$("#cao_qName").val(json.data.qName);
$("#cao_description").val(json.data.description);
$("#cao_type").val(json.data.type);
});
UI.openDialog('ItemAdd');
}
function btnCmpAdd_Click(ref) {
var buttons={};
buttons[langPack.core.iface.dialodAddButton]=function() {
let data=UI.collectForm("ItemAdd", true,false, false,true);
if (data.validateErrCount > 0) {
return;
}
let vals=data.getVals();
API.exec("PUT",UI.parceURL().module+"/company",vals, function onAjaxSuccess(json,_textStatus) {
UI.closeDialog('ItemAdd');
reloadCompanies();
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { UI.closeDialog('ItemAdd'); }
UI.createDialog(
UI.addFieldGroup([
UI.addField({title: langPack.core.iface.title, item: "cao_name", type: "input",reqx: true}),
UI.addField({title: langPack.core.iface.comps.qName, item: "cao_qName", type: "input",reqx: true}),
UI.addField({title: langPack.core.iface.desc, item: "cao_description", type: "input"}),
UI.addField({title: langPack.core.iface.type, item: "cao_type", type: "select", reqx: true, args: [
{val:"",title: ""},
{val:"company",title: langPack.core.iface.comps.company},
{val:"client",title: langPack.core.iface.comps.client},
{val:"supplier",title: langPack.core.iface.comps.supplier},
{val:"parner",title: langPack.core.iface.comps.partner},
]}),
]),
langPack.core.iface.add,
buttons,
350,1,'ItemAdd'
);
UI.openDialog('ItemAdd');
}

View File

@ -12,6 +12,7 @@ export var menuSelector={
"group":"adminGrous", "group":"adminGrous",
mailAccounts:"mailAccounts", mailAccounts:"mailAccounts",
oauth: "oauth", oauth: "oauth",
comps: "adminComps"
}; };
export function load() { export function load() {
@ -71,6 +72,12 @@ export function load() {
mod.load(); mod.load();
}) })
break; break;
case "comps":
import("./coreComps.js").then(function(mod) {
mod.load();
})
break;
default: default:

View File

@ -18,6 +18,8 @@ export var langItem={
notImplementedYet: "Функция не реализована", notImplementedYet: "Функция не реализована",
generalError: "Ошибка", generalError: "Ошибка",
active: "Активен", active: "Активен",
user: "Пользователь",
group: "Группа",
version: "Версия", version: "Версия",
title: "Наименование", title: "Наименование",
url: "URL", url: "URL",
@ -26,7 +28,10 @@ export var langItem={
install: "Установить", install: "Установить",
set: "Установить", set: "Установить",
edit: "Изменить", edit: "Изменить",
copy: "Копировать",
paste: "Вставить",
updated: "Обновлен", updated: "Обновлен",
reload: "Обновить",
invCode: "invCode", invCode: "invCode",
dialodCloseButton: "Закрыть", dialodCloseButton: "Закрыть",
dialodDelButton: "Удалить", dialodDelButton: "Удалить",
@ -43,6 +48,8 @@ export var langItem={
dialogReсoveryTitle: "Восстановление доступа", dialogReсoveryTitle: "Восстановление доступа",
genDescTitle: "Информация", genDescTitle: "Информация",
contextMenuCopySelected: "Копировать выделение", contextMenuCopySelected: "Копировать выделение",
contextMenu: "Открыть действия",
actions: "Действия",
language:"Язык", language:"Язык",
yes: "Да", yes: "Да",
no: "Нет", no: "Нет",
@ -61,6 +68,18 @@ export var langItem={
confCodeFmtErr: "Неверный формат кода подтверждения", confCodeFmtErr: "Неверный формат кода подтверждения",
recoverFormEmtyX: "Должно быть заполнено одно из полей", recoverFormEmtyX: "Должно быть заполнено одно из полей",
oauthLoginFailedUNR: "Пользователь не зарегистрирован в системе. Для продолжения работы нужно зарегистрироваться на начальной странице.", oauthLoginFailedUNR: "Пользователь не зарегистрирован в системе. Для продолжения работы нужно зарегистрироваться на начальной странице.",
type: "Тип",
types: "Типы",
company: "Компания",
companies: "Компании",
uid: "UID",
comps: {
qName: "Алиас",
company: "Компания",
client: "Клиент",
supplier: "Поставщик",
partner: "Партнёр",
},
modules: { modules: {
installedTabTitle: "Установленные", installedTabTitle: "Установленные",
availTabTitle: "Все доступные", availTabTitle: "Все доступные",

View File

@ -20,7 +20,7 @@ export async function lpInit () {
API = mod; API = mod;
}); });
var lang=API.settings.get("language"); var lang=API.session.getLang();
console.log("load "+lang+" language..."); console.log("load "+lang+" language...");
let modules=[]; let modules=[];

View File

@ -1,11 +1,17 @@
import * as API from './api.js'; $("#jsNotStartedStub").remove();
import * as UI from './ui.js';
import { lpInit } from './langpack.js'; import { lpInit } from './langpack.js';
import { foxMenu } from './ui.menu.js'; import { foxMenu } from './ui.menu.js';
var popState_Installed=false; var popState_Installed=false;
$(document).ready(function() { $(document).ready(async function() {
let i=0;
while (UI ==undefined || API ==undefined) {
await new Promise(r => setTimeout(r, 10));
}
load(); load();
}); });
@ -20,7 +26,7 @@ export function boot(xlite) {
if (xlite==undefined) { xlite=false; } if (xlite==undefined) { xlite=false; }
if (!xlite) { if (!xlite) {
UI.setTitle(API.settings.get("title")); UI.setTitle(API.settings.get("title"));
UI.loadCSS("/static/theme/"+API.settings.get("theme")+"/main.css"); UI.loadCSS("/static/theme/"+API.session.getConfigItem("theme")+"/main.css");
} }
UI.contextMenuClose(); UI.contextMenuClose();

View File

@ -118,18 +118,22 @@ export function empty() {
*/ */
export function createLeftPanel(panels) { export function createLeftPanel(panels) {
$("<div>", { class: "widget_panel_left" }).appendTo(".t_main #mainframe"); let ref=($(".t_main #mainframe div.widget_panel_left"));
if (ref.length==0) {
ref=$("<div>", { class: "widget_panel_left" }).appendTo(".t_main #mainframe");
} else {
ref.empty();
}
var divAccord=$("<div>",{ var divAccord=$("<div>",{
id: "accordion", id: "accordion",
class: "accordion" class: "accordion"
}).appendTo(".widget_panel_left"); }).appendTo(ref);
$.each(panels,function (index,panel) { $.each(panels,function (index,panel) {
let px = $("<h3>",{text: panel.title, id: panel.id+"_title", append: panel.buttons, style: panel.disabled?"display: none":"" })
let px = $("<h3>",{text: panel.title, append: panel.buttons }) .add($("<div>", { class: "widget lock c_contacts", id: panel.id, text: langPack.core.iface.dataLoading, style: panel.disabled?"display: none":""}));
.add($("<div>", { class: "widget lock c_contacts", id: "gendesc", text: langPack.core.iface.dataLoading}));
px.appendTo(divAccord); px.appendTo(divAccord);
}); });
@ -159,13 +163,18 @@ function panelActivate(panel, callback) {
} }
export function createRightPanel(panels) { export function createRightPanel(panels) {
let ref=$("<div>", { class: "widget_panel_right" }).appendTo(".t_main #mainframe"); let ref=($(".t_main #mainframe div.widget_panel_right"));
if (ref.length==0) {
ref=$("<div>", { class: "widget_panel_right" }).appendTo(".t_main #mainframe");
}
createTabsPanel(panels,ref); createTabsPanel(panels,ref);
} }
export function createTabsPanel(panels,ref) { export function createTabsPanel(panels,ref) {
if (ref===undefined) { if (ref===undefined) {
ref=$(".t_main #mainframe"); ref=$(".t_main #mainframe");
} else {
ref.empty();
} }
var callback={}; var callback={};
@ -226,6 +235,12 @@ export function createTabsPanel(panels,ref) {
panelBeforeActivate(ui.newPanel,callback); panelBeforeActivate(ui.newPanel,callback);
} }
}); });
$.each(panels,function (_index,panel) {
if (panel.disabled) {
$( "#item_tabs").tabs("disable","#tab-"+panel.id);
}
});
} }
@ -243,10 +258,10 @@ export function addFieldGroup(items)
* } * }
*/ */
export function addField(ref)//title, item, blockstyle, fieldstyle, type, args, onChange) export function addField(ref)//title, item, blockstyle, fieldstyle, type, args, onChange, onClick, onContextMenu)
{ {
var item=undefined; var item=undefined;
if (ref.type === undefined || ref.item===undefined) { if (ref.type === undefined || (ref.item===undefined && ref.type != "href")) {
var type = 'label'; var type = 'label';
} else { } else {
var type=ref.type; var type=ref.type;
@ -338,7 +353,11 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
break; break;
case "label": case "label":
item = $("<span>", {class: "i", html: ref.val}); item = $("<span>", {id: ref.item, class: "i", html: ref.val});
break;
case "href":
item = $("<a>", {href: ref.href, id: ref.item, class: "i", html: ref.val, click: smartClick});
break; break;
case "multiline": case "multiline":
@ -396,7 +415,10 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
if (ref.disabled) { item.attr("disabled","true")} if (ref.disabled) { item.attr("disabled","true")}
} }
item.change(function() {$(this).addClass('changed');}); item.change(function() {$(this).addClass('changed');})
if (ref.attrs && ref.attrs.disabled) {
item.attr("disabled",ref.attrs.disabled)
}
if (typeof(ref.onChange)=='function') { if (typeof(ref.onChange)=='function') {
item.change(ref.onChange); item.change(ref.onChange);
} }
@ -422,6 +444,18 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
style: ref.fieldstyle style: ref.fieldstyle
})) }))
}); });
if (type=="label") {
if (typeof(ref.onClick)=="function") {
rv.click(ref.onClick);
rv.addClass("clickable");
}
if (typeof(ref.onContextMenu)=="function") {
rv.bind('contextmenu click', ref.onContextMenu);
rv.addClass("withContextMenu");
}
}
$.each(ref.attrs, function(key, val) { $.each(ref.attrs, function(key, val) {
rv.attr(key,val); rv.attr(key,val);
@ -443,7 +477,13 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
} }
export function breadcrumbsUpdate(text) { export function breadcrumbsUpdate(text) {
$("#breadcrumbs_label").text(text); if (typeof(text)=="string") {
$("#breadcrumbs_label").text(text);
} else {
$("#breadcrumbs_label").empty();
$("#breadcrumbs_label").append(text);
}
} }
export function breadcrumbsUpdateSuffix(text) { export function breadcrumbsUpdateSuffix(text) {
@ -885,7 +925,7 @@ export function contextMenuOpen(event, itemsList, title) {
$("body").unbind('contextmenu click', contextMenuClose); $("body").unbind('contextmenu click', contextMenuClose);
let selText = getSelectionText(); let selText = getSelectionText();
$("div#contextMenu").empty(); $("div#contextMenu").empty();
$("<p>",{class: "title cmTitle", text: title}).appendTo("div#contextMenu"); if (isset(title)) { $("<p>",{class: "title cmTitle", text: title}).appendTo("div#contextMenu"); }
let items=$("<div>",{class: "items"}).appendTo("div#contextMenu"); let items=$("<div>",{class: "items"}).appendTo("div#contextMenu");
if (isset(selText)) { if (isset(selText)) {
@ -1110,9 +1150,15 @@ Number.prototype.pad = function(size) {
href={href: href}; href={href: href};
} }
if (href.external==true) { if (href.external==true) {
this.click(function() { document.location.href=href.href; return false;}); this.click(function() {
if (getSelectionText()) { return; }
document.location.href=href.href; return false;
});
} else { } else {
this.prop("href",href.href).click(xClick); this.prop("href",href.href).click(function(ref) {
if (getSelectionText()) { return; }
xClick(ref);
});
} }
this.addClass("clickable"); this.addClass("clickable");
$.each(this.find("td"),function(key,val) { $.each(this.find("td"),function(key,val) {
@ -1122,5 +1168,15 @@ Number.prototype.pad = function(size) {
} }
}); });
return this; return this;
},
onEnter: function(callback) {
if (typeof(callback)=="function") {
this.on('keyup', function (e) {
if (e.key === 'Enter' || e.keyCode === 13) {
callback(this);
}
})
}
} }
})})(jQuery) })})(jQuery)

View File

@ -16,10 +16,11 @@ export class foxMenu {
href: "logout" href: "logout"
}; };
foxMenu.drawMenuNode(items,$("#menu_base_1")); foxMenu.drawMenuNode(items,$("#menu_base_1"));
foxMenu.menuClose();
} }
static drawMenuNode(items,ref,deep,lang, xmodkey) { static drawMenuNode(items,ref,deep,lang, xmodkey) {
if (lang==undefined) { lang=API.settings.get("language");} if (lang==undefined) { lang=API.session.getLang();}
if (deep==undefined) { deep=0; } if (deep==undefined) { deep=0; }
let xref=$("<ul>") let xref=$("<ul>")
.appendTo(ref); .appendTo(ref);
@ -28,6 +29,9 @@ export class foxMenu {
if (val.xmodkey!=undefined) { xmodkey=val.xmodkey; } if (val.xmodkey!=undefined) { xmodkey=val.xmodkey; }
let aclMatch=(val.accessRule==undefined) || API.session.checkAccess(val.accessRule, xmodkey);
if (!aclMatch) { return; }
let title=val.title[lang]; let title=val.title[lang];
if (title==undefined) { title=Object.values(val.title).shift();}; if (title==undefined) { title=Object.values(val.title).shift();};
let xlabel; let xlabel;
@ -52,9 +56,8 @@ export class foxMenu {
.append($("<i>",{ class: "fas fa-minus minus"})) .append($("<i>",{ class: "fas fa-minus minus"}))
.append(xlabel) .append(xlabel)
) )
.appendTo(xref) .appendTo(xref)
if (deep==0) { if (deep==0) {
yref.addClass("xmenu_root"); yref.addClass("xmenu_root");
} else { } else {
@ -70,20 +73,22 @@ export class foxMenu {
} }
static menuItemClick(ref,noExec) { static menuClose() {
let xref=$(ref.target).closest("li");
let xchilds=xref.children("ul").children("li");
let xparents=xref.parents("li");
let xroot=xref.parents("li.xmenu_root");
$(".xmenu_dx").hide(); $(".xmenu_dx").hide();
$(".xmenu").removeClass("xmenu_open") $(".xmenu").removeClass("xmenu_open")
.removeClass("xmenu_active") .removeClass("xmenu_active")
.removeClass("xmenu_axtree") .removeClass("xmenu_axtree")
.addClass("xmenu_closed"); .addClass("xmenu_closed");
}
static menuItemClick(ref,noExec) {
let xref=$(ref.target).closest("li");
let xchilds=xref.children("ul").children("li");
let xparents=xref.parents("li");
let xroot=xref.parents("li.xmenu_root");
foxMenu.menuClose();
xref.addClass("xmenu_open") xref.addClass("xmenu_open")
.addClass("xmenu_active") .addClass("xmenu_active")
.removeClass("xmenu_closed") .removeClass("xmenu_closed")

View File

@ -148,7 +148,7 @@ table.datatable td.icon,
table.datatable th.icon table.datatable th.icon
{ {
width: 20px; width: 20px;
font-size: 16px; font-size: 14px;
text-align: center; text-align: center;
} }
@ -919,7 +919,8 @@ table.datatable td
table.datatable.sel tr.clickable, table.datatable.sel tr.clickable,
table.datatable.selm tbody.clickable, table.datatable.selm tbody.clickable,
table.datatable.selm tr.ss.clickable { table.datatable.selm tr.ss.clickable,
div.crm_entity_field_block.clickable {
cursor: pointer; cursor: pointer;
} }
@ -928,7 +929,8 @@ table.datatable.selm tbody.contextMenu,
table.datatable.selm tr.ss.contextMenu, table.datatable.selm tr.ss.contextMenu,
table.datatable.sel tr.contextMenu a, table.datatable.sel tr.contextMenu a,
table.datatable.selm tbody.contextMenu a, table.datatable.selm tbody.contextMenu a,
table.datatable.selm tr.ss.contextMenu a { table.datatable.selm tr.ss.contextMenu a,
div.crm_entity_field_block.withContextMenu {
cursor: context-menu; cursor: context-menu;
} }
@ -1675,9 +1677,11 @@ table.datatable td.code
color: rgba(255,255,255,0.8); color: rgba(255,255,255,0.8);
} }
table.datatable th.code table.datatable th.code,
table.datatable td.code
{ {
min-width: 110px; min-width: 110px;
width: 105px;
} }
table.datatable td.comment table.datatable td.comment
{ {
@ -2182,11 +2186,6 @@ td.hideable, th.hideable
display: none; display: none;
} }
table.datatable th.code
{
min-width: 40px;
}
td.hideablealt, th.hideablealt td.hideablealt, th.hideablealt
{ {
display: table-cell; display: table-cell;