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
*
* @desc baseClass mk 2 class
* @desc FOX Base Class
* @copyright MX STAR LLC 2021
* @version 4.0.0
* @author Pavel Dmitriev
@ -17,6 +16,7 @@ use Exception;
*
**/
class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportable
{
@ -33,7 +33,7 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
// id for xConstruct;
# if null - generated automatically
public static $sqlSelectTemplate = null;
public static $baseSqlSelectTemplate = null;
# primary index field. Default is id
public static $sqlIdx = "id";
@ -146,10 +146,10 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
# How to call from child template:
# parent::__construct($id, $sql, $prefix, $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`";
} else {
$this->__sqlSelectTemplate = $this::$sqlSelectTemplate;
$this->__sqlSelectTemplate = $this::$baseSqlSelectTemplate;
}
if (isset($sql)) {
@ -208,6 +208,8 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
protected function fillFromRow($row)
{
foreach ($row as $key => $val) {
if (! empty($this->fillPrefix)) {
if (! preg_match("/^" . $this->fillPrefix . "/", $key)) {
continue;
@ -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
{
$this->checkSql();
@ -553,7 +565,7 @@ class baseClass extends dbStoredBase implements \JsonSerializable, jsonImportabl
$where.=(empty($where)?"":" OR ")."`$key` like '%".common::clearInput($pattern)."'";
break;
case "invCode":
case "invcode":
$where.=(empty($where)?"":" OR ")."`$key`='".UID::clear($pattern)."'";
break;

View File

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

View File

@ -11,7 +11,7 @@ namespace fox;
* @license GPLv3
*
*/
class company extends baseClass
class company extends baseClass implements externalCallable
{
protected $id;
@ -28,6 +28,13 @@ class company extends baseClass
public bool $deleted = false;
public const types=[
"company",
"client",
"supplier",
"partner"
];
public static $sqlTable = "tblCompany";
public static $deletedFieldName = "deleted";
@ -35,18 +42,26 @@ class company extends baseClass
public static $sqlColumns = [
"name" => [
"type" => "VARCHAR(255)",
"nullable" => false
"nullable" => false,
"search"=>"like"
],
"qName" => [
"type" => "VARCHAR(255)",
"nullable" => false
"nullable" => false,
"search"=>"like"
],
"description" => [
"type" => "VARCHAR(255)"
"type" => "VARCHAR(255)",
"search"=>"like"
],
"type" => [
"type" => "VARCHAR(255)",
"nullable" => false
],
"invCode"=> [
"type"=>"CHAR(10)",
"nullable"=>false,
"search"=>"invCode"
]
];
@ -57,10 +72,98 @@ class company extends baseClass
protected function validateSave()
{
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));
}
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";
public const STATUS_ERR = "ERR";
public const STATUS_WARN = "WARN";
public const STATUS_ALERT = "ALERT";
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)
{
$e = new self($message, $code, $prev);

View File

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

View File

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

View File

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

View File

@ -173,5 +173,13 @@ class request extends baseClass implements noSqlMigration
}
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;
public $login;
protected UID $invCode;
protected $__secret;
public $authType;
public $authRefId;
public int $offlineAuthCtr = 0;
public $fullName;
protected bool $active = true;
protected bool $deleted = false;
public string $uiTheme = "default";
public $companyId;
public $eMail;
public bool $eMailConfirmed = false;
protected array $settings = [
"pagesize" => 30
];
protected array $config = [];
protected ?company $__company = null;
protected static $excludeProps = ["settings","authRefId"];
protected static $excludeProps = ["authRefId"];
public static $sqlTable = "tblUsers";
public static $deletedFieldName = "deleted";
public static $sqlColumns = [
@ -300,6 +279,13 @@ class user extends baseClass implements externalCallable
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)
{
if (! $request->user->checkAccess("adminUsers", "core")) {
@ -313,7 +299,7 @@ class user extends baseClass implements externalCallable
throw new foxException("Forbidden", 403);
}
$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;

View File

@ -136,6 +136,15 @@ class userGroup extends baseClass implements externalCallable
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>")
{
if (array_key_exists($modInstance, $this->accessRules) && (array_search($rule, $this->accessRules[$modInstance]) !== false)) {

View File

@ -135,7 +135,7 @@ class userGroupMembership extends baseClass implements externalCallable
if (empty($page) || !(is_numeric($page))) {$page=0;}
@$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)) {
$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 type="module" src="/static/js/core/main.js"></script>
</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>

View File

@ -40,7 +40,7 @@ export function exec(requestType, method , data, onSuccess,noblank,onError,versi
data: JSON.stringify(data),
type: requestType,
headers: headers,
async: ref.async==undefined?true:ref.async,
complete: function(data,textStatus,request) {
if (data.getResponseHeader('X-Fox-Token-Renew') !=null) {
session.updateToken(data.getResponseHeader('X-Fox-Token-Renew'));
@ -164,6 +164,19 @@ export class auth {
}
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() {
localStorage.removeItem("token");
localStorage.removeItem("tokenExpire");
@ -203,6 +216,14 @@ export class session {
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() {
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",
mailAccounts:"mailAccounts",
oauth: "oauth",
comps: "adminComps"
};
export function load() {
@ -72,6 +73,12 @@ export function load() {
})
break;
case "comps":
import("./coreComps.js").then(function(mod) {
mod.load();
})
break;
default:
throw new Error(404);

View File

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

View File

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

View File

@ -1,11 +1,17 @@
import * as API from './api.js';
import * as UI from './ui.js';
$("#jsNotStartedStub").remove();
import { lpInit } from './langpack.js';
import { foxMenu } from './ui.menu.js';
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();
});
@ -20,7 +26,7 @@ export function boot(xlite) {
if (xlite==undefined) { xlite=false; }
if (!xlite) {
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();

View File

@ -118,18 +118,22 @@ export function empty() {
*/
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>",{
id: "accordion",
class: "accordion"
}).appendTo(".widget_panel_left");
}).appendTo(ref);
$.each(panels,function (index,panel) {
let px = $("<h3>",{text: panel.title, append: panel.buttons })
.add($("<div>", { class: "widget lock c_contacts", id: "gendesc", text: langPack.core.iface.dataLoading}));
let px = $("<h3>",{text: panel.title, id: panel.id+"_title", append: panel.buttons, style: panel.disabled?"display: none":"" })
.add($("<div>", { class: "widget lock c_contacts", id: panel.id, text: langPack.core.iface.dataLoading, style: panel.disabled?"display: none":""}));
px.appendTo(divAccord);
});
@ -159,13 +163,18 @@ function panelActivate(panel, callback) {
}
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);
}
export function createTabsPanel(panels,ref) {
if (ref===undefined) {
ref=$(".t_main #mainframe");
} else {
ref.empty();
}
var callback={};
@ -226,6 +235,12 @@ export function createTabsPanel(panels,ref) {
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;
if (ref.type === undefined || ref.item===undefined) {
if (ref.type === undefined || (ref.item===undefined && ref.type != "href")) {
var type = 'label';
} else {
var type=ref.type;
@ -338,7 +353,11 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
break;
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;
case "multiline":
@ -396,7 +415,10 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
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') {
item.change(ref.onChange);
}
@ -423,6 +445,18 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
}))
});
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) {
rv.attr(key,val);
});
@ -443,7 +477,13 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
}
export function breadcrumbsUpdate(text) {
if (typeof(text)=="string") {
$("#breadcrumbs_label").text(text);
} else {
$("#breadcrumbs_label").empty();
$("#breadcrumbs_label").append(text);
}
}
export function breadcrumbsUpdateSuffix(text) {
@ -885,7 +925,7 @@ export function contextMenuOpen(event, itemsList, title) {
$("body").unbind('contextmenu click', contextMenuClose);
let selText = getSelectionText();
$("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");
if (isset(selText)) {
@ -1110,9 +1150,15 @@ Number.prototype.pad = function(size) {
href={href: href};
}
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 {
this.prop("href",href.href).click(xClick);
this.prop("href",href.href).click(function(ref) {
if (getSelectionText()) { return; }
xClick(ref);
});
}
this.addClass("clickable");
$.each(this.find("td"),function(key,val) {
@ -1122,5 +1168,15 @@ Number.prototype.pad = function(size) {
}
});
return this;
},
onEnter: function(callback) {
if (typeof(callback)=="function") {
this.on('keyup', function (e) {
if (e.key === 'Enter' || e.keyCode === 13) {
callback(this);
}
})
}
}
})})(jQuery)

View File

@ -16,10 +16,11 @@ export class foxMenu {
href: "logout"
};
foxMenu.drawMenuNode(items,$("#menu_base_1"));
foxMenu.menuClose();
}
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; }
let xref=$("<ul>")
.appendTo(ref);
@ -28,6 +29,9 @@ export class foxMenu {
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];
if (title==undefined) { title=Object.values(val.title).shift();};
let xlabel;
@ -52,7 +56,6 @@ export class foxMenu {
.append($("<i>",{ class: "fas fa-minus minus"}))
.append(xlabel)
)
.appendTo(xref)
if (deep==0) {
@ -70,13 +73,7 @@ export class foxMenu {
}
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");
static menuClose() {
$(".xmenu_dx").hide();
$(".xmenu").removeClass("xmenu_open")
@ -84,6 +81,14 @@ export class foxMenu {
.removeClass("xmenu_axtree")
.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")
.addClass("xmenu_active")
.removeClass("xmenu_closed")

View File

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