Push changes to frontend

This commit is contained in:
Thilina Hasantha
2019-02-03 13:57:59 +01:00
parent 96b0ad8496
commit 067af27b76
139 changed files with 69635 additions and 12 deletions

View File

@@ -1 +0,0 @@
../gettext/languages/bin/export-plural-rules

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env php
<?php
include 'export-plural-rules.php';

View File

@@ -1 +0,0 @@
../gettext/languages/bin/export-plural-rules.php

View 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;
}
}

View File

@@ -1 +0,0 @@
../cebe/markdown/bin/markdown

170
core/lib/composer/vendor/bin/markdown vendored Executable file
View 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);
}

View File

@@ -1 +0,0 @@
../phpunit/phpunit/phpunit

53
core/lib/composer/vendor/bin/phpunit vendored Executable file
View 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();

View File

@@ -1 +0,0 @@
../consolidation/robo/robo

22
core/lib/composer/vendor/bin/robo vendored Executable file
View 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);

View File

@@ -1 +0,0 @@
docs/guide.txt

View 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

File diff suppressed because one or more lines are too long

2
web/admin/dist/company_structure.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/dashboard.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/data.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/employees.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/fieldnames.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/jobs.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/loans.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/metadata.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/modules.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/overtime.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/payroll.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/permissions.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/projects.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/qualifications.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/reports.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/salary.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/settings.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/travel.js vendored Normal file

File diff suppressed because one or more lines are too long

2
web/admin/dist/users.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import { AttendanceAdapter, AttendanceStatusAdapter } from './lib';
window.AttendanceAdapter = AttendanceAdapter;
window.AttendanceStatusAdapter = AttendanceStatusAdapter;

View 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 };

View File

@@ -0,0 +1,4 @@
import { CompanyStructureAdapter, CompanyGraphAdapter } from './lib';
window.CompanyStructureAdapter = CompanyStructureAdapter;
window.CompanyGraphAdapter = CompanyGraphAdapter;

View 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 };

View File

@@ -0,0 +1,3 @@
import { DashboardAdapter } from './lib';
window.DashboardAdapter = DashboardAdapter;

View 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 };

View 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
View 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 };

View 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;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
import { FieldNameAdapter, CustomFieldAdapter } from './lib';
window.FieldNameAdapter = FieldNameAdapter;
window.CustomFieldAdapter = CustomFieldAdapter;

View 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 };

View 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
View 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 };

View 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
View 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,
};

View 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;

View 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,
};

View File

@@ -0,0 +1,4 @@
import { ModuleAdapter, UsageAdapter } from './lib';
window.ModuleAdapter = ModuleAdapter;
window.UsageAdapter = UsageAdapter;

View 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 };

View File

@@ -0,0 +1,7 @@
import {
OvertimeCategoryAdapter,
EmployeeOvertimeAdminAdapter,
} from './lib';
window.OvertimeCategoryAdapter = OvertimeCategoryAdapter;
window.EmployeeOvertimeAdminAdapter = EmployeeOvertimeAdminAdapter;

View 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,
};

View 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;

View 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,
};

View File

@@ -0,0 +1,3 @@
import { PermissionAdapter } from './lib';
window.PermissionAdapter = PermissionAdapter;

View 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 };

View File

@@ -0,0 +1,9 @@
import {
ClientAdapter,
ProjectAdapter,
EmployeeProjectAdapter,
} from './lib';
window.ClientAdapter = ClientAdapter;
window.ProjectAdapter = ProjectAdapter;
window.EmployeeProjectAdapter = EmployeeProjectAdapter;

View 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,
};

View File

@@ -0,0 +1,11 @@
import {
SkillAdapter,
EducationAdapter,
CertificationAdapter,
LanguageAdapter,
} from './lib';
window.SkillAdapter = SkillAdapter;
window.EducationAdapter = EducationAdapter;
window.CertificationAdapter = CertificationAdapter;
window.LanguageAdapter = LanguageAdapter;

View 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,
};

View File

@@ -0,0 +1,4 @@
import { ReportAdapter, ReportGenAdapter } from './lib';
window.ReportAdapter = ReportAdapter;
window.ReportGenAdapter = ReportGenAdapter;

View 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 };

View 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
View 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,
};

View File

@@ -0,0 +1,3 @@
import { SettingAdapter } from './lib';
window.SettingAdapter = SettingAdapter;

View 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 };

View 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
View 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,
};

View 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
View 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
View 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;

View 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;

View 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
View 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;

View 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");
}

View 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 = '&hellip;';
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="#">&larr; '+oLang.sPrevious+'</a></li>'+
'<li class="next disabled"><a href="#">'+oLang.sNext+' &rarr; </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"
}
} );
}

File diff suppressed because it is too large Load Diff

28
web/api-common/entry.js Normal file
View 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;
};

View 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;

View 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;

View 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;

View 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;

View 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
View 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
View 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>&nbsp;&nbsp;<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

File diff suppressed because it is too large Load Diff

216
web/api/ObjectAdapter.js Normal file
View 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
View 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
View 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;

View File

@@ -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

View 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
View File

@@ -0,0 +1,7 @@
input, button, select, textarea {
direction: rtl;
}
table {
direction: rtl;
}

2
web/dist/common.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Some files were not shown because too many files have changed in this diff Show More