Push changes to frontend
@@ -1 +0,0 @@
|
|||||||
../gettext/languages/bin/export-plural-rules
|
|
||||||
4
core/lib/composer/vendor/bin/export-plural-rules
vendored
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
include 'export-plural-rules.php';
|
||||||
@@ -1 +0,0 @@
|
|||||||
../gettext/languages/bin/export-plural-rules.php
|
|
||||||
234
core/lib/composer/vendor/bin/export-plural-rules.php
vendored
Executable file
@@ -0,0 +1,234 @@
|
|||||||
|
<?php
|
||||||
|
use Gettext\Languages\Exporter\Exporter;
|
||||||
|
use Gettext\Languages\Language;
|
||||||
|
|
||||||
|
// Let's start by imposing that we don't accept any error or warning.
|
||||||
|
// This is a really life-saving approach.
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
|
||||||
|
Enviro::echoErr("$errstr\nFile: $errfile\nLine: $errline\nCode: $errno\n");
|
||||||
|
die(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
require_once dirname(__DIR__).'/src/autoloader.php';
|
||||||
|
|
||||||
|
// Parse the command line options
|
||||||
|
Enviro::initialize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isset(Enviro::$languages)) {
|
||||||
|
$languages = array();
|
||||||
|
foreach (Enviro::$languages as $languageId) {
|
||||||
|
$language = Language::getById($languageId);
|
||||||
|
if (!isset($language)) {
|
||||||
|
throw new Exception("Unable to find the language with id '$languageId'");
|
||||||
|
}
|
||||||
|
$languages[] = $language;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$languages = Language::getAll();
|
||||||
|
}
|
||||||
|
if (Enviro::$reduce) {
|
||||||
|
$languages = Enviro::reduce($languages);
|
||||||
|
}
|
||||||
|
if (isset(Enviro::$outputFilename)) {
|
||||||
|
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toFile'), $languages, Enviro::$outputFilename, array('us-ascii' => Enviro::$outputUSAscii));
|
||||||
|
} else {
|
||||||
|
echo call_user_func(array(Exporter::getExporterClassName(Enviro::$outputFormat), 'toString'), $languages, array('us-ascii' => Enviro::$outputUSAscii));
|
||||||
|
}
|
||||||
|
} catch (Exception $x) {
|
||||||
|
Enviro::echoErr($x->getMessage()."\n");
|
||||||
|
Enviro::echoErr("Trace:\n");
|
||||||
|
Enviro::echoErr($x->getTraceAsString()."\n");
|
||||||
|
die(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
die(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to handle command line options.
|
||||||
|
*/
|
||||||
|
class Enviro
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Shall the output contain only US-ASCII characters?
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public static $outputUSAscii;
|
||||||
|
/**
|
||||||
|
* The output format.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $outputFormat;
|
||||||
|
/**
|
||||||
|
* Output file name.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $outputFilename;
|
||||||
|
/**
|
||||||
|
* List of wanted language IDs; it not set: all languages will be returned.
|
||||||
|
* @var array|null
|
||||||
|
*/
|
||||||
|
public static $languages;
|
||||||
|
/**
|
||||||
|
* Reduce the language list to the minimum common denominator.
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public static $reduce;
|
||||||
|
/**
|
||||||
|
* Parse the command line options.
|
||||||
|
*/
|
||||||
|
public static function initialize()
|
||||||
|
{
|
||||||
|
global $argv;
|
||||||
|
self::$outputUSAscii = false;
|
||||||
|
self::$outputFormat = null;
|
||||||
|
self::$outputFilename = null;
|
||||||
|
self::$languages = null;
|
||||||
|
self::$reduce = null;
|
||||||
|
$exporters = Exporter::getExporters();
|
||||||
|
if (isset($argv) && is_array($argv)) {
|
||||||
|
foreach ($argv as $argi => $arg) {
|
||||||
|
if ($argi === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_string($arg)) {
|
||||||
|
$argLC = trim(strtolower($arg));
|
||||||
|
switch ($argLC) {
|
||||||
|
case '--us-ascii':
|
||||||
|
self::$outputUSAscii = true;
|
||||||
|
break;
|
||||||
|
case '--reduce=yes':
|
||||||
|
self::$reduce = true;
|
||||||
|
break;
|
||||||
|
case '--reduce=no':
|
||||||
|
self::$reduce = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (preg_match('/^--output=.+$/', $argLC)) {
|
||||||
|
if (isset(self::$outputFilename)) {
|
||||||
|
self::echoErr("The output file name has been specified more than once!\n");
|
||||||
|
self::showSyntax();
|
||||||
|
die(3);
|
||||||
|
}
|
||||||
|
list(, self::$outputFilename) = explode('=', $arg, 2);
|
||||||
|
self::$outputFilename = trim(self::$outputFilename);
|
||||||
|
} elseif (preg_match('/^--languages?=.+$/', $argLC)) {
|
||||||
|
list(, $s) = explode('=', $arg, 2);
|
||||||
|
$list = explode(',', $s);
|
||||||
|
if (is_array(self::$languages)) {
|
||||||
|
self::$languages = array_merge(self::$languages, $list);
|
||||||
|
} else {
|
||||||
|
self::$languages = $list;
|
||||||
|
}
|
||||||
|
} elseif (isset($exporters[$argLC])) {
|
||||||
|
if (isset(self::$outputFormat)) {
|
||||||
|
self::echoErr("The output format has been specified more than once!\n");
|
||||||
|
self::showSyntax();
|
||||||
|
die(3);
|
||||||
|
}
|
||||||
|
self::$outputFormat = $argLC;
|
||||||
|
} else {
|
||||||
|
self::echoErr("Unknown option: $arg\n");
|
||||||
|
self::showSyntax();
|
||||||
|
die(2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset(self::$outputFormat)) {
|
||||||
|
self::showSyntax();
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
|
if (isset(self::$languages)) {
|
||||||
|
self::$languages = array_values(array_unique(self::$languages));
|
||||||
|
}
|
||||||
|
if (!isset(self::$reduce)) {
|
||||||
|
self::$reduce = isset(self::$languages) ? false : true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write out the syntax.
|
||||||
|
*/
|
||||||
|
public static function showSyntax()
|
||||||
|
{
|
||||||
|
$exporters = array_keys(Exporter::getExporters(true));
|
||||||
|
self::echoErr("Syntax: php ".basename(__FILE__)." [--us-ascii] [--languages=<LanguageId>[,<LanguageId>,...]] [--reduce=yes|no] [--output=<file name>] <".implode('|', $exporters).">\n");
|
||||||
|
self::echoErr("Where:\n");
|
||||||
|
self::echoErr("--us-ascii : if specified, the output will contain only US-ASCII characters.\n");
|
||||||
|
self::echoErr("--languages: (or --language) export only the specified language codes.\n");
|
||||||
|
self::echoErr(" Separate languages with commas; you can also use this argument\n");
|
||||||
|
self::echoErr(" more than once; it's case insensitive and accepts both '_' and\n");
|
||||||
|
self::echoErr(" '-' as locale chunks separator (eg we accept 'it_IT' as well as\n");
|
||||||
|
self::echoErr(" 'it-it').\n");
|
||||||
|
self::echoErr("--reduce : if set to yes the output won't contain languages with the same\n");
|
||||||
|
self::echoErr(" base language and rules.\n For instance nl_BE ('Flemish') will be\n");
|
||||||
|
self::echoErr(" omitted because it's the same as nl ('Dutch').\n");
|
||||||
|
self::echoErr(" Defaults to 'no' --languages is specified, to 'yes' otherwise.\n");
|
||||||
|
self::echoErr("--output : if specified, the output will be saved to <file name>. If not\n");
|
||||||
|
self::echoErr(" specified we'll output to standard output.\n");
|
||||||
|
self::echoErr("Output formats\n");
|
||||||
|
$len = max(array_map('strlen', $exporters));
|
||||||
|
foreach ($exporters as $exporter) {
|
||||||
|
self::echoErr(str_pad($exporter, $len).": ".Exporter::getExporterDescription($exporter)."\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Print a string to stderr.
|
||||||
|
* @param string $str The string to be printed out.
|
||||||
|
*/
|
||||||
|
public static function echoErr($str)
|
||||||
|
{
|
||||||
|
$hStdErr = @fopen('php://stderr', 'a');
|
||||||
|
if ($hStdErr === false) {
|
||||||
|
echo $str;
|
||||||
|
} else {
|
||||||
|
fwrite($hStdErr, $str);
|
||||||
|
fclose($hStdErr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reduce a language list to the minimum common denominator.
|
||||||
|
* @param Language[] $languages
|
||||||
|
* @return Language[]
|
||||||
|
*/
|
||||||
|
public static function reduce($languages)
|
||||||
|
{
|
||||||
|
for ($numChunks = 3; $numChunks >= 2; $numChunks--) {
|
||||||
|
$filtered = array();
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
$chunks = explode('_', $language->id);
|
||||||
|
$compatibleFound = false;
|
||||||
|
if (count($chunks) === $numChunks) {
|
||||||
|
$categoriesHash = serialize($language->categories);
|
||||||
|
$otherIds = array();
|
||||||
|
$otherIds[] = $chunks[0];
|
||||||
|
for ($k = 2; $k < $numChunks; $k++) {
|
||||||
|
$otherIds[] = $chunks[0].'_'.$chunks[$numChunks - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($languages as $check) {
|
||||||
|
foreach ($otherIds as $otherId) {
|
||||||
|
if (($check->id === $otherId) && ($check->formula === $language->formula) && (serialize($check->categories) === $categoriesHash)) {
|
||||||
|
$compatibleFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($compatibleFound === true) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$compatibleFound) {
|
||||||
|
$filtered[] = $language;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$languages = $filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $languages;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
core/lib/composer/vendor/bin/markdown
vendored
@@ -1 +0,0 @@
|
|||||||
../cebe/markdown/bin/markdown
|
|
||||||
170
core/lib/composer/vendor/bin/markdown
vendored
Executable file
@@ -0,0 +1,170 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2014 Carsten Brandt
|
||||||
|
* @license https://github.com/cebe/markdown/blob/master/LICENSE
|
||||||
|
* @link https://github.com/cebe/markdown#readme
|
||||||
|
*/
|
||||||
|
|
||||||
|
$composerAutoload = [
|
||||||
|
__DIR__ . '/../vendor/autoload.php', // standalone with "composer install" run
|
||||||
|
__DIR__ . '/../../../autoload.php', // script is installed as a composer binary
|
||||||
|
];
|
||||||
|
foreach ($composerAutoload as $autoload) {
|
||||||
|
if (file_exists($autoload)) {
|
||||||
|
require($autoload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send all errors to stderr
|
||||||
|
ini_set('display_errors', 'stderr');
|
||||||
|
|
||||||
|
$flavor = 'cebe\\markdown\\Markdown';
|
||||||
|
$flavors = [
|
||||||
|
'gfm' => ['cebe\\markdown\\GithubMarkdown', __DIR__ . '/../GithubMarkdown.php'],
|
||||||
|
'extra' => ['cebe\\markdown\\MarkdownExtra', __DIR__ . '/../MarkdownExtra.php'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$full = false;
|
||||||
|
$src = [];
|
||||||
|
foreach($argv as $k => $arg) {
|
||||||
|
if ($k == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($arg[0] == '-') {
|
||||||
|
$arg = explode('=', $arg);
|
||||||
|
switch($arg[0]) {
|
||||||
|
case '--flavor':
|
||||||
|
if (isset($arg[1])) {
|
||||||
|
if (isset($flavors[$arg[1]])) {
|
||||||
|
require($flavors[$arg[1]][1]);
|
||||||
|
$flavor = $flavors[$arg[1]][0];
|
||||||
|
} else {
|
||||||
|
error("Unknown flavor: " . $arg[1], "usage");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error("Incomplete argument --flavor!", "usage");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '--full':
|
||||||
|
$full = true;
|
||||||
|
break;
|
||||||
|
case '-h':
|
||||||
|
case '--help':
|
||||||
|
echo "PHP Markdown to HTML converter\n";
|
||||||
|
echo "------------------------------\n\n";
|
||||||
|
echo "by Carsten Brandt <mail@cebe.cc>\n\n";
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error("Unknown argument " . $arg[0], "usage");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$src[] = $arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($src)) {
|
||||||
|
$markdown = file_get_contents("php://stdin");
|
||||||
|
} elseif (count($src) == 1) {
|
||||||
|
$file = reset($src);
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
error("File does not exist:" . $file);
|
||||||
|
}
|
||||||
|
$markdown = file_get_contents($file);
|
||||||
|
} else {
|
||||||
|
error("Converting multiple files is not yet supported.", "usage");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var cebe\markdown\Parser $md */
|
||||||
|
$md = new $flavor();
|
||||||
|
$markup = $md->parse($markdown);
|
||||||
|
|
||||||
|
if ($full) {
|
||||||
|
echo <<<HTML
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; }
|
||||||
|
code { background: #eeeeff; padding: 2px; }
|
||||||
|
li { margin-bottom: 5px; }
|
||||||
|
img { max-width: 1200px; }
|
||||||
|
table, td, th { border: solid 1px #ccc; border-collapse: collapse; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
$markup
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
HTML;
|
||||||
|
} else {
|
||||||
|
echo $markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display usage information
|
||||||
|
*/
|
||||||
|
function usage() {
|
||||||
|
global $argv;
|
||||||
|
$cmd = $argv[0];
|
||||||
|
echo <<<EOF
|
||||||
|
Usage:
|
||||||
|
$cmd [--flavor=<flavor>] [--full] [file.md]
|
||||||
|
|
||||||
|
--flavor specifies the markdown flavor to use. If omitted the original markdown by John Gruber [1] will be used.
|
||||||
|
Available flavors:
|
||||||
|
|
||||||
|
gfm - Github flavored markdown [2]
|
||||||
|
extra - Markdown Extra [3]
|
||||||
|
|
||||||
|
--full ouput a full HTML page with head and body. If not given, only the parsed markdown will be output.
|
||||||
|
|
||||||
|
--help shows this usage information.
|
||||||
|
|
||||||
|
If no file is specified input will be read from STDIN.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Render a file with original markdown:
|
||||||
|
|
||||||
|
$cmd README.md > README.html
|
||||||
|
|
||||||
|
Render a file using gihtub flavored markdown:
|
||||||
|
|
||||||
|
$cmd --flavor=gfm README.md > README.html
|
||||||
|
|
||||||
|
Convert the original markdown description to html using STDIN:
|
||||||
|
|
||||||
|
curl http://daringfireball.net/projects/markdown/syntax.text | $cmd > md.html
|
||||||
|
|
||||||
|
|
||||||
|
[1] http://daringfireball.net/projects/markdown/syntax
|
||||||
|
[2] https://help.github.com/articles/github-flavored-markdown
|
||||||
|
[3] http://michelf.ca/projects/php-markdown/extra/
|
||||||
|
|
||||||
|
EOF;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send custom error message to stderr
|
||||||
|
* @param $message string
|
||||||
|
* @param $callback mixed called before script exit
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function error($message, $callback = null) {
|
||||||
|
$fe = fopen("php://stderr", "w");
|
||||||
|
fwrite($fe, "Error: " . $message . "\n");
|
||||||
|
|
||||||
|
if (is_callable($callback)) {
|
||||||
|
call_user_func($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
1
core/lib/composer/vendor/bin/phpunit
vendored
@@ -1 +0,0 @@
|
|||||||
../phpunit/phpunit/phpunit
|
|
||||||
53
core/lib/composer/vendor/bin/phpunit
vendored
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of PHPUnit.
|
||||||
|
*
|
||||||
|
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (version_compare('7.0.0', PHP_VERSION, '>')) {
|
||||||
|
fwrite(
|
||||||
|
STDERR,
|
||||||
|
sprintf(
|
||||||
|
'This version of PHPUnit is supported on PHP 7.0 and PHP 7.1.' . PHP_EOL .
|
||||||
|
'You are using PHP %s (%s).' . PHP_EOL,
|
||||||
|
PHP_VERSION,
|
||||||
|
PHP_BINARY
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ini_get('date.timezone')) {
|
||||||
|
ini_set('date.timezone', 'UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array(__DIR__ . '/../../autoload.php', __DIR__ . '/../vendor/autoload.php', __DIR__ . '/vendor/autoload.php') as $file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
define('PHPUNIT_COMPOSER_INSTALL', $file);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($file);
|
||||||
|
|
||||||
|
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
|
||||||
|
fwrite(
|
||||||
|
STDERR,
|
||||||
|
'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL .
|
||||||
|
' composer install' . PHP_EOL . PHP_EOL .
|
||||||
|
'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL
|
||||||
|
);
|
||||||
|
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require PHPUNIT_COMPOSER_INSTALL;
|
||||||
|
|
||||||
|
PHPUnit\TextUI\Command::main();
|
||||||
1
core/lib/composer/vendor/bin/robo
vendored
@@ -1 +0,0 @@
|
|||||||
../consolidation/robo/robo
|
|
||||||
22
core/lib/composer/vendor/bin/robo
vendored
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if we're running from phar load the phar autoload,
|
||||||
|
* else let the script 'robo' search for the autoloader
|
||||||
|
*/
|
||||||
|
if (strpos(basename(__FILE__), 'phar')) {
|
||||||
|
require_once 'phar://robo.phar/vendor/autoload.php';
|
||||||
|
} else {
|
||||||
|
if (file_exists(__DIR__.'/vendor/autoload.php')) {
|
||||||
|
require_once __DIR__.'/vendor/autoload.php';
|
||||||
|
} elseif (file_exists(__DIR__.'/../../autoload.php')) {
|
||||||
|
require_once __DIR__ . '/../../autoload.php';
|
||||||
|
} else {
|
||||||
|
require_once 'phar://robo.phar/vendor/autoload.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$runner = new \Robo\Runner();
|
||||||
|
$runner->setSelfUpdateRepository('consolidation/robo');
|
||||||
|
$statusCode = $runner->execute($_SERVER['argv']);
|
||||||
|
exit($statusCode);
|
||||||
@@ -1 +0,0 @@
|
|||||||
docs/guide.txt
|
|
||||||
267
core/lib/composer/vendor/pear/net_smtp/README.rst
vendored
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
======================
|
||||||
|
The Net_SMTP Package
|
||||||
|
======================
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
User Documentation
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
:Author: Jon Parise
|
||||||
|
:Contact: jon@php.net
|
||||||
|
|
||||||
|
.. contents:: Table of Contents
|
||||||
|
.. section-numbering::
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
============
|
||||||
|
|
||||||
|
The ``PEAR_Error`` Class
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The Net_SMTP package uses the `PEAR_Error`_ class for all of its `error
|
||||||
|
handling`_.
|
||||||
|
|
||||||
|
The ``Net_Socket`` Package
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
The Net_Socket_ package is used as the basis for all network communications.
|
||||||
|
Connection options can be specified via the `$socket_options` construction
|
||||||
|
parameter::
|
||||||
|
|
||||||
|
$socket_options = array('ssl' => array('verify_peer_name' => false));
|
||||||
|
$smtp = new Net_SMTP($host, null, null, false, 0, $socket_options);
|
||||||
|
|
||||||
|
**Note:** PHP 5.6 introduced `OpenSSL changes`_. Peer certificate verification
|
||||||
|
is now enabled by default. Although not recommended, `$socket_options` can be
|
||||||
|
used to disable peer verification (as shown above).
|
||||||
|
|
||||||
|
.. _OpenSSL changes: http://php.net/manual/en/migration56.openssl.php
|
||||||
|
|
||||||
|
The ``Auth_SASL`` Package
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The `Auth_SASL`_ package is an optional dependency. If it is available, the
|
||||||
|
Net_SMTP package will be able to support the DIGEST-MD5_ and CRAM-MD5_ SMTP
|
||||||
|
authentication methods. Otherwise, only the LOGIN_ and PLAIN_ methods will
|
||||||
|
be available.
|
||||||
|
|
||||||
|
Error Handling
|
||||||
|
==============
|
||||||
|
|
||||||
|
All of the Net_SMTP class's public methods return a PEAR_Error_ object if an
|
||||||
|
error occurs. The standard way to check for a PEAR_Error object is by using
|
||||||
|
`PEAR::isError()`_::
|
||||||
|
|
||||||
|
if (PEAR::isError($error = $smtp->connect())) {
|
||||||
|
die($error->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
.. _PEAR::isError(): http://pear.php.net/manual/en/core.pear.pear.iserror.php
|
||||||
|
|
||||||
|
SMTP Authentication
|
||||||
|
===================
|
||||||
|
|
||||||
|
The Net_SMTP package supports the SMTP authentication standard (as defined
|
||||||
|
by RFC-2554_). The Net_SMTP package supports the following authentication
|
||||||
|
methods, in order of preference:
|
||||||
|
|
||||||
|
.. _RFC-2554: http://www.ietf.org/rfc/rfc2554.txt
|
||||||
|
|
||||||
|
DIGEST-MD5
|
||||||
|
----------
|
||||||
|
|
||||||
|
The DIGEST-MD5 authentication method uses `RSA Data Security Inc.`_'s MD5
|
||||||
|
Message Digest algorithm. It is considered the most secure method of SMTP
|
||||||
|
authentication.
|
||||||
|
|
||||||
|
**Note:** The DIGEST-MD5 authentication method is only supported if the
|
||||||
|
AUTH_SASL_ package is available.
|
||||||
|
|
||||||
|
.. _RSA Data Security Inc.: http://www.rsasecurity.com/
|
||||||
|
|
||||||
|
CRAM-MD5
|
||||||
|
--------
|
||||||
|
|
||||||
|
The CRAM-MD5 authentication method has been superseded by the DIGEST-MD5_
|
||||||
|
method in terms of security. It is provided here for compatibility with
|
||||||
|
older SMTP servers that may not support the newer DIGEST-MD5 algorithm.
|
||||||
|
|
||||||
|
**Note:** The CRAM-MD5 authentication method is only supported if the
|
||||||
|
AUTH_SASL_ package is available.
|
||||||
|
|
||||||
|
LOGIN
|
||||||
|
-----
|
||||||
|
|
||||||
|
The LOGIN authentication method encrypts the user's password using the
|
||||||
|
Base64_ encoding scheme. Because decrypting a Base64-encoded string is
|
||||||
|
trivial, LOGIN is not considered a secure authentication method and should
|
||||||
|
be avoided.
|
||||||
|
|
||||||
|
.. _Base64: http://www.php.net/manual/en/function.base64-encode.php
|
||||||
|
|
||||||
|
PLAIN
|
||||||
|
-----
|
||||||
|
|
||||||
|
The PLAIN authentication method sends the user's password in plain text.
|
||||||
|
This method of authentication is not secure and should be avoided.
|
||||||
|
|
||||||
|
Secure Connections
|
||||||
|
==================
|
||||||
|
|
||||||
|
If `secure socket transports`_ have been enabled in PHP, it is possible to
|
||||||
|
establish a secure connection to the remote SMTP server::
|
||||||
|
|
||||||
|
$smtp = new Net_SMTP('ssl://mail.example.com', 465);
|
||||||
|
|
||||||
|
This example connects to ``mail.example.com`` on port 465 (a common SMTPS
|
||||||
|
port) using the ``ssl://`` transport.
|
||||||
|
|
||||||
|
.. _secure socket transports: http://www.php.net/transports
|
||||||
|
|
||||||
|
Sending Data
|
||||||
|
============
|
||||||
|
|
||||||
|
Message data is sent using the ``data()`` method. The data can be supplied
|
||||||
|
as a single string or as an open file resource.
|
||||||
|
|
||||||
|
If a string is provided, it is passed through the `data quoting`_ system and
|
||||||
|
sent to the socket connection as a single block. These operations are all
|
||||||
|
memory-based, so sending large messages may result in high memory usage.
|
||||||
|
|
||||||
|
If an open file resource is provided, the ``data()`` method will read the
|
||||||
|
message data from the file line-by-line. Each chunk will be quoted and sent
|
||||||
|
to the socket connection individually, reducing the overall memory overhead of
|
||||||
|
this data sending operation.
|
||||||
|
|
||||||
|
Header data can be specified separately from message body data by passing it
|
||||||
|
as the optional second parameter to ``data()``. This is especially useful
|
||||||
|
when an open file resource is being used to supply message data because it
|
||||||
|
allows header fields (like *Subject:*) to be built dynamically at runtime.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$smtp->data($fp, "Subject: My Subject");
|
||||||
|
|
||||||
|
Data Quoting
|
||||||
|
============
|
||||||
|
|
||||||
|
By default, all outbound string data is quoted in accordance with SMTP
|
||||||
|
standards. This means that all native Unix (``\n``) and Mac (``\r``) line
|
||||||
|
endings are converted to Internet-standard CRLF (``\r\n``) line endings.
|
||||||
|
Also, because the SMTP protocol uses a single leading period (``.``) to signal
|
||||||
|
an end to the message data, single leading periods in the original data
|
||||||
|
string are "doubled" (e.g. "``..``").
|
||||||
|
|
||||||
|
These string transformation can be expensive when large blocks of data are
|
||||||
|
involved. For example, the Net_SMTP package is not aware of MIME parts (it
|
||||||
|
just sees the MIME message as one big string of characters), so it is not
|
||||||
|
able to skip non-text attachments when searching for characters that may
|
||||||
|
need to be quoted.
|
||||||
|
|
||||||
|
Because of this, it is possible to extend the Net_SMTP class in order to
|
||||||
|
implement your own custom quoting routine. Just create a new class based on
|
||||||
|
the Net_SMTP class and reimplement the ``quotedata()`` method::
|
||||||
|
|
||||||
|
require 'Net_SMTP.php';
|
||||||
|
|
||||||
|
class Net_SMTP_custom extends Net_SMTP
|
||||||
|
{
|
||||||
|
function quotedata($data)
|
||||||
|
{
|
||||||
|
/* Perform custom data quoting */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that the ``$data`` parameter will be passed to the ``quotedata()``
|
||||||
|
function `by reference`_. This means that you can operate directly on
|
||||||
|
``$data``. It also the overhead of copying a large ``$data`` string to and
|
||||||
|
from the ``quotedata()`` method.
|
||||||
|
|
||||||
|
.. _by reference: http://www.php.net/manual/en/language.references.pass.php
|
||||||
|
|
||||||
|
Server Responses
|
||||||
|
================
|
||||||
|
|
||||||
|
The Net_SMTP package retains the server's last response for further
|
||||||
|
inspection. The ``getResponse()`` method returns a 2-tuple (two element
|
||||||
|
array) containing the server's response code as an integer and the response's
|
||||||
|
arguments as a string.
|
||||||
|
|
||||||
|
Upon a successful connection, the server's greeting string is available via
|
||||||
|
the ``getGreeting()`` method.
|
||||||
|
|
||||||
|
Debugging
|
||||||
|
=========
|
||||||
|
|
||||||
|
The Net_SMTP package contains built-in debugging output routines (disabled by
|
||||||
|
default). Debugging output must be explicitly enabled via the ``setDebug()``
|
||||||
|
method::
|
||||||
|
|
||||||
|
$smtp->setDebug(true);
|
||||||
|
|
||||||
|
The debugging messages will be sent to the standard output stream by default.
|
||||||
|
If you need more control over the output, you can optionally install your own
|
||||||
|
debug handler.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
function debugHandler($smtp, $message)
|
||||||
|
{
|
||||||
|
echo "[$smtp->host] $message\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$smtp->setDebug(true, "debugHandler");
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
|
Basic Use
|
||||||
|
---------
|
||||||
|
|
||||||
|
The following script demonstrates how a simple email message can be sent
|
||||||
|
using the Net_SMTP package::
|
||||||
|
|
||||||
|
require 'Net/SMTP.php';
|
||||||
|
|
||||||
|
$host = 'mail.example.com';
|
||||||
|
$from = 'user@example.com';
|
||||||
|
$rcpt = array('recipient1@example.com', 'recipient2@example.com');
|
||||||
|
$subj = "Subject: Test Message\n";
|
||||||
|
$body = "Body Line 1\nBody Line 2";
|
||||||
|
|
||||||
|
/* Create a new Net_SMTP object. */
|
||||||
|
if (! ($smtp = new Net_SMTP($host))) {
|
||||||
|
die("Unable to instantiate Net_SMTP object\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to the SMTP server. */
|
||||||
|
if (PEAR::isError($e = $smtp->connect())) {
|
||||||
|
die($e->getMessage() . "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the 'MAIL FROM:' SMTP command. */
|
||||||
|
if (PEAR::isError($smtp->mailFrom($from))) {
|
||||||
|
die("Unable to set sender to <$from>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Address the message to each of the recipients. */
|
||||||
|
foreach ($rcpt as $to) {
|
||||||
|
if (PEAR::isError($res = $smtp->rcptTo($to))) {
|
||||||
|
die("Unable to add recipient <$to>: " . $res->getMessage() . "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the body of the message. */
|
||||||
|
if (PEAR::isError($smtp->data($subj . "\r\n" . $body))) {
|
||||||
|
die("Unable to send data\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disconnect from the SMTP server. */
|
||||||
|
$smtp->disconnect();
|
||||||
|
|
||||||
|
.. _PEAR_Error: http://pear.php.net/manual/en/core.pear.pear-error.php
|
||||||
|
.. _Net_Socket: http://pear.php.net/package/Net_Socket
|
||||||
|
.. _Auth_SASL: http://pear.php.net/package/Auth_SASL
|
||||||
|
|
||||||
|
.. vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab textwidth=78 ft=rst:
|
||||||
2
web/admin/dist/attendance.js
vendored
Normal file
2
web/admin/dist/company_structure.js
vendored
Normal file
2
web/admin/dist/dashboard.js
vendored
Normal file
2
web/admin/dist/data.js
vendored
Normal file
2
web/admin/dist/employees.js
vendored
Normal file
2
web/admin/dist/fieldnames.js
vendored
Normal file
2
web/admin/dist/jobs.js
vendored
Normal file
2
web/admin/dist/loans.js
vendored
Normal file
2
web/admin/dist/metadata.js
vendored
Normal file
2
web/admin/dist/modules.js
vendored
Normal file
2
web/admin/dist/overtime.js
vendored
Normal file
2
web/admin/dist/payroll.js
vendored
Normal file
2
web/admin/dist/permissions.js
vendored
Normal file
2
web/admin/dist/projects.js
vendored
Normal file
2
web/admin/dist/qualifications.js
vendored
Normal file
2
web/admin/dist/reports.js
vendored
Normal file
2
web/admin/dist/salary.js
vendored
Normal file
2
web/admin/dist/settings.js
vendored
Normal file
2
web/admin/dist/travel.js
vendored
Normal file
2
web/admin/dist/users.js
vendored
Normal file
4
web/admin/src/attendance/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { AttendanceAdapter, AttendanceStatusAdapter } from './lib';
|
||||||
|
|
||||||
|
window.AttendanceAdapter = AttendanceAdapter;
|
||||||
|
window.AttendanceStatusAdapter = AttendanceStatusAdapter;
|
||||||
289
web/admin/src/attendance/lib.js
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import FormValidation from '../../../api/FormValidation';
|
||||||
|
|
||||||
|
class AttendanceAdapter extends AdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.photoAttendance = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'in_time',
|
||||||
|
'out_time',
|
||||||
|
'note',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Time-In' },
|
||||||
|
{ sTitle: 'Time-Out' },
|
||||||
|
{ sTitle: 'Note' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['employee', {
|
||||||
|
label: 'Employee', type: 'select2', 'allow-null': false, 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
||||||
|
}],
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['in_time', { label: 'Time-In', type: 'datetime' }],
|
||||||
|
['out_time', { label: 'Time-Out', type: 'datetime', validation: 'none' }],
|
||||||
|
['note', { label: 'Note', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['employee', {
|
||||||
|
label: 'Employee', type: 'select2', 'allow-null': false, 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
||||||
|
}],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setPhotoAttendance(val) {
|
||||||
|
this.photoAttendance = parseInt(val, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getCustomTableParams() {
|
||||||
|
const that = this;
|
||||||
|
const dataTableParams = {
|
||||||
|
aoColumnDefs: [
|
||||||
|
{
|
||||||
|
fnRender(data, cell) {
|
||||||
|
return that.preProcessRemoteTableData(data, cell, 2);
|
||||||
|
},
|
||||||
|
aTargets: [2],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fnRender(data, cell) {
|
||||||
|
return that.preProcessRemoteTableData(data, cell, 3);
|
||||||
|
},
|
||||||
|
aTargets: [3],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fnRender(data, cell) {
|
||||||
|
return that.preProcessRemoteTableData(data, cell, 4);
|
||||||
|
},
|
||||||
|
aTargets: [4],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fnRender: that.getActionButtons,
|
||||||
|
aTargets: [that.getDataMapping().length],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return dataTableParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
preProcessRemoteTableData(data, cell, id) {
|
||||||
|
if (id === 2) {
|
||||||
|
if (cell === '0000-00-00 00:00:00' || cell === '' || cell === undefined || cell == null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return Date.parse(cell).toString('yyyy MMM d <b>HH:mm</b>');
|
||||||
|
} if (id === 3) {
|
||||||
|
if (cell === '0000-00-00 00:00:00' || cell === '' || cell === undefined || cell == null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return Date.parse(cell).toString('MMM d <b>HH:mm</b>');
|
||||||
|
} if (id === 4) {
|
||||||
|
if (cell !== undefined && cell !== null) {
|
||||||
|
if (cell.length > 10) {
|
||||||
|
return `${cell.substring(0, 10)}..`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
save() {
|
||||||
|
const validator = new FormValidation(`${this.getTableName()}_submit`, true, { ShowPopup: false, LabelErrorClass: 'error' });
|
||||||
|
if (validator.checkValues()) {
|
||||||
|
const params = validator.getFormParameters();
|
||||||
|
|
||||||
|
const msg = this.doCustomValidation(params);
|
||||||
|
if (msg == null) {
|
||||||
|
const id = $(`#${this.getTableName()}_submit #id`).val();
|
||||||
|
if (id != null && id !== undefined && id !== '') {
|
||||||
|
params.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reqJson = JSON.stringify(params);
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'saveSuccessCallback';
|
||||||
|
callBackData.callBackFail = 'saveFailCallback';
|
||||||
|
|
||||||
|
this.customAction('savePunch', 'admin=attendance', reqJson, callBackData);
|
||||||
|
} else {
|
||||||
|
const label = $(`#${this.getTableName()}Form .label`);
|
||||||
|
label.html(msg);
|
||||||
|
label.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
saveSuccessCallback(callBackData) {
|
||||||
|
this.get(callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
saveFailCallback(callBackData) {
|
||||||
|
this.showMessage('Error saving attendance entry', callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubProfileTable() {
|
||||||
|
return this.user.user_level !== 'Admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
showPunchImages(id) {
|
||||||
|
const reqJson = JSON.stringify({ id });
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'getImagesSuccessCallback';
|
||||||
|
callBackData.callBackFail = 'getImagesFailCallback';
|
||||||
|
this.customAction('getImages', 'admin=attendance', reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getImagesSuccessCallback(callBackData) {
|
||||||
|
$('#attendancePhotoModel').modal('show');
|
||||||
|
$('#attendnaceCanvasEmp').html(callBackData.employee_Name);
|
||||||
|
if (callBackData.in_time) {
|
||||||
|
$('#attendnaceCanvasPunchInTime').html(Date.parse(callBackData.in_time).toString('yyyy MMM d <b>HH:mm</b>'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callBackData.image_in) {
|
||||||
|
const myCanvas = document.getElementById('attendnaceCanvasIn');
|
||||||
|
const ctx = myCanvas.getContext('2d');
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = function () {
|
||||||
|
ctx.drawImage(img, 0, 0); // Or at whatever offset you like
|
||||||
|
};
|
||||||
|
img.src = callBackData.image_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callBackData.out_time) {
|
||||||
|
$('#attendnaceCanvasPunchOutTime').html(Date.parse(callBackData.out_time).toString('yyyy MMM d <b>HH:mm</b>'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callBackData.image_out) {
|
||||||
|
const myCanvas = document.getElementById('attendnaceCanvasOut');
|
||||||
|
const ctx = myCanvas.getContext('2d');
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = function () {
|
||||||
|
ctx.drawImage(img, 0, 0); // Or at whatever offset you like
|
||||||
|
};
|
||||||
|
img.src = callBackData.image_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getImagesFailCallback(callBackData) {
|
||||||
|
this.showMessage('Error', callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 deleteButton = '<img class="tableActionButton" src="_BASE_images/delete.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Delete" onclick="modJs.deleteRow(_id_);return false;"></img>';
|
||||||
|
const photoButton = '<img class="tableActionButton" src="_BASE_images/cam.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Show Photo" onclick="modJs.showPunchImages(_id_);return false;"></img>';
|
||||||
|
|
||||||
|
let html;
|
||||||
|
if (this.photoAttendance === 1) {
|
||||||
|
html = '<div style="width:80px;">_edit__delete__photo_</div>';
|
||||||
|
} else {
|
||||||
|
html = '<div style="width:80px;">_edit__delete_</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace('_photo_', photoButton);
|
||||||
|
|
||||||
|
if (this.showDelete) {
|
||||||
|
html = html.replace('_delete_', deleteButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_delete_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showEdit) {
|
||||||
|
html = html.replace('_edit_', editButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_edit_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Attendance Status
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AttendanceStatusAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Clocked In Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['employee', {
|
||||||
|
label: 'Employee', type: 'select2', 'allow-null': false, 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
||||||
|
}],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
let html = '<div class="online-button-_COLOR_"></div>';
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
if (data[2] == 'Not Clocked In') {
|
||||||
|
html = html.replace(/_COLOR_/g, 'gray');
|
||||||
|
} else if (data[2] == 'Clocked Out') {
|
||||||
|
html = html.replace(/_COLOR_/g, 'yellow');
|
||||||
|
} else if (data[2] == 'Clocked In') {
|
||||||
|
html = html.replace(/_COLOR_/g, 'green');
|
||||||
|
}
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isSubProfileTable() {
|
||||||
|
return this.user.user_level !== 'Admin';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { AttendanceAdapter, AttendanceStatusAdapter };
|
||||||
4
web/admin/src/company_structure/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { CompanyStructureAdapter, CompanyGraphAdapter } from './lib';
|
||||||
|
|
||||||
|
window.CompanyStructureAdapter = CompanyStructureAdapter;
|
||||||
|
window.CompanyGraphAdapter = CompanyGraphAdapter;
|
||||||
311
web/admin/src/company_structure/lib.js
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
/* eslint-disable prefer-destructuring */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
/* global d3, nv */
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
class CompanyStructureAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'address',
|
||||||
|
'type',
|
||||||
|
'country',
|
||||||
|
'timezone',
|
||||||
|
'parent',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Address', bSortable: false },
|
||||||
|
{ sTitle: 'Type' },
|
||||||
|
{ sTitle: 'Country', sClass: 'center' },
|
||||||
|
{ sTitle: 'Time Zone' },
|
||||||
|
{ sTitle: 'Parent Structure' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden', validation: '' }],
|
||||||
|
['title', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['description', { label: 'Details', type: 'textarea', validation: '' }],
|
||||||
|
['address', { label: 'Address', type: 'textarea', validation: 'none' }],
|
||||||
|
['type', { label: 'Type', type: 'select', source: [['Company', 'Company'], ['Head Office', 'Head Office'], ['Regional Office', 'Regional Office'], ['Department', 'Department'], ['Unit', 'Unit'], ['Sub Unit', 'Sub Unit'], ['Other', 'Other']] }],
|
||||||
|
['country', { label: 'Country', type: 'select2', 'remote-source': ['Country', 'code', 'name'] }],
|
||||||
|
['timezone', {
|
||||||
|
label: 'Time Zone', type: 'select2', 'allow-null': false, 'remote-source': ['Timezone', 'name', 'details'],
|
||||||
|
}],
|
||||||
|
['parent', {
|
||||||
|
label: 'Parent Structure', type: 'select', 'allow-null': true, 'remote-source': ['CompanyStructure', 'id', 'title'],
|
||||||
|
}],
|
||||||
|
['heads', {
|
||||||
|
label: 'Heads', type: 'select2multi', 'allow-null': true, 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
||||||
|
}],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
postRenderForm(object, $tempDomObj) {
|
||||||
|
if (object === undefined
|
||||||
|
|| object === null
|
||||||
|
|| object.id === null
|
||||||
|
|| object.id === undefined || object.id === ''
|
||||||
|
) {
|
||||||
|
$tempDomObj.find('#field_heads').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Company Graph
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyGraphAdapter extends CompanyStructureAdapter {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.nodeIdCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToTree(data) {
|
||||||
|
const ice = {};
|
||||||
|
ice.id = -1;
|
||||||
|
ice.title = '';
|
||||||
|
ice.name = '';
|
||||||
|
ice.children = [];
|
||||||
|
|
||||||
|
let parent = null;
|
||||||
|
|
||||||
|
const added = {};
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
data[i].name = data[i].title;
|
||||||
|
|
||||||
|
if (data[i].parent != null && data[i].parent !== undefined) {
|
||||||
|
parent = this.findParent(data, data[i].parent);
|
||||||
|
if (parent != null) {
|
||||||
|
if (parent.children === undefined || parent.children == null) {
|
||||||
|
parent.children = [];
|
||||||
|
}
|
||||||
|
parent.children.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].parent == null || data[i].parent === undefined) {
|
||||||
|
ice.children.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
findParent(data, parent) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].title === parent || data[i].title === parent) {
|
||||||
|
return data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createTable(elementId) {
|
||||||
|
$('#tabPageCompanyGraph').html('');
|
||||||
|
const that = this;
|
||||||
|
const sourceData = this.sourceData;
|
||||||
|
|
||||||
|
// this.fixCyclicParent(sourceData);
|
||||||
|
const treeData = this.convertToTree(sourceData);
|
||||||
|
const m = [20, 120, 20, 120];
|
||||||
|
const w = 5000 - m[1] - m[3];
|
||||||
|
const h = 1000 - m[0] - m[2];
|
||||||
|
|
||||||
|
const tree = d3.layout.tree()
|
||||||
|
.size([h, w]);
|
||||||
|
|
||||||
|
this.diagonal = d3.svg.diagonal()
|
||||||
|
.projection(d => [d.y, d.x]);
|
||||||
|
|
||||||
|
this.vis = d3.select('#tabPageCompanyGraph').append('svg:svg')
|
||||||
|
.attr('width', w + m[1] + m[3])
|
||||||
|
.attr('height', h + m[0] + m[2])
|
||||||
|
.append('svg:g')
|
||||||
|
.attr('transform', `translate(${m[3]},${m[0]})`);
|
||||||
|
|
||||||
|
const root = treeData;
|
||||||
|
root.x0 = h / 2;
|
||||||
|
root.y0 = 0;
|
||||||
|
|
||||||
|
function toggleAll(d) {
|
||||||
|
if (d.children) {
|
||||||
|
console.log(d.name);
|
||||||
|
d.children.forEach(toggleAll);
|
||||||
|
that.toggle(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.update(root, tree, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(source, tree, root) {
|
||||||
|
const that = this;
|
||||||
|
const duration = d3.event && d3.event.altKey ? 5000 : 500;
|
||||||
|
|
||||||
|
// Compute the new tree layout.
|
||||||
|
const nodes = tree.nodes(root).reverse();
|
||||||
|
|
||||||
|
// Normalize for fixed-depth.
|
||||||
|
nodes.forEach((d) => { d.y = d.depth * 180; });
|
||||||
|
|
||||||
|
// Update the nodes<65>
|
||||||
|
const node = that.vis.selectAll('g.node')
|
||||||
|
.data(nodes, d => d.id || (d.id = ++that.nodeIdCounter));
|
||||||
|
|
||||||
|
// Enter any new nodes at the parent's previous position.
|
||||||
|
const nodeEnter = node.enter().append('svg:g')
|
||||||
|
.attr('class', 'node')
|
||||||
|
.attr('transform', d => `translate(${source.y0},${source.x0})`)
|
||||||
|
.on('click', (d) => { that.toggle(d); that.update(d, tree, root); });
|
||||||
|
|
||||||
|
nodeEnter.append('svg:circle')
|
||||||
|
.attr('r', 1e-6)
|
||||||
|
.style('fill', d => (d._children ? 'lightsteelblue' : '#fff'));
|
||||||
|
|
||||||
|
nodeEnter.append('svg:text')
|
||||||
|
.attr('x', d => (d.children || d._children ? -10 : 10))
|
||||||
|
.attr('dy', '.35em')
|
||||||
|
.attr('text-anchor', d => (d.children || d._children ? 'end' : 'start'))
|
||||||
|
.text(d => d.name)
|
||||||
|
.style('fill-opacity', 1e-6);
|
||||||
|
|
||||||
|
// Transition nodes to their new position.
|
||||||
|
const nodeUpdate = node.transition()
|
||||||
|
.duration(duration)
|
||||||
|
.attr('transform', d => `translate(${d.y},${d.x})`);
|
||||||
|
|
||||||
|
nodeUpdate.select('circle')
|
||||||
|
.attr('r', 4.5)
|
||||||
|
.style('fill', d => (d._children ? 'lightsteelblue' : '#fff'));
|
||||||
|
|
||||||
|
nodeUpdate.select('text')
|
||||||
|
.style('fill-opacity', 1);
|
||||||
|
|
||||||
|
// Transition exiting nodes to the parent's new position.
|
||||||
|
const nodeExit = node.exit().transition()
|
||||||
|
.duration(duration)
|
||||||
|
.attr('transform', d => `translate(${source.y},${source.x})`)
|
||||||
|
.remove();
|
||||||
|
|
||||||
|
nodeExit.select('circle')
|
||||||
|
.attr('r', 1e-6);
|
||||||
|
|
||||||
|
nodeExit.select('text')
|
||||||
|
.style('fill-opacity', 1e-6);
|
||||||
|
|
||||||
|
// Update the links<6B>
|
||||||
|
const link = that.vis.selectAll('path.link')
|
||||||
|
.data(tree.links(nodes), d => d.target.id);
|
||||||
|
|
||||||
|
// Enter any new links at the parent's previous position.
|
||||||
|
link.enter().insert('svg:path', 'g')
|
||||||
|
.attr('class', 'link')
|
||||||
|
.attr('d', (d) => {
|
||||||
|
const o = { x: source.x0, y: source.y0 };
|
||||||
|
return that.diagonal({ source: o, target: o });
|
||||||
|
})
|
||||||
|
.transition()
|
||||||
|
.duration(duration)
|
||||||
|
.attr('d', that.diagonal);
|
||||||
|
|
||||||
|
// Transition links to their new position.
|
||||||
|
link.transition()
|
||||||
|
.duration(duration)
|
||||||
|
.attr('d', that.diagonal);
|
||||||
|
|
||||||
|
// Transition exiting nodes to the parent's new position.
|
||||||
|
link.exit().transition()
|
||||||
|
.duration(duration)
|
||||||
|
.attr('d', (d) => {
|
||||||
|
const o = { x: source.x, y: source.y };
|
||||||
|
return that.diagonal({ source: o, target: o });
|
||||||
|
})
|
||||||
|
.remove();
|
||||||
|
|
||||||
|
// Stash the old positions for transition.
|
||||||
|
nodes.forEach((d) => {
|
||||||
|
d.x0 = d.x;
|
||||||
|
d.y0 = d.y;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle children.
|
||||||
|
toggle(d) {
|
||||||
|
if (d.children) {
|
||||||
|
d._children = d.children;
|
||||||
|
d.children = null;
|
||||||
|
} else {
|
||||||
|
d.children = d._children;
|
||||||
|
d._children = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getSourceDataById(id) {
|
||||||
|
for (let i = 0; i < this.sourceData.length; i++) {
|
||||||
|
if (this.sourceData[i].id == id) {
|
||||||
|
return this.sourceData[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixCyclicParent(sourceData) {
|
||||||
|
let errorMsg = '';
|
||||||
|
for (let i = 0; i < sourceData.length; i++) {
|
||||||
|
const obj = sourceData[i];
|
||||||
|
|
||||||
|
|
||||||
|
let curObj = obj;
|
||||||
|
const parentIdArr = {};
|
||||||
|
parentIdArr[curObj.id] = 1;
|
||||||
|
|
||||||
|
while (curObj.parent != null && curObj.parent != undefined) {
|
||||||
|
const parent = this.getSourceDataById(curObj.parent);
|
||||||
|
if (parent == null) {
|
||||||
|
break;
|
||||||
|
} else if (parentIdArr[parent.id] == 1) {
|
||||||
|
errorMsg = `${obj.title}'s parent structure set to ${parent.title}<br/>`;
|
||||||
|
obj.parent = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parentIdArr[parent.id] = 1;
|
||||||
|
curObj = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg !== '') {
|
||||||
|
this.showMessage('Company Structure is having a cyclic dependency', `We found a cyclic dependency due to following reasons:<br/>${errorMsg}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHelpLink() {
|
||||||
|
return 'https://thilinah.gitbooks.io/icehrm-guide/content/employee-information-setup.html';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { CompanyStructureAdapter, CompanyGraphAdapter };
|
||||||
3
web/admin/src/dashboard/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { DashboardAdapter } from './lib';
|
||||||
|
|
||||||
|
window.DashboardAdapter = DashboardAdapter;
|
||||||
56
web/admin/src/dashboard/lib.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
class DashboardAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get(callBackData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getInitData() {
|
||||||
|
const that = this;
|
||||||
|
const object = {};
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'getInitDataSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'getInitDataFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('getInitData', 'admin=dashboard', reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getInitDataSuccessCallBack(data) {
|
||||||
|
$('#numberOfEmployees').html(`${data.numberOfEmployees} Employees`);
|
||||||
|
$('#numberOfCompanyStuctures').html(`${data.numberOfCompanyStuctures} Departments`);
|
||||||
|
$('#numberOfUsers').html(`${data.numberOfUsers} Users`);
|
||||||
|
$('#numberOfProjects').html(`${data.numberOfProjects} Active Projects`);
|
||||||
|
$('#numberOfAttendanceLastWeek').html(`${data.numberOfAttendanceLastWeek} Entries Last Week`);
|
||||||
|
$('#numberOfLeaves').html(`${data.numberOfLeaves} Upcoming`);
|
||||||
|
$('#numberOfTimeEntries').html(data.numberOfTimeEntries);
|
||||||
|
$('#numberOfCandidates').html(`${data.numberOfCandidates} Candidates`);
|
||||||
|
$('#numberOfJobs').html(`${data.numberOfJobs} Active`);
|
||||||
|
$('#numberOfCourses').html(`${data.numberOfCourses} Courses`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInitDataFailCallBack(callBackData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { DashboardAdapter };
|
||||||
4
web/admin/src/data/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { DataImportAdapter, DataImportFileAdapter } from './lib';
|
||||||
|
|
||||||
|
window.DataImportAdapter = DataImportAdapter;
|
||||||
|
window.DataImportFileAdapter = DataImportFileAdapter;
|
||||||
185
web/admin/src/data/lib.js
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
/* global dependOnField */
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataImportAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DataImportAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'dataType',
|
||||||
|
'details',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Data Type' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['dataType', { label: 'Data Type', type: 'text', validation: '' }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
['columns', {
|
||||||
|
label: 'Columns',
|
||||||
|
type: 'datagroup',
|
||||||
|
form: [
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['title', { label: 'Filed Title', type: 'text', validation: 'none' }],
|
||||||
|
['type', {
|
||||||
|
label: 'Type', type: 'select', sort: 'none', source: [['Normal', 'Normal'], ['Reference', 'Reference'], ['Attached', 'Attached']],
|
||||||
|
}],
|
||||||
|
['dependOn', {
|
||||||
|
label: 'Depends On',
|
||||||
|
type: 'select',
|
||||||
|
'allow-null': true,
|
||||||
|
'null-label': 'N/A',
|
||||||
|
source: [['EmergencyContact', 'Emergency Contacts'], ['Ethnicity', 'Ethnicity'], ['Nationality', 'Nationality'], ['JobTitle', 'JobTitle'], ['PayFrequency', 'PayFrequency'], ['PayGrade', 'PayGrade'], ['EmploymentStatus', 'EmploymentStatus'], ['CompanyStructure', 'CompanyStructure'], ['Employee', 'Employee']],
|
||||||
|
}],
|
||||||
|
['dependOnField', { label: 'Depends On Field', type: 'text', validation: 'none' }],
|
||||||
|
['isKeyField', {
|
||||||
|
label: 'Is Key Field', type: 'select', validation: '', source: [['No', 'No'], ['Yes', 'Yes']],
|
||||||
|
}],
|
||||||
|
['idField', {
|
||||||
|
label: 'Is ID Field', type: 'select', validation: '', source: [['No', 'No'], ['Yes', 'Yes']],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
html: '<div id="#_id_#" class="panel panel-default"><div class="panel-heading"><b>#_name_#</b> #_delete_##_edit_#</div><div class="panel-body"><b>Header Title: </b>#_title_#<br/><span style="color:#999;font-size:11px;font-weight:bold">Type: #_type_# </span><br/></div></div>',
|
||||||
|
validation: 'none',
|
||||||
|
'custom-validate-function': function (data) {
|
||||||
|
const res = {};
|
||||||
|
res.params = data;
|
||||||
|
res.valid = true;
|
||||||
|
if (data.type === 'Reference') {
|
||||||
|
if (data.dependOn === 'NULL') {
|
||||||
|
res.message = 'If the type is Reference this field should referring another table';
|
||||||
|
res.valid = false;
|
||||||
|
} else if (dependOnField === null || dependOnField === undefined) {
|
||||||
|
res.message = "If the type is Reference then 'Depends On Field' can not be empty";
|
||||||
|
res.valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
|
}],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataImportFileAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DataImportFileAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'data_import_definition',
|
||||||
|
'status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Data Import Definition' },
|
||||||
|
{ sTitle: 'Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['data_import_definition', { label: 'Data Import Definitions', type: 'select', 'remote-source': ['DataImport', 'id', 'name'] }],
|
||||||
|
['file', {
|
||||||
|
label: 'File to Import', type: 'fileupload', validation: '', filetypes: 'csv,txt',
|
||||||
|
}],
|
||||||
|
['details', { label: 'Last Export Result', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 processButton = '<img class="tableActionButton" src="_BASE_images/run.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Process" onclick="modJs.process(_id_,\'_status_\');return false;"></img>';
|
||||||
|
const deleteButton = '<img class="tableActionButton" src="_BASE_images/delete.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Delete" onclick="modJs.deleteRow(_id_);return false;"></img>';
|
||||||
|
const cloneButton = '<img class="tableActionButton" src="_BASE_images/clone.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Copy" onclick="modJs.copyRow(_id_);return false;"></img>';
|
||||||
|
|
||||||
|
let html = '<div style="width:120px;">_edit__process__clone__delete_</div>';
|
||||||
|
|
||||||
|
|
||||||
|
if (this.showAddNew) {
|
||||||
|
html = html.replace('_clone_', cloneButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_clone_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showDelete) {
|
||||||
|
html = html.replace('_delete_', deleteButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_delete_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showEdit) {
|
||||||
|
html = html.replace('_edit_', editButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_edit_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[3] === 'Not Processed') {
|
||||||
|
html = html.replace('_process_', processButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_process_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_status_/g, data[6]);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
process(id) {
|
||||||
|
const that = this;
|
||||||
|
const object = { id };
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'processSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'processFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('processDataFile', 'admin=data', reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
processSuccessCallBack(callBackData) {
|
||||||
|
this.showMessage('Success', 'File imported successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
processFailCallBack(callBackData) {
|
||||||
|
this.showMessage('Error', `File import unsuccessful. Result:${callBackData}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { DataImportAdapter, DataImportFileAdapter };
|
||||||
39
web/admin/src/employees/index.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
EmployeeAdapter,
|
||||||
|
TerminatedEmployeeAdapter,
|
||||||
|
ArchivedEmployeeAdapter,
|
||||||
|
EmployeeSkillAdapter,
|
||||||
|
EmployeeEducationAdapter,
|
||||||
|
EmployeeCertificationAdapter,
|
||||||
|
EmployeeLanguageAdapter,
|
||||||
|
EmployeeDependentAdapter,
|
||||||
|
EmergencyContactAdapter,
|
||||||
|
EmployeeImmigrationAdapter,
|
||||||
|
EmployeeSubSkillsAdapter,
|
||||||
|
EmployeeSubEducationAdapter,
|
||||||
|
EmployeeSubCertificationAdapter,
|
||||||
|
EmployeeSubLanguageAdapter,
|
||||||
|
EmployeeSubDependentAdapter,
|
||||||
|
EmployeeSubEmergencyContactAdapter,
|
||||||
|
EmployeeSubDocumentAdapter,
|
||||||
|
EmployeeDocumentAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.EmployeeAdapter = EmployeeAdapter;
|
||||||
|
window.TerminatedEmployeeAdapter = TerminatedEmployeeAdapter;
|
||||||
|
window.ArchivedEmployeeAdapter = ArchivedEmployeeAdapter;
|
||||||
|
window.EmployeeSkillAdapter = EmployeeSkillAdapter;
|
||||||
|
window.EmployeeEducationAdapter = EmployeeEducationAdapter;
|
||||||
|
window.EmployeeCertificationAdapter = EmployeeCertificationAdapter;
|
||||||
|
window.EmployeeLanguageAdapter = EmployeeLanguageAdapter;
|
||||||
|
window.EmployeeDependentAdapter = EmployeeDependentAdapter;
|
||||||
|
window.EmergencyContactAdapter = EmergencyContactAdapter;
|
||||||
|
window.EmployeeImmigrationAdapter = EmployeeImmigrationAdapter;
|
||||||
|
window.EmployeeSubSkillsAdapter = EmployeeSubSkillsAdapter;
|
||||||
|
window.EmployeeSubEducationAdapter = EmployeeSubEducationAdapter;
|
||||||
|
window.EmployeeSubCertificationAdapter = EmployeeSubCertificationAdapter;
|
||||||
|
window.EmployeeSubLanguageAdapter = EmployeeSubLanguageAdapter;
|
||||||
|
window.EmployeeSubDependentAdapter = EmployeeSubDependentAdapter;
|
||||||
|
window.EmployeeSubEmergencyContactAdapter = EmployeeSubEmergencyContactAdapter;
|
||||||
|
window.EmployeeSubDocumentAdapter = EmployeeSubDocumentAdapter;
|
||||||
|
window.EmployeeDocumentAdapter = EmployeeDocumentAdapter;
|
||||||
1877
web/admin/src/employees/lib.js
Normal file
4
web/admin/src/fieldnames/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { FieldNameAdapter, CustomFieldAdapter } from './lib';
|
||||||
|
|
||||||
|
window.FieldNameAdapter = FieldNameAdapter;
|
||||||
|
window.CustomFieldAdapter = CustomFieldAdapter;
|
||||||
47
web/admin/src/fieldnames/lib.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import CustomFieldAdapter from '../../../api/CustomFieldAdapter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FieldNameAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FieldNameAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'textOrig',
|
||||||
|
'textMapped',
|
||||||
|
'display',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Original Text' },
|
||||||
|
{ sTitle: 'Mapped Text' },
|
||||||
|
{ sTitle: 'Display Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['type', { label: 'Type', type: 'placeholder', validation: '' }],
|
||||||
|
['name', { label: 'Name', type: 'placeholder', validation: '' }],
|
||||||
|
['textOrig', { label: 'Original Text', type: 'placeholder', validation: '' }],
|
||||||
|
['textMapped', { label: 'Mapped Text', type: 'text', validation: '' }],
|
||||||
|
['display', { label: 'Display Status', type: 'select', source: [['Form', 'Show'], ['Hidden', 'Hidden']] }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { FieldNameAdapter, CustomFieldAdapter };
|
||||||
5
web/admin/src/jobs/index.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { JobTitleAdapter, PayGradeAdapter, EmploymentStatusAdapter } from './lib';
|
||||||
|
|
||||||
|
window.JobTitleAdapter = JobTitleAdapter;
|
||||||
|
window.PayGradeAdapter = PayGradeAdapter;
|
||||||
|
window.EmploymentStatusAdapter = EmploymentStatusAdapter;
|
||||||
124
web/admin/src/jobs/lib.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JobTitleAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class JobTitleAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'code',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Code' },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['code', { label: 'Job Title Code', type: 'text' }],
|
||||||
|
['name', { label: 'Job Title', type: 'text' }],
|
||||||
|
['description', { label: 'Description', type: 'textarea' }],
|
||||||
|
['specification', { label: 'Specification', type: 'textarea' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHelpLink() {
|
||||||
|
return 'http://blog.icehrm.com/docs/jobdetails/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PayGradeAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PayGradeAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'currency',
|
||||||
|
'min_salary',
|
||||||
|
'max_salary',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Currency' },
|
||||||
|
{ sTitle: 'Min Salary' },
|
||||||
|
{ sTitle: 'Max Salary' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Pay Grade Name', type: 'text' }],
|
||||||
|
['currency', { label: 'Currency', type: 'select2', 'remote-source': ['CurrencyType', 'code', 'name'] }],
|
||||||
|
['min_salary', { label: 'Min Salary', type: 'text', validation: 'float' }],
|
||||||
|
['max_salary', { label: 'Max Salary', type: 'text', validation: 'float' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
doCustomValidation(params) {
|
||||||
|
try {
|
||||||
|
if (parseFloat(params.min_salary) > parseFloat(params.max_salary)) {
|
||||||
|
return 'Min Salary should be smaller than Max Salary';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// D/N
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmploymentStatusAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EmploymentStatusAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID' },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Description' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Employment Status', type: 'text' }],
|
||||||
|
['description', { label: 'Description', type: 'textarea', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { JobTitleAdapter, PayGradeAdapter, EmploymentStatusAdapter };
|
||||||
7
web/admin/src/loans/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import {
|
||||||
|
CompanyLoanAdapter,
|
||||||
|
EmployeeCompanyLoanAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.CompanyLoanAdapter = CompanyLoanAdapter;
|
||||||
|
window.EmployeeCompanyLoanAdapter = EmployeeCompanyLoanAdapter;
|
||||||
101
web/admin/src/loans/lib.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompanyLoanAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CompanyLoanAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'details',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EmployeeCompanyLoanAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EmployeeCompanyLoanAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'loan',
|
||||||
|
'start_date',
|
||||||
|
'period_months',
|
||||||
|
'currency',
|
||||||
|
'amount',
|
||||||
|
'status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Loan Type' },
|
||||||
|
{ sTitle: 'Loan Start Date' },
|
||||||
|
{ sTitle: 'Loan Period (Months)' },
|
||||||
|
{ sTitle: 'Currency' },
|
||||||
|
{ sTitle: 'Amount' },
|
||||||
|
{ sTitle: 'Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
['loan', { label: 'Loan Type', type: 'select', 'remote-source': ['CompanyLoan', 'id', 'name'] }],
|
||||||
|
['start_date', { label: 'Loan Start Date', type: 'date', validation: '' }],
|
||||||
|
['last_installment_date', { label: 'Last Installment Date', type: 'date', validation: 'none' }],
|
||||||
|
['period_months', { label: 'Loan Period (Months)', type: 'text', validation: 'number' }],
|
||||||
|
['currency', { label: 'Currency', type: 'select2', 'remote-source': ['CurrencyType', 'id', 'name'] }],
|
||||||
|
['amount', { label: 'Loan Amount', type: 'text', validation: 'float' }],
|
||||||
|
['monthly_installment', { label: 'Monthly Installment', type: 'text', validation: 'float' }],
|
||||||
|
['status', { label: 'Status', type: 'select', source: [['Approved', 'Approved'], ['Paid', 'Paid'], ['Suspended', 'Suspended']] }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['employee', {
|
||||||
|
label: 'Employee', type: 'select2', 'allow-null': true, 'null-label': 'All Employees', 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
||||||
|
}],
|
||||||
|
['loan', {
|
||||||
|
label: 'Loan Type', type: 'select', 'allow-null': true, 'null-label': 'All Loan Types', 'remote-source': ['CompanyLoan', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CompanyLoanAdapter,
|
||||||
|
EmployeeCompanyLoanAdapter,
|
||||||
|
};
|
||||||
15
web/admin/src/metadata/index.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import {
|
||||||
|
CountryAdapter,
|
||||||
|
ProvinceAdapter,
|
||||||
|
CurrencyTypeAdapter,
|
||||||
|
NationalityAdapter,
|
||||||
|
ImmigrationStatusAdapter,
|
||||||
|
EthnicityAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.CountryAdapter = CountryAdapter;
|
||||||
|
window.ProvinceAdapter = ProvinceAdapter;
|
||||||
|
window.CurrencyTypeAdapter = CurrencyTypeAdapter;
|
||||||
|
window.NationalityAdapter = NationalityAdapter;
|
||||||
|
window.ImmigrationStatusAdapter = ImmigrationStatusAdapter;
|
||||||
|
window.EthnicityAdapter = EthnicityAdapter;
|
||||||
142
web/admin/src/metadata/lib.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import IdNameAdapter from '../../../api/IdNameAdapter';
|
||||||
|
/**
|
||||||
|
* CountryAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CountryAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'code',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Code' },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['code', { label: 'Code', type: 'text', validation: '' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProvinceAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ProvinceAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'code',
|
||||||
|
'name',
|
||||||
|
'country',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Code' },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Country' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['code', { label: 'Code', type: 'text', validation: '' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['country', { label: 'Country', type: 'select2', 'remote-source': ['Country', 'code', 'name'] }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['country', { label: 'Country', type: 'select2', 'remote-source': ['Country', 'code', 'name'] }],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CurrencyTypeAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CurrencyTypeAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'code',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Code' },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['code', { label: 'Code', type: 'text', validation: '' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NationalityAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NationalityAdapter extends IdNameAdapter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImmigrationStatusAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ImmigrationStatusAdapter extends IdNameAdapter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EthnicityAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EthnicityAdapter extends IdNameAdapter {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CountryAdapter,
|
||||||
|
ProvinceAdapter,
|
||||||
|
CurrencyTypeAdapter,
|
||||||
|
NationalityAdapter,
|
||||||
|
ImmigrationStatusAdapter,
|
||||||
|
EthnicityAdapter,
|
||||||
|
};
|
||||||
4
web/admin/src/modules/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { ModuleAdapter, UsageAdapter } from './lib';
|
||||||
|
|
||||||
|
window.ModuleAdapter = ModuleAdapter;
|
||||||
|
window.UsageAdapter = UsageAdapter;
|
||||||
133
web/admin/src/modules/lib.js
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
/**
|
||||||
|
* ModuleAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ModuleAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'label',
|
||||||
|
'menu',
|
||||||
|
'mod_group',
|
||||||
|
'mod_order',
|
||||||
|
'status',
|
||||||
|
'version',
|
||||||
|
'update_path',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Menu', bVisible: false },
|
||||||
|
{ sTitle: 'Group' },
|
||||||
|
{ sTitle: 'Order' },
|
||||||
|
{ sTitle: 'Status' },
|
||||||
|
{ sTitle: 'Version', bVisible: false },
|
||||||
|
{ sTitle: 'Path', bVisible: false },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['label', { label: 'Label', type: 'text', validation: '' }],
|
||||||
|
['status', { label: 'Status', type: 'select', source: [['Enabled', 'Enabled'], ['Disabled', 'Disabled']] }],
|
||||||
|
['user_levels', { label: 'User Levels', type: 'select2multi', source: [['Admin', 'Admin'], ['Manager', 'Manager'], ['Employee', 'Employee'], ['Other', 'Other']] }],
|
||||||
|
['user_roles', { label: 'User Roles', type: 'select2multi', 'remote-source': ['UserRole', 'id', 'name'] }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
const nonEditableFields = {};
|
||||||
|
nonEditableFields['admin_Company Structure'] = 1;
|
||||||
|
nonEditableFields.admin_Employees = 1;
|
||||||
|
nonEditableFields['admin_Job Details Setup'] = 1;
|
||||||
|
nonEditableFields.admin_Leaves = 1;
|
||||||
|
nonEditableFields['admin_Manage Modules'] = 1;
|
||||||
|
nonEditableFields.admin_Projects = 1;
|
||||||
|
nonEditableFields.admin_Qualifications = 1;
|
||||||
|
nonEditableFields.admin_Settings = 1;
|
||||||
|
nonEditableFields.admin_Users = 1;
|
||||||
|
nonEditableFields.admin_Upgrade = 1;
|
||||||
|
nonEditableFields.admin_Dashboard = 1;
|
||||||
|
|
||||||
|
nonEditableFields['user_Basic Information'] = 1;
|
||||||
|
nonEditableFields.user_Dashboard = 1;
|
||||||
|
|
||||||
|
if (nonEditableFields[`${data[3]}_${data[1]}`] === 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
let html = '<div style="width:80px;"><img class="tableActionButton" src="_BASE_images/edit.png" style="cursor:pointer;" rel="tooltip" title="Edit" onclick="modJs.edit(_id_);return false;"/></div>';
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UsageAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class UsageAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get(callBackData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUsage() {
|
||||||
|
const object = {};
|
||||||
|
const arr = [];
|
||||||
|
$('.module-check').each(function () {
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
arr.push($(this).val());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (arr.length === 0) {
|
||||||
|
alert('Please select one or more module groups');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object.groups = arr.join(',');
|
||||||
|
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'getInitDataSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'getInitDataFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('saveUsage', 'admin=modules', reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
saveUsageSuccessCallBack(data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUsageFailCallBack(callBackData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { ModuleAdapter, UsageAdapter };
|
||||||
7
web/admin/src/overtime/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import {
|
||||||
|
OvertimeCategoryAdapter,
|
||||||
|
EmployeeOvertimeAdminAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.OvertimeCategoryAdapter = OvertimeCategoryAdapter;
|
||||||
|
window.EmployeeOvertimeAdminAdapter = EmployeeOvertimeAdminAdapter;
|
||||||
100
web/admin/src/overtime/lib.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import ApproveAdminAdapter from '../../../api/ApproveAdminAdapter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OvertimeCategoryAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class OvertimeCategoryAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmployeeOvertimeAdminAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeOvertimeAdminAdapter extends ApproveAdminAdapter {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.itemName = 'OvertimeRequest';
|
||||||
|
this.itemNameLower = 'overtimerequest';
|
||||||
|
this.modulePathName = 'overtime';
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'category',
|
||||||
|
'start_time',
|
||||||
|
'end_time',
|
||||||
|
'project',
|
||||||
|
'status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Category' },
|
||||||
|
{ sTitle: 'Start Time' },
|
||||||
|
{ sTitle: 'End Time' },
|
||||||
|
{ sTitle: 'Project' },
|
||||||
|
{ sTitle: 'Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['employee', {
|
||||||
|
label: 'Employee',
|
||||||
|
type: 'select2',
|
||||||
|
sort: 'none',
|
||||||
|
'allow-null': false,
|
||||||
|
'remote-source': ['Employee', 'id', 'first_name+last_name', 'getActiveSubordinateEmployees'],
|
||||||
|
}],
|
||||||
|
['category', {
|
||||||
|
label: 'Category', type: 'select2', 'allow-null': false, 'remote-source': ['OvertimeCategory', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['start_time', { label: 'Start Time', type: 'datetime', validation: '' }],
|
||||||
|
['end_time', { label: 'End Time', type: 'datetime', validation: '' }],
|
||||||
|
['project', {
|
||||||
|
label: 'Project', type: 'select2', 'allow-null': true, 'null=label': 'none', 'remote-source': ['Project', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['notes', { label: 'Notes', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
OvertimeCategoryAdapter,
|
||||||
|
EmployeeOvertimeAdminAdapter,
|
||||||
|
};
|
||||||
21
web/admin/src/payroll/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import {
|
||||||
|
PaydayAdapter,
|
||||||
|
PayrollAdapter,
|
||||||
|
PayrollDataAdapter,
|
||||||
|
PayrollColumnAdapter,
|
||||||
|
PayrollColumnTemplateAdapter,
|
||||||
|
PayrollEmployeeAdapter,
|
||||||
|
DeductionAdapter,
|
||||||
|
DeductionGroupAdapter,
|
||||||
|
PayslipTemplateAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.PaydayAdapter = PaydayAdapter;
|
||||||
|
window.PayrollAdapter = PayrollAdapter;
|
||||||
|
window.PayrollDataAdapter = PayrollDataAdapter;
|
||||||
|
window.PayrollColumnAdapter = PayrollColumnAdapter;
|
||||||
|
window.PayrollColumnTemplateAdapter = PayrollColumnTemplateAdapter;
|
||||||
|
window.PayrollEmployeeAdapter = PayrollEmployeeAdapter;
|
||||||
|
window.DeductionAdapter = DeductionAdapter;
|
||||||
|
window.DeductionGroupAdapter = DeductionGroupAdapter;
|
||||||
|
window.PayslipTemplateAdapter = PayslipTemplateAdapter;
|
||||||
631
web/admin/src/payroll/lib.js
Normal file
@@ -0,0 +1,631 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
/* global modJs, modJsList */
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import TableEditAdapter from '../../../api/TableEditAdapter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PaydayAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PaydayAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Select Pay Frequency' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddNewLabel() {
|
||||||
|
return 'Run Payroll';
|
||||||
|
}
|
||||||
|
|
||||||
|
createTable(elementId) {
|
||||||
|
$('#payday_all').off();
|
||||||
|
super.createTable(elementId);
|
||||||
|
$('#payday_all').off().on('click', function () {
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
$('.paydayCheck').prop('checked', true);
|
||||||
|
} else {
|
||||||
|
$('.paydayCheck').prop('checked', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
const editButton = '<input type="checkbox" class="paydayCheck" id="payday__id_" name="payday__id_" value="checkbox_payday__id_"/>';
|
||||||
|
|
||||||
|
let html = '<div style="width:120px;">_edit_</div>';
|
||||||
|
html = html.replace('_edit_', editButton);
|
||||||
|
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonHeader() {
|
||||||
|
return { sTitle: '<input type="checkbox" id="payday_all" name="payday_all" value="checkbox_payday_all"/>', sClass: 'center' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PayrollAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PayrollAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'pay_period',
|
||||||
|
'department',
|
||||||
|
'date_start',
|
||||||
|
'date_end',
|
||||||
|
'status',
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Pay Frequency' },
|
||||||
|
{ sTitle: 'Department' },
|
||||||
|
{ sTitle: 'Date Start' },
|
||||||
|
{ sTitle: 'Date End' },
|
||||||
|
{ sTitle: 'Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text' }],
|
||||||
|
['pay_period', {
|
||||||
|
label: 'Pay Frequency', type: 'select', 'remote-source': ['PayFrequency', 'id', 'name'], sort: 'none',
|
||||||
|
}],
|
||||||
|
['deduction_group', {
|
||||||
|
label: 'Calculation Group', type: 'select', 'remote-source': ['DeductionGroup', 'id', 'name'], sort: 'none',
|
||||||
|
}],
|
||||||
|
['payslipTemplate', { label: 'Payslip Template', type: 'select', 'remote-source': ['PayslipTemplate', 'id', 'name'] }],
|
||||||
|
['department', {
|
||||||
|
label: 'Department', type: 'select2', 'remote-source': ['CompanyStructure', 'id', 'title'], sort: 'none',
|
||||||
|
}],
|
||||||
|
['date_start', { label: 'Start Date', type: 'date', validation: '' }],
|
||||||
|
['date_end', { label: 'End Date', type: 'date', validation: '' }],
|
||||||
|
// [ "column_template", {"label":"Report Column Template","type":"select","remote-source":["PayrollColumnTemplate","id","name"]}],
|
||||||
|
['columns', { label: 'Payroll Columns', type: 'select2multi', 'remote-source': ['PayrollColumn', 'id', 'name'] }],
|
||||||
|
['status', {
|
||||||
|
label: 'Status', type: 'select', source: [['Draft', 'Draft'], ['Completed', 'Completed']], sort: 'none',
|
||||||
|
}],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
postRenderForm(object, $tempDomObj) {
|
||||||
|
if (object != null && object !== undefined && object.id !== undefined && object.id != null) {
|
||||||
|
$tempDomObj.find('#pay_period').attr('disabled', 'disabled');
|
||||||
|
$tempDomObj.find('#department').attr('disabled', 'disabled');
|
||||||
|
// $tempDomObj.find("#date_start").attr('disabled','disabled');
|
||||||
|
// $tempDomObj.find("#date_end").attr('disabled','disabled');
|
||||||
|
// $tempDomObj.find("#column_template").attr('disabled','disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process(id, status) {
|
||||||
|
// eslint-disable-next-line no-global-assign
|
||||||
|
modJs = modJsList.tabPayrollData;
|
||||||
|
modJs.setCurrentPayroll(id);
|
||||||
|
$('#Payroll').hide();
|
||||||
|
$('#PayrollData').show();
|
||||||
|
$('#PayrollDataButtons').show();
|
||||||
|
|
||||||
|
if (status === 'Completed') {
|
||||||
|
$('.completeBtnTable').hide();
|
||||||
|
$('.saveBtnTable').hide();
|
||||||
|
} else {
|
||||||
|
$('.completeBtnTable').show();
|
||||||
|
$('.saveBtnTable').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
modJs.get([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 processButton = '<img class="tableActionButton" src="_BASE_images/run.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Process" onclick="modJs.process(_id_,\'_status_\');return false;"></img>';
|
||||||
|
const deleteButton = '<img class="tableActionButton" src="_BASE_images/delete.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Delete" onclick="modJs.deleteRow(_id_);return false;"></img>';
|
||||||
|
const cloneButton = '<img class="tableActionButton" src="_BASE_images/clone.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Copy" onclick="modJs.copyRow(_id_);return false;"></img>';
|
||||||
|
|
||||||
|
let html = '<div style="width:120px;">_edit__process__clone__delete_</div>';
|
||||||
|
|
||||||
|
|
||||||
|
if (this.showAddNew) {
|
||||||
|
html = html.replace('_clone_', cloneButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_clone_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showDelete) {
|
||||||
|
html = html.replace('_delete_', deleteButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_delete_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showEdit) {
|
||||||
|
html = html.replace('_edit_', editButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_edit_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace('_process_', processButton);
|
||||||
|
|
||||||
|
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_status_/g, data[6]);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(callBackData) {
|
||||||
|
$('#PayrollData').hide();
|
||||||
|
$('#PayrollForm').hide();
|
||||||
|
$('#PayrollDataButtons').hide();
|
||||||
|
$('#Payroll').show();
|
||||||
|
modJsList.tabPayrollData.setCurrentPayroll(null);
|
||||||
|
super.get(callBackData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PayrollDataAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PayrollDataAdapter extends TableEditAdapter {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.cellDataUpdates = {};
|
||||||
|
this.payrollId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
validateCellValue(element, evt, newValue) {
|
||||||
|
modJs.addCellDataUpdate(element.data('colId'), element.data('rowId'), newValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentPayroll(val) {
|
||||||
|
this.payrollId = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addAdditionalRequestData(type, req) {
|
||||||
|
if (type === 'updateData') {
|
||||||
|
req.payrollId = this.payrollId;
|
||||||
|
} else if (type === 'updateAllData') {
|
||||||
|
req.payrollId = this.payrollId;
|
||||||
|
} else if (type === 'getAllData') {
|
||||||
|
req.payrollId = this.payrollId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyCSVHeader(header) {
|
||||||
|
header.unshift('');
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCSVData() {
|
||||||
|
let csv = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < this.csvData.length; i++) {
|
||||||
|
csv += this.csvData[i].join(',');
|
||||||
|
if (i < this.csvData.length - 1) {
|
||||||
|
csv += '\r\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return csv;
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadPayroll() {
|
||||||
|
const element = document.createElement('a');
|
||||||
|
element.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(this.getCSVData())}`);
|
||||||
|
element.setAttribute('download', `payroll_${this.payrollId}.csv`);
|
||||||
|
|
||||||
|
element.style.display = 'none';
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
element.click();
|
||||||
|
|
||||||
|
document.body.removeChild(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PayrollColumnAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PayrollColumnAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'colorder',
|
||||||
|
'calculation_hook',
|
||||||
|
'deduction_group',
|
||||||
|
'editable',
|
||||||
|
'enabled',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Column Order' },
|
||||||
|
{ sTitle: 'Calculation Method' },
|
||||||
|
{ sTitle: 'Calculation Group' },
|
||||||
|
{ sTitle: 'Editable' },
|
||||||
|
{ sTitle: 'Enabled' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
const fucntionColumnList = ['calculation_columns', {
|
||||||
|
label: 'Calculation Columns',
|
||||||
|
type: 'datagroup',
|
||||||
|
form: [
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['column', { label: 'Column', type: 'select2', 'remote-source': ['PayrollColumn', 'id', 'name'] }],
|
||||||
|
],
|
||||||
|
html: '<div id="#_id_#" class="panel panel-default">#_delete_##_edit_#<div class="panel-body">#_renderFunction_#</div></div>',
|
||||||
|
validation: 'none',
|
||||||
|
render(item) {
|
||||||
|
const output = `Variable:${item.name}`;
|
||||||
|
return output;
|
||||||
|
},
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['calculation_hook', {
|
||||||
|
label: 'Predefined Calculations', type: 'select2', 'allow-null': true, 'null-label': 'None', 'remote-source': ['CalculationHook', 'code', 'name'],
|
||||||
|
}],
|
||||||
|
['deduction_group', {
|
||||||
|
label: 'Calculation Group', type: 'select2', 'allow-null': true, 'null-label': 'Common', 'remote-source': ['DeductionGroup', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['salary_components', { label: 'Salary Components', type: 'select2multi', 'remote-source': ['SalaryComponent', 'id', 'name'] }],
|
||||||
|
['deductions', { label: 'Calculation Method', type: 'select2multi', 'remote-source': ['Deduction', 'id', 'name'] }],
|
||||||
|
['add_columns', { label: 'Columns to Add', type: 'select2multi', 'remote-source': ['PayrollColumn', 'id', 'name'] }],
|
||||||
|
['sub_columns', { label: 'Columns to Subtract', type: 'select2multi', 'remote-source': ['PayrollColumn', 'id', 'name'] }],
|
||||||
|
['colorder', { label: 'Column Order', type: 'text', validation: 'number' }],
|
||||||
|
['editable', { label: 'Editable', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
||||||
|
['enabled', { label: 'Enabled', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
||||||
|
['default_value', { label: 'Default Value', type: 'text', validation: '' }],
|
||||||
|
fucntionColumnList,
|
||||||
|
['calculation_function', { label: 'Function', type: 'text', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['deduction_group', {
|
||||||
|
label: 'Calculation Group', type: 'select2', 'allow-null': true, 'null-label': 'Any', 'remote-source': ['DeductionGroup', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PayrollColumnTemplateAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PayrollColumnTemplateAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: true },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['columns', { label: 'Payroll Columns', type: 'select2multi', 'remote-source': ['PayrollColumn', 'id', 'name'] }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PayrollEmployeeAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PayrollEmployeeAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'pay_frequency',
|
||||||
|
'deduction_group',
|
||||||
|
'currency',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Pay Frequency' },
|
||||||
|
{ sTitle: 'Calculation Group' },
|
||||||
|
{ sTitle: 'Currency' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
['pay_frequency', { label: 'Pay Frequency', type: 'select2', 'remote-source': ['PayFrequency', 'id', 'name'] }],
|
||||||
|
['currency', { label: 'Currency', type: 'select2', 'remote-source': ['CurrencyType', 'id', 'code'] }],
|
||||||
|
['deduction_group', {
|
||||||
|
label: 'Calculation Group', type: 'select2', 'allow-null': true, 'null-label': 'None', 'remote-source': ['DeductionGroup', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['deduction_exemptions', {
|
||||||
|
label: 'Calculation Exemptions', type: 'select2multi', 'remote-source': ['Deduction', 'id', 'name'], validation: 'none',
|
||||||
|
}],
|
||||||
|
['deduction_allowed', {
|
||||||
|
label: 'Calculations Assigned', type: 'select2multi', 'remote-source': ['Deduction', 'id', 'name'], validation: 'none',
|
||||||
|
}],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeductionAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DeductionAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'deduction_group',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Calculation Group' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
const rangeAmounts = ['rangeAmounts', {
|
||||||
|
label: 'Calculation Process',
|
||||||
|
type: 'datagroup',
|
||||||
|
form: [
|
||||||
|
['lowerCondition', { label: 'Lower Limit Condition', type: 'select', source: [['No Lower Limit', 'No Lower Limit'], ['gt', 'Greater than'], ['gte', 'Greater than or Equal']] }],
|
||||||
|
['lowerLimit', { label: 'Lower Limit', type: 'text', validation: 'float' }],
|
||||||
|
['upperCondition', { label: 'Upper Limit Condition', type: 'select', source: [['No Upper Limit', 'No Upper Limit'], ['lt', 'Less than'], ['lte', 'Less than or Equal']] }],
|
||||||
|
['upperLimit', { label: 'Upper Limit', type: 'text', validation: 'float' }],
|
||||||
|
['amount', { label: 'Value', type: 'text', validation: '' }],
|
||||||
|
],
|
||||||
|
html: '<div id="#_id_#" class="panel panel-default">#_delete_##_edit_#<div class="panel-body">#_renderFunction_#</div></div>',
|
||||||
|
validation: 'none',
|
||||||
|
'custom-validate-function': function (data) {
|
||||||
|
const res = {};
|
||||||
|
res.valid = true;
|
||||||
|
if (data.lowerCondition === 'No Lower Limit') {
|
||||||
|
data.lowerLimit = 0;
|
||||||
|
}
|
||||||
|
if (data.upperCondition === 'No Upper Limit') {
|
||||||
|
data.upperLimit = 0;
|
||||||
|
}
|
||||||
|
res.params = data;
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
render(item) {
|
||||||
|
let output = '';
|
||||||
|
const getSymbol = function (text) {
|
||||||
|
const map = {};
|
||||||
|
map.gt = '>';
|
||||||
|
map.gte = '>=';
|
||||||
|
map.lt = '<';
|
||||||
|
map.lte = '<=';
|
||||||
|
|
||||||
|
return map[text];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (item.lowerCondition !== 'No Lower Limit') {
|
||||||
|
output += `${item.lowerLimit} ${getSymbol(item.lowerCondition)} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.upperCondition !== 'No Upper Limit') {
|
||||||
|
output += ' and ';
|
||||||
|
output += `${getSymbol(item.upperCondition)} ${item.upperLimit} `;
|
||||||
|
}
|
||||||
|
if (output === '') {
|
||||||
|
return `Deduction is ${item.amount} for all ranges`;
|
||||||
|
}
|
||||||
|
return `If salary component ${output} deduction is ${item.amount}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['componentType', {
|
||||||
|
label: 'Salary Component Type', type: 'select2multi', 'allow-null': true, 'remote-source': ['SalaryComponentType', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['component', {
|
||||||
|
label: 'Salary Component', type: 'select2multi', 'allow-null': true, 'remote-source': ['SalaryComponent', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['payrollColumn', {
|
||||||
|
label: 'Payroll Report Column', type: 'select2', 'allow-null': true, 'remote-source': ['PayrollColumn', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
rangeAmounts,
|
||||||
|
['deduction_group', {
|
||||||
|
label: 'Calculation Group', type: 'select2', 'allow-null': true, 'null-label': 'None', 'remote-source': ['DeductionGroup', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeductionGroupAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DeductionGroupAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['description', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PayslipTemplateAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PayslipTemplateAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
const payslipFields = ['data', {
|
||||||
|
label: 'Payslip Fields',
|
||||||
|
type: 'datagroup',
|
||||||
|
form: [
|
||||||
|
['type', {
|
||||||
|
label: 'Type', type: 'select', sort: 'none', source: [['Payroll Column', 'Payroll Column'], ['Text', 'Text'], ['Company Name', 'Company Name'], ['Company Logo', 'Company Logo'], ['Separators', 'Separators']],
|
||||||
|
}],
|
||||||
|
['payrollColumn', {
|
||||||
|
label: 'Payroll Column', type: 'select2', sort: 'none', 'allow-null': true, 'null-label': 'None', 'remote-source': ['PayrollColumn', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
|
||||||
|
['label', { label: 'Label', type: 'text', validation: 'none' }],
|
||||||
|
['text', { label: 'Text', type: 'textarea', validation: 'none' }],
|
||||||
|
['status', {
|
||||||
|
label: 'Status', type: 'select', sort: 'none', source: [['Show', 'Show'], ['Hide', 'Hide']],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
|
||||||
|
// "html":'<div id="#_id_#" class="panel panel-default">#_delete_##_edit_#<div class="panel-body"><table class="table table-striped"><tr><td>Type</td><td>#_type_#</td></tr><tr><td>Label</td><td>#_label_#</td></tr><tr><td>Text</td><td>#_text_#</td></tr><tr><td>Font Size</td><td>#_fontSize_#</td></tr><tr><td>Font Style</td><td>#_fontStyle_#</td></tr><tr><td>Font Color</td><td>#_fontColor_#</td></tr><tr><td>Status</td><td>#_status_#</td></tr></table> </div></div>',
|
||||||
|
html: '<div id="#_id_#" class="panel panel-default">#_delete_##_edit_#<div class="panel-body">#_type_# #_label_# <br/> #_text_#</div></div>',
|
||||||
|
validation: 'none',
|
||||||
|
'custom-validate-function': function (data) {
|
||||||
|
const res = {};
|
||||||
|
res.valid = true;
|
||||||
|
if (data.type === 'Payroll Column') {
|
||||||
|
if (data.payrollColumn === 'NULL') {
|
||||||
|
res.valid = false;
|
||||||
|
res.message = 'Please select payroll column';
|
||||||
|
} else {
|
||||||
|
data.payrollColumn = 'NULL';
|
||||||
|
}
|
||||||
|
} else if (data.type === 'Text') {
|
||||||
|
if (data.text === '') {
|
||||||
|
res.valid = false;
|
||||||
|
res.message = 'Text can not be empty';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.params = data;
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
payslipFields,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
PaydayAdapter,
|
||||||
|
PayrollAdapter,
|
||||||
|
PayrollDataAdapter,
|
||||||
|
PayrollColumnAdapter,
|
||||||
|
PayrollColumnTemplateAdapter,
|
||||||
|
PayrollEmployeeAdapter,
|
||||||
|
DeductionAdapter,
|
||||||
|
DeductionGroupAdapter,
|
||||||
|
PayslipTemplateAdapter,
|
||||||
|
};
|
||||||
3
web/admin/src/permissions/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { PermissionAdapter } from './lib';
|
||||||
|
|
||||||
|
window.PermissionAdapter = PermissionAdapter;
|
||||||
73
web/admin/src/permissions/lib.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PermissionAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PermissionAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'user_level',
|
||||||
|
'module_id',
|
||||||
|
'permission',
|
||||||
|
'value',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'User Level' },
|
||||||
|
{ sTitle: 'Module' },
|
||||||
|
{ sTitle: 'Permission' },
|
||||||
|
{ sTitle: 'Value' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['user_level', { label: 'User Level', type: 'placeholder', validation: 'none' }],
|
||||||
|
['module_id', { label: 'Module', type: 'placeholder', 'remote-source': ['Module', 'id', 'menu+name'] }],
|
||||||
|
['permission', { label: 'Permission', type: 'placeholder', validation: 'none' }],
|
||||||
|
['value', { label: 'Value', type: 'text', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['module_id', {
|
||||||
|
label: 'Module', type: 'select2', 'allow-null': true, 'null-label': 'All Modules', 'remote-source': ['Module', 'id', 'menu+name'],
|
||||||
|
}],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
let html = '<div style="width:80px;"><img class="tableActionButton" src="_BASE_images/edit.png" style="cursor:pointer;" rel="tooltip" title="Edit" onclick="modJs.edit(_id_);return false;"></img></div>';
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getMetaFieldForRendering(fieldName) {
|
||||||
|
if (fieldName === 'value') {
|
||||||
|
return 'meta';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fillForm(object) {
|
||||||
|
super.fillForm(object);
|
||||||
|
$('#helptext').html(object.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { PermissionAdapter };
|
||||||
9
web/admin/src/projects/index.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import {
|
||||||
|
ClientAdapter,
|
||||||
|
ProjectAdapter,
|
||||||
|
EmployeeProjectAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.ClientAdapter = ClientAdapter;
|
||||||
|
window.ProjectAdapter = ProjectAdapter;
|
||||||
|
window.EmployeeProjectAdapter = EmployeeProjectAdapter;
|
||||||
163
web/admin/src/projects/lib.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClientAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ClientAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'details',
|
||||||
|
'address',
|
||||||
|
'contact_number',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
{ sTitle: 'Address' },
|
||||||
|
{ sTitle: 'Contact Number' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
if (this.showSave) {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text' }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
['address', { label: 'Address', type: 'textarea', validation: 'none' }],
|
||||||
|
['contact_number', { label: 'Contact Number', type: 'text', validation: 'none' }],
|
||||||
|
['contact_email', { label: 'Contact Email', type: 'text', validation: 'none' }],
|
||||||
|
['company_url', { label: 'Company Url', type: 'text', validation: 'none' }],
|
||||||
|
['status', { label: 'Status', type: 'select', source: [['Active', 'Active'], ['Inactive', 'Inactive']] }],
|
||||||
|
['first_contact_date', { label: 'First Contact Date', type: 'date', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'placeholder' }],
|
||||||
|
['details', { label: 'Details', type: 'placeholder', validation: 'none' }],
|
||||||
|
['address', { label: 'Address', type: 'placeholder', validation: 'none' }],
|
||||||
|
['contact_number', { label: 'Contact Number', type: 'placeholder', validation: 'none' }],
|
||||||
|
['contact_email', { label: 'Contact Email', type: 'placeholder', validation: 'none' }],
|
||||||
|
['company_url', { label: 'Company Url', type: 'placeholder', validation: 'none' }],
|
||||||
|
['status', { label: 'Status', type: 'placeholder', source: [['Active', 'Active'], ['Inactive', 'Inactive']] }],
|
||||||
|
['first_contact_date', { label: 'First Contact Date', type: 'placeholder', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHelpLink() {
|
||||||
|
return 'http://blog.icehrm.com/docs/projects/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ProjectAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'client',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Client' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
if (this.showSave) {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text' }],
|
||||||
|
['client', {
|
||||||
|
label: 'Client', type: 'select2', 'allow-null': true, 'remote-source': ['Client', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
['status', { label: 'Status', type: 'select', source: [['Active', 'Active'], ['On Hold', 'On Hold'], ['Completed', 'Completed'], ['Dropped', 'Dropped']] }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'placeholder' }],
|
||||||
|
['client', {
|
||||||
|
label: 'Client', type: 'placeholder', 'allow-null': true, 'remote-source': ['Client', 'id', 'name'],
|
||||||
|
}],
|
||||||
|
['details', { label: 'Details', type: 'placeholder', validation: 'none' }],
|
||||||
|
['status', { label: 'Status', type: 'select', source: [['Active', 'Active'], ['On Hold', 'On Hold'], ['Completed', 'Completed'], ['Dropped', 'Dropped']] }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHelpLink() {
|
||||||
|
return 'http://blog.icehrm.com/docs/projects/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EmployeeProjectAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeProjectAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'project',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Project' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
['project', { label: 'Project', type: 'select2', 'remote-source': ['Project', 'id', 'name'] }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHelpLink() {
|
||||||
|
return 'http://blog.icehrm.com/docs/projects/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ClientAdapter,
|
||||||
|
ProjectAdapter,
|
||||||
|
EmployeeProjectAdapter,
|
||||||
|
};
|
||||||
11
web/admin/src/qualifications/index.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
SkillAdapter,
|
||||||
|
EducationAdapter,
|
||||||
|
CertificationAdapter,
|
||||||
|
LanguageAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.SkillAdapter = SkillAdapter;
|
||||||
|
window.EducationAdapter = EducationAdapter;
|
||||||
|
window.CertificationAdapter = CertificationAdapter;
|
||||||
|
window.LanguageAdapter = LanguageAdapter;
|
||||||
140
web/admin/src/qualifications/lib.js
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SkillAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SkillAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Description' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text' }],
|
||||||
|
['description', { label: 'Description', type: 'textarea', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHelpLink() {
|
||||||
|
return 'http://blog.icehrm.com/docs/qualifications/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EducationAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EducationAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Description' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text' }],
|
||||||
|
['description', { label: 'Description', type: 'textarea', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CertificationAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CertificationAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Description' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text' }],
|
||||||
|
['description', { label: 'Description', type: 'textarea', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LanguageAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LanguageAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Description' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text' }],
|
||||||
|
['description', { label: 'Description', type: 'textarea', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SkillAdapter,
|
||||||
|
EducationAdapter,
|
||||||
|
CertificationAdapter,
|
||||||
|
LanguageAdapter,
|
||||||
|
};
|
||||||
4
web/admin/src/reports/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { ReportAdapter, ReportGenAdapter } from './lib';
|
||||||
|
|
||||||
|
window.ReportAdapter = ReportAdapter;
|
||||||
|
window.ReportGenAdapter = ReportGenAdapter;
|
||||||
372
web/admin/src/reports/lib.js
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
/* global SignaturePad, modJs */
|
||||||
|
/* eslint-disable no-underscore-dangle */
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReportAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class ReportAdapter extends AdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this._construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
_construct() {
|
||||||
|
this._formFileds = [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'label', validation: '' }],
|
||||||
|
['parameters', { label: 'Parameters', type: 'fieldset', validation: 'none' }],
|
||||||
|
];
|
||||||
|
this.remoteFieldsExists = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_initLocalFormFields() {
|
||||||
|
this._formFileds = [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'label', validation: '' }],
|
||||||
|
['parameters', { label: 'Parameters', type: 'fieldset', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setRemoteFieldExists(val) {
|
||||||
|
this.remoteFieldsExists = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'icon',
|
||||||
|
'name',
|
||||||
|
'details',
|
||||||
|
'parameters',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: '', bSortable: false, sWidth: '22px' },
|
||||||
|
{ sTitle: 'Name', sWidth: '30%' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
{ sTitle: 'Parameters', bVisible: false },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return this._formFileds;
|
||||||
|
}
|
||||||
|
|
||||||
|
processFormFieldsWithObject(object) {
|
||||||
|
const that = this;
|
||||||
|
this._initLocalFormFields();
|
||||||
|
const len = this._formFileds.length;
|
||||||
|
const fieldIDsToDelete = [];
|
||||||
|
const fieldsToDelete = [];
|
||||||
|
this.remoteFieldsExists = false;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
if (this._formFileds[i][1].type === 'fieldset') {
|
||||||
|
const newFields = JSON.parse(object[this._formFileds[i][0]]);
|
||||||
|
fieldsToDelete.push(this._formFileds[i][0]);
|
||||||
|
newFields.forEach((entry) => {
|
||||||
|
that._formFileds.push(entry);
|
||||||
|
if (entry[1]['remote-source'] !== undefined && entry[1]['remote-source'] != null) {
|
||||||
|
that.remoteFieldsExists = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempArray = [];
|
||||||
|
that._formFileds.forEach((entry) => {
|
||||||
|
if (jQuery.inArray(entry[0], fieldsToDelete) < 0) {
|
||||||
|
tempArray.push(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
that._formFileds = tempArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
renderForm(object) {
|
||||||
|
const that = this;
|
||||||
|
this.processFormFieldsWithObject(object);
|
||||||
|
if (this.remoteFieldsExists) {
|
||||||
|
const cb = function () {
|
||||||
|
that.renderFormNew(object);
|
||||||
|
};
|
||||||
|
this.initFieldMasterData(cb);
|
||||||
|
} else {
|
||||||
|
this.initFieldMasterData();
|
||||||
|
that.renderFormNew(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentReport = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFormNew(object) {
|
||||||
|
const that = this;
|
||||||
|
const signatureIds = [];
|
||||||
|
if (object == null || object === undefined) {
|
||||||
|
this.currentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.preRenderForm(object);
|
||||||
|
|
||||||
|
let formHtml = this.templates.formTemplate;
|
||||||
|
let html = '';
|
||||||
|
const fields = this.getFormFields();
|
||||||
|
|
||||||
|
for (let i = 0; i < fields.length; i++) {
|
||||||
|
const metaField = this.getMetaFieldForRendering(fields[i][0]);
|
||||||
|
if (metaField === '' || metaField === undefined) {
|
||||||
|
html += this.renderFormField(fields[i]);
|
||||||
|
} else {
|
||||||
|
const metaVal = object[metaField];
|
||||||
|
if (metaVal !== '' && metaVal != null && metaVal !== undefined && metaVal.trim() !== '') {
|
||||||
|
html += this.renderFormField(JSON.parse(metaVal));
|
||||||
|
} else {
|
||||||
|
html += this.renderFormField(fields[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formHtml = formHtml.replace(/_id_/g, `${this.getTableName()}_submit`);
|
||||||
|
formHtml = formHtml.replace(/_fields_/g, html);
|
||||||
|
|
||||||
|
|
||||||
|
let $tempDomObj;
|
||||||
|
const randomFormId = this.generateRandom(14);
|
||||||
|
if (!this.showFormOnPopup) {
|
||||||
|
$tempDomObj = $(`#${this.getTableName()}Form`);
|
||||||
|
} else {
|
||||||
|
$tempDomObj = $('<div class="reviewBlock popupForm" data-content="Form"></div>');
|
||||||
|
$tempDomObj.attr('id', randomFormId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tempDomObj.html(formHtml);
|
||||||
|
|
||||||
|
|
||||||
|
$tempDomObj.find('.datefield').datepicker({ viewMode: 2 });
|
||||||
|
$tempDomObj.find('.timefield').datetimepicker({
|
||||||
|
language: 'en',
|
||||||
|
pickDate: false,
|
||||||
|
});
|
||||||
|
$tempDomObj.find('.datetimefield').datetimepicker({
|
||||||
|
language: 'en',
|
||||||
|
});
|
||||||
|
|
||||||
|
$tempDomObj.find('.colorpick').colorpicker();
|
||||||
|
|
||||||
|
// $tempDomObj.find('.select2Field').select2();
|
||||||
|
$tempDomObj.find('.select2Field').each(function () {
|
||||||
|
$(this).select2().select2('val', $(this).find('option:eq(0)').val());
|
||||||
|
});
|
||||||
|
|
||||||
|
$tempDomObj.find('.select2Multi').each(function () {
|
||||||
|
$(this).select2().on('change', function (e) {
|
||||||
|
const parentRow = $(this).parents('.row');
|
||||||
|
const height = parentRow.find('.select2-choices').height();
|
||||||
|
parentRow.height(parseInt(height, 10));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$tempDomObj.find('.signatureField').each(function () {
|
||||||
|
// $(this).data('signaturePad',new SignaturePad($(this)));
|
||||||
|
signatureIds.push($(this).attr('id'));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < fields.length; i++) {
|
||||||
|
if (fields[i][1].type === 'datagroup') {
|
||||||
|
$tempDomObj.find(`#${fields[i][0]}`).data('field', fields[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showSave === false) {
|
||||||
|
$tempDomObj.find('.saveBtn').remove();
|
||||||
|
} else {
|
||||||
|
$tempDomObj.find('.saveBtn').off();
|
||||||
|
$tempDomObj.find('.saveBtn').data('modJs', this);
|
||||||
|
$tempDomObj.find('.saveBtn').on('click', function () {
|
||||||
|
if ($(this).data('modJs').saveSuccessItemCallback != null && $(this).data('modJs').saveSuccessItemCallback !== undefined) {
|
||||||
|
$(this).data('modJs').save($(this).data('modJs').retriveItemsAfterSave(), $(this).data('modJs').saveSuccessItemCallback);
|
||||||
|
} else {
|
||||||
|
$(this).data('modJs').save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showCancel === false) {
|
||||||
|
$tempDomObj.find('.cancelBtn').remove();
|
||||||
|
} else {
|
||||||
|
$tempDomObj.find('.cancelBtn').off();
|
||||||
|
$tempDomObj.find('.cancelBtn').data('modJs', this);
|
||||||
|
$tempDomObj.find('.cancelBtn').on('click', function () {
|
||||||
|
$(this).data('modJs').cancel();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.showFormOnPopup) {
|
||||||
|
$(`#${this.getTableName()}Form`).show();
|
||||||
|
$(`#${this.getTableName()}`).hide();
|
||||||
|
|
||||||
|
for (let i = 0; i < signatureIds.length; i++) {
|
||||||
|
$(`#${signatureIds[i]}`)
|
||||||
|
.data('signaturePad',
|
||||||
|
new SignaturePad(document.getElementById(signatureIds[i])));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object !== undefined && object != null) {
|
||||||
|
this.fillForm(object);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// var tHtml = $tempDomObj.wrap('<div>').parent().html();
|
||||||
|
// this.showMessage("Edit",tHtml,null,null,true);
|
||||||
|
this.showMessage('Edit', '', null, null, true);
|
||||||
|
|
||||||
|
$('#plainMessageModel .modal-body').html('');
|
||||||
|
$('#plainMessageModel .modal-body').append($tempDomObj);
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < signatureIds.length; i++) {
|
||||||
|
$(`#${signatureIds[i]}`)
|
||||||
|
.data('signaturePad',
|
||||||
|
new SignaturePad(document.getElementById(signatureIds[i])));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object !== undefined && object != null) {
|
||||||
|
this.fillForm(object, `#${randomFormId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.postRenderForm(object, $tempDomObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
let html = '<div style="width:80px;"><img class="tableActionButton" src="_BASE_images/download.png" style="cursor:pointer;" rel="tooltip" title="Download" onclick="modJs.edit(_id_);return false;"></img></div>';
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSuccessCallBack(callBackData, serverData) {
|
||||||
|
const fileName = serverData[0];
|
||||||
|
let link;
|
||||||
|
|
||||||
|
if (fileName.indexOf('https:') === 0) {
|
||||||
|
link = `<a href="${fileName}" target="_blank" style="font-size:14px;font-weight:bold;">Download Report <img src="_BASE_images/download.png"></img> </a>`;
|
||||||
|
} else {
|
||||||
|
link = `<a href="${modJs.getCustomActionUrl('download', { file: fileName })}" target="_blank" style="font-size:14px;font-weight:bold;">Download Report <img src="_BASE_images/download.png"></img> </a>`;
|
||||||
|
}
|
||||||
|
link = link.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
|
||||||
|
if (this.currentReport.output === 'PDF' || this.currentReport.output === 'JSON') {
|
||||||
|
this.showMessage('Download Report', link);
|
||||||
|
} else {
|
||||||
|
if (serverData[1].length === 0) {
|
||||||
|
this.showMessage('Empty Report', 'There were no data for selected filters');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const tableHtml = `${link}<br/><br/><div class="box-body table-responsive" style="overflow-x:scroll;padding: 5px;border: solid 1px #DDD;"><table id="tempReportTable" cellpadding="0" cellspacing="0" border="0" class="table table-bordered table-striped"></table></div>`;
|
||||||
|
|
||||||
|
// Delete existing temp report table
|
||||||
|
$('#tempReportTable').remove();
|
||||||
|
|
||||||
|
// this.showMessage("Report",tableHtml);
|
||||||
|
|
||||||
|
$(`#${this.table}`).html(tableHtml);
|
||||||
|
$(`#${this.table}`).show();
|
||||||
|
$(`#${this.table}Form`).hide();
|
||||||
|
|
||||||
|
// Prepare headers
|
||||||
|
const headers = [];
|
||||||
|
for (const index in serverData[1]) {
|
||||||
|
headers.push({ sTitle: serverData[1][index] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = serverData[2];
|
||||||
|
|
||||||
|
|
||||||
|
const dataTableParams = {
|
||||||
|
oLanguage: {
|
||||||
|
sLengthMenu: '_MENU_ records per page',
|
||||||
|
},
|
||||||
|
aaData: data,
|
||||||
|
aoColumns: headers,
|
||||||
|
bSort: false,
|
||||||
|
iDisplayLength: 15,
|
||||||
|
iDisplayStart: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#tempReportTable').dataTable(dataTableParams);
|
||||||
|
|
||||||
|
$('.dataTables_paginate ul').addClass('pagination');
|
||||||
|
$('.dataTables_length').hide();
|
||||||
|
$('.dataTables_filter input').addClass('form-control');
|
||||||
|
$('.dataTables_filter input').attr('placeholder', 'Search');
|
||||||
|
$('.dataTables_filter label').contents().filter(function () {
|
||||||
|
return (this.nodeType === 3);
|
||||||
|
}).remove();
|
||||||
|
$('.tableActionButton').tooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fillForm(object) {
|
||||||
|
const fields = this.getFormFields();
|
||||||
|
for (let i = 0; i < fields.length; i++) {
|
||||||
|
if (fields[i][1].type === 'label') {
|
||||||
|
$(`#${this.getTableName()}Form #${fields[i][0]}`).html(object[fields[i][0]]);
|
||||||
|
} else {
|
||||||
|
$(`#${this.getTableName()}Form #${fields[i][0]}`).val(object[fields[i][0]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ReportGenAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
let html = '<div style="width:80px;"><img class="tableActionButton" src="_BASE_images/download.png" style="cursor:pointer;" rel="tooltip" title="Download" onclick="download(_name_);return false;"></img></div>';
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_name_/g, data[1]);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { ReportAdapter, ReportGenAdapter };
|
||||||
9
web/admin/src/salary/index.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import {
|
||||||
|
SalaryComponentTypeAdapter,
|
||||||
|
SalaryComponentAdapter,
|
||||||
|
EmployeeSalaryAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.SalaryComponentTypeAdapter = SalaryComponentTypeAdapter;
|
||||||
|
window.SalaryComponentAdapter = SalaryComponentAdapter;
|
||||||
|
window.EmployeeSalaryAdapter = EmployeeSalaryAdapter;
|
||||||
120
web/admin/src/salary/lib.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SalaryComponentTypeAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SalaryComponentTypeAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'code',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Code' },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['code', { label: 'Code', type: 'text', validation: '' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SalaryComponentAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SalaryComponentAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'componentType',
|
||||||
|
'details',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Salary Component Type' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['componentType', { label: 'Salary Component Type', type: 'select2', 'remote-source': ['SalaryComponentType', 'id', 'name'] }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EmployeeSalaryAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EmployeeSalaryAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'component',
|
||||||
|
'amount',
|
||||||
|
'details',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Salary Component' },
|
||||||
|
{ sTitle: 'Amount' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
['component', { label: 'Salary Component', type: 'select2', 'remote-source': ['SalaryComponent', 'id', 'name'] }],
|
||||||
|
['amount', { label: 'Amount', type: 'text', validation: 'float' }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SalaryComponentTypeAdapter,
|
||||||
|
SalaryComponentAdapter,
|
||||||
|
EmployeeSalaryAdapter,
|
||||||
|
};
|
||||||
3
web/admin/src/settings/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { SettingAdapter } from './lib';
|
||||||
|
|
||||||
|
window.SettingAdapter = SettingAdapter;
|
||||||
109
web/admin/src/settings/lib.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SettingAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SettingAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'value',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Value' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['value', { label: 'Value', type: 'text', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
let html = '<div style="width:80px;"><img class="tableActionButton" src="_BASE_images/edit.png" style="cursor:pointer;" rel="tooltip" title="Edit" onclick="modJs.edit(_id_);return false;"></img></div>';
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getMetaFieldForRendering(fieldName) {
|
||||||
|
if (fieldName === 'value') {
|
||||||
|
return 'meta';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(id) {
|
||||||
|
this.loadRemoteDataForSettings();
|
||||||
|
super.edit(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fillForm(object) {
|
||||||
|
const metaField = this.getMetaFieldForRendering('value');
|
||||||
|
const metaVal = object[metaField];
|
||||||
|
let formFields = null;
|
||||||
|
|
||||||
|
if (metaVal !== '' && metaVal !== undefined) {
|
||||||
|
formFields = [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
JSON.parse(metaVal),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
super.fillForm(object, null, formFields);
|
||||||
|
$('#helptext').html(object.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadRemoteDataForSettings() {
|
||||||
|
const fields = [];
|
||||||
|
let field = null;
|
||||||
|
fields.push(['country', { label: 'Country', type: 'select2multi', 'remote-source': ['Country', 'id', 'name'] }]);
|
||||||
|
fields.push(['countryCompany', { label: 'Country', type: 'select2', 'remote-source': ['Country', 'code', 'name'] }]);
|
||||||
|
fields.push(['currency', { label: 'Currency', type: 'select2multi', 'remote-source': ['CurrencyType', 'id', 'code+name'] }]);
|
||||||
|
fields.push(['nationality', { label: 'Nationality', type: 'select2multi', 'remote-source': ['Nationality', 'id', 'name'] }]);
|
||||||
|
fields.push(['supportedLanguage', {
|
||||||
|
label: 'Value', type: 'select2', 'allow-null': false, 'remote-source': ['SupportedLanguage', 'name', 'description'],
|
||||||
|
}]);
|
||||||
|
|
||||||
|
for (const index in fields) {
|
||||||
|
field = fields[index];
|
||||||
|
if (field[1]['remote-source'] !== undefined && field[1]['remote-source'] !== null) {
|
||||||
|
const key = `${field[1]['remote-source'][0]}_${field[1]['remote-source'][1]}_${field[1]['remote-source'][2]}`;
|
||||||
|
this.fieldMasterDataKeys[key] = false;
|
||||||
|
this.sourceMapping[field[0]] = field[1]['remote-source'];
|
||||||
|
|
||||||
|
const callBackData = {};
|
||||||
|
callBackData.callBack = 'initFieldMasterDataResponse';
|
||||||
|
callBackData.callBackData = [key];
|
||||||
|
|
||||||
|
this.getFieldValues(field[1]['remote-source'], callBackData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getHelpLink() {
|
||||||
|
return 'http://blog.icehrm.com/docs/settings/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { SettingAdapter };
|
||||||
11
web/admin/src/travel/index.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
ImmigrationDocumentAdapter,
|
||||||
|
EmployeeImmigrationAdapter,
|
||||||
|
EmployeeTravelRecordAdminAdapter,
|
||||||
|
CustomFieldAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.ImmigrationDocumentAdapter = ImmigrationDocumentAdapter;
|
||||||
|
window.EmployeeImmigrationAdapter = EmployeeImmigrationAdapter;
|
||||||
|
window.EmployeeTravelRecordAdminAdapter = EmployeeTravelRecordAdminAdapter;
|
||||||
|
window.CustomFieldAdapter = CustomFieldAdapter;
|
||||||
195
web/admin/src/travel/lib.js
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import CustomFieldAdapter from '../../../api/CustomFieldAdapter';
|
||||||
|
import ApproveAdminAdapter from '../../../api/ApproveAdminAdapter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImmigrationDocumentAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ImmigrationDocumentAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'details',
|
||||||
|
'required',
|
||||||
|
'alert_on_missing',
|
||||||
|
'alert_before_expiry',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Details' },
|
||||||
|
{ sTitle: 'Compulsory' },
|
||||||
|
{ sTitle: 'Alert If Not Found' },
|
||||||
|
{ sTitle: 'Alert Before Expiry' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
const fields = [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
['required', { label: 'Compulsory', type: 'select', source: [['No', 'No'], ['Yes', 'Yes']] }],
|
||||||
|
['alert_on_missing', { label: 'Alert If Not Found', type: 'select', source: [['No', 'No'], ['Yes', 'Yes']] }],
|
||||||
|
['alert_before_expiry', { label: 'Alert Before Expiry', type: 'select', source: [['No', 'No'], ['Yes', 'Yes']] }],
|
||||||
|
['alert_before_day_number', { label: 'Days for Expiry Alert', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.customFields.length; i++) {
|
||||||
|
fields.push(this.customFields[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmployeeImmigrationAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeImmigrationAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'document',
|
||||||
|
'documentname',
|
||||||
|
'valid_until',
|
||||||
|
'status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Document' },
|
||||||
|
{ sTitle: 'Document Id' },
|
||||||
|
{ sTitle: 'Valid Until' },
|
||||||
|
{ sTitle: 'Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
['document', { label: 'Document', type: 'select2', 'remote-source': ['ImmigrationDocument', 'id', 'name'] }],
|
||||||
|
['documentname', { label: 'Document Id', type: 'text', validation: '' }],
|
||||||
|
['valid_until', { label: 'Valid Until', type: 'date', validation: 'none' }],
|
||||||
|
['status', { label: 'Status', type: 'select', source: [['Active', 'Active'], ['Inactive', 'Inactive'], ['Draft', 'Draft']] }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
['attachment1', { label: 'Attachment 1', type: 'fileupload', validation: 'none' }],
|
||||||
|
['attachment2', { label: 'Attachment 2', type: 'fileupload', validation: 'none' }],
|
||||||
|
['attachment3', { label: 'Attachment 3', type: 'fileupload', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['employee', { label: 'Employee', type: 'select2', 'remote-source': ['Employee', 'id', 'first_name+last_name'] }],
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmployeeTravelRecordAdminAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeTravelRecordAdminAdapter extends ApproveAdminAdapter {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.itemName = 'TravelRequest';
|
||||||
|
this.itemNameLower = 'travelrequest';
|
||||||
|
this.modulePathName = 'travel';
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'employee',
|
||||||
|
'type',
|
||||||
|
'purpose',
|
||||||
|
'travel_from',
|
||||||
|
'travel_to',
|
||||||
|
'travel_date',
|
||||||
|
'status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'Travel Type' },
|
||||||
|
{ sTitle: 'Purpose' },
|
||||||
|
{ sTitle: 'From' },
|
||||||
|
{ sTitle: 'To' },
|
||||||
|
{ sTitle: 'Travel Date' },
|
||||||
|
{ sTitle: 'Status' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return this.addCustomFields([
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['employee', {
|
||||||
|
label: 'Employee',
|
||||||
|
type: 'select2',
|
||||||
|
sort: 'none',
|
||||||
|
'allow-null': false,
|
||||||
|
'remote-source': ['Employee', 'id', 'first_name+last_name', 'getActiveSubordinateEmployees'],
|
||||||
|
}],
|
||||||
|
['type', {
|
||||||
|
label: 'Means of Transportation',
|
||||||
|
type: 'select',
|
||||||
|
source: [
|
||||||
|
['Plane', 'Plane'],
|
||||||
|
['Rail', 'Rail'],
|
||||||
|
['Taxi', 'Taxi'],
|
||||||
|
['Own Vehicle', 'Own Vehicle'],
|
||||||
|
['Rented Vehicle', 'Rented Vehicle'],
|
||||||
|
['Other', 'Other'],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['purpose', { label: 'Purpose of Travel', type: 'textarea', validation: '' }],
|
||||||
|
['travel_from', { label: 'Travel From', type: 'text', validation: '' }],
|
||||||
|
['travel_to', { label: 'Travel To', type: 'text', validation: '' }],
|
||||||
|
['travel_date', { label: 'Travel Date', type: 'datetime', validation: '' }],
|
||||||
|
['return_date', { label: 'Return Date', type: 'datetime', validation: '' }],
|
||||||
|
['details', { label: 'Notes', type: 'textarea', validation: 'none' }],
|
||||||
|
['currency', {
|
||||||
|
label: 'Currency', type: 'select2', 'allow-null': false, 'remote-source': ['CurrencyType', 'id', 'code'],
|
||||||
|
}],
|
||||||
|
['funding', {
|
||||||
|
label: 'Total Funding Proposed', type: 'text', validation: 'float', default: '0.00', mask: '9{0,10}.99',
|
||||||
|
}],
|
||||||
|
['attachment1', { label: 'Attachment', type: 'fileupload', validation: 'none' }],
|
||||||
|
['attachment2', { label: 'Attachment', type: 'fileupload', validation: 'none' }],
|
||||||
|
['attachment3', { label: 'Attachment', type: 'fileupload', validation: 'none' }],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ImmigrationDocumentAdapter,
|
||||||
|
EmployeeImmigrationAdapter,
|
||||||
|
EmployeeTravelRecordAdminAdapter,
|
||||||
|
CustomFieldAdapter,
|
||||||
|
};
|
||||||
7
web/admin/src/users/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import {
|
||||||
|
UserAdapter,
|
||||||
|
UserRoleAdapter,
|
||||||
|
} from './lib';
|
||||||
|
|
||||||
|
window.UserAdapter = UserAdapter;
|
||||||
|
window.UserRoleAdapter = UserRoleAdapter;
|
||||||
201
web/admin/src/users/lib.js
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import FormValidation from '../../../api/FormValidation';
|
||||||
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
|
||||||
|
|
||||||
|
class UserAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'username',
|
||||||
|
'email',
|
||||||
|
'employee',
|
||||||
|
'user_level',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID' },
|
||||||
|
{ sTitle: 'User Name' },
|
||||||
|
{ sTitle: 'Authentication Email' },
|
||||||
|
{ sTitle: 'Employee' },
|
||||||
|
{ sTitle: 'User Level' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden', validation: '' }],
|
||||||
|
['username', { label: 'User Name', type: 'text', validation: 'username' }],
|
||||||
|
['email', { label: 'Email', type: 'text', validation: 'email' }],
|
||||||
|
['employee', {
|
||||||
|
label: 'Employee', type: 'select2', 'allow-null': true, 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
||||||
|
}],
|
||||||
|
['user_level', { label: 'User Level', type: 'select', source: [['Admin', 'Admin'], ['Manager', 'Manager'], ['Employee', 'Employee'], ['Other', 'Other']] }],
|
||||||
|
['user_roles', { label: 'User Roles', type: 'select2multi', 'remote-source': ['UserRole', 'id', 'name'] }],
|
||||||
|
['lang', {
|
||||||
|
label: 'Language', type: 'select2', 'allow-null': true, 'remote-source': ['SupportedLanguage', 'id', 'description'],
|
||||||
|
}],
|
||||||
|
['default_module', {
|
||||||
|
label: 'Default Module', type: 'select2', 'null-label': 'No Default Module', 'allow-null': true, 'remote-source': ['Module', 'id', 'menu+label'],
|
||||||
|
}],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
postRenderForm(object, $tempDomObj) {
|
||||||
|
if (object == null || object === undefined) {
|
||||||
|
$tempDomObj.find('#changePasswordBtn').remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changePassword() {
|
||||||
|
$('#adminUsersModel').modal('show');
|
||||||
|
$('#adminUsersChangePwd #newpwd').val('');
|
||||||
|
$('#adminUsersChangePwd #conpwd').val('');
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUserSuccessCallBack(callBackData, serverData) {
|
||||||
|
const user = callBackData[0];
|
||||||
|
if (callBackData[1]) {
|
||||||
|
this.showMessage('Create User', `An email has been sent to ${user.email} with a temporary password to login to IceHrm.`);
|
||||||
|
} else {
|
||||||
|
this.showMessage('Create User', 'User created successfully. But there was a problem sending welcome email.');
|
||||||
|
}
|
||||||
|
this.get([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUserFailCallBack(callBackData, serverData) {
|
||||||
|
this.showMessage('Error', callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
doCustomValidation(params) {
|
||||||
|
let msg = null;
|
||||||
|
if ((params.user_level !== 'Admin' && params.user_level !== 'Other') && params.employee === 'NULL') {
|
||||||
|
msg = 'For this user type, you have to assign an employee when adding or editing the user.<br/>';
|
||||||
|
msg += " You may create a new employee through 'Admin'->'Employees' menu";
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
const validator = new FormValidation(`${this.getTableName()}_submit`, true, { ShowPopup: false, LabelErrorClass: 'error' });
|
||||||
|
if (validator.checkValues()) {
|
||||||
|
const params = validator.getFormParameters();
|
||||||
|
|
||||||
|
const msg = this.doCustomValidation(params);
|
||||||
|
if (msg == null) {
|
||||||
|
const id = $(`#${this.getTableName()}_submit #id`).val();
|
||||||
|
params.csrf = $(`#${this.getTableName()}Form`).data('csrf');
|
||||||
|
if (id != null && id !== undefined && id !== '') {
|
||||||
|
params.id = id;
|
||||||
|
this.add(params, []);
|
||||||
|
} else {
|
||||||
|
const reqJson = JSON.stringify(params);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'saveUserSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'saveUserFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('saveUser', 'admin=users', reqJson, callBackData);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// $("#"+this.getTableName()+'Form .label').html(msg);
|
||||||
|
// $("#"+this.getTableName()+'Form .label').show();
|
||||||
|
this.showMessage('Error Saving User', msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
changePasswordConfirm() {
|
||||||
|
$('#adminUsersChangePwd_error').hide();
|
||||||
|
|
||||||
|
const passwordValidation = function (str) {
|
||||||
|
return str.length > 7;
|
||||||
|
};
|
||||||
|
|
||||||
|
const password = $('#adminUsersChangePwd #newpwd').val();
|
||||||
|
|
||||||
|
if (!passwordValidation(password)) {
|
||||||
|
$('#adminUsersChangePwd_error').html('Password should be longer than 7 characters');
|
||||||
|
$('#adminUsersChangePwd_error').show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conPassword = $('#adminUsersChangePwd #conpwd').val();
|
||||||
|
|
||||||
|
if (conPassword !== password) {
|
||||||
|
$('#adminUsersChangePwd_error').html("Passwords don't match");
|
||||||
|
$('#adminUsersChangePwd_error').show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = { id: this.currentId, pwd: conPassword };
|
||||||
|
const reqJson = JSON.stringify(req);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'changePasswordSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'changePasswordFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('changePassword', 'admin=users', reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeChangePassword() {
|
||||||
|
$('#adminUsersModel').modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
changePasswordSuccessCallBack(callBackData, serverData) {
|
||||||
|
this.closeChangePassword();
|
||||||
|
this.showMessage('Password Change', 'Password changed successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
changePasswordFailCallBack(callBackData, serverData) {
|
||||||
|
this.closeChangePassword();
|
||||||
|
this.showMessage('Error', callBackData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserRoleAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class UserRoleAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
postRenderForm(object, $tempDomObj) {
|
||||||
|
$tempDomObj.find('#changePasswordBtn').remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
UserAdapter,
|
||||||
|
UserRoleAdapter,
|
||||||
|
};
|
||||||
497
web/api-common/Aes.js
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
/* AES implementation in JavaScript (c) Chris Veness 2005-2014 / MIT Licence */
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/* jshint node:true *//* global define */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES (Rijndael cipher) encryption routines,
|
||||||
|
*
|
||||||
|
* Reference implementation of FIPS-197 http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf.
|
||||||
|
*
|
||||||
|
* @namespace
|
||||||
|
*/
|
||||||
|
var Aes = {};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES Cipher function: encrypt 'input' state with Rijndael algorithm [§5.1];
|
||||||
|
* applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage.
|
||||||
|
*
|
||||||
|
* @param {number[]} input - 16-byte (128-bit) input state array.
|
||||||
|
* @param {number[][]} w - Key schedule as 2D byte-array (Nr+1 x Nb bytes).
|
||||||
|
* @returns {number[]} Encrypted output state array.
|
||||||
|
*/
|
||||||
|
Aes.cipher = function (input, w) {
|
||||||
|
const Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
|
||||||
|
const Nr = w.length / Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
|
||||||
|
|
||||||
|
let state = [[], [], [], []]; // initialise 4xNb byte-array 'state' with input [§3.4]
|
||||||
|
for (var i = 0; i < 4 * Nb; i++) state[i % 4][Math.floor(i / 4)] = input[i];
|
||||||
|
|
||||||
|
state = Aes.addRoundKey(state, w, 0, Nb);
|
||||||
|
|
||||||
|
for (let round = 1; round < Nr; round++) {
|
||||||
|
state = Aes.subBytes(state, Nb);
|
||||||
|
state = Aes.shiftRows(state, Nb);
|
||||||
|
state = Aes.mixColumns(state, Nb);
|
||||||
|
state = Aes.addRoundKey(state, w, round, Nb);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = Aes.subBytes(state, Nb);
|
||||||
|
state = Aes.shiftRows(state, Nb);
|
||||||
|
state = Aes.addRoundKey(state, w, Nr, Nb);
|
||||||
|
|
||||||
|
const output = new Array(4 * Nb); // convert state to 1-d array before returning [§3.4]
|
||||||
|
for (var i = 0; i < 4 * Nb; i++) output[i] = state[i % 4][Math.floor(i / 4)];
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform key expansion to generate a key schedule from a cipher key [§5.2].
|
||||||
|
*
|
||||||
|
* @param {number[]} key - Cipher key as 16/24/32-byte array.
|
||||||
|
* @returns {number[][]} Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes).
|
||||||
|
*/
|
||||||
|
Aes.keyExpansion = function (key) {
|
||||||
|
const Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
|
||||||
|
const Nk = key.length / 4; // key length (in words): 4/6/8 for 128/192/256-bit keys
|
||||||
|
const Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys
|
||||||
|
|
||||||
|
const w = new Array(Nb * (Nr + 1));
|
||||||
|
let temp = new Array(4);
|
||||||
|
|
||||||
|
// initialise first Nk words of expanded key with cipher key
|
||||||
|
for (var i = 0; i < Nk; i++) {
|
||||||
|
const r = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]];
|
||||||
|
w[i] = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expand the key into the remainder of the schedule
|
||||||
|
for (var i = Nk; i < (Nb * (Nr + 1)); i++) {
|
||||||
|
w[i] = new Array(4);
|
||||||
|
for (var t = 0; t < 4; t++) temp[t] = w[i - 1][t];
|
||||||
|
// each Nk'th word has extra transformation
|
||||||
|
if (i % Nk == 0) {
|
||||||
|
temp = Aes.subWord(Aes.rotWord(temp));
|
||||||
|
for (var t = 0; t < 4; t++) temp[t] ^= Aes.rCon[i / Nk][t];
|
||||||
|
}
|
||||||
|
// 256-bit key has subWord applied every 4th word
|
||||||
|
else if (Nk > 6 && i % Nk == 4) {
|
||||||
|
temp = Aes.subWord(temp);
|
||||||
|
}
|
||||||
|
// xor w[i] with w[i-1] and w[i-Nk]
|
||||||
|
for (var t = 0; t < 4; t++) w[i][t] = w[i - Nk][t] ^ temp[t];
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply SBox to state S [§5.1.1]
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Aes.subBytes = function (s, Nb) {
|
||||||
|
for (let r = 0; r < 4; r++) {
|
||||||
|
for (let c = 0; c < Nb; c++) s[r][c] = Aes.sBox[s[r][c]];
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift row r of state S left by r bytes [§5.1.2]
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Aes.shiftRows = function (s, Nb) {
|
||||||
|
const t = new Array(4);
|
||||||
|
for (let r = 1; r < 4; r++) {
|
||||||
|
for (var c = 0; c < 4; c++) t[c] = s[r][(c + r) % Nb]; // shift into temp copy
|
||||||
|
for (var c = 0; c < 4; c++) s[r][c] = t[c]; // and copy back
|
||||||
|
} // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
|
||||||
|
return s; // see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combine bytes of each col of state S [§5.1.3]
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Aes.mixColumns = function (s, Nb) {
|
||||||
|
for (let c = 0; c < 4; c++) {
|
||||||
|
const a = new Array(4); // 'a' is a copy of the current column from 's'
|
||||||
|
const b = new Array(4); // 'b' is a•{02} in GF(2^8)
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
a[i] = s[i][c];
|
||||||
|
b[i] = s[i][c] & 0x80 ? s[i][c] << 1 ^ 0x011b : s[i][c] << 1;
|
||||||
|
}
|
||||||
|
// a[n] ^ b[n] is a•{03} in GF(2^8)
|
||||||
|
s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // {02}•a0 + {03}•a1 + a2 + a3
|
||||||
|
s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 • {02}•a1 + {03}•a2 + a3
|
||||||
|
s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + {02}•a2 + {03}•a3
|
||||||
|
s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // {03}•a0 + a1 + a2 + {02}•a3
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Xor Round Key into state S [§5.1.4]
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Aes.addRoundKey = function (state, w, rnd, Nb) {
|
||||||
|
for (let r = 0; r < 4; r++) {
|
||||||
|
for (let c = 0; c < Nb; c++) state[r][c] ^= w[rnd * 4 + c][r];
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply SBox to 4-byte word w
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Aes.subWord = function (w) {
|
||||||
|
for (let i = 0; i < 4; i++) w[i] = Aes.sBox[w[i]];
|
||||||
|
return w;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate 4-byte word w left by one byte
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Aes.rotWord = function (w) {
|
||||||
|
const tmp = w[0];
|
||||||
|
for (let i = 0; i < 3; i++) w[i] = w[i + 1];
|
||||||
|
w[3] = tmp;
|
||||||
|
return w;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1]
|
||||||
|
Aes.sBox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||||
|
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||||
|
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||||
|
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||||
|
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||||
|
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||||
|
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||||
|
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||||
|
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||||
|
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||||
|
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||||
|
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||||
|
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||||
|
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||||
|
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||||
|
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
|
||||||
|
|
||||||
|
|
||||||
|
// rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
|
||||||
|
Aes.rCon = [[0x00, 0x00, 0x00, 0x00],
|
||||||
|
[0x01, 0x00, 0x00, 0x00],
|
||||||
|
[0x02, 0x00, 0x00, 0x00],
|
||||||
|
[0x04, 0x00, 0x00, 0x00],
|
||||||
|
[0x08, 0x00, 0x00, 0x00],
|
||||||
|
[0x10, 0x00, 0x00, 0x00],
|
||||||
|
[0x20, 0x00, 0x00, 0x00],
|
||||||
|
[0x40, 0x00, 0x00, 0x00],
|
||||||
|
[0x80, 0x00, 0x00, 0x00],
|
||||||
|
[0x1b, 0x00, 0x00, 0x00],
|
||||||
|
[0x36, 0x00, 0x00, 0x00]];
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
if (typeof module !== 'undefined' && module.exports) module.exports = Aes; // CommonJs export
|
||||||
|
if (typeof define === 'function' && define.amd) define([], () => Aes); // AMD
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
/* AES Counter-mode implementation in JavaScript (c) Chris Veness 2005-2014 / MIT Licence */
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/* jshint node:true *//* global define, escape, unescape, btoa, atob */
|
||||||
|
'use strict';
|
||||||
|
if (typeof module !== 'undefined' && module.exports) var Aes = require('./aes'); // CommonJS (Node.js)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aes.Ctr: Counter-mode (CTR) wrapper for AES.
|
||||||
|
*
|
||||||
|
* This encrypts a Unicode string to produces a base64 ciphertext using 128/192/256-bit AES,
|
||||||
|
* and the converse to decrypt an encrypted ciphertext.
|
||||||
|
*
|
||||||
|
* See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
||||||
|
*
|
||||||
|
* @augments Aes
|
||||||
|
*/
|
||||||
|
Aes.Ctr = {};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt a text using AES encryption in Counter mode of operation.
|
||||||
|
*
|
||||||
|
* Unicode multi-byte character safe
|
||||||
|
*
|
||||||
|
* @param {string} plaintext - Source text to be encrypted.
|
||||||
|
* @param {string} password - The password to use to generate a key.
|
||||||
|
* @param {number} nBits - Number of bits to be used in the key; 128 / 192 / 256.
|
||||||
|
* @returns {string} Encrypted text.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* var encr = Aes.Ctr.encrypt('big secret', 'pāşšŵōřđ', 256); // encr: 'lwGl66VVwVObKIr6of8HVqJr'
|
||||||
|
*/
|
||||||
|
Aes.Ctr.encrypt = function (plaintext, password, nBits) {
|
||||||
|
const blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
|
||||||
|
if (!(nBits == 128 || nBits == 192 || nBits == 256)) return ''; // standard allows 128/192/256 bit keys
|
||||||
|
plaintext = String(plaintext).utf8Encode();
|
||||||
|
password = String(password).utf8Encode();
|
||||||
|
|
||||||
|
// use AES itself to encrypt password to get cipher key (using plain password as source for key
|
||||||
|
// expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use)
|
||||||
|
const nBytes = nBits / 8; // no bytes in key (16/24/32)
|
||||||
|
const pwBytes = new Array(nBytes);
|
||||||
|
for (var i = 0; i < nBytes; i++) { // use 1st 16/24/32 chars of password for key
|
||||||
|
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
|
||||||
|
}
|
||||||
|
let key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes)); // gives us 16-byte key
|
||||||
|
key = key.concat(key.slice(0, nBytes - 16)); // expand key to 16/24/32 bytes long
|
||||||
|
|
||||||
|
// initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
|
||||||
|
// [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
|
||||||
|
const counterBlock = new Array(blockSize);
|
||||||
|
|
||||||
|
const nonce = (new Date()).getTime(); // timestamp: milliseconds since 1-Jan-1970
|
||||||
|
const nonceMs = nonce % 1000;
|
||||||
|
const nonceSec = Math.floor(nonce / 1000);
|
||||||
|
const nonceRnd = Math.floor(Math.random() * 0xffff);
|
||||||
|
// for debugging: nonce = nonceMs = nonceSec = nonceRnd = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < 2; i++) counterBlock[i] = (nonceMs >>> i * 8) & 0xff;
|
||||||
|
for (var i = 0; i < 2; i++) counterBlock[i + 2] = (nonceRnd >>> i * 8) & 0xff;
|
||||||
|
for (var i = 0; i < 4; i++) counterBlock[i + 4] = (nonceSec >>> i * 8) & 0xff;
|
||||||
|
|
||||||
|
// and convert it to a string to go on the front of the ciphertext
|
||||||
|
let ctrTxt = '';
|
||||||
|
for (var i = 0; i < 8; i++) ctrTxt += String.fromCharCode(counterBlock[i]);
|
||||||
|
|
||||||
|
// generate key schedule - an expansion of the key into distinct Key Rounds for each round
|
||||||
|
const keySchedule = Aes.keyExpansion(key);
|
||||||
|
|
||||||
|
const blockCount = Math.ceil(plaintext.length / blockSize);
|
||||||
|
const ciphertxt = new Array(blockCount); // ciphertext as array of strings
|
||||||
|
|
||||||
|
for (let b = 0; b < blockCount; b++) {
|
||||||
|
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
|
||||||
|
// done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
|
||||||
|
for (var c = 0; c < 4; c++) counterBlock[15 - c] = (b >>> c * 8) & 0xff;
|
||||||
|
for (var c = 0; c < 4; c++) counterBlock[15 - c - 4] = (b / 0x100000000 >>> c * 8);
|
||||||
|
|
||||||
|
const cipherCntr = Aes.cipher(counterBlock, keySchedule); // -- encrypt counter block --
|
||||||
|
|
||||||
|
// block size is reduced on final block
|
||||||
|
const blockLength = b < blockCount - 1 ? blockSize : (plaintext.length - 1) % blockSize + 1;
|
||||||
|
const cipherChar = new Array(blockLength);
|
||||||
|
|
||||||
|
for (var i = 0; i < blockLength; i++) { // -- xor plaintext with ciphered counter char-by-char --
|
||||||
|
cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b * blockSize + i);
|
||||||
|
cipherChar[i] = String.fromCharCode(cipherChar[i]);
|
||||||
|
}
|
||||||
|
ciphertxt[b] = cipherChar.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// use Array.join() for better performance than repeated string appends
|
||||||
|
let ciphertext = ctrTxt + ciphertxt.join('');
|
||||||
|
ciphertext = ciphertext.base64Encode();
|
||||||
|
|
||||||
|
return ciphertext;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt a text encrypted by AES in counter mode of operation
|
||||||
|
*
|
||||||
|
* @param {string} ciphertext - Source text to be encrypted.
|
||||||
|
* @param {string} password - Password to use to generate a key.
|
||||||
|
* @param {number} nBits - Number of bits to be used in the key; 128 / 192 / 256.
|
||||||
|
* @returns {string} Decrypted text
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* var decr = Aes.Ctr.encrypt('lwGl66VVwVObKIr6of8HVqJr', 'pāşšŵōřđ', 256); // decr: 'big secret'
|
||||||
|
*/
|
||||||
|
Aes.Ctr.decrypt = function (ciphertext, password, nBits) {
|
||||||
|
const blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
|
||||||
|
if (!(nBits == 128 || nBits == 192 || nBits == 256)) return ''; // standard allows 128/192/256 bit keys
|
||||||
|
ciphertext = String(ciphertext).base64Decode();
|
||||||
|
password = String(password).utf8Encode();
|
||||||
|
|
||||||
|
// use AES to encrypt password (mirroring encrypt routine)
|
||||||
|
const nBytes = nBits / 8; // no bytes in key
|
||||||
|
const pwBytes = new Array(nBytes);
|
||||||
|
for (var i = 0; i < nBytes; i++) {
|
||||||
|
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
|
||||||
|
}
|
||||||
|
let key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes));
|
||||||
|
key = key.concat(key.slice(0, nBytes - 16)); // expand key to 16/24/32 bytes long
|
||||||
|
|
||||||
|
// recover nonce from 1st 8 bytes of ciphertext
|
||||||
|
const counterBlock = new Array(8);
|
||||||
|
const ctrTxt = ciphertext.slice(0, 8);
|
||||||
|
for (var i = 0; i < 8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
|
||||||
|
|
||||||
|
// generate key schedule
|
||||||
|
const keySchedule = Aes.keyExpansion(key);
|
||||||
|
|
||||||
|
// separate ciphertext into blocks (skipping past initial 8 bytes)
|
||||||
|
const nBlocks = Math.ceil((ciphertext.length - 8) / blockSize);
|
||||||
|
const ct = new Array(nBlocks);
|
||||||
|
for (var b = 0; b < nBlocks; b++) ct[b] = ciphertext.slice(8 + b * blockSize, 8 + b * blockSize + blockSize);
|
||||||
|
ciphertext = ct; // ciphertext is now array of block-length strings
|
||||||
|
|
||||||
|
// plaintext will get generated block-by-block into array of block-length strings
|
||||||
|
const plaintxt = new Array(ciphertext.length);
|
||||||
|
|
||||||
|
for (var b = 0; b < nBlocks; b++) {
|
||||||
|
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
|
||||||
|
for (var c = 0; c < 4; c++) counterBlock[15 - c] = ((b) >>> c * 8) & 0xff;
|
||||||
|
for (var c = 0; c < 4; c++) counterBlock[15 - c - 4] = (((b + 1) / 0x100000000 - 1) >>> c * 8) & 0xff;
|
||||||
|
|
||||||
|
const cipherCntr = Aes.cipher(counterBlock, keySchedule); // encrypt counter block
|
||||||
|
|
||||||
|
const plaintxtByte = new Array(ciphertext[b].length);
|
||||||
|
for (var i = 0; i < ciphertext[b].length; i++) {
|
||||||
|
// -- xor plaintxt with ciphered counter byte-by-byte --
|
||||||
|
plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i);
|
||||||
|
plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]);
|
||||||
|
}
|
||||||
|
plaintxt[b] = plaintxtByte.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// join array of blocks into single plaintext string
|
||||||
|
let plaintext = plaintxt.join('');
|
||||||
|
plaintext = plaintext.utf8Decode(); // decode from UTF8 back to Unicode multi-byte chars
|
||||||
|
|
||||||
|
return plaintext;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|
||||||
|
/** Extend String object with method to encode multi-byte string to utf8
|
||||||
|
* - monsur.hossa.in/2012/07/20/utf-8-in-javascript.html */
|
||||||
|
if (typeof String.prototype.utf8Encode === 'undefined') {
|
||||||
|
String.prototype.utf8Encode = function () {
|
||||||
|
return unescape(encodeURIComponent(this));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extend String object with method to decode utf8 string to multi-byte */
|
||||||
|
if (typeof String.prototype.utf8Decode === 'undefined') {
|
||||||
|
String.prototype.utf8Decode = function () {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(escape(this));
|
||||||
|
} catch (e) {
|
||||||
|
return this; // invalid UTF-8? return as-is
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Extend String object with method to encode base64
|
||||||
|
* - developer.mozilla.org/en-US/docs/Web/API/window.btoa, nodejs.org/api/buffer.html
|
||||||
|
* note: if btoa()/atob() are not available (eg IE9-), try github.com/davidchambers/Base64.js */
|
||||||
|
if (typeof String.prototype.base64Encode === 'undefined') {
|
||||||
|
String.prototype.base64Encode = function () {
|
||||||
|
if (typeof btoa !== 'undefined') return btoa(this); // browser
|
||||||
|
if (typeof Buffer !== 'undefined') return new Buffer(this, 'utf8').toString('base64'); // Node.js
|
||||||
|
throw new Error('No Base64 Encode');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extend String object with method to decode base64 */
|
||||||
|
if (typeof String.prototype.base64Decode === 'undefined') {
|
||||||
|
String.prototype.base64Decode = function () {
|
||||||
|
if (typeof atob !== 'undefined') return atob(this); // browser
|
||||||
|
if (typeof Buffer !== 'undefined') return new Buffer(this, 'base64').toString('utf8'); // Node.js
|
||||||
|
throw new Error('No Base64 Decode');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
if (typeof module !== 'undefined' && module.exports) module.exports = Aes.Ctr; // CommonJs export
|
||||||
|
if (typeof define === 'function' && define.amd) define(['Aes'], () => Aes.Ctr); // AMD
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
/* Encrypt/decrypt files */
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
function encryptFile(file) {
|
||||||
|
// use FileReader.readAsArrayBuffer to handle binary files
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
reader.onload = function (evt) {
|
||||||
|
$('body').css({ cursor: 'wait' });
|
||||||
|
|
||||||
|
// Aes.Ctr.encrypt expects a string, but converting binary file directly to string could
|
||||||
|
// give invalid Unicode sequences, so convert bytestream ArrayBuffer to single-byte chars
|
||||||
|
const contentBytes = new Uint8Array(reader.result); // ≡ evt.target.result
|
||||||
|
let contentStr = '';
|
||||||
|
for (let i = 0; i < contentBytes.length; i++) {
|
||||||
|
contentStr += String.fromCharCode(contentBytes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const password = $('#password-file').val();
|
||||||
|
|
||||||
|
const t1 = new Date();
|
||||||
|
const ciphertext = Aes.Ctr.encrypt(contentStr, password, 256);
|
||||||
|
const t2 = new Date();
|
||||||
|
|
||||||
|
// use Blob to save encrypted file
|
||||||
|
const blob = new Blob([ciphertext], { type: 'text/plain' });
|
||||||
|
const filename = `${file.name}.encrypted`;
|
||||||
|
saveAs(blob, filename);
|
||||||
|
|
||||||
|
$('#encrypt-file-time').html(`${(t2 - t1) / 1000}s`); // display time taken
|
||||||
|
$('body').css({ cursor: 'default' });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decryptFile(file) {
|
||||||
|
// use FileReader.ReadAsText to read (base64-encoded) ciphertext file
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(file);
|
||||||
|
reader.onload = function (evt) {
|
||||||
|
$('body').css({ cursor: 'wait' });
|
||||||
|
|
||||||
|
const content = reader.result; // ≡ evt.target.result
|
||||||
|
const password = $('#password-file').val();
|
||||||
|
|
||||||
|
const t1 = new Date();
|
||||||
|
const plaintext = Aes.Ctr.decrypt(content, password, 256);
|
||||||
|
const t2 = new Date();
|
||||||
|
|
||||||
|
// convert single-byte character stream to ArrayBuffer bytestream
|
||||||
|
const contentBytes = new Uint8Array(plaintext.length);
|
||||||
|
for (let i = 0; i < plaintext.length; i++) {
|
||||||
|
contentBytes[i] = plaintext.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use Blob to save decrypted file
|
||||||
|
const blob = new Blob([contentBytes], { type: 'application/octet-stream' });
|
||||||
|
const filename = `${file.name.replace(/\.encrypted$/, '')}.decrypted`;
|
||||||
|
saveAs(blob, filename);
|
||||||
|
|
||||||
|
$('#decrypt-file-time').html(`${(t2 - t1) / 1000}s`); // display time taken
|
||||||
|
$('body').css({ cursor: 'default' });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Aes;
|
||||||
124
web/api-common/Notifications.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
class NotificationManager {
|
||||||
|
constructor() {
|
||||||
|
this.baseUrl = '';
|
||||||
|
this.templates = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
setBaseUrl(url) {
|
||||||
|
this.baseUrl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTemplates(data) {
|
||||||
|
this.templates = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeUtils(timeUtils) {
|
||||||
|
this.timeUtils = timeUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotifications(name, data) {
|
||||||
|
const that = this;
|
||||||
|
$.getJSON(this.baseUrl, { a: 'getNotifications' }, (_data) => {
|
||||||
|
if (_data.status === 'SUCCESS') {
|
||||||
|
that.renderNotifications(_data.data[1], _data.data[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearPendingNotifications(name, data) {
|
||||||
|
const that = this;
|
||||||
|
$.getJSON(this.baseUrl, { a: 'clearNotifications' }, (_data) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNotifications(notifications, unreadCount) {
|
||||||
|
if (notifications.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = this.templates.notifications;
|
||||||
|
if (unreadCount > 0) {
|
||||||
|
t = t.replace('#_count_#', unreadCount);
|
||||||
|
if (unreadCount > 1) {
|
||||||
|
t = t.replace('#_header_#', `You have ${unreadCount} new notifications`);
|
||||||
|
} else {
|
||||||
|
t = t.replace('#_header_#', `You have ${unreadCount} new notification`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t = t.replace('#_count_#', '');
|
||||||
|
t = t.replace('#_header_#', 'You have no new notifications');
|
||||||
|
}
|
||||||
|
|
||||||
|
let notificationStr = '';
|
||||||
|
|
||||||
|
for (const index in notifications) {
|
||||||
|
notificationStr += this.renderNotification(notifications[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t.replace('#_notifications_#', notificationStr);
|
||||||
|
|
||||||
|
const $obj = $(t);
|
||||||
|
|
||||||
|
if (unreadCount === 0) {
|
||||||
|
$obj.find('.label-danger').remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
$obj.attr('id', 'notifications');
|
||||||
|
const k = $('#notifications');
|
||||||
|
k.replaceWith($obj);
|
||||||
|
|
||||||
|
$('.navbar .menu').slimscroll({
|
||||||
|
height: '320px',
|
||||||
|
alwaysVisible: false,
|
||||||
|
size: '3px',
|
||||||
|
}).css('width', '100%');
|
||||||
|
|
||||||
|
this.timeUtils.convertToRelativeTime($('.notificationTime'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
renderNotification(notification) {
|
||||||
|
let t = this.templates.notification;
|
||||||
|
t = t.replace('#_image_#', notification.image);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(notification.action);
|
||||||
|
t = t.replace('#_url_#', this.baseUrl.replace('service.php', '?') + json.url);
|
||||||
|
} catch (e) {
|
||||||
|
t = t.replace('#_url_#', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t.replace('#_time_#', notification.time);
|
||||||
|
t = t.replace('#_fromName_#', notification.type);
|
||||||
|
t = t.replace('#_message_#', this.getLineBreakString(notification.message, 27));
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getLineBreakString(str, len) {
|
||||||
|
let t = '';
|
||||||
|
try {
|
||||||
|
const arr = str.split(' ');
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
count += arr[i].length + 1;
|
||||||
|
if (count > len) {
|
||||||
|
t += `${arr[i]}<br/>`;
|
||||||
|
count = 0;
|
||||||
|
} else {
|
||||||
|
t += `${arr[i]} `;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotificationManager;
|
||||||
69
web/api-common/RequestCache.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RequestCache
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RequestCache {
|
||||||
|
getKey(url, params) {
|
||||||
|
let key = `${url}|`;
|
||||||
|
for (const index in params) {
|
||||||
|
key += `${index}=${params[index]}|`;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateTable(table) {
|
||||||
|
let key;
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
key = localStorage.key(i);
|
||||||
|
if (key.indexOf(`t=${table}`) > 0) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getData(key) {
|
||||||
|
let data;
|
||||||
|
|
||||||
|
if (typeof (Storage) === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const strData = localStorage.getItem(key);
|
||||||
|
if (strData !== undefined && strData != null && strData !== '') {
|
||||||
|
data = JSON.parse(strData);
|
||||||
|
if (data === undefined || data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.status !== undefined && data.status != null && data.status !== 'SUCCESS') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setData(key, data) {
|
||||||
|
if (typeof (Storage) === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.status !== undefined && data.status != null && data.status !== 'SUCCESS') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const strData = JSON.stringify(data);
|
||||||
|
localStorage.setItem(key, strData);
|
||||||
|
return strData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RequestCache;
|
||||||
143
web/api-common/TimeUtils.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/* eslint-disable camelcase,brace-style */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TimeUtils {
|
||||||
|
setServerGMToffset(serverGMToffset) {
|
||||||
|
this.serverGMToffset = serverGMToffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMySQLFormatDate(date) {
|
||||||
|
const format = function (val) {
|
||||||
|
if (val < 10) { return `0${val}`; }
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
return `${date.getUTCFullYear()}-${format(date.getUTCMonth() + 1)}-${format(date.getUTCDate())}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToRelativeTime(selector) {
|
||||||
|
const that = this;
|
||||||
|
|
||||||
|
const getAmPmTime = function (curHour, curMin) {
|
||||||
|
let amPm = 'am';
|
||||||
|
let amPmHour = curHour;
|
||||||
|
if (amPmHour >= 12) {
|
||||||
|
amPm = 'pm';
|
||||||
|
if (amPmHour > 12) {
|
||||||
|
amPmHour -= 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let prefixCurMin = '';
|
||||||
|
if (curMin < 10) {
|
||||||
|
prefixCurMin = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefixCurHour = '';
|
||||||
|
if (curHour === 0) {
|
||||||
|
prefixCurHour = '0';
|
||||||
|
}
|
||||||
|
return ` at ${prefixCurHour}${amPmHour}:${prefixCurMin}${curMin}${amPm}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBrowserTimeZone = function () {
|
||||||
|
const current_date = new Date();
|
||||||
|
const gmt_offset = current_date.getTimezoneOffset() / 60;
|
||||||
|
return -gmt_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
const curDate = new Date();
|
||||||
|
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||||
|
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||||
|
|
||||||
|
|
||||||
|
const timezoneDiff = this.serverGMToffset - getBrowserTimeZone();
|
||||||
|
const timezoneTimeDiff = timezoneDiff * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
|
||||||
|
selector.each(function () {
|
||||||
|
try {
|
||||||
|
const thisValue = $(this).html();
|
||||||
|
// Split value into date and time
|
||||||
|
const thisValueArray = thisValue.split(' ');
|
||||||
|
const thisValueDate = thisValueArray[0];
|
||||||
|
const thisValueTime = thisValueArray[1];
|
||||||
|
|
||||||
|
// Split date into components
|
||||||
|
const thisValueDateArray = thisValueDate.split('-');
|
||||||
|
const curYear = thisValueDateArray[0];
|
||||||
|
const curMonth = thisValueDateArray[1] - 1;
|
||||||
|
const curDay = thisValueDateArray[2];
|
||||||
|
|
||||||
|
// Split time into components
|
||||||
|
const thisValueTimeArray = thisValueTime.split(':');
|
||||||
|
const curHour = thisValueTimeArray[0];
|
||||||
|
const curMin = thisValueTimeArray[1];
|
||||||
|
const curSec = thisValueTimeArray[2];
|
||||||
|
|
||||||
|
// Create this date
|
||||||
|
const thisDate = new Date(curYear, curMonth, curDay, curHour, curMin, curSec);
|
||||||
|
const thisTime = thisDate.getTime();
|
||||||
|
const tzDate = new Date(thisTime - timezoneTimeDiff);
|
||||||
|
// var tzDay = tzDate.getDay();//getDay will return the day of the week not the month
|
||||||
|
// var tzDay = tzDate.getUTCDate(); //getUTCDate will return the day of the month
|
||||||
|
const tzDay = tzDate.toString('d'); //
|
||||||
|
const tzYear = tzDate.getFullYear();
|
||||||
|
const tzHour = tzDate.getHours();
|
||||||
|
const tzMin = tzDate.getMinutes();
|
||||||
|
|
||||||
|
// Create the full date
|
||||||
|
// var fullDate = days[tzDate.getDay()] + ", " + months[tzDate.getMonth()] + " " + tzDay + ", " + tzYear + getAmPmTime(tzHour, tzMin);
|
||||||
|
const fullDate = `${days[tzDate.getDay()]}, ${months[tzDate.getMonth()]} ${tzDay}, ${tzYear}${getAmPmTime(tzHour, tzMin)}`;
|
||||||
|
|
||||||
|
// Get the time different
|
||||||
|
const timeDiff = (curDate.getTime() - tzDate.getTime()) / 1000;
|
||||||
|
const minDiff = Math.abs(timeDiff / 60);
|
||||||
|
const hourDiff = Math.abs(timeDiff / (60 * 60));
|
||||||
|
const dayDiff = Math.abs(timeDiff / (60 * 60 * 24));
|
||||||
|
const yearDiff = Math.abs(timeDiff / (60 * 60 * 24 * 365));
|
||||||
|
|
||||||
|
// If more than a day old, display the month, day and time (and year, if applicable)
|
||||||
|
let fbDate = '';
|
||||||
|
if (dayDiff > 1) {
|
||||||
|
// fbDate = curDay + " " + months[tzDate.getMonth()].substring(0,3);
|
||||||
|
fbDate = `${tzDay} ${months[tzDate.getMonth()].substring(0, 3)}`;
|
||||||
|
// Add the year, if applicable
|
||||||
|
if (yearDiff > 1) {
|
||||||
|
fbDate = `${fbDate} ${curYear}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the time
|
||||||
|
fbDate += getAmPmTime(tzHour, tzMin);
|
||||||
|
}
|
||||||
|
// Less than a day old, and more than an hour old
|
||||||
|
else if (hourDiff >= 1) {
|
||||||
|
const roundedHour = Math.round(hourDiff);
|
||||||
|
if (roundedHour === 1) fbDate = 'about an hour ago';
|
||||||
|
else fbDate = `${roundedHour} hours ago`;
|
||||||
|
}
|
||||||
|
// Less than an hour, and more than a minute
|
||||||
|
else if (minDiff >= 1) {
|
||||||
|
const roundedMin = Math.round(minDiff);
|
||||||
|
if (roundedMin === 1) fbDate = 'about a minute ago';
|
||||||
|
else fbDate = `${roundedMin} minutes ago`;
|
||||||
|
}
|
||||||
|
// Less than a minute
|
||||||
|
else if (minDiff < 1) {
|
||||||
|
fbDate = 'less than a minute ago';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update this element
|
||||||
|
$(this).html(fbDate);
|
||||||
|
$(this).attr('title', fullDate);
|
||||||
|
} catch (e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimeUtils;
|
||||||
184
web/api-common/app-global.js
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
var uploadId="";
|
||||||
|
var uploadAttr="";
|
||||||
|
var popupUpload = null;
|
||||||
|
|
||||||
|
function showUploadDialog(id,msg,group,user,postUploadId,postUploadAttr,postUploadResultAttr,fileType){
|
||||||
|
var ts = Math.round((new Date()).getTime() / 1000);
|
||||||
|
uploadId = postUploadId;
|
||||||
|
uploadAttr = postUploadAttr;
|
||||||
|
uploadResultAttr = postUploadResultAttr;
|
||||||
|
var html='<div><iframe src="'+CLIENT_BASE_URL+'fileupload_page.php?id=_id_&msg=_msg_&file_group=_file_group_&file_type=_file_type_&user=_user_" frameborder="0" scrolling="no" width="300px" height="55px"></iframe></div>';
|
||||||
|
var html = html.replace(/_id_/g,id);
|
||||||
|
var html = html.replace(/_msg_/g,msg);
|
||||||
|
var html = html.replace(/_file_group_/g,group);
|
||||||
|
var html = html.replace(/_user_/g,user);
|
||||||
|
var html = html.replace(/_file_type_/g,fileType);
|
||||||
|
|
||||||
|
modJs.renderModel('upload',"Upload File",html);
|
||||||
|
$('#uploadModel').modal('show');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeUploadDialog(success,error,data){
|
||||||
|
var arr = data.split("|");
|
||||||
|
var file = arr[0];
|
||||||
|
var fileBaseName = arr[1];
|
||||||
|
var fileId = arr[2];
|
||||||
|
|
||||||
|
if(success == 1){
|
||||||
|
//popupUpload.close();
|
||||||
|
$('#uploadModel').modal('hide');
|
||||||
|
if(uploadResultAttr == "url"){
|
||||||
|
if(uploadAttr == "val"){
|
||||||
|
$('#'+uploadId).val(file);
|
||||||
|
}else if(uploadAttr == "html"){
|
||||||
|
$('#'+uploadId).html(file);
|
||||||
|
}else{
|
||||||
|
$('#'+uploadId).attr(uploadAttr,file);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if(uploadResultAttr == "name"){
|
||||||
|
if(uploadAttr == "val"){
|
||||||
|
$('#'+uploadId).val(fileBaseName);
|
||||||
|
}else if(uploadAttr == "html"){
|
||||||
|
$('#'+uploadId).html(fileBaseName);
|
||||||
|
$('#'+uploadId).attr("val",fileBaseName);
|
||||||
|
}else{
|
||||||
|
$('#'+uploadId).attr(uploadAttr,fileBaseName);
|
||||||
|
}
|
||||||
|
$('#'+uploadId).show();
|
||||||
|
$('#'+uploadId+"_download").show();
|
||||||
|
$('#'+uploadId+"_remove").show();
|
||||||
|
}else if(uploadResultAttr == "id"){
|
||||||
|
if(uploadAttr == "val"){
|
||||||
|
$('#'+uploadId).attr(uploadAttr,fileId);
|
||||||
|
}else if(uploadAttr == "html"){
|
||||||
|
$('#'+uploadId).html(fileBaseName);
|
||||||
|
$('#'+uploadId).attr("val",fileId);
|
||||||
|
}else{
|
||||||
|
$('#'+uploadId).attr(uploadAttr,fileId);
|
||||||
|
}
|
||||||
|
$('#'+uploadId).show();
|
||||||
|
$('#'+uploadId+"_download").show();
|
||||||
|
$('#'+uploadId+"_remove").show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
//popupUpload.close();
|
||||||
|
$('#uploadModel').modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function download(name, closeCallback, closeCallbackData){
|
||||||
|
|
||||||
|
var successCallback = function(data){
|
||||||
|
|
||||||
|
var link;
|
||||||
|
var fileParts;
|
||||||
|
var viewableImages = ["png","jpg","gif","bmp","jpge"];
|
||||||
|
var viewableFiles = ["pdf","xml"];
|
||||||
|
|
||||||
|
$('.modal').modal('hide');
|
||||||
|
|
||||||
|
if(data['filename'].indexOf("https:") == 0 || data['filename'].indexOf("http:") == 0){
|
||||||
|
fileParts = data['filename'].split("?");
|
||||||
|
fileParts = fileParts[0].split(".");
|
||||||
|
|
||||||
|
if(jQuery.inArray(fileParts[fileParts.length - 1], viewableFiles ) >= 0) {
|
||||||
|
var win = window.open(data['filename'], '_blank');
|
||||||
|
win.focus();
|
||||||
|
}else{
|
||||||
|
link = '<a href="'+data['filename']+'" target="_blank">Download File <i class="icon-download-alt"></i> </a>';
|
||||||
|
if(jQuery.inArray(fileParts[fileParts.length - 1], viewableImages ) >= 0) {
|
||||||
|
link += '<br/><br/><img style="max-width:545px;max-height:350px;" src="'+data['filename']+'"/>';
|
||||||
|
}
|
||||||
|
modJs.showMessage("Download File Attachment",link,closeCallback,closeCallbackData);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
fileParts = data['filename'].split(".");
|
||||||
|
link = '<a href="'+modJs.getCustomActionUrl("download",{'file':data['filename']})+'" target="_blank">Download File <i class="icon-download-alt"></i> </a>';
|
||||||
|
if(jQuery.inArray(fileParts[fileParts.length - 1], viewableImages ) >= 0) {
|
||||||
|
link += '<br/><br/><img style="max-width:545px;max-height:350px;" src="'+modJs.getClientDataUrl()+data['filename']+'"/>';
|
||||||
|
}
|
||||||
|
|
||||||
|
modJs.showMessage("Download File Attachment",link,closeCallback,closeCallbackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var failCallback = function(data){
|
||||||
|
modJs.showMessage("Error Downloading File","File not found");
|
||||||
|
};
|
||||||
|
|
||||||
|
modJs.sendCustomRequest("file",{'name':name},successCallback,failCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomString(length){
|
||||||
|
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split('');
|
||||||
|
|
||||||
|
if (! length) {
|
||||||
|
length = Math.floor(Math.random() * chars.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var str = '';
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
str += chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyInstance(key){
|
||||||
|
var object = {};
|
||||||
|
object['a'] = "verifyInstance";
|
||||||
|
object['key'] = key;
|
||||||
|
$.post(this.baseUrl, object, function(data) {
|
||||||
|
if(data.status == "SUCCESS"){
|
||||||
|
$("#verifyModel").hide();
|
||||||
|
$('body').removeClass('modal-open');
|
||||||
|
$('.modal-backdrop').remove();
|
||||||
|
alert("Success: Instance Verified");
|
||||||
|
}else{
|
||||||
|
alert("Error: "+data.message);
|
||||||
|
}
|
||||||
|
},"json");
|
||||||
|
}
|
||||||
|
|
||||||
|
function nl2br(str, is_xhtml) {
|
||||||
|
// discuss at: http://phpjs.org/functions/nl2br/
|
||||||
|
// original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
||||||
|
// improved by: Philip Peterson
|
||||||
|
// improved by: Onno Marsman
|
||||||
|
// improved by: Atli <20><>r
|
||||||
|
// improved by: Brett Zamir (http://brett-zamir.me)
|
||||||
|
// improved by: Maximusya
|
||||||
|
// bugfixed by: Onno Marsman
|
||||||
|
// bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
||||||
|
// input by: Brett Zamir (http://brett-zamir.me)
|
||||||
|
// example 1: nl2br('Kevin\nvan\nZonneveld');
|
||||||
|
// returns 1: 'Kevin<br />\nvan<br />\nZonneveld'
|
||||||
|
// example 2: nl2br("\nOne\nTwo\n\nThree\n", false);
|
||||||
|
// returns 2: '<br>\nOne<br>\nTwo<br>\n<br>\nThree<br>\n'
|
||||||
|
// example 3: nl2br("\nOne\nTwo\n\nThree\n", true);
|
||||||
|
// returns 3: '<br />\nOne<br />\nTwo<br />\n<br />\nThree<br />\n'
|
||||||
|
|
||||||
|
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br ' + '/>' : '<br>'; // Adjust comment to avoid issue on phpjs.org display
|
||||||
|
|
||||||
|
return (str + '')
|
||||||
|
.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLanguage(language) {
|
||||||
|
var object = {};
|
||||||
|
object['a'] = "updateLanguage";
|
||||||
|
object['language'] = language;
|
||||||
|
$.post(this.baseUrl, object, function(data) {
|
||||||
|
if(data.status == "SUCCESS"){
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
alert("Error occurred while changing language");
|
||||||
|
}
|
||||||
|
},"json");
|
||||||
|
}
|
||||||
250
web/api-common/datatables/dataTables.bootstrap.js
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
/* Set the defaults for DataTables initialisation */
|
||||||
|
$.extend( true, $.fn.dataTable.defaults, {
|
||||||
|
"sDom":
|
||||||
|
"<'row'<'col-xs-6'l><'col-xs-6'f>r>"+
|
||||||
|
"t"+
|
||||||
|
"<'row'<'col-xs-6'i><'col-xs-6'p>>",
|
||||||
|
"oLanguage": {
|
||||||
|
"sLengthMenu": "_MENU_ records per page"
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
/* Default class modification */
|
||||||
|
$.extend( $.fn.dataTableExt.oStdClasses, {
|
||||||
|
"sWrapper": "dataTables_wrapper form-inline",
|
||||||
|
"sFilterInput": "form-control input-sm",
|
||||||
|
"sLengthSelect": "form-control input-sm"
|
||||||
|
} );
|
||||||
|
|
||||||
|
// In 1.10 we use the pagination renderers to draw the Bootstrap paging,
|
||||||
|
// rather than custom plug-in
|
||||||
|
if ( $.fn.dataTable.Api ) {
|
||||||
|
$.fn.dataTable.defaults.renderer = 'bootstrap';
|
||||||
|
$.fn.dataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
|
||||||
|
var api = new $.fn.dataTable.Api( settings );
|
||||||
|
var classes = settings.oClasses;
|
||||||
|
var lang = settings.oLanguage.oPaginate;
|
||||||
|
var btnDisplay, btnClass;
|
||||||
|
|
||||||
|
var attach = function( container, buttons ) {
|
||||||
|
var i, ien, node, button;
|
||||||
|
var clickHandler = function ( e ) {
|
||||||
|
e.preventDefault();
|
||||||
|
if ( e.data.action !== 'ellipsis' ) {
|
||||||
|
api.page( e.data.action ).draw( false );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
|
||||||
|
button = buttons[i];
|
||||||
|
|
||||||
|
if ( $.isArray( button ) ) {
|
||||||
|
attach( container, button );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
btnDisplay = '';
|
||||||
|
btnClass = '';
|
||||||
|
|
||||||
|
switch ( button ) {
|
||||||
|
case 'ellipsis':
|
||||||
|
btnDisplay = '…';
|
||||||
|
btnClass = 'disabled';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'first':
|
||||||
|
btnDisplay = lang.sFirst;
|
||||||
|
btnClass = button + (page > 0 ?
|
||||||
|
'' : ' disabled');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'previous':
|
||||||
|
btnDisplay = lang.sPrevious;
|
||||||
|
btnClass = button + (page > 0 ?
|
||||||
|
'' : ' disabled');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'next':
|
||||||
|
btnDisplay = lang.sNext;
|
||||||
|
btnClass = button + (page < pages-1 ?
|
||||||
|
'' : ' disabled');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'last':
|
||||||
|
btnDisplay = lang.sLast;
|
||||||
|
btnClass = button + (page < pages-1 ?
|
||||||
|
'' : ' disabled');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
btnDisplay = button + 1;
|
||||||
|
btnClass = page === button ?
|
||||||
|
'active' : '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( btnDisplay ) {
|
||||||
|
node = $('<li>', {
|
||||||
|
'class': classes.sPageButton+' '+btnClass,
|
||||||
|
'aria-controls': settings.sTableId,
|
||||||
|
'tabindex': settings.iTabIndex,
|
||||||
|
'id': idx === 0 && typeof button === 'string' ?
|
||||||
|
settings.sTableId +'_'+ button :
|
||||||
|
null
|
||||||
|
} )
|
||||||
|
.append( $('<a>', {
|
||||||
|
'href': '#'
|
||||||
|
} )
|
||||||
|
.html( btnDisplay )
|
||||||
|
)
|
||||||
|
.appendTo( container );
|
||||||
|
|
||||||
|
settings.oApi._fnBindAction(
|
||||||
|
node, {action: button}, clickHandler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
attach(
|
||||||
|
$(host).empty().html('<ul class="pagination"/>').children('ul'),
|
||||||
|
buttons
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Integration for 1.9-
|
||||||
|
$.fn.dataTable.defaults.sPaginationType = 'bootstrap';
|
||||||
|
|
||||||
|
/* API method to get paging information */
|
||||||
|
$.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"iStart": oSettings._iDisplayStart,
|
||||||
|
"iEnd": oSettings.fnDisplayEnd(),
|
||||||
|
"iLength": oSettings._iDisplayLength,
|
||||||
|
"iTotal": oSettings.fnRecordsTotal(),
|
||||||
|
"iFilteredTotal": oSettings.fnRecordsDisplay(),
|
||||||
|
"iPage": oSettings._iDisplayLength === -1 ?
|
||||||
|
0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
|
||||||
|
"iTotalPages": oSettings._iDisplayLength === -1 ?
|
||||||
|
0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Bootstrap style pagination control */
|
||||||
|
$.extend( $.fn.dataTableExt.oPagination, {
|
||||||
|
"bootstrap": {
|
||||||
|
"fnInit": function( oSettings, nPaging, fnDraw ) {
|
||||||
|
var oLang = oSettings.oLanguage.oPaginate;
|
||||||
|
var fnClickHandler = function ( e ) {
|
||||||
|
e.preventDefault();
|
||||||
|
if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
|
||||||
|
fnDraw( oSettings );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(nPaging).append(
|
||||||
|
'<ul class="pagination">'+
|
||||||
|
'<li class="prev disabled"><a href="#">← '+oLang.sPrevious+'</a></li>'+
|
||||||
|
'<li class="next disabled"><a href="#">'+oLang.sNext+' → </a></li>'+
|
||||||
|
'</ul>'
|
||||||
|
);
|
||||||
|
var els = $('a', nPaging);
|
||||||
|
$(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
|
||||||
|
$(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler );
|
||||||
|
},
|
||||||
|
|
||||||
|
"fnUpdate": function ( oSettings, fnDraw ) {
|
||||||
|
var iListLength = 5;
|
||||||
|
var oPaging = oSettings.oInstance.fnPagingInfo();
|
||||||
|
var an = oSettings.aanFeatures.p;
|
||||||
|
var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
|
||||||
|
|
||||||
|
if ( oPaging.iTotalPages < iListLength) {
|
||||||
|
iStart = 1;
|
||||||
|
iEnd = oPaging.iTotalPages;
|
||||||
|
}
|
||||||
|
else if ( oPaging.iPage <= iHalf ) {
|
||||||
|
iStart = 1;
|
||||||
|
iEnd = iListLength;
|
||||||
|
} else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) {
|
||||||
|
iStart = oPaging.iTotalPages - iListLength + 1;
|
||||||
|
iEnd = oPaging.iTotalPages;
|
||||||
|
} else {
|
||||||
|
iStart = oPaging.iPage - iHalf + 1;
|
||||||
|
iEnd = iStart + iListLength - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( i=0, ien=an.length ; i<ien ; i++ ) {
|
||||||
|
// Remove the middle elements
|
||||||
|
$('li:gt(0)', an[i]).filter(':not(:last)').remove();
|
||||||
|
|
||||||
|
// Add the new list items and their event handlers
|
||||||
|
for ( j=iStart ; j<=iEnd ; j++ ) {
|
||||||
|
sClass = (j==oPaging.iPage+1) ? 'class="active"' : '';
|
||||||
|
$('<li '+sClass+'><a href="#">'+j+'</a></li>')
|
||||||
|
.insertBefore( $('li:last', an[i])[0] )
|
||||||
|
.bind('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength;
|
||||||
|
fnDraw( oSettings );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add / remove disabled classes from the static elements
|
||||||
|
if ( oPaging.iPage === 0 ) {
|
||||||
|
$('li:first', an[i]).addClass('disabled');
|
||||||
|
} else {
|
||||||
|
$('li:first', an[i]).removeClass('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
|
||||||
|
$('li:last', an[i]).addClass('disabled');
|
||||||
|
} else {
|
||||||
|
$('li:last', an[i]).removeClass('disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TableTools Bootstrap compatibility
|
||||||
|
* Required TableTools 2.1+
|
||||||
|
*/
|
||||||
|
if ( $.fn.DataTable.TableTools ) {
|
||||||
|
// Set the classes that TableTools uses to something suitable for Bootstrap
|
||||||
|
$.extend( true, $.fn.DataTable.TableTools.classes, {
|
||||||
|
"container": "DTTT btn-group",
|
||||||
|
"buttons": {
|
||||||
|
"normal": "btn btn-default",
|
||||||
|
"disabled": "disabled"
|
||||||
|
},
|
||||||
|
"collection": {
|
||||||
|
"container": "DTTT_dropdown dropdown-menu",
|
||||||
|
"buttons": {
|
||||||
|
"normal": "",
|
||||||
|
"disabled": "disabled"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"print": {
|
||||||
|
"info": "DTTT_print_info modal"
|
||||||
|
},
|
||||||
|
"select": {
|
||||||
|
"row": "active"
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Have the collection use a bootstrap compatible dropdown
|
||||||
|
$.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, {
|
||||||
|
"collection": {
|
||||||
|
"container": "ul",
|
||||||
|
"button": "li",
|
||||||
|
"liner": "a"
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
12131
web/api-common/datatables/jquery.dataTables.js
vendored
Normal file
28
web/api-common/entry.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* global timeUtils */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import NotificationManager from './Notifications';
|
||||||
|
import TimeUtils from './TimeUtils';
|
||||||
|
|
||||||
|
import RequestCache from './RequestCache';
|
||||||
|
|
||||||
|
const Aes = require('./Aes');
|
||||||
|
|
||||||
|
window.RequestCache = RequestCache;
|
||||||
|
|
||||||
|
window.setupTimeUtils = (diffHoursBetweenServerTimezoneWithGMT) => {
|
||||||
|
const timeUtils = new TimeUtils();
|
||||||
|
timeUtils.setServerGMToffset(diffHoursBetweenServerTimezoneWithGMT);
|
||||||
|
|
||||||
|
return timeUtils;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.setupNotifications = (baseUrl) => {
|
||||||
|
const notificationManager = new NotificationManager();
|
||||||
|
notificationManager.setBaseUrl(baseUrl);
|
||||||
|
notificationManager.setTimeUtils(timeUtils);
|
||||||
|
|
||||||
|
return notificationManager;
|
||||||
|
};
|
||||||
134
web/api/ApproveAdminAdapter.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ApproveAdminAdapter
|
||||||
|
*/
|
||||||
|
import LogViewAdapter from './LogViewAdapter';
|
||||||
|
|
||||||
|
class ApproveAdminAdapter extends LogViewAdapter {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusFieldPosition() {
|
||||||
|
const dm = this.getDataMapping();
|
||||||
|
return dm.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
openStatus(id, status) {
|
||||||
|
$(`#${this.itemNameLower}StatusModel`).modal('show');
|
||||||
|
$(`#${this.itemNameLower}_status`).html(this.getStatusOptions(status));
|
||||||
|
$(`#${this.itemNameLower}_status`).val(status);
|
||||||
|
this.statusChangeId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDialog() {
|
||||||
|
$(`#${this.itemNameLower}StatusModel`).modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStatus() {
|
||||||
|
const status = $(`#${this.itemNameLower}_status`).val();
|
||||||
|
const reason = $(`#${this.itemNameLower}_reason`).val();
|
||||||
|
|
||||||
|
if (status == undefined || status == null || status == '') {
|
||||||
|
this.showMessage('Error', `Please select ${this.itemNameLower} status`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const object = { id: this.statusChangeId, status, reason };
|
||||||
|
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'changeStatusSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'changeStatusFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('changeStatus', `admin=${this.modulePathName}`, reqJson, callBackData);
|
||||||
|
|
||||||
|
this.closeDialog();
|
||||||
|
this.statusChangeId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStatusSuccessCallBack(callBackData) {
|
||||||
|
this.showMessage('Successful', `${this.itemName} Request status changed successfully`);
|
||||||
|
this.get([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStatusFailCallBack(callBackData) {
|
||||||
|
this.showMessage('Error', `Error occurred while changing ${this.itemName} request status`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 deleteButton = '<img class="tableActionButton" src="_BASE_images/delete.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Delete" onclick="modJs.deleteRow(_id_);return false;"></img>';
|
||||||
|
const statusChangeButton = '<img class="tableActionButton" src="_BASE_images/run.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Change Status" onclick="modJs.openStatus(_id_, \'_cstatus_\');return false;"></img>';
|
||||||
|
const viewLogsButton = '<img class="tableActionButton" src="_BASE_images/log.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="View Logs" onclick="modJs.getLogs(_id_);return false;"></img>';
|
||||||
|
|
||||||
|
let html = '<div style="width:120px;">_edit__delete__status__logs_</div>';
|
||||||
|
|
||||||
|
const optiondata = this.getStatusOptionsData(data[this.getStatusFieldPosition()]);
|
||||||
|
if (Object.keys(optiondata).length > 0) {
|
||||||
|
html = html.replace('_status_', statusChangeButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_status_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace('_logs_', viewLogsButton);
|
||||||
|
|
||||||
|
if (this.showDelete) {
|
||||||
|
html = html.replace('_delete_', deleteButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_delete_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showEdit) {
|
||||||
|
html = html.replace('_edit_', editButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_edit_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
html = html.replace(/_cstatus_/g, data[this.getStatusFieldPosition()]);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubProfileTable() {
|
||||||
|
if (this.user.user_level == 'Admin') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusOptionsData(currentStatus) {
|
||||||
|
const data = {};
|
||||||
|
if (currentStatus == 'Approved') {
|
||||||
|
|
||||||
|
} else if (currentStatus == 'Pending') {
|
||||||
|
data.Approved = 'Approved';
|
||||||
|
data.Rejected = 'Rejected';
|
||||||
|
} else if (currentStatus == 'Rejected') {
|
||||||
|
|
||||||
|
} else if (currentStatus == 'Cancelled') {
|
||||||
|
|
||||||
|
} else if (currentStatus == 'Processing') {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data['Cancellation Requested'] = 'Cancellation Requested';
|
||||||
|
data.Cancelled = 'Cancelled';
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusOptions(currentStatus) {
|
||||||
|
return this.generateOptions(this.getStatusOptionsData(currentStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApproveAdminAdapter;
|
||||||
50
web/api/ApproveApproverAdapter.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ApproveApproverAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ApproveApproverAdapter {
|
||||||
|
getActionButtonsHtml(id, data) {
|
||||||
|
const statusChangeButton = '<img class="tableActionButton" src="_BASE_images/run.png" style="cursor:pointer;" rel="tooltip" title="Change Status" onclick="modJs.openStatus(_id_, \'_cstatus_\');return false;"></img>';
|
||||||
|
const viewLogsButton = '<img class="tableActionButton" src="_BASE_images/log.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="View Logs" onclick="modJs.getLogs(_id_);return false;"></img>';
|
||||||
|
|
||||||
|
let html = '<div style="width:80px;">_status__logs_</div>';
|
||||||
|
|
||||||
|
|
||||||
|
html = html.replace('_logs_', viewLogsButton);
|
||||||
|
|
||||||
|
|
||||||
|
if (data[this.getStatusFieldPosition()] == 'Processing') {
|
||||||
|
html = html.replace('_status_', statusChangeButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_status_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
html = html.replace(/_cstatus_/g, data[this.getStatusFieldPosition()]);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusOptionsData(currentStatus) {
|
||||||
|
const data = {};
|
||||||
|
if (currentStatus != 'Processing') {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data.Approved = 'Approved';
|
||||||
|
data.Rejected = 'Rejected';
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusOptions(currentStatus) {
|
||||||
|
return this.generateOptions(this.getStatusOptionsData(currentStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApproveApproverAdapter;
|
||||||
68
web/api/ApproveModuleAdapter.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import LogViewAdapter from './LogViewAdapter';
|
||||||
|
|
||||||
|
class ApproveModuleAdapter extends LogViewAdapter {
|
||||||
|
cancelRequest(id) {
|
||||||
|
const object = {};
|
||||||
|
object.id = id;
|
||||||
|
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'cancelSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'cancelFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('cancel', `modules=${this.modulePathName}`, reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
cancelSuccessCallBack(callBackData) {
|
||||||
|
this.showMessage('Successful', `${this.itemName} cancellation request sent`);
|
||||||
|
this.get([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelFailCallBack(callBackData) {
|
||||||
|
this.showMessage(`Error Occurred while cancelling ${this.itemName}`, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 deleteButton = '<img class="tableActionButton" src="_BASE_images/delete.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Delete" onclick="modJs.deleteRow(_id_);return false;"></img>';
|
||||||
|
const requestCancellationButton = `<img class="tableActionButton" src="_BASE_images/delete.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="Cancel ${this.itemName}" onclick="modJs.cancelRequest(_id_);return false;"></img>`;
|
||||||
|
const viewLogsButton = '<img class="tableActionButton" src="_BASE_images/log.png" style="margin-left:15px;cursor:pointer;" rel="tooltip" title="View Logs" onclick="modJs.getLogs(_id_);return false;"></img>';
|
||||||
|
|
||||||
|
|
||||||
|
let html = '<div style="width:120px;">_edit__logs__delete_</div>';
|
||||||
|
|
||||||
|
html = html.replace('_logs_', viewLogsButton);
|
||||||
|
|
||||||
|
if (this.showDelete) {
|
||||||
|
if (data[7] === 'Approved') {
|
||||||
|
html = html.replace('_delete_', requestCancellationButton);
|
||||||
|
} else if (data[7] === 'Pending' || this.user.user_level === 'Admin') {
|
||||||
|
html = html.replace('_delete_', deleteButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_delete_', '');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html = html.replace('_delete_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showEdit) {
|
||||||
|
html = html.replace('_edit_', editButton);
|
||||||
|
} else {
|
||||||
|
html = html.replace('_edit_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
html = html.replace(/_id_/g, id);
|
||||||
|
html = html.replace(/_BASE_/g, this.baseUrl);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApproveModuleAdapter;
|
||||||
27
web/api/BaseGraphAdapter.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from './AdapterBase';
|
||||||
|
|
||||||
|
class BaseGraphAdapter extends AdapterBase {
|
||||||
|
getDataMapping() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
createTable(elementId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BaseGraphAdapter;
|
||||||
102
web/api/CustomFieldAdapter.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from './AdapterBase';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CustomFieldAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CustomFieldAdapter extends AdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.tableType = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'display',
|
||||||
|
'display_order',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Display Status' },
|
||||||
|
{ sTitle: 'Priority' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['display', { label: 'Display Status', type: 'select', source: [['Form', 'Show'], ['Hidden', 'Hidden']] }],
|
||||||
|
['field_type', { label: 'Field Type', type: 'select', source: [['text', 'Text Field'], ['textarea', 'Text Area'], ['select', 'Select'], ['select2', 'Select2'], ['select2multi', 'Multi Select'], ['fileupload', 'File Upload'], ['date', 'Date'], ['datetime', 'Date Time'], ['time', 'Time']] }],
|
||||||
|
['field_label', { label: 'Field Label', type: 'text', validation: '' }],
|
||||||
|
['field_validation', {
|
||||||
|
label: 'Validation', type: 'select2', validation: 'none', sort: 'none', 'null-label': 'Required', 'allow-null': true, source: [['none', 'None'], ['number', 'Number'], ['numberOrEmpty', 'Number or Empty'], ['float', 'Decimal'], ['email', 'Email'], ['emailOrEmpty', 'Email or Empty']],
|
||||||
|
}],
|
||||||
|
['field_options', {
|
||||||
|
label: 'Field Options',
|
||||||
|
type: 'datagroup',
|
||||||
|
form: [
|
||||||
|
['label', { label: 'Label', type: 'text', validation: '' }],
|
||||||
|
['value', { label: 'Value', type: 'text', validation: 'none' }],
|
||||||
|
],
|
||||||
|
html: '<div id="#_id_#" class="panel panel-default"><div class="panel-body">#_delete_##_edit_#<span style="color:#999;font-size:13px;font-weight:bold">#_label_#</span>:#_value_#</div></div>',
|
||||||
|
validation: 'none',
|
||||||
|
}],
|
||||||
|
['display_order', { label: 'Priority', type: 'text', validation: 'number' }],
|
||||||
|
['display_section', { label: 'Display Section', type: 'text', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setTableType(type) {
|
||||||
|
this.tableType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
doCustomValidation(params) {
|
||||||
|
const validateName = function (str) {
|
||||||
|
const name = /^[a-z][a-z0-9._]+$/;
|
||||||
|
return str != null && name.test(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!validateName(params.name)) {
|
||||||
|
return 'Invalid name for custom field';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
forceInjectValuesBeforeSave(params) {
|
||||||
|
const data = [params.name]; const options = []; let
|
||||||
|
optionsData;
|
||||||
|
data.push({});
|
||||||
|
data[1].label = params.field_label;
|
||||||
|
data[1].type = params.field_type;
|
||||||
|
data[1].validation = params.field_validation;
|
||||||
|
if (['select', 'select2', 'select2multi'].indexOf(params.field_type) >= 0) {
|
||||||
|
optionsData = (params.field_options === '' || params.field_options === undefined)
|
||||||
|
? [] : JSON.parse(params.field_options);
|
||||||
|
for (const index in optionsData) {
|
||||||
|
options.push([optionsData[index].value, optionsData[index].label]);
|
||||||
|
}
|
||||||
|
data[1].source = options;
|
||||||
|
}
|
||||||
|
if (params.field_validation == null || params.field_validation === undefined) {
|
||||||
|
params.field_validation = '';
|
||||||
|
}
|
||||||
|
params.data = JSON.stringify(data);
|
||||||
|
params.type = this.tableType;
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomFieldAdapter;
|
||||||
37
web/api/IdNameAdapter.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from './AdapterBase';
|
||||||
|
/**
|
||||||
|
* IdNameAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class IdNameAdapter extends AdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IdNameAdapter;
|
||||||
58
web/api/LogViewAdapter.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
/* global timeUtils */
|
||||||
|
/**
|
||||||
|
* LogViewAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
import AdapterBase from './AdapterBase';
|
||||||
|
|
||||||
|
class LogViewAdapter extends AdapterBase {
|
||||||
|
getLogs(id) {
|
||||||
|
const object = { id };
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'getLogsSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'getLogsFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('getLogs', `admin=${this.modulePathName}`, reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogsSuccessCallBack(callBackData) {
|
||||||
|
let tableLog = '<table class="table table-condensed table-bordered table-striped" style="font-size:14px;"><thead><tr><th>Notes</th></tr></thead><tbody>_days_</tbody></table> ';
|
||||||
|
const rowLog = '<tr><td><span class="logTime label label-default">_date_</span> <b>_status_</b><br/>_note_</td></tr>';
|
||||||
|
|
||||||
|
const logs = callBackData.data;
|
||||||
|
let html = '';
|
||||||
|
let rowsLogs = '';
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < logs.length; i++) {
|
||||||
|
let trow = rowLog;
|
||||||
|
trow = trow.replace(/_date_/g, logs[i].time);
|
||||||
|
trow = trow.replace(/_status_/g, `${logs[i].status_from} -> ${logs[i].status_to}`);
|
||||||
|
trow = trow.replace(/_note_/g, logs[i].note);
|
||||||
|
rowsLogs += trow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowsLogs !== '') {
|
||||||
|
tableLog = tableLog.replace('_days_', rowsLogs);
|
||||||
|
html += tableLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showMessage('Logs', html);
|
||||||
|
|
||||||
|
timeUtils.convertToRelativeTime($('.logTime'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
getLogsFailCallBack(callBackData) {
|
||||||
|
this.showMessage('Error', 'Error occured while getting data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogViewAdapter;
|
||||||
2528
web/api/ModuleBase.js
Normal file
216
web/api/ObjectAdapter.js
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from './AdapterBase';
|
||||||
|
/**
|
||||||
|
* ObjectAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ObjectAdapter extends AdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.container = null;
|
||||||
|
this.loadMoreButton = null;
|
||||||
|
this.start = 0;
|
||||||
|
this.pageSize = 6;
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.hasMoreData = true;
|
||||||
|
this.searchTerm = '';
|
||||||
|
this.searchInput = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getObjectHTML(object) {
|
||||||
|
const template = this.getCustomTemplate(this.getTemplateName());
|
||||||
|
let t = template;
|
||||||
|
for (const index in object) {
|
||||||
|
t = t.replace(new RegExp(`#_${index}_#`, 'g'), object[index]);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPageSize(pageSize) {
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDomEvents(object) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getTemplateName() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
renderObject(object) {
|
||||||
|
const objDom = this.getObjectDom(object.id);
|
||||||
|
|
||||||
|
const html = this.getObjectHTML(object);
|
||||||
|
const domObj = $(html);
|
||||||
|
|
||||||
|
|
||||||
|
if (objDom !== undefined && objDom != null) {
|
||||||
|
objDom.replace(domObj);
|
||||||
|
} else {
|
||||||
|
this.container.append(domObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addDomEvents(domObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
setContainer(container) {
|
||||||
|
this.container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadMoreButton(loadMoreButton) {
|
||||||
|
const that = this;
|
||||||
|
this.loadMoreButton = loadMoreButton;
|
||||||
|
this.loadMoreButton.off().on('click', () => {
|
||||||
|
that.loadMoreButton.attr('disabled', 'disabled');
|
||||||
|
that.loadMore([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showLoadError(msg) {
|
||||||
|
$(`#${this.getTableName()}_error`).html(msg);
|
||||||
|
$(`#${this.getTableName()}_error`).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
hideLoadError() {
|
||||||
|
$(`#${this.getTableName()}_error`).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
setSearchBox(searchInput) {
|
||||||
|
const that = this;
|
||||||
|
this.searchInput = searchInput;
|
||||||
|
this.searchInput.off();
|
||||||
|
this.searchInput.keydown(function (event) {
|
||||||
|
const val = $(this).val();
|
||||||
|
if (event.which === 13) {
|
||||||
|
event.preventDefault();
|
||||||
|
that.search([]);
|
||||||
|
} else if ((event.which === 8 || event.which === 46) && val.length === 1 && that.searchTerm !== '') {
|
||||||
|
that.search([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getObjectDom(id) {
|
||||||
|
const obj = this.container.find(`#obj_${id}`);
|
||||||
|
if (obj.length) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMore(callBackData) {
|
||||||
|
if (!this.hasMoreData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentPage++;
|
||||||
|
this.get(callBackData, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(callBackData, loadMore) {
|
||||||
|
const that = this;
|
||||||
|
|
||||||
|
this.hideLoadError();
|
||||||
|
|
||||||
|
if (!loadMore) {
|
||||||
|
this.currentPage = 1;
|
||||||
|
if (this.container != null) {
|
||||||
|
this.container.html('');
|
||||||
|
}
|
||||||
|
this.hasMoreData = true;
|
||||||
|
this.tableData = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.start = (this.currentPage - 1) * this.pageSize;
|
||||||
|
|
||||||
|
|
||||||
|
this.container = $(`#${this.getTableName()}`).find('.objectList');
|
||||||
|
|
||||||
|
that.showLoader();
|
||||||
|
|
||||||
|
|
||||||
|
let url = `${this.getDataUrl(that.getDataMapping())
|
||||||
|
}&iDisplayStart=${this.start}&iDisplayLength=${this.pageSize}&objects=1`;
|
||||||
|
|
||||||
|
if (this.searchTerm !== '' && this.searchTerm !== undefined && this.searchTerm != null) {
|
||||||
|
url += `&sSearch=${this.searchTerm}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.post(url, (data) => {
|
||||||
|
that.getSuccessCallBack(callBackData, data);
|
||||||
|
}, 'json').always(() => { that.hideLoader(); });
|
||||||
|
|
||||||
|
that.initFieldMasterData();
|
||||||
|
|
||||||
|
this.trackEvent('get', this.tab, this.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
search(callBackData) {
|
||||||
|
const that = this;
|
||||||
|
this.searchTerm = $(`#${this.getTableName()}_search`).val();
|
||||||
|
|
||||||
|
this.get(callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getSuccessCallBack(callBackData, serverData) {
|
||||||
|
const data = [];
|
||||||
|
|
||||||
|
if (serverData.length === 0 && this.container.html() === '') {
|
||||||
|
this.showLoadError('No Results Found !!!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getFilters() == null) {
|
||||||
|
$(`#${this.getTableName()}_filterBtn`).hide();
|
||||||
|
$(`#${this.getTableName()}_resetFilters`).hide();
|
||||||
|
} else {
|
||||||
|
$(`#${this.getTableName()}_filterBtn`).show();
|
||||||
|
$(`#${this.getTableName()}_resetFilters`).show();
|
||||||
|
if (this.currentFilterString !== '' && this.currentFilterString != null) {
|
||||||
|
$(`#${this.getTableName()}_resetFilters`).html(`${this.currentFilterString}<i class="fa fa-times"></i>`);
|
||||||
|
} else {
|
||||||
|
$(`#${this.getTableName()}_resetFilters`).html('Reset Filters');
|
||||||
|
$(`#${this.getTableName()}_resetFilters`).hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(`#${this.getTableName()}`).find('.search-controls').show();
|
||||||
|
if (serverData.length > this.pageSize) {
|
||||||
|
this.hasMoreData = true;
|
||||||
|
serverData.pop();
|
||||||
|
this.loadMoreButton.removeAttr('disabled');
|
||||||
|
this.loadMoreButton.show();
|
||||||
|
} else {
|
||||||
|
this.hasMoreData = false;
|
||||||
|
this.loadMoreButton.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scrollToElementBottom(this.container);
|
||||||
|
for (let i = 0; i < serverData.length; i++) {
|
||||||
|
data.push(this.preProcessTableData(serverData[i]));
|
||||||
|
}
|
||||||
|
this.sourceData = serverData;
|
||||||
|
if (callBackData.callBack !== undefined && callBackData.callBack != null) {
|
||||||
|
if (callBackData.callBackData === undefined || callBackData.callBackData == null) {
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
}
|
||||||
|
callBackData.callBackData.push(serverData);
|
||||||
|
callBackData.callBackData.push(data);
|
||||||
|
this.callFunction(callBackData.callBack, callBackData.callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tableData = data;
|
||||||
|
|
||||||
|
if (!(callBackData.noRender !== undefined && callBackData.noRender != null && callBackData.noRender === true)) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
this.renderObject(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectAdapter;
|
||||||
60
web/api/SubAdapterBase.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from './AdapterBase';
|
||||||
|
/**
|
||||||
|
* @class SubAdapterBase
|
||||||
|
* @param endPoint
|
||||||
|
* @param tab
|
||||||
|
* @param filter
|
||||||
|
* @param orderBy
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SubAdapterBase extends AdapterBase {
|
||||||
|
deleteRow(id) {
|
||||||
|
this.deleteParams.id = id;
|
||||||
|
this.confirmDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
createTable(elementId) {
|
||||||
|
let item; let itemHtml; let itemDelete; let itemEdit;
|
||||||
|
const data = this.getTableData();
|
||||||
|
|
||||||
|
const deleteButton = `<button id="#_id_#_delete" onclick="modJs.subModJsList['tab${elementId}'].deleteRow('_id_');return false;" type="button" style="position: absolute;bottom: 5px;right: 5px;font-size: 13px;" tooltip="Delete"><li class="fa fa-times"></li></button>`;
|
||||||
|
const editButton = `<button id="#_id_#_edit" onclick="modJs.subModJsList['tab${elementId}'].edit('_id_');return false;" type="button" style="position: absolute;bottom: 5px;right: 35px;font-size: 13px;" tooltip="Edit"><li class="fa fa-edit"></li></button>`;
|
||||||
|
|
||||||
|
const table = $('<div class="list-group"></div>');
|
||||||
|
|
||||||
|
// add Header
|
||||||
|
const header = this.getSubHeader();
|
||||||
|
table.append(header);
|
||||||
|
if (data.length === 0) {
|
||||||
|
table.append(`<a href="#" class="list-group-item">${this.getNoDataMessage()}</a>`);
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
item = data[i];
|
||||||
|
itemDelete = deleteButton.replace(/_id_/g, item[0]);
|
||||||
|
itemEdit = editButton.replace(/_id_/g, item[0]);
|
||||||
|
itemHtml = this.getSubItemHtml(item, itemDelete, itemEdit);
|
||||||
|
table.append(itemHtml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(`#${elementId}`).html('');
|
||||||
|
$(`#${elementId}`).append(table);
|
||||||
|
$('#plainMessageModel').modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
getNoDataMessage() {
|
||||||
|
return 'No data found';
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubHeader() {
|
||||||
|
const header = $(`<a href="#" onclick="return false;" class="list-group-item" style="background:#eee;"><h4 class="list-group-item-heading">${this.getSubHeaderTitle()}</h4></a>`);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default SubAdapterBase;
|
||||||
297
web/api/TableEditAdapter.js
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
/* global modJs */
|
||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import AdapterBase from './AdapterBase';
|
||||||
|
/**
|
||||||
|
* TableEditAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TableEditAdapter extends AdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.cellDataUpdates = {};
|
||||||
|
this.modulePath = '';
|
||||||
|
this.rowFieldName = '';
|
||||||
|
this.columnFieldName = '';
|
||||||
|
this.rowTable = '';
|
||||||
|
this.columnTable = '';
|
||||||
|
this.valueTable = '';
|
||||||
|
this.csvData = [];
|
||||||
|
this.columnIDMap = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
setModulePath(path) {
|
||||||
|
this.modulePath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRowFieldName(name) {
|
||||||
|
this.rowFieldName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTables(rowTable, columnTable, valueTable) {
|
||||||
|
this.rowTable = rowTable;
|
||||||
|
this.columnTable = columnTable;
|
||||||
|
this.valueTable = valueTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColumnFieldName(name) {
|
||||||
|
this.columnFieldName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
this.getAllData();
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllData(save) {
|
||||||
|
let req = {};
|
||||||
|
req.rowTable = this.rowTable;
|
||||||
|
req.columnTable = this.columnTable;
|
||||||
|
req.valueTable = this.valueTable;
|
||||||
|
req = this.addAdditionalRequestData('getAllData', req);
|
||||||
|
req.save = (save === undefined || save == null || save === false) ? 0 : 1;
|
||||||
|
const reqJson = JSON.stringify(req);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'getAllDataSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'getAllDataFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('getAllData', this.modulePath, reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataItem(row, column, allData) {
|
||||||
|
const columnData = allData[1];
|
||||||
|
const rowData = allData[0];
|
||||||
|
const serverData = allData[2];
|
||||||
|
|
||||||
|
if (column === -1) {
|
||||||
|
return rowData[row].name;
|
||||||
|
}
|
||||||
|
return this.getDataItemByKeyValues(this.rowFieldName, rowData[row].id, this.columnFieldName, columnData[column].id, serverData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataItemByKeyValues(rowKeyName, rowKeyVal, colKeyName, colKeyVal, data) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data[i][rowKeyName] === rowKeyVal && data[i][colKeyName] === colKeyVal) {
|
||||||
|
return (data[i].amount !== undefined && data[i].amount != null) ? data[i].amount : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllDataSuccessCallBack(allData) {
|
||||||
|
const serverData = allData[2];
|
||||||
|
const columnData = allData[1];
|
||||||
|
const rowData = allData[0];
|
||||||
|
const data = [];
|
||||||
|
for (let i = 0; i < rowData.length; i++) {
|
||||||
|
const row = [];
|
||||||
|
for (let j = -1; j < columnData.length; j++) {
|
||||||
|
row[j + 1] = this.getDataItem(i, j, allData);
|
||||||
|
}
|
||||||
|
data.push(this.preProcessTableData(row));
|
||||||
|
}
|
||||||
|
this.sourceData = serverData;
|
||||||
|
|
||||||
|
|
||||||
|
this.tableData = data;
|
||||||
|
this.setHeaders(columnData, rowData);
|
||||||
|
this.createTable(this.getTableName());
|
||||||
|
$(`#${this.getTableName()}Form`).hide();
|
||||||
|
$(`#${this.getTableName()}`).show();
|
||||||
|
|
||||||
|
this.csvData = [];
|
||||||
|
|
||||||
|
let tmpRow = [];
|
||||||
|
for (let i = 0; i < columnData.length; i++) {
|
||||||
|
tmpRow.push(columnData[i].name);
|
||||||
|
}
|
||||||
|
tmpRow = this.modifyCSVHeader(tmpRow);
|
||||||
|
this.csvData.push(tmpRow);
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
this.csvData.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyCSVHeader(header) {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllDataFailCallBack(callBackData, serverData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setHeaders(columns, rows) {
|
||||||
|
const headers = [];
|
||||||
|
headers.push({ sTitle: '', sWidth: '180px;' });
|
||||||
|
let sclass = '';
|
||||||
|
this.columnIDMap = {};
|
||||||
|
for (let i = 0; i < columns.length; i++) {
|
||||||
|
this.columnIDMap[columns[i].id] = i;
|
||||||
|
if (columns[i].editable === undefined || columns[i].editable == null || columns[i].editable === 'Yes') {
|
||||||
|
sclass = 'editcell';
|
||||||
|
} else {
|
||||||
|
sclass = '';
|
||||||
|
}
|
||||||
|
headers.push({
|
||||||
|
sTitle: columns[i].name,
|
||||||
|
sClass: sclass,
|
||||||
|
fnCreatedCell(nTd, sData, oData, iRow, iCol) {
|
||||||
|
$(nTd).data('colId', columns[iCol - 1].id);
|
||||||
|
$(nTd).data('rowId', rows[iRow].id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return this.headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
createTable(elementId) {
|
||||||
|
const data = this.getTableData();
|
||||||
|
const headers = this.getHeaders();
|
||||||
|
|
||||||
|
if (this.showActionButtons()) {
|
||||||
|
headers.push({ sTitle: '', sClass: 'center' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.showActionButtons()) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
data[i].push(this.getActionButtonsHtml(data[i][0], data[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let html = '';
|
||||||
|
html = `${this.getTableTopButtonHtml()}<div class="box-body table-responsive"><table cellpadding="0" cellspacing="0" border="0" class="table table-bordered table-striped" id="grid"></table></div>`;
|
||||||
|
|
||||||
|
// Find current page
|
||||||
|
const activePage = $(`#${elementId} .dataTables_paginate .active a`).html();
|
||||||
|
let start = 0;
|
||||||
|
if (activePage !== undefined && activePage != null) {
|
||||||
|
start = parseInt(activePage, 10) * 15 - 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(`#${elementId}`).html(html);
|
||||||
|
|
||||||
|
const dataTableParams = {
|
||||||
|
oLanguage: {
|
||||||
|
sLengthMenu: '_MENU_ records per page',
|
||||||
|
},
|
||||||
|
aaData: data,
|
||||||
|
aoColumns: headers,
|
||||||
|
bSort: false,
|
||||||
|
iDisplayLength: 15,
|
||||||
|
iDisplayStart: start,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const customTableParams = this.getCustomTableParams();
|
||||||
|
|
||||||
|
$.extend(dataTableParams, customTableParams);
|
||||||
|
|
||||||
|
$(`#${elementId} #grid`).dataTable(dataTableParams);
|
||||||
|
|
||||||
|
$('.dataTables_paginate ul').addClass('pagination');
|
||||||
|
$('.dataTables_length').hide();
|
||||||
|
$('.dataTables_filter input').addClass('form-control');
|
||||||
|
$('.dataTables_filter input').attr('placeholder', 'Search');
|
||||||
|
$('.dataTables_filter label').contents().filter(function () {
|
||||||
|
return (this.nodeType === 3);
|
||||||
|
}).remove();
|
||||||
|
// $('.tableActionButton').tooltip();
|
||||||
|
$(`#${elementId} #grid`).editableTableWidget();
|
||||||
|
|
||||||
|
$(`#${elementId} #grid .editcell`).on('validate', function (evt, newValue) {
|
||||||
|
return modJs.validateCellValue($(this), evt, newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.afterCreateTable(elementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterCreateTable(elementId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
addCellDataUpdate(colId, rowId, data) {
|
||||||
|
this.cellDataUpdates[`${colId}=${rowId}`] = [colId, rowId, data];
|
||||||
|
}
|
||||||
|
|
||||||
|
addAdditionalRequestData(type, req) {
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendCellDataUpdates() {
|
||||||
|
let req = this.cellDataUpdates;
|
||||||
|
req.rowTable = this.rowTable;
|
||||||
|
req.columnTable = this.columnTable;
|
||||||
|
req.valueTable = this.valueTable;
|
||||||
|
req = this.addAdditionalRequestData('updateData', req);
|
||||||
|
const reqJson = JSON.stringify(req);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'updateDataSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'updateDataFailCallBack';
|
||||||
|
this.showLoader();
|
||||||
|
this.customAction('updateData', this.modulePath, reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDataSuccessCallBack(callBackData, serverData) {
|
||||||
|
this.hideLoader();
|
||||||
|
modJs.cellDataUpdates = {};
|
||||||
|
modJs.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDataFailCallBack(callBackData, serverData) {
|
||||||
|
this.hideLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
sendAllCellDataUpdates() {
|
||||||
|
let req = this.cellDataUpdates;
|
||||||
|
req.rowTable = this.rowTable;
|
||||||
|
req.columnTable = this.columnTable;
|
||||||
|
req.valueTable = this.valueTable;
|
||||||
|
req = this.addAdditionalRequestData('updateAllData', req);
|
||||||
|
const reqJson = JSON.stringify(req);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'updateDataAllSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'updateDataAllFailCallBack';
|
||||||
|
this.showLoader();
|
||||||
|
this.customAction('updateAllData', this.modulePath, reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDataAllSuccessCallBack(callBackData, serverData) {
|
||||||
|
this.hideLoader();
|
||||||
|
modJs.cellDataUpdates = {};
|
||||||
|
modJs.getAllData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDataAllFailCallBack(callBackData, serverData) {
|
||||||
|
this.hideLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
showActionButtons() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TableEditAdapter;
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icon-css-ae" width="640" height="480">
|
|
||||||
<path fill="#00732f" d="M0 0h640v160H0z"/>
|
|
||||||
<path fill="#fff" d="M0 160h640v160H0z"/>
|
|
||||||
<path d="M0 320h640v160H0z"/>
|
|
||||||
<path fill="red" d="M0 0h220v480H0z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 257 B |
31
web/bower_components/flag-icon-css/flags/4x3/ar-back.svg
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="flag-icon-css-ar" width="640" height="480">
|
||||||
|
<path fill="#74acdf" d="M0 0h640v480H0z"/>
|
||||||
|
<path fill="#fff" d="M0 160h640v160H0z"/>
|
||||||
|
<g id="c" transform="translate(-64) scale(.96)">
|
||||||
|
<path id="a" fill="#f6b40e" stroke="#85340a" stroke-width="1.1" d="M396.8 251.3l28.5 62s.5 1.2 1.3.9c.8-.4.3-1.5.3-1.5l-23.7-64m-.7 24.1c-.4 9.4 5.4 14.6 4.7 23-.8 8.5 3.8 13.2 5 16.5 1 3.3-1.3 5.2-.3 5.7s3-2.1 2.4-6.8c-.7-4.6-4.2-6-3.4-16.3.8-10.3-4.2-12.7-3-22"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(22.5 400 250)" xlink:href="#a"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(45 400 250)" xlink:href="#a"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(67.5 400 250)" xlink:href="#a"/>
|
||||||
|
<path id="b" fill="#85340a" d="M404.3 274.4c.5 9 5.6 13 4.6 21.3 2.2-6.5-3.1-11.6-2.8-21.2m-7.7-23.8l19.5 42.6-16.3-43.9"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(22.5 400 250)" xlink:href="#b"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(45 400 250)" xlink:href="#b"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(67.5 400 250)" xlink:href="#b"/>
|
||||||
|
</g>
|
||||||
|
<use width="100%" height="100%" transform="rotate(90 320 240)" xlink:href="#c"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(180 320 240)" xlink:href="#c"/>
|
||||||
|
<use width="100%" height="100%" transform="rotate(-90 320 240)" xlink:href="#c"/>
|
||||||
|
<circle cx="320" cy="240" r="26.7" fill="#f6b40e" stroke="#85340a" stroke-width="1.4"/>
|
||||||
|
<path id="h" fill="#843511" d="M329.1 234.3c-1.8 0-3.6.8-4.6 2.4 2 1.9 6.6 2 9.7-.2a7 7 0 0 0-5.1-2.2zm0 .4c1.7 0 3.4.8 3.6 1.6-2 2.3-5.3 2-7.4.4a4.3 4.3 0 0 1 3.8-2z"/>
|
||||||
|
<use width="100%" height="100%" transform="matrix(-1 0 0 1 640.2 0)" xlink:href="#d"/>
|
||||||
|
<use width="100%" height="100%" transform="matrix(-1 0 0 1 640.2 0)" xlink:href="#e"/>
|
||||||
|
<use width="100%" height="100%" transform="translate(18.1)" xlink:href="#f"/>
|
||||||
|
<use width="100%" height="100%" transform="matrix(-1 0 0 1 640.2 0)" xlink:href="#g"/>
|
||||||
|
<path fill="#85340a" d="M316 243.7a1.9 1.9 0 1 0 1.8 2.9 4 4 0 0 0 2.2.6h.2a3.9 3.9 0 0 0 2.3-.6 1.9 1.9 0 1 0 1.8-3c.5.3.8.7.8 1.3 0 .6-.5 1.2-1.2 1.2a1.2 1.2 0 0 1-1.2-1.2 3 3 0 0 1-2.6 1.7 3 3 0 0 1-2.5-1.7 1.2 1.2 0 0 1-1.3 1.2c-.6 0-1.2-.6-1.2-1.2s.3-1 .8-1.2zm2 5.5c-2.1 0-3 1.8-4.8 3 1-.4 1.9-1.2 3.3-2s2.7.2 3.5.2c.8 0 2-1 3.5-.2 1.4.8 2.3 1.6 3.3 2-1.9-1.2-2.7-3-4.8-3a5.5 5.5 0 0 0-2 .6 5.5 5.5 0 0 0-2-.7z"/>
|
||||||
|
<path fill="#85340a" d="M317.2 251.6c-.8 0-1.8.2-3.4.6 3.7-.8 4.5.5 6.2.5 1.6 0 2.4-1.3 6.1-.5-4-1.2-4.9-.4-6.1-.4-.8 0-1.4-.3-2.8-.2z"/>
|
||||||
|
<path fill="#85340a" d="M314 252.2h-.8c4.3.5 2.3 3 6.8 3s2.5-2.5 6.8-3c-4.5-.4-3.1 2.3-6.8 2.3-3.5 0-2.4-2.3-6-2.3zm9.7 6.7a3.7 3.7 0 0 0-7.4 0 3.8 3.8 0 0 1 7.4 0z"/>
|
||||||
|
<path id="e" fill="#85340a" d="M303.4 234.3c4.7-4.1 10.7-4.8 14-1.7a8 8 0 0 1 1.5 3.5c.4 2.3-.3 4.8-2.1 7.4l.8.4a14.6 14.6 0 0 0 1.6-9.4 13.3 13.3 0 0 0-.6-2.3c-4.5-3.7-10.7-4-15.2 2z"/>
|
||||||
|
<path id="d" fill="#85340a" d="M310.8 233c2.7 0 3.3.7 4.5 1.7 1.2 1 1.9.8 2 1 .3.2 0 .8-.3.6-.5-.2-1.3-.6-2.5-1.6s-2.5-1-3.7-1c-3.7 0-5.7 3-6.2 2.8-.3-.2 2.1-3.5 6.2-3.5z"/>
|
||||||
|
<use width="100%" height="100%" transform="translate(-18.4)" xlink:href="#h"/>
|
||||||
|
<circle id="f" cx="310.9" cy="236.3" r="1.9" fill="#85340a"/>
|
||||||
|
<path id="g" fill="#85340a" d="M305.9 237.5c3.5 2.7 7 2.5 9 1.3 2-1.3 2-1.7 1.6-1.7-.4 0-.8.4-2.4 1.3-1.7.8-4.1.8-8.2-.9z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
7
web/css/rtl.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
input, button, select, textarea {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
2
web/dist/common.js
vendored
Normal file
BIN
web/dist/img/bootstrap-colorpicker/alpha-horizontal.png
vendored
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
web/dist/img/bootstrap-colorpicker/alpha.png
vendored
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
web/dist/img/bootstrap-colorpicker/hue-horizontal.png
vendored
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
web/dist/img/bootstrap-colorpicker/hue.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
web/dist/img/bootstrap-colorpicker/saturation.png
vendored
Normal file
|
After Width: | Height: | Size: 8.6 KiB |