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 |