This commit is contained in:
Pavel Dmitriev 2022-08-24 00:40:48 +03:00
parent f59fdf61ca
commit 0aaddfbaec
19 changed files with 876 additions and 176 deletions

View File

@ -1,111 +0,0 @@
---
kind: pipeline
name: SonarQube check
image_pull_secrets:
- dockerconfig
trigger:
branch:
- develop
event:
- push
- custom
steps:
- name: SonarQube check
image: sonarsource/sonar-scanner-cli
environment:
SONAR_PROJECT:
from_secret: sonarProjectId
SONAR_TOKEN:
from_secret: sonarToken
SONAR_HOST:
from_secret: sonarHost
TEST: test
commands:
- sonar-scanner -Dsonar.projectKey=$${SONAR_PROJECT} -Dsonar.sources=. -Dsonar.host.url=$${SONAR_HOST} -Dsonar.login=$${SONAR_TOKEN}
depends_on:
- clone
---
kind: pipeline
name: Build image
image_pull_secrets:
- dockerconfig
trigger:
ref:
- refs/heads/testing
- refs/heads/master
- refs/tags/*
steps:
- name: Prepare image
image: mxfox.ru/mxfox/fox-web-basic:latest
commands:
- |
apt-get update -y
apt-get install curl -y
cd /tmp
curl -sS https://getcomposer.org/installer -o composer-setup.php
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
DEBIAN_FRONTEND=noninteractive TZ=Europe/Moscow apt-get -y install tzdata
cd -
- composer install
- |
find . -name "*~" -prune -exec rm -rf '{}' \;
find . -name "*.bak" -prune -exec rm -rf '{}' \;
find . -name "*.old" -prune -exec rm -rf '{}' \;
find . -name ".git" -prune -exec rm -rf '{}' \;
find . -name ".settings" -prune -exec rm -rf '{}' \;
find . -name ".buildpath" -prune -exec rm -rf '{}' \;
find . -name ".project" -prune -exec rm -rf '{}' \;
find . -name "README.*" -prune -exec rm -rf '{}' \;
find . -name "*.md" -prune -exec rm -rf '{}' \;
find . -name "composer.*" -prune -exec rm -rf '{}' \;
find . -name ".travis*" -prune -exec rm -rf '{}' \;
find . -name "installed.json" -prune -exec rm -rf '{}' \;
find . -name "*.sample" -prune -exec rm -rf '{}' \;
find . -type d ! -path './.git/**' ! -path './static/**' ! -path "./static" ! -path ./*/modules/*/static* -exec bash -c 'test -f {}/.htaccess && echo -n "[ SKIP ] " || (cp ./docker-build/.htaccess {} && echo -n "[ ADD ] ") && echo {}/.htaccess' \;
cp docker-build/Dockerfile Dockerfile
rm -f composer.*
- name: Build docker image
image: mxfox.ru/mxfox/docker-dind.buildx:latest
privileged: true
environment:
DOCKER_AUTH:
from_secret: dockerconfig
IMAGE_PREFIX: mxfox.ru/mxfox/chimera-mk2-core
commands:
- buildx-bgstart.sh
- echo $${DOCKER_AUTH} > ~/.docker/config.json
- echo "CB ${CI_COMMIT_BRANCH}"
- echo "DT ${DRONE_TAG}"
- |
if [ -n "${DRONE_TAG}" ]
then
export xBuildSuffix=" -t $${IMAGE_PREFIX}:${DRONE_TAG} -t $${IMAGE_PREFIX}:latest --push"
else
export xBuildSuffix=" -t $${IMAGE_PREFIX}:${CI_COMMIT_BRANCH}-${CI_BUILD_NUMBER}-${DRONE_COMMIT_SHA:0:10} -t $${IMAGE_PREFIX}:${CI_COMMIT_BRANCH} --push"
fi
- docker buildx build --platform linux/amd64,linux/arm64 . $${xBuildSuffix}
---
kind: pipeline
name: Export code
image_pull_secrets:
- dockerconfig
trigger:
ref:
- refs/tags/*
steps:
- name: Export code
image: mxfox.ru/mxfox/fox-web-basic:latest
commands:
- echo "Not implemented yet"

View File

@ -17,6 +17,7 @@ use fox\foxException;
use fox\request;
use fox\modules;
use fox\foxRequestResult;
use fox\moduleInfo;
class session implements externalCallable
{
@ -35,6 +36,7 @@ class session implements externalCallable
if ($request->authOK) {
$modules=[];
$i = 0;
foreach (modules::listInstalled() as $mod) {
if ($request->user->checkAccess($mod->globalAccessKey, $mod->name)) {
$i ++;
@ -48,11 +50,12 @@ class session implements externalCallable
}
}
return [
"updated" => time(),
"user" => $request->token->user,
"acls" => $request->user->getAccessRules(),
"modules" => $modules
"modules" => $modules,
];
}
throw new foxException("Unauthorized", 401);

View File

@ -77,6 +77,8 @@ class baseModule
public static $crontab=[];
public static $themes=[];
public static function getModInfo(): moduleInfo
{
$mi = new moduleInfo();
@ -90,6 +92,7 @@ class baseModule
$mi->globalAccessKey = static::$globalAccessKey;
$mi->languages = static::$languages;
$mi->configKeys=static::$configKeys;
$mi->themes=static::$themes;
return $mi;
}

View File

@ -84,6 +84,11 @@ class confirmCode extends baseClass {
return !$this->fillByHash();
}
public static function flushExpired() {
$sql=static::qGetSql();
$sql->quickExec("DELETE FROM `".static::$sqlTable."` where `expireStamp` < '".time::current()."'");
}
}
?>

View File

@ -30,9 +30,10 @@ class file extends baseClass implements externalCallable {
protected $__data;
public static $sqlTable="tblFiles";
public static $allowDeleteFromDB = true;
# file access token expires in 30 seconds
const fileTokenTTL=30;
# file access token expires in 120 seconds
const fileTokenTTL=120;
const defaultBucket="files";
protected function __xConstruct() {
@ -78,6 +79,16 @@ class file extends baseClass implements externalCallable {
];
protected function validateDelete()
{
if (empty($this->id)) { throw new foxException("Unable to delete empty file"); }
if ($this->uuid) {
$s3=new s3client();
$s3->deleteObject(static::defaultBucket, $this->uuid);
}
return true;
}
protected function validateSave()
{
if (empty($this->uuid)) { $this->uuid = common::getUUID(); }
@ -103,6 +114,10 @@ class file extends baseClass implements externalCallable {
$xWhere .= ($xWhere?" AND ":"")." `refId` = '".common::clearInput($options["refId"])."'";
}
if (!empty($options["expired"])) {
$xWhere .= ($xWhere?" AND ":"")." `expireStamp` <= '".time::current()."'";
}
if ($xWhere) {
if ($where) {
@ -114,7 +129,7 @@ class file extends baseClass implements externalCallable {
return ["where"=>$where, "join"=>null];
}
protected static function prepareFileUpload($data, $fileName, object $ref, string $instance=null, user $owner=null, $ttl=null) {
protected static function prepareFileUpload($fileName, object $ref, string $instance=null, user $owner=null, $ttl=null) {
$f=new static();
$f->fileName=$fileName;
$f->module=$instance;
@ -125,14 +140,26 @@ class file extends baseClass implements externalCallable {
return $f;
}
public static function directUpload($data, $fileName, object $ref, string $instance=null, user $owner=null, $ttl=null) {
$f=static::prepareFileUpload($data, $fileName, $ref, $instance, $owner, $ttl);
public function upload($data) {
if (!empty($this->id)) {
throw new foxException("Not allowed for existing files");
}
if (empty($this->uuid)) {
throw new foxException("Empty UUID not allowed here");
}
$s3=new s3client();
if (!$s3->headBucket(static::defaultBucket)) {
$s3->createBucket(static::defaultBucket);
}
$s3->putObject(static::defaultBucket,$f->uuid, $data);
$f->save();
$s3->putObject(static::defaultBucket,$this->uuid, $data);
$this->save();
}
public static function directUpload($data, $fileName, object $ref, string $instance=null, user $owner=null, $ttl=null) {
$f=static::prepareFileUpload($fileName, $ref, $instance, $owner, $ttl);
$f->upload($data);
return $f;
}
@ -163,20 +190,21 @@ class file extends baseClass implements externalCallable {
return $token;
}
public static function getByToken($token) {
public static function getByToken($token, $upload=false) {
$c=new cache();
$ftile=$c->get('fileDnldToken-'.$token,true);
$prefix=$upload?"fileUpldToken":"fileDnldToken";
$ftile=$c->get($prefix.'-'.$token,true);
if ($ftile) {
$file = new static($ftile);
$c->del('fileDnldToken-'.$token);
$c->del($prefix.'-'.$token);
return $file;
} else {
throw new foxException("Invalid token",404);
}
}
public static function getUploadToken($data, $fileName, object $ref, string $instance=null, user $owner=null) {
$f=static::prepareFileUpload($data, $fileName, $ref, $instance, $owner);
public static function getUploadToken($fileName, object $ref, string $instance=null, user $owner=null) {
$f=static::prepareFileUpload($fileName, $ref, $instance, $owner);
$token=common::genPasswd(32);
$c=new cache();
$c->set('fileUpldToken-'.$token,$f,static::fileTokenTTL);
@ -196,6 +224,44 @@ class file extends baseClass implements externalCallable {
exit;
}
public static function API_UnAuth_POST(request $request) {
if (!empty($request->parameters)) throw new foxException("Invalid request", 400);
$err=false;
$rv=[];
foreach ($_FILES as $sFile) {
$sOK=$sFile["error"]==0;
if (!$sOK) {
throw new foxException("File upload error");
}
}
foreach ($_FILES as $token=>$sFile) {
$sOK=$sFile["error"]==0;
if ($sOK) {
$file = file::getByToken(common::clearInput($token),true);
$tmpPath=$sFile["tmp_name"];
$data=file_get_contents($tmpPath);
$file->upload($data);
$rv[$token]=[
"fileName"=>$sFile["name"],
"fileSize"=>$sFile["size"],
"status"=>"OK",
];
} else {
$err=true;
$rv[$token]=[
"fileName"=>$sFile["name"],
"status"=>"Fail",
];
}
}
return [
"status"=>$err?"ERR":"OK",
"details"=>$rv,
];
}
}

View File

@ -12,9 +12,11 @@ namespace fox\meta;
*
**/
use fox\cache;
use fox\externalCallable;
use fox\request;
use fox\config;
use fox\moduleInfo;
use fox\time;
use fox\modules;
use fox\oAuthProfile;
@ -23,18 +25,44 @@ class settings implements externalCallable
{
public static function APICall(request $request)
{
$profiles = oAuthProfile::search()->result;
$oauth=[];
foreach ($profiles as $p) {
if ($p->enabled) {
$oauth[] = [
"name"=>$p->name,
"id"=>$p->id,
"icon"=>$p->getClient(null)->getAuthIcon(),
];
$c=new cache();
$oauth=$c->get("coreSettingsOauthProfiles");
if ($oauth==null) {
$profiles = oAuthProfile::search()->result;
$oauth=[];
foreach ($profiles as $p) {
if ($p->enabled) {
$oauth[] = [
"name"=>$p->name,
"id"=>$p->id,
"icon"=>$p->getClient(null)->getAuthIcon(),
];
}
}
$c->set("coreSettingsOauthProfiles", $oauth);
}
$themes=$c->get("coreSettingsThemes");
if ($themes==null) {
$themes=[];
foreach (moduleInfo::getByFeature("theme") as $mod) {
if ($mod->themes) {
$themes=array_merge($themes, $mod->themes);
} else {
$themes[$mod->name]=$mod->title;
}
}
$c->set("coreSettingsThemes",$themes);
}
$coreLangs=$c->get("coreSettingsLanguages");
if ($coreLangs==null) {
$coreLangs=modules::list()["core"]->languages;
$c->set("coreSettingsLanguages",$coreLangs);
}
return [
"title" => config::get("TITLE"),
"sitePrefix" => config::get("SITEPREFIX"),
@ -45,8 +73,9 @@ class settings implements externalCallable
"language" => config::get("DEFAULT_LANGUAGE") === null ? "ru" : config::get("DEFAULT_LANGUAGE"),
"defaultModule" => config::get("DEFAULT_MODULE") === null ? "core" : config::get("DEFAULT_MODULE"),
"sessionRenewInterval" => config::get("SESSION_RENEW_SEC") === null ? "3600" : config::get("SESSION_RENEW_SEC"),
"coreLanguages" => modules::list()["core"]->languages,
"coreLanguages" => $coreLangs,
"oauthProfiles"=>$oauth,
"themes"=>$themes,
];
}
}

View File

@ -29,6 +29,8 @@ class moduleInfo extends baseClass implements externalCallable
public array $features = [];
public array $themes=[];
public bool $enabled = true;
public string $modVersion = "1.0.0";
@ -104,6 +106,9 @@ class moduleInfo extends baseClass implements externalCallable
],
"configKeys" => [
"type" => "SKIP"
],
"themes" => [
"type" => "SKIP"
]
];
@ -123,6 +128,7 @@ class moduleInfo extends baseClass implements externalCallable
$this->namespace = $this->template->namespace;
$this->languages = $this->template->languages;
$this->configKeys= $this->template->configKeys;
$this->themes=$this->template->themes;
}
return $rv;
}

View File

@ -25,7 +25,8 @@ class modules implements externalCallable
"namespace" => "fox",
"features" => [
"page",
"menu"
"menu",
"theme"
],
"isTemplate" => true,
"singleInstanceOnly" => true,
@ -33,6 +34,10 @@ class modules implements externalCallable
"languages" => [
"ru"
],
"themes"=>[
"chimera"=>"Chimera Theme",
"polarfox"=>"PolarFox Theme",
],
"ACLRules" => [
"isRoot" => "Superadmin user",
"adminViewModules"=>"Manage modules",

View File

@ -184,7 +184,7 @@ class user extends baseClass implements externalCallable
public static function getByEmail($eMail) {
$ref=new static();
$sql = $ref->getSql();
$res = $sql->quickExec1Line($ref->sqlSelectTemplate. " where `eMail`='".common::clearInput($eMail,"@0-9A-Za-z._-")."'");
$res = $sql->quickExec1Line($ref->sqlSelectTemplate. " where `eMail`='".common::clearInput($eMail,"@0-9A-Za-z._-")."' and `deleted` != 1");
if ($res) {
return new static($res);
} else {
@ -354,9 +354,74 @@ class user extends baseClass implements externalCallable
static::log($request->instance,__FUNCTION__, "Mail address confirmed for user ".$request->user->login,$request->user,"user",$request->user->id,null,logEntry::sevInfo);
return;
} else {
foxException::throw("ERR", "Validation failed", 400);
foxException::throw("ERR", "Validation failed", 400,"IVCC");
}
}
public static function API_PATCH(request $request) {
if (!empty($request->parameters)) { throw new foxException("Invalid request", 400); }
if ($request->user->id == $request->function) {
$u=$request->user;
} else {
$request->blockIfNoAccess("adminUsers", "core");
$u=new static(common::clearInput($request->function));
}
$resendEMailConfirmation=false;
if (property_exists($request->requestBody, "fullName")) { $u->fullName=$request->requestBody->fullName; }
if (property_exists($request->requestBody, "enabled")) { $u->active=$request->requestBody->enabled==1; }
if (property_exists($request->requestBody, "eMail") && $request->requestBody->eMail != $u->eMail) {
if ($request->requestBody->eMail && !common::validateEMail($request->requestBody->eMail)) {
foxException::throw(foxException::STATUS_ERR, "Invalid email format", 400, "WREML");
}
if ($request->requestBody->eMail) {
if ($rx=static::getByEmail($request->requestBody->eMail)) {
trigger_error(json_encode($rx));
foxException::throw(foxException::STATUS_ERR, "EMail already in-use", 400, "UAX");
}
$u->eMail=$request->requestBody->eMail;
$u->eMailConfirmed=false;
$resendEMailConfirmation=true;
} else {
$u->eMail=null;
}
}
if (!empty($request->requestBody->password)) {
if (strlen($request->requestBody->password) < 6) {
throw new foxException("Password too short");
}
$u->setPassword($request->requestBody->password);
}
$u->save();
if ($resendEMailConfirmation) {
$u->sendEMailConfirmation();
}
return $u;
}
public static function APIX_PATCH_settings(request $request) {
if ($request->user->id == $request->function) {
$u=$request->user;
} else {
$request->blockIfNoAccess("adminUsers", "core");
$u=new static(common::clearInput($request->function));
}
foreach ($request->requestBody as $key=>$val) {
$u->config[common::clearInput($key)]=$val;
}
$u->save();
return $u->config;
}
public static function API_DELETE(request $request) {
$request->blockIfNoAccess("adminUsers", "core");
$u=new static(common::clearInput($request->function));
$u->delete();
}
}
?>

View File

@ -7,4 +7,6 @@
# | | | | |
# * * * * * user-name command to be executed
* * * * * www-data bash -c '/var/www/html/fox-cron.d/cron.php &>> /var/log/fox/fox-cron.log'
* * * * * www-data bash -c '/var/www/html/fox-cron.d/expungeFiles.php &>> /var/log/fox/fox-cron.log'
* * * * * www-data bash -c '/var/www/html/fox-cron.d/flushExpiredConfirmationCodes.php &>> /var/log/fox/fox-cron.log'

26
fox-cron.d/expungeFiles.php Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/php
<?php
/**
*
* Script expungeFiles.php
*
* @copyright MX STAR LLC 2018-2022
* @version 4.0.0
* @author Pavel Dmitriev
* @license GPLv3
*
**/
use fox\file;
if (php_sapi_name() != 'cli') {
throw new Exception("This script can be run via CLI only");
}
require_once(getenv("FOX_WEBROOT")."/Autoloader.php");
$list = file::search(options: ["expired"=>1]);
foreach ($list->result as $file) {
$file->delete();
}

View File

@ -0,0 +1,23 @@
#!/usr/bin/php
<?php
/**
*
* Script flushExpiredConfirmationCodes.php
*
* @copyright MX STAR LLC 2018-2022
* @version 4.0.0
* @author Pavel Dmitriev
* @license GPLv3
*
**/
use fox\confirmCode;
if (php_sapi_name() != 'cli') {
throw new Exception("This script can be run via CLI only");
}
require_once(getenv("FOX_WEBROOT")."/Autoloader.php");
confirmCode::flushExpired();

View File

@ -119,6 +119,38 @@ export function getFileByToken(token, fileName) {
document.body.removeChild(a);
}
function uploadFileToken(file, token){
let formData = new FormData();
formData.append(token, file);
fetch('/api/v2/core/file', {
method: "POST", body: formData
});
}
export function uploadFiles(method, filesFieldId, extraFields) {
let data={};
if (typeof(extraFields)=='object') {
let data=extraFields;
}
let files = document.getElementById(filesFieldId).files;
$.each(files, function(_key, file) {
data.fileName=file.name;
data.fileSize=file.size;
API.exec({
requestType: "POST",
method: method,
data: data,
onSuccess: function(json) {
uploadFileToken(file, json.data.Token);
return false;
}
})
})
return;
}
export class settings {
static async load() {
if (sessionStorage.getItem("baseSettings")==undefined) {
@ -273,6 +305,11 @@ export class session {
sessionStorage.setItem("session",JSON.stringify(xsession));
}
static async reload(callback) {
sessionStorage.removeItem("session");
this.load(callback);
}
static async load(callback) {
let token=localStorage.getItem("token");
if (token===null) {

View File

@ -2,7 +2,262 @@ import * as API from './api.js';
import * as UI from './ui.js';
import { langPack } from './langpack.js';
const userSettings={
language: {
type: "select",
source: function() {
let rv={};
$.each(API.settings.get("coreLanguages"), function() {
rv[this]=(langPack.core.languages && langPack.core.languages[this])?langPack.core.languages[this]:this;
})
return rv;
},
reqx: true,
},
pageSize: {
type: "input",
regx: "^[0-9]{2}$",
reqx: true,
},
theme: {
type: "select",
source: API.settings.get("themes"),
reqx: true,
}
}
export function load() {
UI.breadcrumbsUpdate(langPack.core.breadcrumbs.modTitle+" / "+langPack.core.breadcrumbs.myprofile);
UI.createLeftPanel([
{id: "gendesc", title: langPack.core.iface.genDescTitle,disabled: false, buttons: [
UI.addButton({
id: "btnGdEdit",
icon: "fas fa-edit",
onClick: btnGdEdit_click,
}),
]},
{id: "config", title: langPack.core.iface.configTitle,disabled: false, buttons: [
UI.addButton({
id: "btnStEdit",
icon: "fas fa-edit",
onClick: btnStEdit_click,
}),
]},
]);
UI.createRightPanel([
{id: "mail", title: langPack.core.iface.messagesTitle,disabled: false},
{id: "agreements", title: langPack.core.iface.agreementsTitle,disabled: true},
{id: "messengers", title: langPack.core.iface.messengersTitle,disabled: true},
{id: "log", title: langPack.core.iface.logTitle,disabled: true},
])
API.session.reload(reloadGenDesc);
}
function btnStEdit_click(_ref) {
UI.blankerShow();
API.session.reload(function() {
UI.blankerHide();
let buttons={};
let dx;
buttons[langPack.core.iface.dialodSaveButton]=function() {
dialogStEdit_Save_Click(dx);
}
buttons[langPack.core.iface.dialodCloseButton]=function() {
UI.closeDialog(dx);
}
let dxflds=[];
$.each(userSettings, function(key) {
let fld={
type: this.type,
item: key,
title: langPack.core.userSettings[key]?langPack.core.userSettings[key]:key,
reqx: this.reqx,
regx: this.regx,
};
if (this.source) {
if (typeof(this.source)=="function") {
fld.args=this.source();
} else {
fld.args=this.source;
}
}
fld.val=API.session.getConfigItem(key);
dxflds.push(UI.addField(fld));
});
dx=UI.createDialog(
UI.addFieldGroup(dxflds),
langPack.core.iface.edit+": "+langPack.core.iface.configTitle,
buttons);
UI.openDialog(dx);
});
}
function btnGdEdit_click(_ref) {
UI.blankerShow();
API.session.reload(function() {
UI.blankerHide();
let buttons={};
let dx;
buttons[langPack.core.iface.dialodSaveButton]=function() {
dialogGdEdit_Save_Click(dx);
}
buttons[langPack.core.iface.dialodCloseButton]=function() {
UI.closeDialog(dx);
}
dx=UI.createDialog(
UI.addFieldGroup([
UI.addField({
title: langPack.core.iface.uid,
val: UI.formatInvCode(API.session.get("user").invCode),
}),
UI.addField({
title: langPack.core.iface.users.login,
val: API.session.get("user").login,
}),
UI.addField({
item: "dx_fullName",
type: "input",
title: langPack.core.iface.users.fullName,
val: API.session.get("user").fullName,
reqx: true,
}),
UI.addField({
item: "dx_eMail",
type: "input",
title: langPack.core.iface.users.email,
val: API.session.get("user").eMail,
regx: "(^$)|(^[0-9a-zA-Z._-]+\@[0-9a-zA-Z._-]+\.[a-zA_Z]+$)",
regxTitle: langPack.core.iface.users.errors.WREML,
}),
UI.addField({
title: langPack.core.iface.password,
type: "passwordNew",
item: "dx_password",
reqx:false,
regx: "(^$)|(.{6,})",
disabled: false,
newPasswdGenCallback: function(passwd) {
$("#dx_password2").val(passwd).addClass("changed").prop("type","input");
}
}),
UI.addField({
title: langPack.core.iface.passConfirm,
type: "password",
item: "dx_password2",
reqx:false,
disabled: false
}),
]),
langPack.core.iface.edit+": "+langPack.core.iface.genDescTitle,
buttons);
}
UI.openDialog(dx);
});
}
function dialogGdEdit_Save_Click(dx) {
let data = UI.collectForm(UI.getId(dx),true,true,false,true);
if(data.validateErrCount) { return };
if (data.password.changed && data.password.val != data.password2.val) {
$("#dx_password").addClass("alert");
$("#dx_password2").addClass("alert");
return;
}
API.exec({
requestType: "PATCH",
method: "core/user/"+API.session.get("user").id,
data: data.getVals(),
onSuccess: function(json) {
UI.closeDialog(dx);
API.session.reload(reloadGenDesc);
}
})
}
function dialogStEdit_Save_Click(dx) {
let data = UI.collectForm(UI.getId(dx),true,true,false,true);
if(data.validateErrCount) { return };
API.exec({
requestType: "PATCH",
method: "core/user/"+API.session.get("user").id+"/settings",
data: data.getVals(),
onSuccess: function(json) {
UI.closeDialog(dx);
API.session.reload(reloadGenDesc);
if (data.theme.changed) {
console.log("Theme changed");
window.location.reload(true);
}
}
})
}
function reloadGenDesc() {
let gdg=UI.addFieldGroup().appendTo($("#gendesc").empty());
UI.addField({title: langPack.core.iface.uid, val: UI.formatInvCode(API.session.get("user").invCode)}).appendTo(gdg);
UI.addField({title: langPack.core.iface.users.login, val: API.session.get("user").login}).appendTo(gdg);
UI.addField({title: langPack.core.iface.users.fullName, val: API.session.get("user").fullName}).appendTo(gdg);
UI.addField({title: langPack.core.iface.users.email, val: API.session.get("user").eMail}).appendTo(gdg);
let emailConfirmationOnContextMenu;
if (!API.session.get("user").eMailConfirmed && API.session.get("user").eMail) {
emailConfirmationOnContextMenu= function(el) {
UI.contextMenuOpen(el,[
{
title: langPack.core.iface.users.resendConfirmationCode,
onClick: function() {
API.exec({
requestType: "GET",
method: "core/user/sendEMailConfirmation",
})
}
}
]);
return false;
};
}
UI.addField({
title: "eMail Confirmed",
val: API.session.get("user").eMailConfirmed?langPack.core.iface.yes:langPack.core.iface.no,
onContextMenu: emailConfirmationOnContextMenu,
}).appendTo(gdg);
let cfg=UI.addFieldGroup().appendTo($("#config").empty());
$.each(userSettings, function(key) {
let val=API.session.getConfigItem(key);
if (userSettings[key] && userSettings[key].type=="select" && userSettings[key].source) {
let opts={};
if (typeof(userSettings[key].source)=="function") {
opts=userSettings[key].source();
} else {
opts=userSettings[key].source;
}
if (opts[val]) {
val=opts[val];
}
}
UI.addField({
title: langPack.core.userSettings[key],
val: val
}).appendTo(cfg);
});
}

View File

@ -23,20 +23,24 @@ function reloadUsers() {
$("<th>",{class: "", text: langPack.core.iface.users.fullName}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.users.eMainConfirmedQTitle}).appendTo(tx);
$("<th>",{class: "", text: langPack.core.iface.users.active}).appendTo(tx);
$("<th>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})}).appendTo(tx);
tx.appendTo("div.widget.users");
let i=0;
$.each(json.data, function(idx, user) {
i++;
let row=$("<tr>",{});
let row=$("<tr>",{}).bind('contextmenu', usmContextMenuOpen).addClass("contextMenu").attr("userId",user.id).attr("xenabled",user.active?1:0).attr("xemconf",user.eMailConfirmed?1:0);
$("<td>",{class: "idx", text: i}).appendTo(row);
$("<td>",{class: "", text: UI.formatInvCode(user.invCode)}).appendTo(row);
$("<td>",{class: "xInvCode", text: UI.formatInvCode(user.invCode)}).appendTo(row);
$("<td>",{class: "", text: user.login}).appendTo(row);
$("<td>",{class: "", text: user.eMail}).appendTo(row);
$("<td>",{class: "", text: user.fullName}).appendTo(row);
$("<td>",{class: "xFullName", text: user.fullName}).appendTo(row);
$("<td>",{class: "", text: user.eMailConfirmed}).appendTo(row);
$("<td>",{class: "", text: user.active}).appendTo(row);
$("<td>",{class: "button", style: "text-align: center", append: $("<i>",{class: "fas fa-ellipsis-h"})})
.click(usmContextMenuOpen)
.appendTo(row);
//row.foxClick("/"+UI.parceURL().module+"/user/"+user.id);
$("<tbody>",{append: row}).appendTo(tx);
});
@ -44,6 +48,84 @@ function reloadUsers() {
})
}
function usmContextMenuOpen(el) {
let userUID=$(el.target).closest("tr").find("td.xInvCode").text();
let userName=$(el.target).closest("tr").find("td.xFullName").text();
let userEnabled=$(el.target).closest("tr").attr("xenabled")==1;
let xemconf=$(el.target).closest("tr").attr("xemconf")==1;
let userId=$(el.target).closest("tr").attr("userId");
let menuitems=[];
if (userEnabled) {
menuitems.push({title: langPack.core.iface.disable, onClick: function() {
toggleUserEnabled(userId,false, el)
}})
} else {
menuitems.push({title: langPack.core.iface.enable, onClick: function() {
toggleUserEnabled(userId,true, el);
}})
}
menuitems.push({title: langPack.core.iface.delete, onClick: function() {
deleteUser(userId, el);
}});
if (!xemconf && userEnabled) {
menuitems.push({title: langPack.core.iface.users.resendConfirmationCode, onClick: function() {
API.exec({
requestType: "GET",
method: "core/user/"+userId+"/sendEMailConfirmation",
})
}});
}
UI.contextMenuOpen(el,menuitems,userUID+" "+userName);
return false;
}
function toggleUserEnabled(userId, state, ref) {
var buttons={};
buttons[langPack.core.iface.dialodSaveButton]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "PATCH",
method: "core/user/"+userId,
data: {"enabled": state==true?1:0 },
onSuccess: reloadUsers,
errDict: langPack.core.iface.groups.errors,
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface[state?"enable":"disable"]+" #"+userId+" "+$(ref.target).closest("tr").find("td.xFullName").text()+"?",langPack.core.iface[state?"enable":"disable"],buttons);
}
function deleteUser(userId, ref) {
var buttons={};
buttons[langPack.core.iface.dialodDelButton]=function() {
$("#dialogInfo").dialog("close");
API.exec({
requestType: "DELETE",
method: "core/user/"+userId,
onSuccess: reloadUsers,
errDict: langPack.core.iface.groups.errors,
});
};
buttons[langPack.core.iface.dialodCloseButton]=function() { $("#dialogInfo").dialog("close"); }
UI.showInfoDialog(langPack.core.iface.delete+" #"+userId+" "+$(ref.target).closest("tr").find("td.xFullName").text()+"?",langPack.core.iface.delete,buttons);
}
function reloadInvites() {
$("div.widget.invites").empty();
API.exec("GET","core/userInvitation/list",{},function(json) {

View File

@ -42,13 +42,18 @@ export var langItem={
open: "Открыть",
dialodAddButton: "Добавить",
dialodCreateButton: "Создать",
dialodSaveButton: "Записать",
dialodSaveButton: "Сохранить",
dialodInfoTitle: "Информация",
dialogRegisterButton: "Регистрация",
dialogRecoveryButton: "Восстановить",
dialogRegisterTitle: "Регистрация",
dialogReсoveryTitle: "Восстановление доступа",
genDescTitle: "Информация",
configTitle: "Настройки",
messagesTitle: "Сообщения",
agreementsTitle: "Документы для ознакомления",
logTitle: "История",
messengersTitle: "Мессенджеры",
contextMenuCopySelected: "Копировать выделение",
contextMenu: "Открыть действия",
actions: "Действия",
@ -76,6 +81,10 @@ export var langItem={
company: "Компания",
companies: "Компании",
uid: "UID",
filesClickForSelect: "Нажмите для выбора файла",
filesSelected: "Выбрано",
filesFile: "файл",
filesFiles: "файлов",
comps: {
qName: "Алиас",
company: "Компания",
@ -137,6 +146,7 @@ export var langItem={
invitationExpire: "Срок действия",
inviteToGroupTitleQ: "Группы",
delInvDialogTitle: "Удалить приглашение",
resendConfirmationCode: "Отправить код подтверждения",
recoveryFormText: "Укажите адрес электронной почты, на который был зарегистрирован аккаунт и код подтверждения (если есть), полученный по eMail для завершения восстановления",
recoverSentSucces: "Код подтверждения отправлен на указанный адрес. Введите его в поле \"код\" для продолжения процедуры.",
enterNewPasswordText: "Введите новый пароль",
@ -154,7 +164,7 @@ export var langItem={
RNE: "Регистрация запрещена. Обратитесь к администратору для получения приглашения",
ILF: "Некорректный формат логина - логин не может начинаться с цифры, может содержать цифры и буквы, а так же должен быть не мельше 5 символов.",
IPF: "Некорректный формат пароля. Пароль должен быть не меньше 6 символов и содержать в своем составе цифры, буквы и спец. символы",
URNF: "Пользователь не найден либо восстановление пароля не предусмотрено спосбом авторизации",
URNF: "Пользователь не найден либо восстановление пароля не предусмотрено способом авторизации",
IVCC: "Некорректный код подтверждения. Проверьте правильность кода и повторите попытку.",
}
},
@ -188,6 +198,15 @@ export var langItem={
}
},
languages: {
ru: "Русский",
en: "Английский",
},
userSettings: {
language: "Язык интерфейса",
pageSize: "Элементов на странице",
theme: "Тема оформления",
},
breadcrumbs: {
modTitle: "Система",
modules: "Модули",

View File

@ -347,7 +347,7 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
}
}
item = $("<input>", {class: "i", id: ref.item, name: name, width: 'calc(100% - 44px)'})
item = $("<input>", {type: "password", class: "i", id: ref.item, name: name, 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;",
@ -359,6 +359,8 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
}
let password=genPassword(args.chars, args.length);
$("#"+item_id).val(password);
$("#"+item_id).prop("type","input");
$("#"+item_id).change();
if (typeof(ref.newPasswdGenCallback)=="function") {
ref.newPasswdGenCallback(password);
@ -372,9 +374,9 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
if (ref.args !== undefined) {
$.each(ref.args, function(arg,val) {
if (typeof(val)=='array' || typeof(val)=='object') {
item.append($("<option>",{value: val.val, text: val.title}));
item.append($("<option>",{value: val.val, text: val.title, selected: val.val==ref.val}));
} else {
item.append($("<option>",{value: arg, text: val}));
item.append($("<option>",{value: arg, text: val, selected: arg==ref.val}));
}
});
@ -394,6 +396,36 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
break;
case "file":
item = $("<input>",{
type: "file",
id: ref.item,
class: "ifile",
multiple: ref.multiple==true,
change: function(ref) {
let files=ref.currentTarget.files;
let label=$("#"+ref.currentTarget.id+"__labelTitle");
$("#"+ref.currentTarget.id+"__label").addClass("changed");
if (files.length==0) {
label.text(langPack.core.iface.filesClickForSelect);
} else if (files.length==1) {
label.text(langPack.core.iface.filesSelected+": 1 "+langPack.core.iface.filesFile);
} else {
label.text(langPack.core.iface.filesSelected+": "+files.length+" "+langPack.core.iface.filesFiles);
}
},
})
.add($("<label>", {
id: ref.item+"__label",
for: ref.item,
append: $("<i>",{ class: "far fa-save", title: langPack.core.iface.filesClickForSelect})
.add($("<span>",{style: "margin-left: 10px;", id: ref.item+"__labelTitle",text: langPack.core.iface.filesClickForSelect}))
,
}))
break;
case "label":
item = $("<span>", {id: ref.item, class: "i", html: ref.val});
break;
@ -501,7 +533,7 @@ export function addField(ref)//title, item, blockstyle, fieldstyle, type, args,
if (typeof(ref.onContextMenu)=="function") {
rv.bind('contextmenu', ref.onContextMenu);
rv.addClass("withContextMenu");
rvv.append($("<i>",{style: "padding: 0 10px 0 10px; float: right;", class: "fas fa-ellipsis-v"}).bind('contextmenu click', ref.onContextMenu))
rvv.append($("<i>",{style: "width: 50px; text-align: right; padding-right: 10px;", class: "fas fa-ellipsis-v"}).bind('contextmenu click', ref.onContextMenu))
}
}
@ -614,6 +646,15 @@ export function genPassword(chars, passwordLength) {
return password;
}
export function getId(ref) {
if (ref.id) {
return ref.id;
} else if (ref[0] && ref[0].id) {
return ref[0].id;
}
}
export function collectForm(formid, getall, withIDS, withREF, validate)
{
if (typeof(formid)=='object') {
@ -633,9 +674,10 @@ export function collectForm(formid, getall, withIDS, withREF, validate)
var cctr=0;
var cerr=0;
var key;
$('#'+formid+' .i'+((getall==true)?"":'.changed')).each(function() {
if (isset(this.name)) {key = this.name} else {key = this.id}
if(!isset(key)) { return; }
if (getall) {
var reqx="false";
@ -723,11 +765,13 @@ export function createDialog(body, title, buttons, height, columns, id) {
if (id==undefined) {id = 'dialogForm'; }
if (columns=='undefined') { columns=1; }
$("<div>",{
let dx=$("<div>",{
id: id,
title: title,
style: "overflow: hidden;"
}).appendTo("body")
})
.prop("dxColumns",columns)
.appendTo("body")
.dialog({
autoOpen: false,
height: height,
@ -740,18 +784,54 @@ export function createDialog(body, title, buttons, height, columns, id) {
buttons: buttons
})
.append(body);
return dx;
}
export function openDialog(id) {
if (id==undefined) {id = 'dialogForm'; }
let dx;
if (typeof(id)=="object") {
dx=$(id);
id=dx.id;
} else {
dx=$("#"+id);
}
$("#"+id+' .i').on("change",function() {on_valChanged($(this)); });
$("#"+id).dialog("open");
dx.dialog("open");
let height=$("#"+id).dialog("option","height");
if (!height) {
let summHeight=0;
let maxHeight=0;
let columns = dx.prop("dxColumns");
$.each(dx.find(".crm_entity_block_group"), function(_key,val) {
summHeight +=val.clientHeight;
if (val.clientHeight>maxHeight) { maxHeight=val.clientHeight; }
})
let halfHeight=Math.ceil(summHeight/columns);
let xHeight=(maxHeight>halfHeight)?maxHeight:halfHeight;
dx.dialog("option","height", xHeight+180);
}
}
export function closeDialog(id) {
if (id==undefined) {id = 'dialogForm'; }
$("#"+id).dialog("close");
$("#"+id).remove();
let dx;
if (typeof(id)=="object") {
dx=$(id);
} else {
dx=$("#"+id);
}
dx.dialog("close");
dx.remove();
}

View File

@ -50,6 +50,7 @@ function doValidation(code) {
};
UI.showInfoDialog(langPack.core.iface.ok0,langPack.core.iface.dialodInfoTitle,buttons);
API.session.reload();
return false;
}
});

View File

@ -957,6 +957,12 @@ div.crm_entity_field_block.withContextMenu a {
cursor: context-menu;
}
div.crm_entity_field_block.withContextMenu span,
div.crm_entity_field_block.withContextMenu title,
div.crm_entity_field_block.withContextMenu a {
margin-right: -60px;
}
table.datatable.sel tr:hover td
@ -1246,7 +1252,12 @@ div.crm_entity_field_block input, div.crm_entity_field_block select
{
height: 26px;
}
div.crm_entity_field_block input, div.crm_entity_field_block select, div.crm_entity_field_block textarea
div.crm_entity_field_block input,
div.crm_entity_field_block select,
div.crm_entity_field_block textarea,
div.crm_entity_field_block label,
div.crm_entity_field_block span.i,
div.crm_entity_field_block a.i
{
border: none;
@ -1256,6 +1267,23 @@ div.crm_entity_field_block input, div.crm_entity_field_block select, div.crm_ent
outline: none;
background: none;
}
div.crm_entity_field_block label {
display: inline-block;
height: 27px;
}
div.crm_entity_field_block span.i,
div.crm_entity_field_block a.i
{
border-bottom-color: rgba(100,100,100,0.1);
}
div.crm_entity_field_block label,
div.crm_entity_field_block span.i,
div.crm_entity_field_block a.i
{
display: inline-block;
height: 27px;
}
/* panel_full_normal */
div.crm_entity_field_value,
@ -1271,7 +1299,10 @@ div.crm_entity_block_group
div.crm_entity_field_block input,
div.crm_entity_field_block select,
div.crm_entity_field_block textarea
div.crm_entity_field_block textarea,
div.crm_entity_field_block label,
div.crm_entity_field_block span.i,
div.crm_entity_field_block a.i
{
width: 300px;
padding: 0 0 0 10px;
@ -1293,7 +1324,10 @@ div.crm_entity_field_block textarea
}
.full-width-input div.crm_entity_field_block input,
.full-width-input div.crm_entity_field_block select,
.full-width-input div.crm_entity_field_block textarea
.full-width-input div.crm_entity_field_block textarea,
.full-width-input div.crm_entity_field_block label,
.full-width-input div.crm_entity_field_block span.i,
.full-width-input div.crm_entity_field_block a.i
{
width: 1633px;
}
@ -1312,7 +1346,10 @@ div.widget_panel_left div.crm_entity_block_group
div.widget_panel_left div.crm_entity_field_block input,
div.widget_panel_left div.crm_entity_field_block select,
div.widget_panel_left div.crm_entity_field_block textarea
div.widget_panel_left div.crm_entity_field_block textarea,
div.widget_panel_left div.crm_entity_field_block label,
div.widget_panel_left div.crm_entity_field_block span.i,
div.widget_panel_left div.crm_entity_field_block a.i
{
width: 280px;
}
@ -1320,6 +1357,9 @@ div.widget_panel_left div.crm_entity_field_block textarea
div.widget_panel_left div.crm_entity_field_block input.half,
div.widget_panel_left div.crm_entity_field_block select.half,
div.widget_panel_left div.crm_entity_field_block textarea.half
div.widget_panel_left div.crm_entity_field_block label.half,
div.widget_panel_left div.crm_entity_field_block span.i.half,
div.widget_panel_left div.crm_entity_field_block a.i.half
{
width: 132px;
}
@ -1338,7 +1378,10 @@ div.widget_panel_right div.crm_entity_block_group
div.widget_panel_right div.crm_entity_field_block input,
div.widget_panel_right div.crm_entity_field_block select,
div.widget_panel_right div.crm_entity_field_block textarea
div.widget_panel_right div.crm_entity_field_block textarea,
div.widget_panel_right div.crm_entity_field_block label,
div.widget_panel_right div.crm_entity_field_block span.i,
div.widget_panel_right div.crm_entity_field_block a.i
{
width: 300px;
}
@ -1355,7 +1398,10 @@ div.ui-dialog-content div.crm_entity_block_group
}
div.ui-dialog-content div.crm_entity_field_block input,
div.ui-dialog-content div.crm_entity_field_block textarea
div.ui-dialog-content div.crm_entity_field_block textarea,
div.ui-dialog-content div.crm_entity_field_block label,
div.ui-dialog-content div.crm_entity_field_block span.i,
div.ui-dialog-content div.crm_entity_field_block a.i
{
width: 290px;
}
@ -1379,13 +1425,21 @@ div.widget_panel_right table.t_icon div.crm_entity_block_group
div.widget_panel_right table.t_icon div.crm_entity_field_block input,
div.widget_panel_right table.t_icon div.crm_entity_field_block select,
div.widget_panel_right table.t_icon div.crm_entity_field_block textarea
div.widget_panel_right table.t_icon,
div.widget_panel_right table.t_icon div.crm_entity_field_block label,
div.widget_panel_right table.t_icon div.crm_entity_field_block span.i,
div.widget_panel_right table.t_icon div.crm_entity_field_block a.i
{
width: 290px;
}
div.crm_entity_field_block input.half, div.crm_entity_field_block select.half, div.crm_entity_field_block textarea.half
div.crm_entity_field_block input.half,
div.crm_entity_field_block select.half,
div.crm_entity_field_block textarea.half,
div.crm_entity_field_block label.half,
div.crm_entity_field_block span.i.half,
div.crm_entity_field_block a.i.half
{
width: 130px;
}
@ -1393,9 +1447,15 @@ div.crm_entity_field_block input.half, div.crm_entity_field_block select.half, d
div.crm_entity_field_block input:focus,
div.crm_entity_field_block select:focus,
div.crm_entity_field_block textarea:focus,
div.crm_entity_field_block label:focus,
div.crm_entity_field_block span.i:focus,
div.crm_entity_field_block a.i:focus,
div.crm_entity_field_block input:focus.changed,
div.crm_entity_field_block select:focus.changed,
div.crm_entity_field_block textarea:focus.changed
div.crm_entity_field_block label:focus.changed,
div.crm_entity_field_block span.i:focus.changed,
div.crm_entity_field_block a.i:focus.changed
{
border: none;
border-bottom: 1px solid var(--mxs-green);
@ -1406,7 +1466,12 @@ div.widget_panel_left div.crm_entity_block_group
margin-right: 0px;
}
div.crm_entity_field_block input:disabled,div.crm_entity_field_block select:disabled,div.crm_entity_field_block textarea:disabled
div.crm_entity_field_block input:disabled,
div.crm_entity_field_block select:disabled,
div.crm_entity_field_block textarea:disabled,
div.crm_entity_field_block label:disabled,
div.crm_entity_field_block span.i:disabled,
div.crm_entity_field_block a.i:disabled
{
border: none;
border-bottom: 1px double var(--mxs-gray);
@ -1644,21 +1709,30 @@ table.t_icon
div.crm_entity_field_block input.changed,
div.crm_entity_field_block select.changed,
div.crm_entity_field_block textarea.changed
div.crm_entity_field_block textarea.changed,
div.crm_entity_field_block label.changed,
div.crm_entity_field_block span.i.changed,
div.crm_entity_field_block a.i.changed
{
border-bottom-color: rgba(255,111,16,0.6);
}
div.crm_entity_field_block input.saved,
div.crm_entity_field_block select.saved,
div.crm_entity_field_block textarea.saved
div.crm_entity_field_block textarea.saved,
div.crm_entity_field_block label.saved,
div.crm_entity_field_block span.i.saved,
div.crm_entity_field_block a.i.saved
{
border-bottom-color: var(--mxs-green);
}
div.crm_entity_field_block input.alert,
div.crm_entity_field_block select.alert,
div.crm_entity_field_block textarea.alert
div.crm_entity_field_block textarea.alert,
div.crm_entity_field_block label.alert,
div.crm_entity_field_block span.i.alert,
div.crm_entity_field_block a.i.alert
{
border-bottom-color: var(--mxs-red);
background-color: var(--mxs-inputalertbg);
@ -1904,7 +1978,10 @@ div.crm_entity_block_group
div.crm_entity_field_block input,
div.crm_entity_field_block select,
div.crm_entity_field_block textarea
div.crm_entity_field_block textarea,
div.crm_entity_field_block label,
div.crm_entity_field_block span.i,
div.crm_entity_field_block a.i
{
width: 304px;
}
@ -1923,7 +2000,10 @@ div.crm_entity_field_block textarea
.full-width-input div.crm_entity_field_block input,
.full-width-input div.crm_entity_field_block select,
.full-width-input div.crm_entity_field_block textarea
.full-width-input div.crm_entity_field_block textarea,
.full-width-input div.crm_entity_field_block label,
.full-width-input div.crm_entity_field_block span.i,
.full-width-input div.crm_entity_field_block a.i
{
width: 1310px;
}
@ -1984,7 +2064,10 @@ div.crm_entity_block_group
div.crm_entity_field_block input,
div.crm_entity_field_block select,
div.crm_entity_field_block textarea
div.crm_entity_field_block textarea,
div.crm_entity_field_block label,
div.crm_entity_field_block span.i,
div.crm_entity_field_block a.i
{
width: 307px;
}
@ -2003,7 +2086,10 @@ div.crm_entity_field_block textarea
.full-width-input div.crm_entity_field_block input,
.full-width-input div.crm_entity_field_block select,
.full-width-input div.crm_entity_field_block textarea
.full-width-input div.crm_entity_field_block textarea,
.full-width-input div.crm_entity_field_block label,
.full-width-input div.crm_entity_field_block span.i,
.full-width-input div.crm_entity_field_block a.i
{
width: 993px;
}
@ -2115,7 +2201,10 @@ div.header
.full-width-input div.crm_entity_field_block input,
.full-width-input div.crm_entity_field_block select,
.full-width-input div.crm_entity_field_block textarea
.full-width-input div.crm_entity_field_block textarea,
.full-width-input div.crm_entity_field_block label,
.full-width-input div.crm_entity_field_block span.i,
.full-width-input div.crm_entity_field_block a.i
{
width: 993px;
}
@ -2233,14 +2322,20 @@ div.widget_panel_left div.crm_entity_field_input
div.widget_panel_left div.crm_entity_field_block input,
div.widget_panel_left div.crm_entity_field_block select,
div.widget_panel_left div.crm_entity_field_block textarea
div.widget_panel_left div.crm_entity_field_block textarea,
div.widget_panel_left div.crm_entity_field_block label,
div.widget_panel_left div.crm_entity_field_block span.i,
div.widget_panel_left div.crm_entity_field_block a.i
{
width: 322px;
}
div.widget_panel_left div.crm_entity_field_block input.half,
div.widget_panel_left div.crm_entity_field_block select.half,
div.widget_panel_left div.crm_entity_field_block textarea.half
div.widget_panel_left div.crm_entity_field_block textarea.half,
div.widget_panel_left div.crm_entity_field_block label.half,
div.widget_panel_left div.crm_entity_field_block span.i.half,
div.widget_panel_left div.crm_entity_field_block a.i.half
{
width: 153px;
}
@ -2259,14 +2354,20 @@ div.crm_entity_block_group
div.crm_entity_field_block input,
div.crm_entity_field_block select,
div.crm_entity_field_block textarea
div.crm_entity_field_block textarea,
div.crm_entity_field_block label,
div.crm_entity_field_block span.i,
div.crm_entity_field_block a.i
{
width: 315px;
}
div.crm_entity_field_block input.half,
div.crm_entity_field_block select.half,
div.crm_entity_field_block textarea.half
div.crm_entity_field_block textarea.half,
div.crm_entity_field_block label.half,
div.crm_entity_field_block span.i.half,
div.crm_entity_field_block a.i.half
{
width: 150px;
}
@ -2290,7 +2391,10 @@ div.login
.full-width-input div.crm_entity_field_block input,
.full-width-input div.crm_entity_field_block select,
.full-width-input div.crm_entity_field_block textarea
.full-width-input div.crm_entity_field_block textarea,
.full-width-input div.crm_entity_field_block label,
.full-width-input div.crm_entity_field_block span.i,
.full-width-input div.crm_entity_field_block a.i
{
width: 670px;
}