Compare commits

...

24 Commits

Author SHA1 Message Date
Alan Cell
5df15d39c1 Example usage of IceHrm model classes 2020-11-13 04:47:50 +01:00
Alan Cell
143bcac1f9 Custom tasks extension, basic implementation 2020-11-12 08:06:36 +01:00
Alan Cell
84548c4f63 Ability to add custom modules 2020-11-12 05:51:31 +01:00
Alan Cell
e5eccf32a7 Merge tag 'v28.1.1.OS' into develop
v28.1.1.OS
2020-11-07 12:52:02 +01:00
Alan Cell
f1dcc6b6a0 Merge branch 'release/v28.1.1.OS' 2020-11-07 12:52:02 +01:00
Alan Cell
b6c0256b49 Update production build 2020-11-07 12:37:11 +01:00
Alan Cell
e31bf5a4b9 Fix api issues and production build 2020-11-07 12:25:12 +01:00
Alan Cell
06a3172a38 Merge branch 'release/v28.1.1.OS' 2020-11-07 11:46:23 +01:00
Alan Cell
e74ca00902 Merge tag 'v28.1.1.OS' into develop
v28.1.1.OS
2020-11-07 11:46:23 +01:00
Alan Cell
28aa16f35c Update version 2020-11-07 11:45:05 +01:00
Alan Cell
fb3b5b562e Merge branch 'release/v28.1.1.OS' 2020-11-07 11:41:24 +01:00
Alan Cell
8663a7aff1 Merge tag 'v28.1.1.OS' into develop
v28.1.1.OS
2020-11-07 11:41:24 +01:00
Alan Cell
e9baf45d7c Production build + fixing code style issues 2020-11-06 20:08:07 +01:00
Alan Cell
2abe52963f Imitate the REST api using url parameter based implementation
Reason for this implementation is some clients having trouble configuring the rest api either due to not having proper access to the webserver in a shared hosting environment or security restrictions. But still from icehrm frontend we need to consume the backend rest api.
2020-11-06 20:00:53 +01:00
Alan Cell
da55c7a2d2 Ability to delete files from S3 2020-11-06 19:41:56 +01:00
Alan Cell
5cd7963f6f Implement password change for employee profile 2020-11-06 19:38:48 +01:00
Alan Cell
d986a2b5bb Fix issue: employee not be selected when filtering employee documents 2020-11-06 19:32:52 +01:00
Alan Cell
1ee4fb4ba1 Update vagrant file 2020-11-06 19:23:05 +01:00
Alan Cell
b06780c466 Fix issue with time-sheets module nit being able to load projects 2020-11-06 18:51:46 +01:00
Alan Cell
5ec497e11d Upgrade vagrant config 2020-11-06 18:41:39 +01:00
Alan Cell
df3b6e968a Update vagrant 2020-11-06 09:46:20 +01:00
Thilina Pituwala
3ceb427479 Fix ci build 2020-11-01 10:17:04 +01:00
Thilina Pituwala
d3b4748cba Remove in lage in readme 2020-11-01 02:37:33 +01:00
Thilina Pituwala
2a9e65d8a8 Remove php5.6 from travis 2020-11-01 02:31:57 +01:00
58 changed files with 1378 additions and 31302 deletions

View File

@@ -14,7 +14,6 @@ install:
script: ant build-ci script: ant build-ci
language: php language: php
php: php:
- '5.6'
- '7.0' - '7.0'
- '7.1' - '7.1'
- '7.2' - '7.2'

16
Vagrantfile vendored
View File

@@ -3,8 +3,6 @@ Vagrant.configure("2") do |config|
config.vm.box_version = "1.0.0" config.vm.box_version = "1.0.0"
config.vm.network "private_network", ip: "192.168.10.12" config.vm.network "private_network", ip: "192.168.10.12"
config.vm.synced_folder ".", "/vagrant", type: "nfs" config.vm.synced_folder ".", "/vagrant", type: "nfs"
config.vm.synced_folder "./deployment/vagrant/sites-available", "/etc/nginx/sites-enabled", type: "nfs"
config.vm.synced_folder "./deployment/vagrant/ssl", "/etc/nginx/ssl", type: "nfs"
config.vm.provider "virtualbox" do |vb| config.vm.provider "virtualbox" do |vb|
vb.memory = "1024" vb.memory = "1024"
@@ -13,9 +11,17 @@ Vagrant.configure("2") do |config|
end end
config.vm.provision "shell", inline: <<-SHELL config.vm.provision "shell", inline: <<-SHELL
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config sudo rm /etc/nginx/ssl/icehrm.*
systemctl restart sshd.service sudo ln -s /vagrant/deployment/vagrant/ssl/icehrm.crt /etc/nginx/ssl/icehrm.crt
sudo service nginx restart sudo ln -s /vagrant/deployment/vagrant/ssl/icehrm.key /etc/nginx/ssl/icehrm.key
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /vagrant/deployment/vagrant/sites-available/default /etc/nginx/sites-enabled/default
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
sudo service nginx restart
sudo chmod 755 -R /var/log
SHELL SHELL
config.vm.hostname = "icehrm.os" config.vm.hostname = "icehrm.os"

View File

@@ -5,7 +5,7 @@ if(!file_exists('config.php')){
} }
include ('config.php'); include ('config.php');
if(!isset($_REQUEST['g']) || !isset($_REQUEST['n'])){ if(!isset($_REQUEST['g']) || !isset($_REQUEST['n'])){
header("Location:".CLIENT_BASE_URL."login.php"); header("Location:".CLIENT_BASE_URL."login.php");
exit(); exit();
} }
$group = $_REQUEST['g']; $group = $_REQUEST['g'];
@@ -14,9 +14,16 @@ $name= $_REQUEST['n'];
$groups = array('admin','modules'); $groups = array('admin','modules');
if($group == 'admin' || $group == 'modules'){ if($group == 'admin' || $group == 'modules'){
$name = str_replace("..","",$name); $name = str_replace("..","",$name);
$name = str_replace("/","",$name); $name = str_replace("/","",$name);
include APP_BASE_PATH.'/'.$group.'/'.$name.'/index.php'; include APP_BASE_PATH.'/'.$group.'/'.$name.'/index.php';
}else if ($group == 'extension'){
$name = str_replace("..","",$name);
$name = str_replace("/","",$name);
$moduleName = $name;
$moduleGroup = 'extensions';
$extensionIndex = APP_BASE_PATH.'/../extensions/'.$name.'/web/index.php';
include APP_BASE_PATH.'extensions/wrapper.php';
}else{ }else{
exit(); exit();
} }

View File

@@ -1,3 +1,3 @@
<?php <?php
include ('config.php'); include ('config.php');
include (APP_BASE_PATH.'rest.php'); include (APP_BASE_PATH.'rest.php');

1
bin/export-plural-rules Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/gettext/languages/bin/export-plural-rules

1
bin/markdown Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/cebe/markdown/bin/markdown

1
bin/pdepend Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/pdepend/pdepend/src/bin/pdepend

1
bin/phpcb Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/mayflower/php-codebrowser/bin/phpcb

1
bin/phpcbf Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/squizlabs/php_codesniffer/scripts/phpcbf

1
bin/phpcpd Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/sebastian/phpcpd/composer/bin/phpcpd

1
bin/phpcs Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/squizlabs/php_codesniffer/scripts/phpcs

1
bin/phploc Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/phploc/phploc/phploc

1
bin/phpmd Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/phpmd/phpmd/src/bin/phpmd

1
bin/phpunit Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/phpunit/phpunit/phpunit

1
bin/release Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/consolidation/self-update/scripts/release

1
bin/robo Symbolic link
View File

@@ -0,0 +1 @@
../core/lib/composer/vendor/consolidation/robo/robo

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project name="icehrm" default="build"> <project name="icehrm" default="build">
<!-- By default, we assume all tools to be on the $PATH --> <!-- By default, we assume all tools to be on the $PATH -->
<property name="toolsdir" value="${basedir}/tools/"/> <property name="toolsdir" value="${basedir}/bin/"/>
<property name="destination" value="${basedir}/build/app"/> <property name="destination" value="${basedir}/build/app"/>
<property name="testdir" value="${basedir}/build/test"/> <property name="testdir" value="${basedir}/build/test"/>
<property name="origin" value="${basedir}"/> <property name="origin" value="${basedir}"/>

47
core/api-rest.php Normal file
View File

@@ -0,0 +1,47 @@
<?php
define('CLIENT_PATH',dirname(__FILE__));
include ("config.base.php");
include ("include.common.php");
include("server.includes.inc.php");
if(\Classes\SettingsManager::getInstance()->getSetting('Api: REST Api Enabled') == '1') {
if (defined('SYM_CLIENT')) {
define('REST_API_PATH', '/'.SYM_CLIENT.'/');
} else if (!defined('REST_API_PATH')){
define('REST_API_PATH', '/');
}
\Utils\LogManager::getInstance()->info("Request: " . $_REQUEST);
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
$echoRoute = \Classes\Macaw::get(REST_API_PATH . 'echo', function () {
echo "Echo " . rand();
});
\Utils\LogManager::getInstance()->debug('Api registered URI: '.$echoRoute);
$moduleManagers = \Classes\BaseService::getInstance()->getModuleManagers();
foreach ($moduleManagers as $moduleManagerObj) {
$moduleManagerObj->setupRestEndPoints();
}
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
\Utils\LogManager::getInstance()->debug('Api dispatch URI: '.$uri);
\Utils\LogManager::getInstance()->debug('Api dispatch method: '.$uri);
if (!defined('SYM_CLIENT')) {
//For hosted installations, dispatch will be done in app/index
\Classes\Macaw::dispatch();
}
}else{
echo "REST Api is not enabled. Please set 'Api: REST Api Enabled' setting to true";
}

41
core/api-url-based.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
define('CLIENT_PATH',dirname(__FILE__));
include ("config.base.php");
include ("include.common.php");
include("server.includes.inc.php");
if(\Classes\SettingsManager::getInstance()->getSetting('Api: REST Api Enabled') == '1') {
\Utils\LogManager::getInstance()->info("Request: " . $_REQUEST);
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
define('REST_API_PATH', '/');
$echoRoute = \Classes\Macaw::get(REST_API_PATH . 'echo', function () {
echo "Echo " . rand();
});
\Utils\LogManager::getInstance()->debug('Api registered URI: '.$echoRoute);
$moduleManagers = \Classes\BaseService::getInstance()->getModuleManagers();
foreach ($moduleManagers as $moduleManagerObj) {
$moduleManagerObj->setupRestEndPoints();
}
$method = $_SERVER['REQUEST_METHOD'];
if (strtoupper($method) === 'GET') {
\Classes\IceRoute::dispatch($_GET['url'], $method);
} else {
$method = strtoupper($_REQUEST['method']);
\Classes\IceRoute::dispatch($_REQUEST['url'], $method);
}
}else{
echo "REST Api is not enabled. Please set 'Api: REST Api Enabled' setting to true";
}

View File

@@ -13,10 +13,10 @@ if(!defined('HOME_LINK_OTHERS')){
} }
//Version //Version
define('VERSION', '28.1.0.OS'); define('VERSION', '28.1.1.OS');
define('CACHE_VALUE', '28.1.0.OS.2020-10311445'); define('CACHE_VALUE', '28.1.1.OS.2020-11071143');
define('VERSION_NUMBER', '280100'); define('VERSION_NUMBER', '280101');
define('VERSION_DATE', '31/10/2020'); define('VERSION_DATE', '07/11/2020');
if(!defined('CONTACT_EMAIL')){define('CONTACT_EMAIL','icehrm@gamonoid.com');} if(!defined('CONTACT_EMAIL')){define('CONTACT_EMAIL','icehrm@gamonoid.com');}
if(!defined('KEY_PREFIX')){define('KEY_PREFIX','IceHrm');} if(!defined('KEY_PREFIX')){define('KEY_PREFIX','IceHrm');}
@@ -36,3 +36,7 @@ define('ALL_CLIENT_BASE_PATH', '/var/www/icehrm.app/icehrmapp/');
define('LDAP_ENABLED', true); define('LDAP_ENABLED', true);
define('RECRUITMENT_ENABLED', false); define('RECRUITMENT_ENABLED', false);
define('APP_WEB_URL', 'https://icehrm.com'); define('APP_WEB_URL', 'https://icehrm.com');
if (!defined('EXTENSIONS_URL')) {
define('EXTENSIONS_URL', str_replace('/web/', '/extensions/', BASE_URL));
}

View File

@@ -0,0 +1,31 @@
<?php
use Classes\ExtensionManager;
use Utils\LogManager;
if (!isset($extensionIndex)) {
exit();
}
define('MODULE_PATH',APP_BASE_PATH.'extensions/'.$moduleName);
include APP_BASE_PATH.'header.php';
$extensionManager = new ExtensionManager();
$meta = $extensionManager->getExtensionMetaData($moduleName);
if (!$meta) {
LogManager::getInstance()->error("Extension metadata.json not found for $moduleName");
exit();
}
if ($meta->headless) {
LogManager::getInstance()->error("Extension running in headless mode for $moduleName");
exit();
}
?>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorReact.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorAntd.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorAntdIcons.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorAntv.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=BASE_URL.'dist/vendorOther.js'?>?v=<?=$jsVersion?>"></script>
<script type="text/javascript" src="<?=EXTENSIONS_URL.$moduleName.'/dist/'.$moduleName.'.js'?>?v=<?=$jsVersion?>"></script>
<?php
include $extensionIndex;
include APP_BASE_PATH.'footer.php';
?>

View File

@@ -215,6 +215,26 @@ if (defined('SYM_CLIENT')) {
<?php }?> <?php }?>
<?php foreach($extensions as $menu){?>
<?php if(count($menu['menu']) == 0){continue;}?>
<li class="treeview" ref="<?="extension_".str_replace(" ", "_", $menu['name'])?>">
<a href="#">
<i class="fa <?=!isset($mainIcons[$menu['name']])?"fa-th":$mainIcons[$menu['name']];?>"></i></i> <span><?=\Classes\LanguageManager::tran($menu['name'])?></span>
<i class="fa fa-angle-left pull-right"></i>
</a>
<ul class="treeview-menu" id="<?="extension_".str_replace(" ", "_", $menu['name'])?>">
<?php foreach ($menu['menu'] as $item){?>
<li>
<a data-turbolinks="true" href="<?=CLIENT_BASE_URL?>?g=extension&n=<?=$item['name']?>&m=<?="extension_".str_replace(" ", "_", $menu['name'])?>">
<i class="fa <?=!isset($item['icon'])?"fa-angle-double-right":$item['icon']?>"></i> <?=\Classes\LanguageManager::tran($item['label'])?>
</a>
</li>
<?php }?>
</ul>
</li>
<?php }?>
<?php <?php
if(file_exists(CLIENT_PATH.'/third_party_meta.json')){ if(file_exists(CLIENT_PATH.'/third_party_meta.json')){
$tpModules = json_decode(file_get_contents(CLIENT_PATH.'/third_party_meta.json'),true); $tpModules = json_decode(file_get_contents(CLIENT_PATH.'/third_party_meta.json'),true);

View File

@@ -277,7 +277,7 @@ $csrfToken = sha1(rand(4500, 100000) . time(). CLIENT_BASE_URL);
<div class="col-lg-6 col-md-8 col-xs-10"> <div class="col-lg-6 col-md-8 col-xs-10">
<div class="bg-white-2 h-100 px-11 pt-11 pb-7"> <div class="bg-white-2 h-100 px-11 pt-11 pb-7">
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<img src="<?=$logoFileUrl?>"/> <img src="<?=$logoFileUrl?>" style="max-width:100%;max-height:280px;"/>
</div> </div>
<hr/> <hr/>
<?php if ($gsuiteEnabled) {?> <?php if ($gsuiteEnabled) {?>

View File

@@ -291,6 +291,12 @@ foreach ($ams as $am) {
} }
} }
$extensionManager = new \Classes\ExtensionManager();
$extensionData = $extensionManager->setupExtensions();
$extensionIcons = $extensionData[0];
$extensionTemp = $extensionData[1];
$extensionMenus = array_keys($extensionIcons);
foreach ($adminModulesTemp as $k => $v) { foreach ($adminModulesTemp as $k => $v) {
ksort($adminModulesTemp[$k]); ksort($adminModulesTemp[$k]);
} }
@@ -299,6 +305,10 @@ foreach ($userModulesTemp as $k => $v) {
ksort($userModulesTemp[$k]); ksort($userModulesTemp[$k]);
} }
foreach ($extensionTemp as $k => $v) {
ksort($extensionTemp[$k]);
}
$adminIcons = json_decode(file_get_contents(CLIENT_PATH.'/admin/meta.json'), true); $adminIcons = json_decode(file_get_contents(CLIENT_PATH.'/admin/meta.json'), true);
$adminMenus = array_keys($adminIcons); $adminMenus = array_keys($adminIcons);
@@ -332,8 +342,6 @@ foreach ($userMenus as $menu) {
} }
} }
$mainIcons = array_merge($adminIcons, $userIcons);
foreach ($userModulesTemp as $k => $v) { foreach ($userModulesTemp as $k => $v) {
if (!in_array($k, $added)) { if (!in_array($k, $added)) {
$arr = array("name"=>$k,"menu"=>$userModulesTemp[$k]); $arr = array("name"=>$k,"menu"=>$userModulesTemp[$k]);
@@ -341,6 +349,25 @@ foreach ($userModulesTemp as $k => $v) {
} }
} }
$extensions = array();
foreach ($extensionMenus as $menu) {
if (isset($extensionTemp[$menu])) {
$arr = array("name"=>$menu,"menu"=>$extensionTemp[$menu]);
$extensions[] = $arr;
$added[] = $menu;
}
}
foreach ($extensionTemp as $k => $v) {
if (!in_array($k, $added)) {
$arr = array("name"=>$k,"menu"=>$extensionTemp[$k]);
$extensions[] = $arr;
}
}
// Merge icons
$mainIcons = array_merge($adminIcons, $userIcons, $extensionIcons);
//Remove modules having no permissions //Remove modules having no permissions
if (!empty($user)) { if (!empty($user)) {
if (!empty($user->user_roles)) { if (!empty($user->user_roles)) {
@@ -393,4 +420,24 @@ if (!empty($user)) {
} }
} }
} }
foreach ($extensions as $fk => $menu) {
foreach ($menu['menu'] as $key => $item) {
// If the user's once of the user roles are blacklisted for the module
$commonRoles = array_intersect($item['user_roles_blacklist'], $userRoles);
if (!empty($commonRoles)) {
unset($extensions[$fk]['menu'][$key]);
}
if (!in_array($user->user_level, $item['user_levels'])) {
if (!empty($userRoles)) {
$commonRoles = array_intersect($item['user_roles'], $userRoles);
if (empty($commonRoles)) {
unset($extensions[$fk]['menu'][$key]);
}
} else {
unset($extensions[$fk]['menu'][$key]);
}
}
}
}
} }

View File

@@ -3,49 +3,8 @@ header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: DELETE, POST, GET, OPTIONS'); header('Access-Control-Allow-Methods: DELETE, POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With'); header('Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
header('Content-Type: application/json'); header('Content-Type: application/json');
define('CLIENT_PATH',dirname(__FILE__)); if (isset($_REQUEST['method']) && isset($_REQUEST['url'])) {
include ("config.base.php"); include(APP_BASE_PATH . 'api-url-based.php');
include ("include.common.php"); } else {
include("server.includes.inc.php"); include(APP_BASE_PATH . 'api-rest.php');
if(\Classes\SettingsManager::getInstance()->getSetting('Api: REST Api Enabled') == '1') {
if (defined('SYM_CLIENT')) {
define('REST_API_PATH', '/'.SYM_CLIENT.'/');
} else if (!defined('REST_API_PATH')){
define('REST_API_PATH', '/');
}
\Utils\LogManager::getInstance()->info("Request: " . $_REQUEST);
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
$echoRoute = \Classes\Macaw::get(REST_API_PATH . 'echo', function () {
echo "Echo " . rand();
});
\Utils\LogManager::getInstance()->debug('Api registered URI: '.$echoRoute);
$moduleManagers = \Classes\BaseService::getInstance()->getModuleManagers();
foreach ($moduleManagers as $moduleManagerObj) {
$moduleManagerObj->setupRestEndPoints();
}
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
\Utils\LogManager::getInstance()->debug('Api dispatch URI: '.$uri);
\Utils\LogManager::getInstance()->debug('Api dispatch method: '.$uri);
if (!defined('SYM_CLIENT')) {
//For hosted installations, dispatch will be done in app/index
\Classes\Macaw::dispatch();
}
}else{
echo "REST Api is not enabled. Please set 'Api: REST Api Enabled' setting to true";
} }

View File

@@ -278,4 +278,12 @@ abstract class AbstractModuleManager
{ {
BaseService::getInstance()->addCalculationHook($code, $name, $class, $method); BaseService::getInstance()->addCalculationHook($code, $name, $class, $method);
} }
public function install() {
}
public function uninstall() {
}
} }

View File

@@ -292,8 +292,8 @@ class BaseService
$childCompaniesIds = array(); $childCompaniesIds = array();
if (\Classes\SettingsManager::getInstance()->getSetting( if (\Classes\SettingsManager::getInstance()->getSetting(
'System: Child Company Structure Managers Enabled' 'System: Child Company Structure Managers Enabled'
) == '1' ) == '1'
) { ) {
$childCompaniesResp = \Company\Common\Model\CompanyStructure::getAllChildCompanyStructures( $childCompaniesResp = \Company\Common\Model\CompanyStructure::getAllChildCompanyStructures(
$cempObj->department $cempObj->department
@@ -486,8 +486,8 @@ class BaseService
$childCompaniesIds = array(); $childCompaniesIds = array();
if (SettingsManager::getInstance()->getSetting( if (SettingsManager::getInstance()->getSetting(
'System: Child Company Structure Managers Enabled' 'System: Child Company Structure Managers Enabled'
) == '1' ) == '1'
) { ) {
$childCompaniesResp = CompanyStructure::getAllChildCompanyStructures($cempObj->department); $childCompaniesResp = CompanyStructure::getAllChildCompanyStructures($cempObj->department);
$childCompanies = $childCompaniesResp->getObject(); $childCompanies = $childCompaniesResp->getObject();
@@ -567,8 +567,8 @@ class BaseService
$childCompaniesIds = array(); $childCompaniesIds = array();
if (SettingsManager::getInstance()->getSetting( if (SettingsManager::getInstance()->getSetting(
'System: Child Company Structure Managers Enabled' 'System: Child Company Structure Managers Enabled'
) == '1' ) == '1'
) { ) {
$childCompaniesResp = CompanyStructure::getAllChildCompanyStructures($cempObj->department); $childCompaniesResp = CompanyStructure::getAllChildCompanyStructures($cempObj->department);
$childCompanies = $childCompaniesResp->getObject(); $childCompanies = $childCompaniesResp->getObject();
@@ -1783,8 +1783,8 @@ END;
) { ) {
$departmentHeadFound = true; $departmentHeadFound = true;
} elseif (SettingsManager::getInstance()->getSetting( } elseif (SettingsManager::getInstance()->getSetting(
'System: Child Company Structure Managers Enabled' 'System: Child Company Structure Managers Enabled'
) == '1' ) == '1'
) { ) {
$companyStructure = new CompanyStructure(); $companyStructure = new CompanyStructure();
$companyStructure->Load('id = ?', array($subordinate->department)); $companyStructure->Load('id = ?', array($subordinate->department));

View File

@@ -0,0 +1,143 @@
<?php
namespace Classes;
use Utils\LogManager;
class ExtensionManager
{
const GROUP = 'extension';
protected function processExtensionInDB() {
$dbModule = new \Modules\Common\Model\Module();
$extensions = $dbModule->Find("mod_group = ?", array(self::GROUP));
$extensionsInDB = [];
foreach ($extensions as $dbm) {
$extensionsInDB[$dbm->name] = $dbm;
ModuleAccessService::getInstance()->setModule($dbm->name, self::GROUP, $dbm);
}
return $extensionsInDB;
}
public function getExtensionsPath() {
return APP_BASE_PATH.'../extensions/';
}
public function getExtensionMetaData($extensionName)
{
return json_decode(file_get_contents($this->getExtensionsPath().$extensionName.'/meta.json'));
}
public function setupExtensions() {
$menu = [];
$extensions = [];
$extensionDirs = scandir($this->getExtensionsPath());
$currentLocation = 0;
$extensionsInDB = $this->processExtensionInDB();
$needToInstall = false;
foreach ($extensionDirs as $extensionDir) {
if (is_dir($this->getExtensionsPath().$extensionDir) && $extensionDir != '.' && $extensionDir != '..') {
$meta = $this->getExtensionMetaData($extensionDir);
$arr = [];
$arr['name'] = $extensionDir;
$arr['label'] = $meta->label;
$arr['icon'] = $meta->icon;
$arr['menu'] = $meta->menu[0];
$arr['order'] = 0;
$arr['status'] = 'Enabled';
$arr['user_levels'] = $meta->user_levels;
$arr['user_roles'] = isset($meta->user_roles)?$meta->user_roles:"";
$arr['model_namespace'] = $meta->model_namespace;
$arr['manager'] = $meta->manager;
// Add menu
$menu[$meta->menu[0]] = $meta->menu[1];
//Check in admin dbmodules
if (isset($extensionsInDB[$arr['name']])) {
$dbModule = $extensionsInDB[$arr['name']];
$arr['name'] = $dbModule->name;
$arr['label'] = $dbModule->label;
$arr['icon'] = $dbModule->icon;
$arr['menu'] = $dbModule->menu;
$arr['status'] = $dbModule->status;
$arr['user_levels'] = json_decode($dbModule->user_levels);
$arr['user_roles'] = empty($dbModule->user_roles)
? [] : json_decode($dbModule->user_roles);
$arr['user_roles_blacklist'] = empty($dbModule->user_roles_blacklist)
? [] : json_decode($dbModule->user_roles_blacklist);
} else {
$dbModule = new \Modules\Common\Model\Module();
$dbModule->menu = $arr['menu'];
$dbModule->name = $arr['name'];
$dbModule->label = $arr['label'];
$dbModule->icon = $arr['icon'];
$dbModule->mod_group = self::GROUP;
$dbModule->mod_order = $arr['order'];
$dbModule->status = "Enabled";
$dbModule->version = isset($meta->version)?$meta->version:"";
$dbModule->update_path = self::GROUP.">".$extensionDir;
$dbModule->user_levels = isset($meta->user_levels)?json_encode($meta->user_levels):"";
$dbModule->user_roles = isset($meta->user_roles)?json_encode($meta->user_roles):"";
$ok = $dbModule->Save();
if (!$ok) {
LogManager::getInstance()->error('Error saving module: '.$dbModule->name);
}
$needToInstall = $ok;
}
/* @var \Classes\AbstractModuleManager */
$manager = $this->includeModuleManager($extensionDir, $arr);
if ($dbModule->status == 'Disabled') {
continue;
}
if ($needToInstall) {
$manager->install();
}
$menuName = $arr['menu'];
if (!isset($extensions[$menuName])) {
$extensions[$menuName] = array();
}
if (!$meta->headless) {
if ($arr['order'] == '0' || $arr['order'] == '') {
$extensions[$menuName]["Z".$currentLocation] = $arr;
$currentLocation++;
} else {
$extensions[$arr['menu']]["A".$arr['order']] = $arr;
}
}
$initializer = $manager->getInitializer();
if ($initializer !== null) {
$initializer->setBaseService(BaseService::getInstance());
$initializer->init();
}
}
}
return [$menu, $extensions];
}
public function includeModuleManager($name, $data)
{
include($this->getExtensionsPath().$name.'/'.$name.'.php');
$moduleManagerClass = $data['manager'];
/* @var \Classes\AbstractModuleManager $moduleManagerObj*/
$moduleManagerObj = new $moduleManagerClass();
$moduleManagerObj->setModuleObject($data);
$moduleManagerObj->setModuleType(self::GROUP);
$moduleManagerObj->setModulePath(CLIENT_PATH.'/'.self::GROUP.'/'.$name);
\Classes\BaseService::getInstance()->addModuleManager($moduleManagerObj);
return $moduleManagerObj;
}
}

View File

@@ -94,6 +94,8 @@ class FileService
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret); $s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$result = $s3FileSys->putObject($s3Bucket, $uploadname, $localFile, 'authenticated-read'); $result = $s3FileSys->putObject($s3Bucket, $uploadname, $localFile, 'authenticated-read');
$file->size = filesize($localFile);
unlink("/tmp/".$file->filename); unlink("/tmp/".$file->filename);
unlink("/tmp/".$file->filename."_orig"); unlink("/tmp/".$file->filename."_orig");
@@ -101,7 +103,6 @@ class FileService
$file->employee = $profileImage->employee; $file->employee = $profileImage->employee;
$file->file_group = 'profile_image_small'; $file->file_group = 'profile_image_small';
$file->size = filesize(CLIENT_BASE_PATH.'data/'.$file->filename);
$file->size_text = $this->getReadableSize($file->size); $file->size_text = $this->getReadableSize($file->size);
if (!empty($result)) { if (!empty($result)) {
@@ -293,8 +294,7 @@ class FileService
if ($file->employee == $profileId) { if ($file->employee == $profileId) {
$ok = $file->Delete(); $ok = $file->Delete();
if ($ok) { if ($ok) {
LogManager::getInstance()->info("Delete File:".CLIENT_BASE_PATH.$file->filename); $this->deleteFileFromDisk($file);
unlink(CLIENT_BASE_PATH.'data/'.$file->filename);
} else { } else {
return false; return false;
} }
@@ -306,8 +306,7 @@ class FileService
if ($file->employee == $profileId) { if ($file->employee == $profileId) {
$ok = $file->Delete(); $ok = $file->Delete();
if ($ok) { if ($ok) {
LogManager::getInstance()->info("Delete File:".CLIENT_BASE_PATH.$file->filename); $this->deleteFileFromDisk($file);
unlink(CLIENT_BASE_PATH.'data/'.$file->filename);
} else { } else {
return false; return false;
} }
@@ -317,6 +316,30 @@ class FileService
return true; return true;
} }
public function deleteFileFromDisk($file)
{
$uploadFilesToS3 = SettingsManager::getInstance()->getSetting("Files: Upload Files to S3");
if ($uploadFilesToS3 == "1") {
$uploadFilesToS3Key = SettingsManager::getInstance()->getSetting(
"Files: Amazon S3 Key for File Upload"
);
$uploadFilesToS3Secret = SettingsManager::getInstance()->getSetting(
"Files: Amazone S3 Secret for File Upload"
);
$s3Bucket = SettingsManager::getInstance()->getSetting("Files: S3 Bucket");
$uploadname = CLIENT_NAME."/".$file->filename;
LogManager::getInstance()->info("Delete from S3:".$uploadname);
$s3FileSys = new S3FileSystem($uploadFilesToS3Key, $uploadFilesToS3Secret);
$s3FileSys->deleteObject($s3Bucket, $uploadname);
} else {
LogManager::getInstance()->info("Delete:".CLIENT_BASE_PATH.'data/'.$file->filename);
unlink(CLIENT_BASE_PATH.'data/'.$file->filename);
}
}
public function deleteFileByField($value, $field) public function deleteFileByField($value, $field)
{ {
LogManager::getInstance()->info("Delete file by field: $field / value: $value"); LogManager::getInstance()->info("Delete file by field: $field / value: $value");

View File

@@ -0,0 +1,20 @@
<?php
namespace Classes;
abstract class IceExtension extends AbstractModuleManager
{
public function initializeUserClasses()
{
// TODO: Implement initializeUserClasses() method.
}
public function initializeFieldMappings()
{
// TODO: Implement initializeFieldMappings() method.
}
public function initializeDatabaseErrorMappings()
{
// TODO: Implement initializeDatabaseErrorMappings() method.
}
}

177
core/src/Classes/IceRoute.php Executable file
View File

@@ -0,0 +1,177 @@
<?php
namespace Classes;
/**
* @method static IceRoute get(string $route, Callable $callback)
* @method static IceRoute post(string $route, Callable $callback)
* @method static IceRoute put(string $route, Callable $callback)
* @method static IceRoute delete(string $route, Callable $callback)
* @method static IceRoute options(string $route, Callable $callback)
* @method static IceRoute head(string $route, Callable $callback)
*/
class IceRoute
{
public static $halts = false;
public static $routes = array();
public static $methods = array();
public static $callbacks = array();
public static $patterns = array(
':any' => '[^/]+',
':num' => '[0-9]+',
':all' => '.*'
);
public static $error_callback;
/**
* Defines a route w/ callback and method
*/
public static function __callstatic($method, $params)
{
$uri = $params[0][0];
$callback = $params[0][1];
array_push(self::$routes, $uri);
array_push(self::$methods, strtoupper($method));
array_push(self::$callbacks, $callback);
return $uri;
}
/**
* Defines callback if route is not found
*/
public static function error($callback)
{
self::$error_callback = $callback;
}
public static function haltOnMatch($flag = true)
{
self::$halts = $flag;
}
/**
* Runs the callback for the given request
*/
public static function dispatch($uri, $method)
{
$searches = array_keys(static::$patterns);
$replaces = array_values(static::$patterns);
$found_route = false;
self::$routes = str_replace('//', '/', self::$routes);
// check if route is defined without regex
if (in_array($uri, self::$routes)) {
$route_pos = array_keys(self::$routes, $uri);
foreach ($route_pos as $route) {
//using an ANY option to match both GET and POST requests
if (self::$methods[$route] == $method || self::$methods[$route] == 'ANY') {
$found_route = true;
//if route is not an object
if (!is_object(self::$callbacks[$route])) {
//grab all parts based on a / separator
$parts = explode('/', self::$callbacks[$route]);
//collect the last index of the array
$last = end($parts);
//grab the controller name and method call
$segments = explode('@', $last);
//instanitate controller
$controller = new $segments[0]();
//call method
$controller->$segments[1]();
if (self::$halts) {
return;
}
} else {
//call closure
call_user_func(self::$callbacks[$route]);
if (self::$halts) {
return;
}
}
}
}
} else {
// check if defined with regex
$pos = 0;
foreach (self::$routes as $route) {
if (strpos($route, ':') !== false) {
$route = str_replace($searches, $replaces, $route);
}
if (preg_match('#^' . $route . '$#', $uri, $matched)) {
if (self::$methods[$pos] == $method) {
$found_route = true;
array_shift($matched); //remove $matched[0] as [1] is the first parameter.
if (!is_object(self::$callbacks[$pos])) {
//grab all parts based on a / separator
$parts = explode('/', self::$callbacks[$pos]);
//collect the last index of the array
$last = end($parts);
//grab the controller name and method call
$segments = explode('@', $last);
//instanitate controller
$controller = new $segments[0]();
//fix multi parameters
if (!method_exists($controller, $segments[1])) {
echo "controller and action not found";
} else {
call_user_func_array(array($controller, $segments[1]), $matched);
}
//call method and pass any extra parameters to the method
// $controller->$segments[1](implode(",", $matched));
if (self::$halts) {
return;
}
} else {
call_user_func_array(self::$callbacks[$pos], $matched);
if (self::$halts) {
return;
}
}
}
}
$pos++;
}
}
// run the error callback if the route was not found
if ($found_route == false) {
if (!self::$error_callback) {
self::$error_callback = function () {
header($_SERVER['SERVER_PROTOCOL']." 404 Not Found");
echo '404';
};
}
call_user_func(self::$error_callback);
}
}
}

View File

@@ -42,6 +42,8 @@ class Macaw
array_push(self::$methods, strtoupper($method)); array_push(self::$methods, strtoupper($method));
array_push(self::$callbacks, $callback); array_push(self::$callbacks, $callback);
call_user_func('\Classes\IceRoute::'.$method, $params);
return $uri; return $uri;
} }

View File

@@ -8,6 +8,8 @@
namespace Classes\Migration; namespace Classes\Migration;
use Utils\LogManager;
abstract class AbstractMigration abstract class AbstractMigration
{ {
protected $file; protected $file;
@@ -16,7 +18,7 @@ abstract class AbstractMigration
protected $lastError; protected $lastError;
public function __construct($file) public function __construct($file = null)
{ {
$this->file = $file; $this->file = $file;
} }
@@ -50,6 +52,7 @@ abstract class AbstractMigration
$ret = $this->db()->Execute($sql); $ret = $this->db()->Execute($sql);
if (!$ret) { if (!$ret) {
$this->lastError = $this->db()->ErrorMsg(); $this->lastError = $this->db()->ErrorMsg();
LogManager::getInstance()->error('Error in migration: '.$this->lastError);
} }
return $ret; return $ret;
} }

View File

@@ -12,7 +12,7 @@ class ModuleAccess
* @param $name * @param $name
* @param $group * @param $group
*/ */
public function __construct($name, $group) public function __construct($name, $group = 'extension')
{ {
$this->name = $name; $this->name = $name;
$this->group = $group; $this->group = $group;

View File

@@ -420,6 +420,8 @@ class RestEndPoint
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) { if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
$token = $matches[1]; $token = $matches[1];
} }
} else {
$token = $_GET['token'];
} }
if (strlen($token) > 32) { if (strlen($token) > 32) {

View File

@@ -147,6 +147,10 @@ class EmployeesActionManager extends SubActionManager
return new IceResponse(IceResponse::ERROR, "Error occurred while changing password"); return new IceResponse(IceResponse::ERROR, "Error occurred while changing password");
} }
if (!PasswordManager::verifyPassword($req->current, $user->password)) {
return new IceResponse(IceResponse::ERROR, "Current password is incorrect");
}
$passwordStrengthResponse = PasswordManager::isQualifiedPassword($req->pwd); $passwordStrengthResponse = PasswordManager::isQualifiedPassword($req->pwd);
if ($passwordStrengthResponse->getStatus() === IceResponse::ERROR) { if ($passwordStrengthResponse->getStatus() === IceResponse::ERROR) {
return $passwordStrengthResponse; return $passwordStrengthResponse;
@@ -158,6 +162,6 @@ class EmployeesActionManager extends SubActionManager
return new IceResponse(IceResponse::ERROR, $user->ErrorMsg()); return new IceResponse(IceResponse::ERROR, $user->ErrorMsg());
} }
return new IceResponse(IceResponse::SUCCESS, $user); return new IceResponse(IceResponse::SUCCESS, []);
} }
} }

1
extensions/gitkeep Executable file
View File

@@ -0,0 +1 @@
git keep

View File

@@ -0,0 +1,13 @@
{
"label": "My Tasks",
"menu": ["Tasks", "fa-list"],
"icon": "fa-tasks",
"user_levels": [
"Admin",
"Manager",
"User"
],
"model_namespace": "\\Tasks\\Model",
"manager": "\\Tasks\\Extension",
"headless": false
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Tasks;
use Classes\IceExtension;
class Extension extends IceExtension
{
public function install() {
$migration = new Migration();
return $migration->up();
}
public function uninstall() {
$migration = new Migration();
return $migration->down();
}
public function setupModuleClassDefinitions()
{
}
public function setupRestEndPoints()
{
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Tasks;
use Classes\Migration\AbstractMigration;
class Migration extends AbstractMigration
{
public function up()
{
$sql = <<<'SQL'
create table `Tasks` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`employee` bigint(20) NULL,
`name` varchar(250) NOT NULL,
`description` TEXT NULL,
`attachment` varchar(100) NULL,
`created` DATETIME default NULL,
`updated` DATETIME default NULL,
primary key (`id`),
CONSTRAINT `Fk_EmployeeTasks_Employees` FOREIGN KEY (`employee`) REFERENCES `Employees` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) engine=innodb default charset=utf8;
SQL;
return $this->executeQuery($sql);
}
public function down()
{
$sql = <<<'SQL'
DROP TABLE IF EXISTS `Tasks`;
SQL;
return $this->executeQuery($sql);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Tasks\Model;
use Classes\ModuleAccess;
use Model\BaseModel;
class Task extends BaseModel
{
public $table = 'Tasks';
public function getAdminAccess()
{
return ["get","element","save","delete"];
}
public function getManagerAccess()
{
return ["get","element"];
}
public function getUserAccess()
{
return [];
}
public function getAnonymousAccess()
{
return [];
}
public function getModuleAccess()
{
return [
new ModuleAccess('tasks'),
];
}
}

View File

@@ -0,0 +1,4 @@
<?php
require_once __DIR__.'/src/Tasks/Extension.php';
require_once __DIR__.'/src/Tasks/Migration.php';
require_once __DIR__.'/src/Tasks/Model/Task.php';

View File

@@ -0,0 +1,33 @@
<?php
$user = \Classes\BaseService::getInstance()->getCurrentUser();
echo "Welcome ".$user->username."<br/>";
echo "Creating a task <br/>";
$task = new \Tasks\Model\Task();
$taskName = 'Task-'.rand(rand(0, 100), 50000);
$task->name = $taskName;
$task->employee = $user->employee;
$task->description = $taskName.' description';
$task->created = date('Y-m-d H:i:s');
$task->updated = date('Y-m-d H:i:s');
/**
* Saving the task, $ok will be false if there were any error during the creation
*/
$ok = $task->Save();
if (!$ok) {
echo "Error: ".$task->ErrorMsg()." <br/>";
}
echo "Find last task <br/>";
$taskFromDB = new \Tasks\Model\Task();
/**
* You can use load method to load the first matching task into an empty model
*/
$taskFromDB->Load('name = ?', [$taskName]);
var_dump($taskFromDB);

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -359,6 +359,48 @@ gulp.task('modules-js', (done) => {
.pipe(gulp.dest('./web/dist')); .pipe(gulp.dest('./web/dist'));
}); });
gulp.task('extension-js', (done) => {
let extension = process.argv.filter((item) => item.substr(0, 3) === '--x');
if (extension.length === 1) {
extension = extension[0].substr(3);
}
// map them to our stream function
return browserify({
entries: [`extensions/${extension}/web/js/index.js`],
basedir: '.',
debug: true,
cache: {},
packageCache: {},
})
.external(vendorsFlat)
.transform('babelify', {
plugins: [
['@babel/plugin-proposal-class-properties', { loose: true }],
],
presets: ['@babel/preset-env', '@babel/preset-react'],
extensions: ['.js', '.jsx'],
})
.transform(require('browserify-css'))
.bundle()
.pipe(source(`${extension}.js`))
.pipe(buffer())
.pipe(ifElse(!isProduction, () => sourcemaps.init({ loadMaps: true })))
.pipe(ifElse(isProduction, () => uglifyes(
{
compress: true,
mangle: {
reserved: [],
},
},
)))
.pipe(ifElse(isProduction, () => javascriptObfuscator({
compact: true,
})))
.pipe(ifElse(!isProduction, () => sourcemaps.write('./')))
.pipe(gulp.dest(`./extensions/${extension}/dist`));
});
gulp.task('watch', () => { gulp.task('watch', () => {
gulp.watch('web/admin/src/*/*.js', gulp.series('admin-js')); gulp.watch('web/admin/src/*/*.js', gulp.series('admin-js'));

View File

@@ -1,5 +1,3 @@
<img src="web/images/logo-sq.png" align="right" />
IceHrm IceHrm
=========== ===========
[![Build Status](https://travis-ci.org/gamonoid/icehrm.svg?branch=master)](https://travis-ci.org/gamonoid/icehrm) [![Build Status](https://travis-ci.org/gamonoid/icehrm.svg?branch=master)](https://travis-ci.org/gamonoid/icehrm)

View File

@@ -154,7 +154,7 @@ class EmployeeDocumentAdapter extends AdapterBase {
getFilters() { getFilters() {
return [ return [
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }], ['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name', 'getActiveSubordinateEmployees'] }],
]; ];
} }

View File

@@ -55,7 +55,7 @@ class AdapterBase extends ModuleBase {
} }
setupApiClient(token) { setupApiClient(token) {
this.apiClient = new IceApiClient(this.apiUrl, token); this.apiClient = new IceApiClient(this.apiUrl, token, window.CLIENT_BASE_URL, true);
} }
setApiUrl(apiUrl) { setApiUrl(apiUrl) {

26
web/api/CustomAction.js Normal file
View File

@@ -0,0 +1,26 @@
const axios = require('axios');
class CustomAction {
constructor(adapter) {
this.adapter = adapter;
}
execute(subAction, module, request, isPost) {
if (!isPost) {
return axios.get(
this.adapter.moduleRelativeURL,
{
params: {
t: this.adapter.table, a: 'ca', sa: subAction, mod: module, req: request,
},
},
);
}
return axios.post(this.moduleRelativeURL, {
t: this.adapter.table, a: 'ca', sa: subAction, mod: module, req: request,
});
}
}
export default CustomAction;

View File

@@ -1,12 +1,19 @@
const axios = require('axios'); const axios = require('axios');
class IceApiClient { class IceApiClient {
constructor(baseUrl, token) { constructor(baseUrl, token, clientBaseUrl, legacyApiWrapper = true) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
this.token = token; this.token = token;
this.clientBaseUrl = clientBaseUrl;
this.legacyApiWrapper = legacyApiWrapper;
} }
get(endpoint) { get(endpoint) {
if (this.legacyApiWrapper) {
const url = `${this.clientBaseUrl}api/index.php?token=${this.token}&method=get&url=/${endpoint}`;
return axios.get(url);
}
return axios.get(this.baseUrl + endpoint, { return axios.get(this.baseUrl + endpoint, {
headers: { headers: {
Authorization: `Bearer ${this.token}`, Authorization: `Bearer ${this.token}`,

View File

@@ -61,10 +61,10 @@ class ModuleBase {
} }
/** /**
* Some browsers do not support sending JSON in get parameters. Set this to true to avoid sending JSON * Some browsers do not support sending JSON in get parameters. Set this to true to avoid sending JSON
* @method setNoJSONRequests * @method setNoJSONRequests
* @param val {Boolean} * @param val {Boolean}
*/ */
setNoJSONRequests(val) { setNoJSONRequests(val) {
this.noJSONRequests = val; this.noJSONRequests = val;
} }
@@ -79,12 +79,12 @@ class ModuleBase {
} }
/** /**
* Check if the current user has a permission * Check if the current user has a permission
* @method checkPermission * @method checkPermission
* @param permission {String} * @param permission {String}
* @example * @example
* this.checkPermission("Upload/Delete Profile Image") * this.checkPermission("Upload/Delete Profile Image")
*/ */
checkPermission(permission) { checkPermission(permission) {
if (this.permissions[permission] === undefined || this.permissions[permission] == null || this.permissions[permission] === 'Yes') { if (this.permissions[permission] === undefined || this.permissions[permission] == null || this.permissions[permission] === 'Yes') {
return 'Yes'; return 'Yes';
@@ -168,15 +168,15 @@ class ModuleBase {
} }
/** /**
* If this method returned false the action buttons in data table for modules will not be displayed. * If this method returned false the action buttons in data table for modules will not be displayed.
* Override this method in module lib.js to hide action buttons * Override this method in module lib.js to hide action buttons
* @method showActionButtons * @method showActionButtons
* @param permission {String} * @param permission {String}
* @example * @example
* EmployeeLeaveEntitlementAdapter.method('showActionButtons() { * EmployeeLeaveEntitlementAdapter.method('showActionButtons() {
* return false; * return false;
* } * }
*/ */
showActionButtons() { showActionButtons() {
return true; return true;
} }
@@ -201,30 +201,30 @@ class ModuleBase {
} }
/** /**
* Get the current profile * Get the current profile
* @method getCurrentProfile * @method getCurrentProfile
* @returns Profile of the current user if the profile is not switched if not switched profile * @returns Profile of the current user if the profile is not switched if not switched profile
*/ */
getCurrentProfile() { getCurrentProfile() {
return this.currentProfile; return this.currentProfile;
} }
/** /**
* Retrive data required to create select boxes for add new /edit forms for a given module. This is called when loading the module * Retrive data required to create select boxes for add new /edit forms for a given module. This is called when loading the module
* @method initFieldMasterData * @method initFieldMasterData
* @param callback {Function} call this once loading completed * @param callback {Function} call this once loading completed
* @param callback {Function} call this once all field loading completed. This indicate that the form can be displayed saftly * @param callback {Function} call this once all field loading completed. This indicate that the form can be displayed saftly
* @example * @example
* ReportAdapter.method('renderForm(object) { * ReportAdapter.method('renderForm(object) {
* var that = this; * var that = this;
* this.processFormFieldsWithObject(object); * this.processFormFieldsWithObject(object);
* var cb = function(){ * var cb = function(){
* that.super.renderForm(object); * that.super.renderForm(object);
* }; * };
* this.initFieldMasterData(cb); * this.initFieldMasterData(cb);
* } * }
*/ */
initFieldMasterData(callback, loadAllCallback, loadAllCallbackData) { initFieldMasterData(callback, loadAllCallback, loadAllCallbackData) {
this.fieldMasterData = {}; this.fieldMasterData = {};
this.fieldMasterDataKeys = {}; this.fieldMasterDataKeys = {};
@@ -236,10 +236,11 @@ class ModuleBase {
for (let i = 0; i < remoteSourceFields.length; i++) { for (let i = 0; i < remoteSourceFields.length; i++) {
const fieldRemote = remoteSourceFields[i]; const fieldRemote = remoteSourceFields[i];
if (fieldRemote[1]['remote-source'] !== undefined && fieldRemote[1]['remote-source'] != null) { if (fieldRemote[1]['remote-source'] !== undefined && fieldRemote[1]['remote-source'] != null) {
let key = `${fieldRemote[1]['remote-source'][0]}_${fieldRemote[1]['remote-source'][1]}_${fieldRemote[1]['remote-source'][2]}`; // let key = `${fieldRemote[1]['remote-source'][0]}_${fieldRemote[1]['remote-source'][1]}_${fieldRemote[1]['remote-source'][2]}`;
if (fieldRemote[1]['remote-source'].length === 4) { // if (fieldRemote[1]['remote-source'].length === 4) {
key = `${key}_${fieldRemote[1]['remote-source'][3]}`; // key = `${key}_${fieldRemote[1]['remote-source'][3]}`;
} // }
const key = this.getRemoteSourceKey(fieldRemote);
this.fieldMasterDataKeys[key] = false; this.fieldMasterDataKeys[key] = false;
const callBackData = {}; const callBackData = {};
@@ -320,26 +321,26 @@ class ModuleBase {
} }
/** /**
* Pass true to this method after creating module JS object to open new/edit entry form for the module on a popup. * Pass true to this method after creating module JS object to open new/edit entry form for the module on a popup.
* @method setShowFormOnPopup * @method setShowFormOnPopup
* @param val {Boolean} * @param val {Boolean}
* @example * @example
* modJs.subModJsList['tabCandidateApplication'] = new CandidateApplicationAdapter('Application','CandidateApplication',{"candidate":data.id} * modJs.subModJsList['tabCandidateApplication'] = new CandidateApplicationAdapter('Application','CandidateApplication',{"candidate":data.id}
* modJs.subModJsList['tabCandidateApplication'].setShowFormOnPopup(true); * modJs.subModJsList['tabCandidateApplication'].setShowFormOnPopup(true);
*/ */
setShowFormOnPopup(val) { setShowFormOnPopup(val) {
this.showFormOnPopup = val; this.showFormOnPopup = val;
} }
/** /**
* Set this to true to if you need the datatable to load data page by page instead of loading all data at once. * Set this to true to if you need the datatable to load data page by page instead of loading all data at once.
* @method setRemoteTable * @method setRemoteTable
* @param val {Boolean} * @param val {Boolean}
* @example * @example
* modJs.subModJsList['tabCandidateApplication'] = new CandidateApplicationAdapter('Application','CandidateApplication',{"candidate":data.id} * modJs.subModJsList['tabCandidateApplication'] = new CandidateApplicationAdapter('Application','CandidateApplication',{"candidate":data.id}
* modJs.subModJsList['tabCandidateApplication'].setRemoteTable(true); * modJs.subModJsList['tabCandidateApplication'].setRemoteTable(true);
*/ */
setRemoteTable(val) { setRemoteTable(val) {
this.createRemoteTable = val; this.createRemoteTable = val;
@@ -372,14 +373,14 @@ class ModuleBase {
} }
if (this.fieldMasterDataCallback !== null if (this.fieldMasterDataCallback !== null
&& this.fieldMasterDataCallback !== undefined && this.fieldMasterDataCallback !== undefined
&& this.isAllLoaded(this.fieldMasterDataKeys) && this.isAllLoaded(this.fieldMasterDataKeys)
&& (this.fieldMasterDataCallbackData !== null && this.fieldMasterDataCallbackData !== undefined) && (this.fieldMasterDataCallbackData !== null && this.fieldMasterDataCallbackData !== undefined)
) { ) {
this.fieldMasterDataCallback(this.fieldMasterDataCallbackData); this.fieldMasterDataCallback(this.fieldMasterDataCallbackData);
} else if (this.fieldMasterDataCallback !== null } else if (this.fieldMasterDataCallback !== null
&& this.fieldMasterDataCallback !== undefined && this.fieldMasterDataCallback !== undefined
&& this.isAllLoaded(this.fieldMasterDataKeys) && this.isAllLoaded(this.fieldMasterDataKeys)
) { ) {
this.fieldMasterDataCallback(); this.fieldMasterDataCallback();
} }
@@ -597,10 +598,10 @@ class ModuleBase {
} }
/** /**
* Create the data table on provided element id * Create the data table on provided element id
* @method createTable * @method createTable
* @param val {Boolean} * @param val {Boolean}
*/ */
createTable(elementId) { createTable(elementId) {
const that = this; const that = this;
@@ -678,10 +679,10 @@ class ModuleBase {
} }
/** /**
* Create a data table on provided element id which loads data page by page * Create a data table on provided element id which loads data page by page
* @method createTableServer * @method createTableServer
* @param val {Boolean} * @param val {Boolean}
*/ */
createTableServer(elementId) { createTableServer(elementId) {
const that = this; const that = this;
@@ -748,10 +749,10 @@ class ModuleBase {
} }
/** /**
* This should be overridden in module lib.js classes to return module headers which are used to create the data table. * This should be overridden in module lib.js classes to return module headers which are used to create the data table.
* @method getHeaders * @method getHeaders
* @example * @example
SettingAdapter.method('getHeaders() { SettingAdapter.method('getHeaders() {
return [ return [
{ "sTitle": "ID" ,"bVisible":false}, { "sTitle": "ID" ,"bVisible":false},
{ "sTitle": "Name" }, { "sTitle": "Name" },
@@ -759,17 +760,17 @@ class ModuleBase {
{ "sTitle": "Details"} { "sTitle": "Details"}
]; ];
} }
*/ */
getHeaders() { getHeaders() {
} }
/** /**
* This should be overridden in module lib.js classes to return module field values which are used to create the data table. * This should be overridden in module lib.js classes to return module field values which are used to create the data table.
* @method getDataMapping * @method getDataMapping
* @example * @example
SettingAdapter.method('getDataMapping() { SettingAdapter.method('getDataMapping() {
return [ return [
"id", "id",
"name", "name",
@@ -777,23 +778,23 @@ class ModuleBase {
"description" "description"
]; ];
} }
*/ */
getDataMapping() { getDataMapping() {
} }
/** /**
* This should be overridden in module lib.js classes to return module from fields which are used to create the add/edit form and also used for initializing select box values in form. * This should be overridden in module lib.js classes to return module from fields which are used to create the add/edit form and also used for initializing select box values in form.
* @method getFormFields * @method getFormFields
* @example * @example
SettingAdapter.method('getFormFields() { SettingAdapter.method('getFormFields() {
return [ return [
[ "id", {"label":"ID","type":"hidden"}], [ "id", {"label":"ID","type":"hidden"}],
[ "value", {"label":"Value","type":"text","validation":"none"}] [ "value", {"label":"Value","type":"text","validation":"none"}]
]; ];
} }
*/ */
getFormFields() { getFormFields() {
} }
@@ -807,26 +808,26 @@ class ModuleBase {
} }
/** /**
* This can be overridden in module lib.js classes inorder to show a filter form * This can be overridden in module lib.js classes inorder to show a filter form
* @method getFilters * @method getFilters
* @example * @example
EmployeeAdapter.method('getFilters() { EmployeeAdapter.method('getFilters() {
return [ return [
[ "job_title", {"label":"Job Title","type":"select2","allow-null":true,"null-label":"All Job Titles","remote-source":["JobTitle","id","name"]}], [ "job_title", {"label":"Job Title","type":"select2","allow-null":true,"null-label":"All Job Titles","remote-source":["JobTitle","id","name"]}],
[ "department", {"label":"Department","type":"select2","allow-null":true,"null-label":"All Departments","remote-source":["CompanyStructure","id","title"]}], [ "department", {"label":"Department","type":"select2","allow-null":true,"null-label":"All Departments","remote-source":["CompanyStructure","id","title"]}],
[ "supervisor", {"label":"Supervisor","type":"select2","allow-null":true,"null-label":"Anyone","remote-source":["Employee","id","first_name+last_name"]}] [ "supervisor", {"label":"Supervisor","type":"select2","allow-null":true,"null-label":"Anyone","remote-source":["Employee","id","first_name+last_name"]}]
]; ];
} }
*/ */
getFilters() { getFilters() {
return null; return null;
} }
/** /**
* Show the edit form for an item * Show the edit form for an item
* @method edit * @method edit
* @param id {int} id of the item to edit * @param id {int} id of the item to edit
*/ */
edit(id) { edit(id) {
this.currentId = id; this.currentId = id;
this.getElement(id, []); this.getElement(id, []);
@@ -890,10 +891,10 @@ class ModuleBase {
} }
/** /**
* Delete an item * Delete an item
* @method deleteRow * @method deleteRow
* @param id {int} id of the item to edit * @param id {int} id of the item to edit
*/ */
deleteRow(id) { deleteRow(id) {
this.deleteParams.id = id; this.deleteParams.id = id;
@@ -902,17 +903,17 @@ class ModuleBase {
} }
/** /**
* Show a popup with message * Show a popup with message
* @method showMessage * @method showMessage
* @param title {String} title of the message box * @param title {String} title of the message box
* @param message {String} message * @param message {String} message
* @param closeCallback {Function} this will be called once the dialog is closed (optional) * @param closeCallback {Function} this will be called once the dialog is closed (optional)
* @param closeCallback {Function} data to pass to close callback (optional) * @param closeCallback {Function} data to pass to close callback (optional)
* @param closeCallbackData * @param closeCallbackData
* @param isPlain {Boolean} if true buttons are not shown (optional / default = true) * @param isPlain {Boolean} if true buttons are not shown (optional / default = true)
* @example * @example
* this.showMessage("Error Occured while Applying Leave", callBackData); * this.showMessage("Error Occured while Applying Leave", callBackData);
*/ */
showMessage(title, message, closeCallback = null, closeCallbackData = null, isPlain = false) { showMessage(title, message, closeCallback = null, closeCallbackData = null, isPlain = false) {
const that = this; const that = this;
let modelId = ''; let modelId = '';
@@ -1005,11 +1006,11 @@ class ModuleBase {
/** /**
* Create or edit an element * Create or edit an element
* @method save * @method save
* @param getFunctionCallBackData {Array} once a success is returned call get() function for this module with these parameters * @param getFunctionCallBackData {Array} once a success is returned call get() function for this module with these parameters
* @param successCallback {Function} this will get called after success response * @param successCallback {Function} this will get called after success response
*/ */
save(callGetFunction, successCallback) { save(callGetFunction, successCallback) {
const validator = new FormValidation(`${this.getTableName()}_submit`, true, { ShowPopup: false, LabelErrorClass: 'error' }); const validator = new FormValidation(`${this.getTableName()}_submit`, true, { ShowPopup: false, LabelErrorClass: 'error' });
@@ -1040,7 +1041,7 @@ class ModuleBase {
const fields = this.getFormFields(); const fields = this.getFormFields();
fields.forEach((field) => { fields.forEach((field) => {
if ((field[1].type === 'date' || field[1].type === 'datetime') if ((field[1].type === 'date' || field[1].type === 'datetime')
&& (params[field[0]] === '' || params[field[0]] === '0000-00-00' || params[field[0]] === '0000-00-00 00:00:00')) { && (params[field[0]] === '' || params[field[0]] === '0000-00-00' || params[field[0]] === '0000-00-00 00:00:00')) {
if (field[1].validation === 'none') { if (field[1].validation === 'none') {
params[field[0]] = 'NULL'; params[field[0]] = 'NULL';
} else { } else {
@@ -1084,23 +1085,23 @@ class ModuleBase {
} }
/** /**
* Override this method to inject attitional parameters or modify existing parameters retrived from * Override this method to inject attitional parameters or modify existing parameters retrived from
* add/edit form before sending to the server * add/edit form before sending to the server
* @method forceInjectValuesBeforeSave * @method forceInjectValuesBeforeSave
* @param params {Array} keys and values in form * @param params {Array} keys and values in form
* @returns {Array} modified parameters * @returns {Array} modified parameters
*/ */
forceInjectValuesBeforeSave(params) { forceInjectValuesBeforeSave(params) {
return params; return params;
} }
/** /**
* Override this method to do custom validations at client side * Override this method to do custom validations at client side
* @method doCustomValidation * @method doCustomValidation
* @param params {Array} keys and values in form * @param params {Array} keys and values in form
* @returns {Null or String} return null if validation success, returns error message if unsuccessful * @returns {Null or String} return null if validation success, returns error message if unsuccessful
* @example * @example
EmployeeLeaveAdapter.method('doCustomValidation(params) { EmployeeLeaveAdapter.method('doCustomValidation(params) {
try{ try{
if(params['date_start'] != params['date_end']){ if(params['date_start'] != params['date_end']){
var ds = new Date(params['date_start']); var ds = new Date(params['date_start']);
@@ -1114,7 +1115,7 @@ class ModuleBase {
} }
return null; return null;
} }
*/ */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
doCustomValidation(params) { doCustomValidation(params) {
return null; return null;
@@ -1177,7 +1178,12 @@ class ModuleBase {
value = 'Not Selected'; value = 'Not Selected';
} }
} else { } else {
value = this.fieldMasterData[`${rmf[0]}_${rmf[1]}_${rmf[2]}`][filters[prop]]; let key = `${rmf[0]}_${rmf[1]}_${rmf[2]}`;
if (rmf.length > 3) {
key = `${key}_${rmf[3]}`;
}
//value = this.fieldMasterData[`${rmf[0]}_${rmf[1]}_${rmf[2]}`][filters[prop]];
value = this.fieldMasterData[key][filters[prop]];
valueOrig = value; valueOrig = value;
} }
} else { } else {
@@ -1231,20 +1237,20 @@ class ModuleBase {
} }
/** /**
* Override this method to do custom validations at client side for values selected in filters * Override this method to do custom validations at client side for values selected in filters
* @method doCustomFilterValidation * @method doCustomFilterValidation
* @param params {Array} keys and values in form * @param params {Array} keys and values in form
* @returns {Null or String} return null if validation success, returns error message if unsuccessful * @returns {Null or String} return null if validation success, returns error message if unsuccessful
*/ */
doCustomFilterValidation(params) { doCustomFilterValidation(params) {
return true; return true;
} }
/** /**
* Reset selected filters * Reset selected filters
* @method resetFilters * @method resetFilters
*/ */
resetFilters() { resetFilters() {
this.filter = this.origFilter; this.filter = this.origFilter;
@@ -1332,7 +1338,8 @@ class ModuleBase {
try { try {
modJs.filterQuery(); modJs.filterQuery();
} catch (err) { } catch (err) {
// Do Nothing console.log(err);
console.log(err.message);
} }
return false; return false;
}); });
@@ -1344,20 +1351,20 @@ class ModuleBase {
/** /**
* Override this method in your module class to make changes to data fo the form before showing the form * Override this method in your module class to make changes to data fo the form before showing the form
* @method preRenderForm * @method preRenderForm
* @param object {Array} keys value list for populating form * @param object {Array} keys value list for populating form
*/ */
preRenderForm(object) { preRenderForm(object) {
} }
/** /**
* Create the form * Create the form
* @method renderForm * @method renderForm
* @param object {Array} keys value list for populating form * @param object {Array} keys value list for populating form
*/ */
renderForm(object) { renderForm(object) {
const signatureIds = []; const signatureIds = [];
@@ -1581,28 +1588,28 @@ class ModuleBase {
} }
/** /**
* Override this method in your module class to make changes to data fo the form after showing it * Override this method in your module class to make changes to data fo the form after showing it
* @method postRenderForm * @method postRenderForm
* @param object {Array} keys value list for populating form * @param object {Array} keys value list for populating form
* @param $tempDomObj {DOM} a DOM element for the form * @param $tempDomObj {DOM} a DOM element for the form
* @example * @example
* UserAdapter.method('postRenderForm(object, $tempDomObj) { * UserAdapter.method('postRenderForm(object, $tempDomObj) {
if(object == null || object == undefined){ if(object == null || object == undefined){
$tempDomObj.find("#changePasswordBtn").remove(); $tempDomObj.find("#changePasswordBtn").remove();
} }
} }
*/ */
postRenderForm(object, $tempDomObj) { postRenderForm(object, $tempDomObj) {
} }
/** /**
* Convert data group field to HTML * Convert data group field to HTML
* @method dataGroupToHtml * @method dataGroupToHtml
* @param val {String} value in the field * @param val {String} value in the field
* @param field {Array} field meta data * @param field {Array} field meta data
*/ */
dataGroupToHtml(val, field) { dataGroupToHtml(val, field) {
const data = JSON.parse(val); const data = JSON.parse(val);
@@ -1657,10 +1664,10 @@ class ModuleBase {
} }
/** /**
* Reset the DataGroup for a given field * Reset the DataGroup for a given field
* @method resetDataGroup * @method resetDataGroup
* @param field {Array} field meta data * @param field {Array} field meta data
*/ */
resetDataGroup(field) { resetDataGroup(field) {
$(`#${field[0]}`).val(''); $(`#${field[0]}`).val('');
$(`#${field[0]}_div`).html(''); $(`#${field[0]}_div`).html('');
@@ -2054,12 +2061,12 @@ class ModuleBase {
/** /**
* Fill a form with required values after showing it * Fill a form with required values after showing it
* @method fillForm * @method fillForm
* @param object {Array} form data * @param object {Array} form data
* @param formId {String} id of the form * @param formId {String} id of the form
* @param formId {Array} field meta data * @param formId {Array} field meta data
*/ */
fillForm(object, formId, fields) { fillForm(object, formId, fields) {
let placeHolderVal; let placeHolderVal;
@@ -2094,7 +2101,8 @@ class ModuleBase {
$(`${formId} #${fields[i][0]}`).html(object[fields[i][0]]); $(`${formId} #${fields[i][0]}`).html(object[fields[i][0]]);
} else if (fields[i][1].type === 'placeholder') { } else if (fields[i][1].type === 'placeholder') {
if (fields[i][1]['remote-source'] !== undefined && fields[i][1]['remote-source'] != null) { if (fields[i][1]['remote-source'] !== undefined && fields[i][1]['remote-source'] != null) {
const key = `${fields[i][1]['remote-source'][0]}_${fields[i][1]['remote-source'][1]}_${fields[i][1]['remote-source'][2]}`; //const key = `${fields[i][1]['remote-source'][0]}_${fields[i][1]['remote-source'][1]}_${fields[i][1]['remote-source'][2]}`;
const key = this.getRemoteSourceKey(fields[i]);
placeHolderVal = this.fieldMasterData[key][object[fields[i][0]]]; placeHolderVal = this.fieldMasterData[key][object[fields[i][0]]];
} else { } else {
placeHolderVal = object[fields[i][0]]; placeHolderVal = object[fields[i][0]];
@@ -2172,7 +2180,7 @@ class ModuleBase {
} }
} else if (fields[i][1].type === 'signature') { } else if (fields[i][1].type === 'signature') {
if (object[fields[i][0]] !== '' || object[fields[i][0]] !== undefined if (object[fields[i][0]] !== '' || object[fields[i][0]] !== undefined
|| object[fields[i][0]] != null) { || object[fields[i][0]] != null) {
$(`${formId} #${fields[i][0]}`).data('signaturePad').fromDataURL(object[fields[i][0]]); $(`${formId} #${fields[i][0]}`).data('signaturePad').fromDataURL(object[fields[i][0]]);
} }
} else if (fields[i][1].type === 'simplemde') { } else if (fields[i][1].type === 'simplemde') {
@@ -2189,9 +2197,9 @@ class ModuleBase {
} }
/** /**
* Cancel edit or add new on modules * Cancel edit or add new on modules
* @method cancel * @method cancel
*/ */
cancel() { cancel() {
$(`#${this.getTableName()}Form`).hide(); $(`#${this.getTableName()}Form`).hide();
@@ -2218,10 +2226,11 @@ class ModuleBase {
if (field[1].source !== undefined && field[1].source != null) { if (field[1].source !== undefined && field[1].source != null) {
t = t.replace('_options_', this.renderFormSelectOptions(field[1].source, field)); t = t.replace('_options_', this.renderFormSelectOptions(field[1].source, field));
} else if (field[1]['remote-source'] !== undefined && field[1]['remote-source'] != null) { } else if (field[1]['remote-source'] !== undefined && field[1]['remote-source'] != null) {
let key = `${field[1]['remote-source'][0]}_${field[1]['remote-source'][1]}_${field[1]['remote-source'][2]}`; // let key = `${field[1]['remote-source'][0]}_${field[1]['remote-source'][1]}_${field[1]['remote-source'][2]}`;
if (field[1]['remote-source'].length === 4) { // if (field[1]['remote-source'].length === 4) {
key = `${key}_${field[1]['remote-source'][3]}`; // key = `${key}_${field[1]['remote-source'][3]}`;
} // }
const key = this.getRemoteSourceKey(field);
t = t.replace('_options_', this.renderFormSelectOptionsRemote(this.fieldMasterData[key], field)); t = t.replace('_options_', this.renderFormSelectOptionsRemote(this.fieldMasterData[key], field));
} }
} else if (field[1].type === 'colorpick') { } else if (field[1].type === 'colorpick') {
@@ -2413,49 +2422,49 @@ class ModuleBase {
} }
/** /**
* Override this method to change add new button label * Override this method to change add new button label
* @method getAddNewLabel * @method getAddNewLabel
*/ */
getAddNewLabel() { getAddNewLabel() {
return 'Add New'; return 'Add New';
} }
/** /**
* Used to set whether to show the add new button for a module * Used to set whether to show the add new button for a module
* @method setShowAddNew * @method setShowAddNew
* @param showAddNew {Boolean} value * @param showAddNew {Boolean} value
*/ */
setShowAddNew(showAddNew) { setShowAddNew(showAddNew) {
this.showAddNew = showAddNew; this.showAddNew = showAddNew;
} }
/** /**
* Used to set whether to show delete button for each entry in module * Used to set whether to show delete button for each entry in module
* @method setShowDelete * @method setShowDelete
* @param val {Boolean} value * @param val {Boolean} value
*/ */
setShowDelete(val) { setShowDelete(val) {
this.showDelete = val; this.showDelete = val;
} }
/** /**
* Used to set whether to show edit button for each entry in module * Used to set whether to show edit button for each entry in module
* @method setShowEdit * @method setShowEdit
* @param val {Boolean} value * @param val {Boolean} value
*/ */
setShowEdit(val) { setShowEdit(val) {
this.showEdit = val; this.showEdit = val;
} }
/** /**
* Used to set whether to show save button in form * Used to set whether to show save button in form
* @method setShowSave * @method setShowSave
* @param val {Boolean} value * @param val {Boolean} value
*/ */
setShowSave(val) { setShowSave(val) {
@@ -2464,20 +2473,20 @@ class ModuleBase {
/** /**
* Used to set whether to show cancel button in form * Used to set whether to show cancel button in form
* @method setShowCancel * @method setShowCancel
* @param val {Boolean} value * @param val {Boolean} value
*/ */
setShowCancel(val) { setShowCancel(val) {
this.showCancel = val; this.showCancel = val;
} }
/** /**
* Datatable option array will be extended with associative array provided here * Datatable option array will be extended with associative array provided here
* @method getCustomTableParams * @method getCustomTableParams
* @param val {Boolean} value * @param val {Boolean} value
*/ */
getCustomTableParams() { getCustomTableParams() {
@@ -2490,12 +2499,12 @@ class ModuleBase {
/** /**
* This return html for action buttons in each row. Override this method if you need to make changes to action buttons. * This return html for action buttons in each row. Override this method if you need to make changes to action buttons.
* @method getActionButtonsHtml * @method getActionButtonsHtml
* @param id {int} id of the row * @param id {int} id of the row
* @param data {Array} data for the row * @param data {Array} data for the row
* @returns {String} html for action buttons * @returns {String} html for action buttons
*/ */
getActionButtonsHtml(id, data) { getActionButtonsHtml(id, data) {
const editButton = '<img class="tableActionButton" src="_BASE_images/edit.png" style="cursor:pointer;" rel="tooltip" title="Edit" onclick="modJs.edit(_id_);return false;"></img>'; const editButton = '<img class="tableActionButton" src="_BASE_images/edit.png" style="cursor:pointer;" rel="tooltip" title="Edit" onclick="modJs.edit(_id_);return false;"></img>';
@@ -2528,11 +2537,11 @@ class ModuleBase {
/** /**
* Generates a random string * Generates a random string
* @method generateRandom * @method generateRandom
* @param length {int} required length of the string * @param length {int} required length of the string
* @returns {String} random string * @returns {String} random string
*/ */
generateRandom(length) { generateRandom(length) {
const d = new Date(); const d = new Date();
@@ -2593,10 +2602,10 @@ class ModuleBase {
} }
/** /**
* Override this method in a module to provide the help link for the module. Help link of the module on frontend will get updated with this. * Override this method in a module to provide the help link for the module. Help link of the module on frontend will get updated with this.
* @method getHelpLink * @method getHelpLink
* @returns {String} help link * @returns {String} help link
*/ */
getHelpLink() { getHelpLink() {
return null; return null;

View File

@@ -0,0 +1,232 @@
import React, {Component} from 'react';
import {
Form,
Modal,
Input,
Button,
message,
} from 'antd';
import CustomAction from '../api/CustomAction';
class UpdatePasswordModal extends React.Component {
state = {
loading: false,
passwordHasError: false,
passwordState: { hasFeedback: false, validateStatus:'', help:'Password must include at least one number, one lowercase letter, one uppercase letter and a symbol' },
confirmationHasError: false,
confirmationState: { hasFeedback: false, validateStatus:'', help:'' },
};
constructor(props) {
super(props);
this.formRef = React.createRef();
this.customAction = new CustomAction(this.props.adapter);
}
componentDidMount() {
message.config({
top: 40,
});
}
clearConfirmFeedback = () => {
this.setState({confirmationHasError: false});
this.setState({
confirmationState: {
hasFeedback : false,
validateStatus:'',
help:'',
}
});
}
updatePasswordState(value) {
const passwordValidationResult = this.validatePassword(value);
if (passwordValidationResult !== null) {
this.setState({passwordHasError: true});
this.setState({
passwordState: {
hasFeedback : true,
validateStatus:'error',
help:passwordValidationResult,
}
});
return false;
} else {
this.setState({passwordHasError: false});
this.setState({
passwordState: {
hasFeedback : true,
validateStatus:'success',
help:'',
}
});
}
return true;
}
updateConfirmPasswordState(values) {
if (values.confirm !== values.new) {
this.setState({confirmationHasError: true});
this.setState({
confirmationState: {
hasFeedback : true,
validateStatus:'error',
help:'Passwords don\'t match',
}
});
return false;
} else {
this.setState({confirmationHasError: false});
this.setState({
confirmationState: {
hasFeedback : false,
validateStatus:'',
help:'',
}
});
}
return true;
}
handleOk = () => {
const from = this.formRef.current;
from
.validateFields()
.then((values) => {
if (this.updatePasswordState(values.new) && this.updateConfirmPasswordState(values)) {
this.updatePassword(values.current, values.new)
.then((response) => {
const data = response.data;
console.log(data);
if (data.status === 'SUCCESS') {
this.handleCancel();
message.success(this.props.adapter.gt('Password updated'));
} else {
message.error(
`${this.props.adapter.gt('Error updating password')}: ${this.props.adapter.gt(data.data)}`
);
}
}).catch((error) => {
message.error(
`${this.props.adapter.gt('Error updating password')}`
);
console.log(error.message);
});
}
})
.catch((info) => {
this.setState({ loading: false });
});
}
handleCancel = () => {
if (this.formRef.current) {
this.formRef.current.resetFields();
}
this.props.closeModal();
}
updatePassword = (oldPassword, newPassword) => {
const req = { current: oldPassword ? oldPassword : '', pwd: newPassword };
const reqJson = JSON.stringify(req);
const callBackData = [];
callBackData.callBackData = [];
callBackData.callBackSuccess = 'changePasswordSuccessCallBack';
callBackData.callBackFail = 'changePasswordFailCallBack';
return this.customAction.execute('changePassword', 'modules=employees', reqJson);
}
validatePassword = (password) => {
if (password.length < 8) {
return this.props.adapter.gt('Password too short');
}
if (password.length > 20) {
return this.props.adapter.gt('Password too long');
}
const numberTester = /.*[0-9]+.*$/;
if (!password.match(numberTester)) {
return this.props.adapter.gt('Password must include at least one number');
}
const lowerTester = /.*[a-z]+.*$/;
if (!password.match(lowerTester)) {
return this.props.adapter.gt('Password must include at least one lowercase letter');
}
const upperTester = /.*[A-Z]+.*$/;
if (!password.match(upperTester)) {
return this.props.adapter.gt('Password must include at least one uppercase letter');
}
const symbolTester = /.*[\W]+.*$/;
if (!password.match(symbolTester)) {
return this.props.adapter.gt('Password must include at least one symbol');
}
return null;
}
render() {
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
return (
<Modal
visible={this.props.visible}
title="Update Password"
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
{this.props.adapter.gt('Cancel')}
</Button>,
<Button key="submit" type="primary" loading={this.state.loading} onClick={this.handleOk}>
{this.props.adapter.gt('Update')}
</Button>,
]}
>
<Form {...layout} ref={this.formRef}>
<Form.Item label="Current Password" key="current" name="current" >
<Input.Password placeholder="current password"/>
</Form.Item>
{ this.state.passwordHasError &&
<Form.Item label="New Password" key="new" name="new" {...this.state.passwordState}>
<Input.Password placeholder="new password" onChange={(event) => this.updatePasswordState(event.target.value)}/>
</Form.Item>
}
{ !this.state.passwordHasError &&
<Form.Item label="New Password" key="new" name="new" {...this.state.passwordState}>
<Input.Password placeholder="new password" onChange={(event) => this.updatePasswordState(event.target.value)}/>
</Form.Item>
}
{ this.state.confirmationHasError &&
<Form.Item label="Confirm Password" key="confirm" name="confirm" {...this.state.confirmationState}>
<Input.Password placeholder="confirm password" onChange={(event) => this.clearConfirmFeedback()}/>
</Form.Item>
}
{ !this.state.confirmationHasError &&
<Form.Item label="Confirm Password" key="confirm" name="confirm" >
<Input.Password placeholder="confirm password" onChange={(event) => this.clearConfirmFeedback()}/>
</Form.Item>
}
</Form>
</Modal>
)
}
}
export default UpdatePasswordModal;

View File

@@ -943,3 +943,7 @@ table.dataTable{
.table-row-dark { .table-row-dark {
background-color: #fbfbfb; background-color: #fbfbfb;
} }
.mod-tab {
margin-bottom:0px;margin-left:5px;border-bottom: none;
}

30987
web/dist/admin-bundle.js vendored

File diff suppressed because one or more lines are too long

4
web/dist/common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,37 +1,32 @@
import React, {Component} from 'react'; import React from 'react';
import { import {
Col, Col,
Card, Card,
Badge,
Avatar, Avatar,
Input,
Row, Row,
Descriptions, Descriptions,
Typography, Typography,
Table,
Space, Space,
Button,
Tag, Tag,
message,
Tabs, Tabs,
Spin,
Skeleton Skeleton
} from 'antd'; } from 'antd';
import { import {
FilterOutlined,
EditOutlined, EditOutlined,
PhoneTwoTone, PhoneTwoTone,
MailTwoTone, MailTwoTone,
SyncOutlined, SyncOutlined,
LockOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import TagList from "../../../../components/TagList"; import TagList from "../../../../components/TagList";
const { Search } = Input; import UpdatePasswordModal from "../../../../components/UpdatePasswordModal";
const { Title, Text } = Typography; const { Title, Text } = Typography;
const { TabPane } = Tabs; const { TabPane } = Tabs;
class EmployeeProfile extends React.Component { class EmployeeProfile extends React.Component {
state = { state = {
loading: true, loading: true,
showPasswordResetModal: false,
}; };
constructor(props) { constructor(props) {
@@ -42,6 +37,10 @@ class EmployeeProfile extends React.Component {
this.setState({ loading: value }); this.setState({ loading: value });
} }
setShowPasswordUpdate(value) {
this.setState({ showPasswordResetModal: value });
}
updateProfileImage() { updateProfileImage() {
showUploadDialog( showUploadDialog(
`profile_image_${this.props.element.id}_${(new Date()).getTime()}`, `profile_image_${this.props.element.id}_${(new Date()).getTime()}`,
@@ -71,6 +70,33 @@ class EmployeeProfile extends React.Component {
</>); </>);
} }
getEditButtonJsxWithPassword() {
return (<>
{this.state.loading &&
<Tag icon={<SyncOutlined spin/>} color="processing">
{this.props.adapter.gt('Edit')}
</Tag>
}
{!this.state.loading &&
<Tag icon={<EditOutlined/>} color="processing"
onClick={() => modJs.edit(this.props.element.id)}>
{this.props.adapter.gt('Edit')}
</Tag>
}
<Tag icon={<LockOutlined/>} color="volcano" onClick={() => this.setShowPasswordUpdate(true)}>
{this.props.adapter.gt('Update Password')}
</Tag>
</>);
}
getUpdatePasswordButtonJsx() {
return (<>
<Tag icon={<SyncOutlined spin/>} color="processing">
{this.props.adapter.gt('Update Password')}
</Tag>
</>);
}
getTabViewEmployeeFilterButtonJsx(tab) { getTabViewEmployeeFilterButtonJsx(tab) {
return ( return (
<Tag icon={<EditOutlined/>} color="processing" <Tag icon={<EditOutlined/>} color="processing"
@@ -86,10 +112,15 @@ class EmployeeProfile extends React.Component {
} }
return ( return (
<> <>
<UpdatePasswordModal
visible={this.state.showPasswordResetModal}
closeModal={() => {this.setState({ showPasswordResetModal: false })}}
adapter={this.props.adapter}
/>
<Row direction="vertical" style={{width: '100%', padding: '10px'}} gutter={24}> <Row direction="vertical" style={{width: '100%', padding: '10px'}} gutter={24}>
<Col span={24}> <Col span={24}>
<Card title={this.props.adapter.gt('Employee Profile')} <Card title={this.props.adapter.gt('Employee Profile')}
extra={this.getEditButtonJsx()} extra={this.getEditButtonJsxWithPassword()}
style={{ width: '100%' }} style={{ width: '100%' }}
> >
<Space size={'large'}> <Space size={'large'}>

View File

@@ -613,8 +613,15 @@ class EmployeeTimeEntryAdapter extends AdapterBase {
return dateArray; return dateArray;
} }
renderForm(object) { renderForm(object) {
this.initMasterDataReader();
this.masterDataReader.updateAllMasterData()
.then(() => {
this._renderForm(object);
});
}
_renderForm(object) {
let formHtml = this.getCustomTemplate('time_entry_form.html'); let formHtml = this.getCustomTemplate('time_entry_form.html');
formHtml = formHtml.replace(/modJs/g, "modJsList['tabEmployeeTimeEntry']"); formHtml = formHtml.replace(/modJs/g, "modJsList['tabEmployeeTimeEntry']");
let html = ''; let html = '';