Merge pull request 'master' (#5) from master into v1.0.2
Reviewed-on: #5
This commit is contained in:
commit
1611624367
376
system/autoload/PEAR2/Autoload.php
Normal file
376
system/autoload/PEAR2/Autoload.php
Normal file
@ -0,0 +1,376 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Standard Autoloader for PEAR2
|
||||
*
|
||||
* PEAR2_Autoload is the standard method of class loading for development and
|
||||
* low-volume web sites using PEAR2 packages.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category PEAR2
|
||||
* @package PEAR2_Autoload
|
||||
* @author Gregory Beaver <cellog@php.net>
|
||||
* @author Brett Bieber <saltybeagle@php.net>
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
||||
* @version 0.3.0
|
||||
* @link http://pear2.php.net/PEAR2_Autoload
|
||||
*/
|
||||
namespace PEAR2;
|
||||
|
||||
if (!class_exists('\PEAR2\Autoload', false)) {
|
||||
/**
|
||||
* Standard Autoloader for PEAR2
|
||||
*
|
||||
* PEAR2_Autoload is the standard method of class loading for development
|
||||
* and low-volume web sites using PEAR2 packages.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category PEAR2
|
||||
* @package PEAR2_Autoload
|
||||
* @author Gregory Beaver <cellog@php.net>
|
||||
* @author Brett Bieber <saltybeagle@php.net>
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD
|
||||
* New BSDLicense
|
||||
* @link http://pear2.php.net/PEAR2_Autoload
|
||||
*/
|
||||
class Autoload
|
||||
{
|
||||
/**
|
||||
* Used at {@link initialize()} to specify that the load function, path
|
||||
* and map should be appended to the respective lists.
|
||||
*/
|
||||
const APPEND = 0;
|
||||
|
||||
/**
|
||||
* Used at {@link initialize()} to specify that the load function should
|
||||
* be prepended on the autoload stack, instead of being appended.
|
||||
*/
|
||||
const PREPEND_LOAD = 1;
|
||||
|
||||
/**
|
||||
* Used at {@link initialize()} to specify that the path should be
|
||||
* prepended on the list of paths, instead of being appended.
|
||||
*/
|
||||
const PREPEND_PATH = 2;
|
||||
|
||||
/**
|
||||
* Used at {@link initialize()} to specify that the map should be
|
||||
* prepended on the list of maps, instead of being appended.
|
||||
*/
|
||||
const PREPEND_MAP = 4;
|
||||
|
||||
/**
|
||||
* Used at {@link initialize()} to specify that the load function, path
|
||||
* and map should be prepended on their respective lists, instead of
|
||||
* being appended.
|
||||
*/
|
||||
const PREPEND = 7;
|
||||
|
||||
/**
|
||||
* Whether the autoload class has been spl_autoload_register-ed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $registered = false;
|
||||
|
||||
/**
|
||||
* Array of PEAR2 autoload paths registered
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $paths = array();
|
||||
|
||||
/**
|
||||
* Array of classname-to-file mapping
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $map = array();
|
||||
|
||||
/**
|
||||
* Array of class maps loaded
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $maps = array();
|
||||
|
||||
/**
|
||||
* Last classmap specified
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $mapfile = null;
|
||||
|
||||
/**
|
||||
* Array of classes loaded automatically not in the map
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $unmapped = array();
|
||||
|
||||
/**
|
||||
* Array of functions to be checked in exception traces.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $checkFunctions = array(
|
||||
'class_exists', 'interface_exists'
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialize the PEAR2 autoloader
|
||||
*
|
||||
* @param string $path Directory path(s) to register.
|
||||
* @param string $mapfile Path to a mapping file to register.
|
||||
* @param int $flags A bitmaks with options for the autoloader.
|
||||
* See the PREPEND(_*) constants for details.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function initialize(
|
||||
$path,
|
||||
$mapfile = null,
|
||||
$flags = self::APPEND
|
||||
) {
|
||||
self::register(0 !== $flags & self::PREPEND_LOAD);
|
||||
self::addPath($path, 0 !== ($flags & self::PREPEND_PATH));
|
||||
self::addMap($mapfile, 0 !== ($flags & self::PREPEND_MAP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the PEAR2 autoload class with spl_autoload_register
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the load function to the
|
||||
* autoload stack instead of appending it.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function register($prepend = false)
|
||||
{
|
||||
if (!self::$registered) {
|
||||
// set up __autoload
|
||||
$autoload = spl_autoload_functions();
|
||||
spl_autoload_register('PEAR2\Autoload::load', true, $prepend);
|
||||
if (function_exists('__autoload') && ($autoload === false)) {
|
||||
// __autoload() was being used, but now would be ignored,
|
||||
// add it to the autoload stack
|
||||
spl_autoload_register('__autoload');
|
||||
}
|
||||
if (function_exists('trait_exists')) {
|
||||
self::$checkFunctions[] = 'trait_exists';
|
||||
}
|
||||
self::$registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a path
|
||||
*
|
||||
* @param string $paths The folder(s) to add to the set of paths.
|
||||
* @param bool $prepend Whether to prepend the path to the list of
|
||||
* paths instead of appending it.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function addPath($paths, $prepend = false)
|
||||
{
|
||||
foreach (explode(PATH_SEPARATOR, $paths) as $path) {
|
||||
if (!in_array($path, self::$paths)) {
|
||||
if ($prepend) {
|
||||
self::$paths = array_merge(array($path), self::$paths);
|
||||
} else {
|
||||
self::$paths[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a classname-to-file map
|
||||
*
|
||||
* @param string $mapfile The filename of the classmap.
|
||||
* @param bool $prepend Whether to prepend the map to the list of maps
|
||||
* instead of appending it.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function addMap($mapfile, $prepend = false)
|
||||
{
|
||||
if (!in_array($mapfile, self::$maps)) {
|
||||
// keep track of specific map file loaded in this
|
||||
// instance so we can update it if necessary
|
||||
self::$mapfile = $mapfile;
|
||||
|
||||
if (is_file($mapfile)) {
|
||||
$map = include $mapfile;
|
||||
if (is_array($map)) {
|
||||
// mapfile contains a valid map, so we'll keep it
|
||||
if ($prepend) {
|
||||
self::$maps = array_merge(
|
||||
array($mapfile),
|
||||
self::$maps
|
||||
);
|
||||
self::$map = array_merge($map, self::$map);
|
||||
} else {
|
||||
self::$maps[] = $mapfile;
|
||||
self::$map = array_merge(self::$map, $map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the class is already defined in a classmap
|
||||
*
|
||||
* @param string $class The class to look for
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isMapped($class)
|
||||
{
|
||||
if (isset(self::$map[$class])) {
|
||||
return true;
|
||||
}
|
||||
if (isset(self::$mapfile) && ! isset(self::$map[$class])) {
|
||||
self::$unmapped[] = $class;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a PEAR2 class
|
||||
*
|
||||
* @param string $class The class to load
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function load($class)
|
||||
{
|
||||
// need to check if there's a current map file specified ALSO.
|
||||
// this could be the first time writing it.
|
||||
$mapped = self::isMapped($class);
|
||||
if ($mapped && is_file(self::$map[$class])) {
|
||||
include self::$map[$class];
|
||||
if (!self::loadSuccessful($class)) {
|
||||
// record this failure & keep going, we may still find it
|
||||
self::$unmapped[] = $class;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$file = '';
|
||||
$className = $class;
|
||||
if (false !== $lastNsPos = strrpos($class, '\\')) {
|
||||
$namespace = substr($class, 0, $lastNsPos);
|
||||
$className = substr($class, $lastNsPos + 1);
|
||||
$file = str_replace(
|
||||
'\\',
|
||||
DIRECTORY_SEPARATOR,
|
||||
$namespace
|
||||
) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
$file .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
|
||||
foreach (self::$paths as $path) {
|
||||
if (is_file($path . DIRECTORY_SEPARATOR . $file)) {
|
||||
include $path . DIRECTORY_SEPARATOR . $file;
|
||||
if (!self::loadSuccessful($class)) {
|
||||
if (count(spl_autoload_functions()) > 1) {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception(
|
||||
'Class ' . $class . ' was not present in ' .
|
||||
$path . DIRECTORY_SEPARATOR . $file .
|
||||
'") [PEAR2_Autoload-@PACKAGE_VERSION@]'
|
||||
);
|
||||
}
|
||||
|
||||
if (in_array($class, self::$unmapped)) {
|
||||
self::updateMap(
|
||||
$class,
|
||||
$path . DIRECTORY_SEPARATOR . $file
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (count(spl_autoload_functions()) > 1) {
|
||||
return false;
|
||||
}
|
||||
$e = new \Exception(
|
||||
'Class ' . $class . ' could not be loaded from ' .
|
||||
$file . ', file does not exist (registered paths="' .
|
||||
implode(PATH_SEPARATOR, self::$paths) .
|
||||
'") [PEAR2_Autoload-@PACKAGE_VERSION@]'
|
||||
);
|
||||
$trace = $e->getTrace();
|
||||
if (isset($trace[2]) && isset($trace[2]['function'])
|
||||
&& in_array($trace[2]['function'], self::$checkFunctions)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (isset($trace[1]) && isset($trace[1]['function'])
|
||||
&& in_array($trace[1]['function'], self::$checkFunctions)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the requested class was loaded from the specified path
|
||||
*
|
||||
* @param string $class The name of the class to check.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function loadSuccessful($class)
|
||||
{
|
||||
return class_exists($class, false)
|
||||
|| interface_exists($class, false)
|
||||
|| (in_array('trait_exists', self::$checkFunctions, true)
|
||||
&& trait_exists($class, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* If possible, update the classmap file with newly-discovered
|
||||
* mapping.
|
||||
*
|
||||
* @param string $class Class name discovered
|
||||
* @param string $origin File where class was found
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function updateMap($class, $origin)
|
||||
{
|
||||
if (is_writable(self::$mapfile)
|
||||
|| is_writable(dirname(self::$mapfile))
|
||||
) {
|
||||
self::$map[$class] = $origin;
|
||||
file_put_contents(
|
||||
self::$mapfile,
|
||||
'<'."?php\n"
|
||||
. "// PEAR2\Autoload auto-generated classmap\n"
|
||||
. "return " . var_export(self::$map, true) . ';',
|
||||
LOCK_EX
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of paths PEAR2 autoload has registered
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPaths()
|
||||
{
|
||||
return self::$paths;
|
||||
}
|
||||
}
|
||||
}
|
||||
Autoload::initialize(dirname(__DIR__));
|
375
system/autoload/PEAR2/Cache/SHM.php
Normal file
375
system/autoload/PEAR2/Cache/SHM.php
Normal file
@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for shared memory and locking functionality across different extensions.
|
||||
|
||||
*
|
||||
* Allows you to share data across requests as long as the PHP process is running. One of APC or WinCache is required to accomplish this, with other extensions being potentially pluggable as adapters.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 0.2.0
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Cache;
|
||||
|
||||
/**
|
||||
* Used as a catch-all for adapter initialization.
|
||||
*/
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* Implements this class.
|
||||
*/
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Used on failures by this class.
|
||||
*/
|
||||
use PEAR2\Cache\SHM\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Main class for this package.
|
||||
*
|
||||
* Automatically chooses an adapter based on the available extensions.
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
abstract class SHM implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* An array of adapter names that meet their requirements.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $_adapters = array();
|
||||
|
||||
/**
|
||||
* Creates a new shared memory storage.
|
||||
*
|
||||
* Establishes a separate persistent storage. Adapter is automatically
|
||||
* chosen based on the available extensions.
|
||||
*
|
||||
* @param string $persistentId The ID for the storage.
|
||||
*
|
||||
* @return static|SHM A new instance of an SHM adapter (child of this
|
||||
* class).
|
||||
*/
|
||||
final public static function factory($persistentId)
|
||||
{
|
||||
foreach (self::$_adapters as $adapter) {
|
||||
try {
|
||||
return new $adapter($persistentId);
|
||||
} catch (E $e) {
|
||||
//In case of a runtime error, try to fallback to other adapters.
|
||||
}
|
||||
}
|
||||
throw new InvalidArgumentException(
|
||||
'No appropriate adapter available',
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the adapter meets its requirements.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public static function isMeetingRequirements()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an adapter.
|
||||
*
|
||||
* Registers an SHM adapter, allowing you to call it with {@link factory()}.
|
||||
*
|
||||
* @param string $adapter FQCN of adapter. A valid adapter is one that
|
||||
* extends this class. The class will be autoloaded if not already
|
||||
* present.
|
||||
* @param bool $prepend Whether to prepend this adapter into the list of
|
||||
* possible adapters, instead of appending to it.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
final public static function registerAdapter($adapter, $prepend = false)
|
||||
{
|
||||
if (class_exists($adapter, true)
|
||||
&& is_subclass_of($adapter, '\\' . __CLASS__)
|
||||
&& $adapter::isMeetingRequirements()
|
||||
) {
|
||||
if ($prepend) {
|
||||
self::$_adapters = array_merge(
|
||||
array($adapter),
|
||||
self::$_adapters
|
||||
);
|
||||
} else {
|
||||
self::$_adapters[] = $adapter;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, or fails if it does.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function __invoke($key, $value, $ttl = 0)
|
||||
{
|
||||
return $this->add($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the shared memory storage.
|
||||
*
|
||||
* This is a magic method, thanks to which any property you attempt to get
|
||||
* the value of will be fetched from the adapter, treating the property name
|
||||
* as the key of the value to get.
|
||||
*
|
||||
* @param string $key Name of key to get.
|
||||
*
|
||||
* @return mixed The current value of the specified key.
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the shared memory storage.
|
||||
*
|
||||
* This is a magic method, thanks to which any property you attempt to set
|
||||
* the value of will be set by the adapter, treating the property name as
|
||||
* the key of the value to set. The value is set without a TTL.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
return $this->set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified key is in the storage.
|
||||
*
|
||||
* This is a magic method, thanks to which any property you call isset() on
|
||||
* will be checked by the adapter, treating the property name as the key
|
||||
* of the value to check.
|
||||
*
|
||||
* @param string $key Name of key to check.
|
||||
*
|
||||
* @return bool TRUE if the key is in the storage, FALSE otherwise.
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return $this->exists($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the shared memory storage.
|
||||
*
|
||||
* This is a magic method, thanks to which any property you attempt to unset
|
||||
* the value of will be unset by the adapter, treating the property name as
|
||||
* the key of the value to delete.
|
||||
*
|
||||
* @param string $key Name of key to delete.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function __unset($key)
|
||||
{
|
||||
return $this->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new shared memory storage.
|
||||
*
|
||||
* Establishes a separate persistent storage.
|
||||
*
|
||||
* @param string $persistentId The ID for the storage. The storage will be
|
||||
* reused if it exists, or created if it doesn't exist. Data and locks
|
||||
* are namespaced by this ID.
|
||||
*/
|
||||
abstract public function __construct($persistentId);
|
||||
|
||||
/**
|
||||
* Obtains a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to obtain. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
* @param double $timeout If the lock can't be immediately obtained, the
|
||||
* script will block for at most the specified amount of seconds.
|
||||
* Setting this to 0 makes lock obtaining non blocking, and setting it
|
||||
* to NULL makes it block without a time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
abstract public function lock($key, $timeout = null);
|
||||
|
||||
/**
|
||||
* Releases a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to release. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
abstract public function unlock($key);
|
||||
|
||||
/**
|
||||
* Checks if a specified key is in the storage.
|
||||
*
|
||||
* @param string $key Name of key to check.
|
||||
*
|
||||
* @return bool TRUE if the key is in the storage, FALSE otherwise.
|
||||
*/
|
||||
abstract public function exists($key);
|
||||
|
||||
/**
|
||||
* Adds a value to the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, or fails if it does.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
abstract public function add($key, $value, $ttl = 0);
|
||||
|
||||
/**
|
||||
* Sets a value in the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
abstract public function set($key, $value, $ttl = 0);
|
||||
|
||||
/**
|
||||
* Gets a value from the shared memory storage.
|
||||
*
|
||||
* Gets the current value, or throws an exception if it's not stored.
|
||||
*
|
||||
* @param string $key Name of key to get the value of.
|
||||
*
|
||||
* @return mixed The current value of the specified key.
|
||||
*/
|
||||
abstract public function get($key);
|
||||
|
||||
/**
|
||||
* Deletes a value from the shared memory storage.
|
||||
*
|
||||
* @param string $key Name of key to delete.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
abstract public function delete($key);
|
||||
|
||||
/**
|
||||
* Increases a value from the shared memory storage.
|
||||
*
|
||||
* Increases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)+$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to increase.
|
||||
* @param int $step Value to increase the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
abstract public function inc($key, $step = 1);
|
||||
|
||||
/**
|
||||
* Decreases a value from the shared memory storage.
|
||||
*
|
||||
* Decreases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)-$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to decrease.
|
||||
* @param int $step Value to decrease the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
abstract public function dec($key, $step = 1);
|
||||
|
||||
/**
|
||||
* Sets a new value if a key has a certain value.
|
||||
*
|
||||
* Sets a new value if a key has a certain value. This function only works
|
||||
* when $old and $new are longs.
|
||||
*
|
||||
* @param string $key Key of the value to compare and set.
|
||||
* @param int $old The value to compare the key against.
|
||||
* @param int $new The value to set the key to.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
abstract public function cas($key, $old, $new);
|
||||
|
||||
/**
|
||||
* Clears the persistent storage.
|
||||
*
|
||||
* Clears the persistent storage, i.e. removes all keys. Locks are left
|
||||
* intact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function clear();
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* Returns an external iterator.
|
||||
*
|
||||
* @param string|null $filter A PCRE regular expression.
|
||||
* Only matching keys will be iterated over.
|
||||
* Setting this to NULL matches all keys of this instance.
|
||||
* @param bool $keysOnly Whether to return only the keys,
|
||||
* or return both the keys and values.
|
||||
*
|
||||
* @return \Traversable An array with all matching keys as array keys,
|
||||
* and values as array values. If $keysOnly is TRUE, the array keys are
|
||||
* numeric, and the array values are key names.
|
||||
*/
|
||||
abstract public function getIterator($filter = null, $keysOnly = false);
|
||||
}
|
||||
|
||||
SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\Placebo');
|
||||
SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\Wincache');
|
||||
SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\APCu');
|
||||
SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\APC');
|
417
system/autoload/PEAR2/Cache/SHM/Adaptor/APC.php
Normal file
417
system/autoload/PEAR2/Cache/SHM/Adaptor/APC.php
Normal file
@ -0,0 +1,417 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for shared memory and locking functionality across different extensions.
|
||||
|
||||
*
|
||||
* Allows you to share data across requests as long as the PHP process is running. One of APC or WinCache is required to accomplish this, with other extensions being potentially pluggable as adapters.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 0.2.0
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Cache\SHM\Adapter;
|
||||
|
||||
/**
|
||||
* Throws exceptions from this namespace, and extends from this class.
|
||||
*/
|
||||
use PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* {@link APC::getIterator()} returns this object.
|
||||
*/
|
||||
use ArrayObject;
|
||||
|
||||
/**
|
||||
* Shared memory adapter for the APC extension.
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
class APC extends SHM
|
||||
{
|
||||
/**
|
||||
* ID of the current storage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $persistentId;
|
||||
|
||||
/**
|
||||
* List of persistent IDs.
|
||||
*
|
||||
* A list of persistent IDs within the current request (as keys) with an int
|
||||
* (as a value) specifying the number of instances in the current request.
|
||||
* Used as an attempt to ensure implicit lock releases even on errors in the
|
||||
* critical sections, since APC doesn't have an actual locking function.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $requestInstances = array();
|
||||
|
||||
/**
|
||||
* Array of lock names for each persistent ID.
|
||||
*
|
||||
* Array of lock names (as values) for each persistent ID (as key) obtained
|
||||
* during the current request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $locksBackup = array();
|
||||
|
||||
/**
|
||||
* Creates a new shared memory storage.
|
||||
*
|
||||
* Establishes a separate persistent storage.
|
||||
*
|
||||
* @param string $persistentId The ID for the storage. The storage will be
|
||||
* reused if it exists, or created if it doesn't exist. Data and locks
|
||||
* are namespaced by this ID.
|
||||
*/
|
||||
public function __construct($persistentId)
|
||||
{
|
||||
$this->persistentId = __CLASS__ . ' ' . $persistentId;
|
||||
if (isset(static::$requestInstances[$this->persistentId])) {
|
||||
static::$requestInstances[$this->persistentId]++;
|
||||
} else {
|
||||
static::$requestInstances[$this->persistentId] = 1;
|
||||
static::$locksBackup[$this->persistentId] = array();
|
||||
}
|
||||
register_shutdown_function(
|
||||
get_called_class() . '::releaseLocks',
|
||||
$this->persistentId,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the adapter meets its requirements.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public static function isMeetingRequirements()
|
||||
{
|
||||
return extension_loaded('apc')
|
||||
&& version_compare(phpversion('apc'), '3.1.1', '>=')
|
||||
&& ini_get('apc.enabled')
|
||||
&& ('cli' !== PHP_SAPI || ini_get('apc.enable_cli'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all locks in a storage.
|
||||
*
|
||||
* This function is not meant to be used directly. It is implicitly called
|
||||
* by the the destructor and as a shutdown function when the request ends.
|
||||
* One of these calls ends up releasing any unreleased locks obtained
|
||||
* during the request. A lock is also implicitly released as soon as there
|
||||
* are no objects left in the current request using the same persistent ID.
|
||||
*
|
||||
* @param string $internalPersistentId The internal persistent ID, the locks
|
||||
* of which are being released.
|
||||
* @param bool $isAtShutdown Whether the function was executed at
|
||||
* shutdown.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function releaseLocks($internalPersistentId, $isAtShutdown)
|
||||
{
|
||||
$hasInstances = 0 !== static::$requestInstances[$internalPersistentId];
|
||||
if ($isAtShutdown === $hasInstances) {
|
||||
foreach (static::$locksBackup[$internalPersistentId] as $key) {
|
||||
apc_delete($internalPersistentId . 'l ' . $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any locks obtained by this instance as soon as there are no more
|
||||
* references to the object's persistent ID.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
static::$requestInstances[$this->persistentId]--;
|
||||
static::releaseLocks($this->persistentId, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to obtain. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
* @param double $timeout If the lock can't be immediately obtained, the
|
||||
* script will block for at most the specified amount of seconds.
|
||||
* Setting this to 0 makes lock obtaining non blocking, and setting it
|
||||
* to NULL makes it block without a time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function lock($key, $timeout = null)
|
||||
{
|
||||
$lock = $this->persistentId . 'l ' . $key;
|
||||
$hasTimeout = $timeout !== null;
|
||||
$start = microtime(true);
|
||||
while (!apc_add($lock, 1)) {
|
||||
if ($hasTimeout && (microtime(true) - $start) > $timeout) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static::$locksBackup[$this->persistentId] = $key;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to release. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$lock = $this->persistentId . 'l ' . $key;
|
||||
$success = apc_delete($lock);
|
||||
if ($success) {
|
||||
unset(
|
||||
static::$locksBackup[$this->persistentId][array_search(
|
||||
$key,
|
||||
static::$locksBackup[$this->persistentId],
|
||||
true
|
||||
)]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified key is in the storage.
|
||||
*
|
||||
* @param string $key Name of key to check.
|
||||
*
|
||||
* @return bool TRUE if the key is in the storage, FALSE otherwise.
|
||||
*/
|
||||
public function exists($key)
|
||||
{
|
||||
return apc_exists($this->persistentId . 'd ' . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, or fails if it does.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function add($key, $value, $ttl = 0)
|
||||
{
|
||||
return apc_add($this->persistentId . 'd ' . $key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function set($key, $value, $ttl = 0)
|
||||
{
|
||||
return apc_store($this->persistentId . 'd ' . $key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the shared memory storage.
|
||||
*
|
||||
* Gets the current value, or throws an exception if it's not stored.
|
||||
*
|
||||
* @param string $key Name of key to get the value of.
|
||||
*
|
||||
* @return mixed The current value of the specified key.
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
$fullKey = $this->persistentId . 'd ' . $key;
|
||||
if (apc_exists($fullKey)) {
|
||||
$value = apc_fetch($fullKey, $success);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to fetch key. ' .
|
||||
'Key has either just now expired or (if no TTL was set) ' .
|
||||
'is possibly in a race condition with another request.',
|
||||
100
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
throw new SHM\InvalidArgumentException('No such key in cache', 101);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the shared memory storage.
|
||||
*
|
||||
* @param string $key Name of key to delete.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return apc_delete($this->persistentId . 'd ' . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases a value from the shared memory storage.
|
||||
*
|
||||
* Increases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)+$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to increase.
|
||||
* @param int $step Value to increase the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function inc($key, $step = 1)
|
||||
{
|
||||
$newValue = apc_inc(
|
||||
$this->persistentId . 'd ' . $key,
|
||||
(int) $step,
|
||||
$success
|
||||
);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to increase the value. Are you sure the value is int?',
|
||||
102
|
||||
);
|
||||
}
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases a value from the shared memory storage.
|
||||
*
|
||||
* Decreases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)-$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to decrease.
|
||||
* @param int $step Value to decrease the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function dec($key, $step = 1)
|
||||
{
|
||||
$newValue = apc_dec(
|
||||
$this->persistentId . 'd ' . $key,
|
||||
(int) $step,
|
||||
$success
|
||||
);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to decrease the value. Are you sure the value is int?',
|
||||
103
|
||||
);
|
||||
}
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value if a key has a certain value.
|
||||
*
|
||||
* Sets a new value if a key has a certain value. This function only works
|
||||
* when $old and $new are longs.
|
||||
*
|
||||
* @param string $key Key of the value to compare and set.
|
||||
* @param int $old The value to compare the key against.
|
||||
* @param int $new The value to set the key to.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function cas($key, $old, $new)
|
||||
{
|
||||
return apc_cas($this->persistentId . 'd ' . $key, $old, $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the persistent storage.
|
||||
*
|
||||
* Clears the persistent storage, i.e. removes all keys. Locks are left
|
||||
* intact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
foreach (new APCIterator(
|
||||
'user',
|
||||
'/^' . preg_quote($this->persistentId, '/') . 'd /',
|
||||
APC_ITER_KEY,
|
||||
100,
|
||||
APC_LIST_ACTIVE
|
||||
) as $key) {
|
||||
apc_delete($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* Returns an external iterator.
|
||||
*
|
||||
* @param string|null $filter A PCRE regular expression.
|
||||
* Only matching keys will be iterated over.
|
||||
* Setting this to NULL matches all keys of this instance.
|
||||
* @param bool $keysOnly Whether to return only the keys,
|
||||
* or return both the keys and values.
|
||||
*
|
||||
* @return ArrayObject An array with all matching keys as array keys,
|
||||
* and values as array values. If $keysOnly is TRUE, the array keys are
|
||||
* numeric, and the array values are key names.
|
||||
*/
|
||||
public function getIterator($filter = null, $keysOnly = false)
|
||||
{
|
||||
$result = array();
|
||||
foreach (new APCIterator(
|
||||
'user',
|
||||
'/^' . preg_quote($this->persistentId, '/') . 'd /',
|
||||
APC_ITER_KEY,
|
||||
100,
|
||||
APC_LIST_ACTIVE
|
||||
) as $key) {
|
||||
$localKey = strstr($key, $this->persistentId . 'd ');
|
||||
if (null === $filter || preg_match($filter, $localKey)) {
|
||||
if ($keysOnly) {
|
||||
$result[] = $localKey;
|
||||
} else {
|
||||
$result[$localKey] = apc_fetch($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayObject($result);
|
||||
}
|
||||
}
|
416
system/autoload/PEAR2/Cache/SHM/Adaptor/APCu.php
Normal file
416
system/autoload/PEAR2/Cache/SHM/Adaptor/APCu.php
Normal file
@ -0,0 +1,416 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for shared memory and locking functionality across different extensions.
|
||||
|
||||
*
|
||||
* Allows you to share data across requests as long as the PHP process is running. One of APC or WinCache is required to accomplish this, with other extensions being potentially pluggable as adapters.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 0.2.0
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Cache\SHM\Adapter;
|
||||
|
||||
/**
|
||||
* Throws exceptions from this namespace, and extends from this class.
|
||||
*/
|
||||
use PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* {@link APC::getIterator()} returns this object.
|
||||
*/
|
||||
use ArrayObject;
|
||||
|
||||
/**
|
||||
* Shared memory adapter for the APC extension.
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
class APCu extends SHM
|
||||
{
|
||||
/**
|
||||
* ID of the current storage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $persistentId;
|
||||
|
||||
/**
|
||||
* List of persistent IDs.
|
||||
*
|
||||
* A list of persistent IDs within the current request (as keys) with an int
|
||||
* (as a value) specifying the number of instances in the current request.
|
||||
* Used as an attempt to ensure implicit lock releases even on errors in the
|
||||
* critical sections, since APC doesn't have an actual locking function.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $requestInstances = array();
|
||||
|
||||
/**
|
||||
* Array of lock names for each persistent ID.
|
||||
*
|
||||
* Array of lock names (as values) for each persistent ID (as key) obtained
|
||||
* during the current request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $locksBackup = array();
|
||||
|
||||
/**
|
||||
* Creates a new shared memory storage.
|
||||
*
|
||||
* Establishes a separate persistent storage.
|
||||
*
|
||||
* @param string $persistentId The ID for the storage. The storage will be
|
||||
* reused if it exists, or created if it doesn't exist. Data and locks
|
||||
* are namespaced by this ID.
|
||||
*/
|
||||
public function __construct($persistentId)
|
||||
{
|
||||
$this->persistentId = __CLASS__ . ' ' . $persistentId;
|
||||
if (isset(static::$requestInstances[$this->persistentId])) {
|
||||
static::$requestInstances[$this->persistentId]++;
|
||||
} else {
|
||||
static::$requestInstances[$this->persistentId] = 1;
|
||||
static::$locksBackup[$this->persistentId] = array();
|
||||
}
|
||||
register_shutdown_function(
|
||||
get_called_class() . '::releaseLocks',
|
||||
$this->persistentId,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the adapter meets its requirements.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public static function isMeetingRequirements()
|
||||
{
|
||||
return extension_loaded('apcu')
|
||||
&& version_compare(phpversion('apcu'), '5.0.0', '>=')
|
||||
&& ini_get('apc.enabled')
|
||||
&& ('cli' !== PHP_SAPI || ini_get('apc.enable_cli'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all locks in a storage.
|
||||
*
|
||||
* This function is not meant to be used directly. It is implicitly called
|
||||
* by the the destructor and as a shutdown function when the request ends.
|
||||
* One of these calls ends up releasing any unreleased locks obtained
|
||||
* during the request. A lock is also implicitly released as soon as there
|
||||
* are no objects left in the current request using the same persistent ID.
|
||||
*
|
||||
* @param string $internalPersistentId The internal persistent ID, the locks
|
||||
* of which are being released.
|
||||
* @param bool $isAtShutdown Whether the function was executed at
|
||||
* shutdown.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function releaseLocks($internalPersistentId, $isAtShutdown)
|
||||
{
|
||||
$hasInstances = 0 !== static::$requestInstances[$internalPersistentId];
|
||||
if ($isAtShutdown === $hasInstances) {
|
||||
foreach (static::$locksBackup[$internalPersistentId] as $key) {
|
||||
apcu_delete($internalPersistentId . 'l ' . $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any locks obtained by this instance as soon as there are no more
|
||||
* references to the object's persistent ID.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
static::$requestInstances[$this->persistentId]--;
|
||||
static::releaseLocks($this->persistentId, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to obtain. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
* @param double $timeout If the lock can't be immediately obtained, the
|
||||
* script will block for at most the specified amount of seconds.
|
||||
* Setting this to 0 makes lock obtaining non blocking, and setting it
|
||||
* to NULL makes it block without a time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function lock($key, $timeout = null)
|
||||
{
|
||||
$lock = $this->persistentId . 'l ' . $key;
|
||||
$hasTimeout = $timeout !== null;
|
||||
$start = microtime(true);
|
||||
while (!apcu_add($lock, 1)) {
|
||||
if ($hasTimeout && (microtime(true) - $start) > $timeout) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static::$locksBackup[$this->persistentId] = $key;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to release. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$lock = $this->persistentId . 'l ' . $key;
|
||||
$success = apcu_delete($lock);
|
||||
if ($success) {
|
||||
unset(
|
||||
static::$locksBackup[$this->persistentId][array_search(
|
||||
$key,
|
||||
static::$locksBackup[$this->persistentId],
|
||||
true
|
||||
)]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified key is in the storage.
|
||||
*
|
||||
* @param string $key Name of key to check.
|
||||
*
|
||||
* @return bool TRUE if the key is in the storage, FALSE otherwise.
|
||||
*/
|
||||
public function exists($key)
|
||||
{
|
||||
return apcu_exists($this->persistentId . 'd ' . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, or fails if it does.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function add($key, $value, $ttl = 0)
|
||||
{
|
||||
return apcu_add($this->persistentId . 'd ' . $key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function set($key, $value, $ttl = 0)
|
||||
{
|
||||
return apcu_store($this->persistentId . 'd ' . $key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the shared memory storage.
|
||||
*
|
||||
* Gets the current value, or throws an exception if it's not stored.
|
||||
*
|
||||
* @param string $key Name of key to get the value of.
|
||||
*
|
||||
* @return mixed The current value of the specified key.
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
$fullKey = $this->persistentId . 'd ' . $key;
|
||||
if (apcu_exists($fullKey)) {
|
||||
$value = apcu_fetch($fullKey, $success);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to fetch key. ' .
|
||||
'Key has either just now expired or (if no TTL was set) ' .
|
||||
'is possibly in a race condition with another request.',
|
||||
100
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
throw new SHM\InvalidArgumentException('No such key in cache', 101);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the shared memory storage.
|
||||
*
|
||||
* @param string $key Name of key to delete.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return apcu_delete($this->persistentId . 'd ' . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases a value from the shared memory storage.
|
||||
*
|
||||
* Increases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)+$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to increase.
|
||||
* @param int $step Value to increase the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function inc($key, $step = 1)
|
||||
{
|
||||
$newValue = apcu_inc(
|
||||
$this->persistentId . 'd ' . $key,
|
||||
(int) $step,
|
||||
$success
|
||||
);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to increase the value. Are you sure the value is int?',
|
||||
102
|
||||
);
|
||||
}
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases a value from the shared memory storage.
|
||||
*
|
||||
* Decreases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)-$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to decrease.
|
||||
* @param int $step Value to decrease the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function dec($key, $step = 1)
|
||||
{
|
||||
$newValue = apcu_dec(
|
||||
$this->persistentId . 'd ' . $key,
|
||||
(int) $step,
|
||||
$success
|
||||
);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to decrease the value. Are you sure the value is int?',
|
||||
103
|
||||
);
|
||||
}
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value if a key has a certain value.
|
||||
*
|
||||
* Sets a new value if a key has a certain value. This function only works
|
||||
* when $old and $new are longs.
|
||||
*
|
||||
* @param string $key Key of the value to compare and set.
|
||||
* @param int $old The value to compare the key against.
|
||||
* @param int $new The value to set the key to.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function cas($key, $old, $new)
|
||||
{
|
||||
return apcu_cas($this->persistentId . 'd ' . $key, $old, $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the persistent storage.
|
||||
*
|
||||
* Clears the persistent storage, i.e. removes all keys. Locks are left
|
||||
* intact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
foreach (new APCIterator(
|
||||
'user',
|
||||
'/^' . preg_quote($this->persistentId, '/') . 'd /',
|
||||
APC_ITER_KEY,
|
||||
100,
|
||||
APC_LIST_ACTIVE
|
||||
) as $key) {
|
||||
apcu_delete($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* Returns an external iterator.
|
||||
*
|
||||
* @param string|null $filter A PCRE regular expression.
|
||||
* Only matching keys will be iterated over.
|
||||
* Setting this to NULL matches all keys of this instance.
|
||||
* @param bool $keysOnly Whether to return only the keys,
|
||||
* or return both the keys and values.
|
||||
*
|
||||
* @return ArrayObject An array with all matching keys as array keys,
|
||||
* and values as array values. If $keysOnly is TRUE, the array keys are
|
||||
* numeric, and the array values are key names.
|
||||
*/
|
||||
public function getIterator($filter = null, $keysOnly = false)
|
||||
{
|
||||
$result = array();
|
||||
foreach (new APCUIterator(
|
||||
'/^' . preg_quote($this->persistentId, '/') . 'd /',
|
||||
APC_ITER_KEY,
|
||||
100,
|
||||
APC_LIST_ACTIVE
|
||||
) as $key) {
|
||||
$localKey = strstr($key, $this->persistentId . 'd ');
|
||||
if (null === $filter || preg_match($filter, $localKey)) {
|
||||
if ($keysOnly) {
|
||||
$result[] = $localKey;
|
||||
} else {
|
||||
$result[$localKey] = apcu_fetch($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayObject($result);
|
||||
}
|
||||
}
|
369
system/autoload/PEAR2/Cache/SHM/Adaptor/Placebo.php
Normal file
369
system/autoload/PEAR2/Cache/SHM/Adaptor/Placebo.php
Normal file
@ -0,0 +1,369 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for shared memory and locking functionality across different extensions.
|
||||
|
||||
*
|
||||
* Allows you to share data across requests as long as the PHP process is running. One of APC or WinCache is required to accomplish this, with other extensions being potentially pluggable as adapters.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 0.2.0
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Cache\SHM\Adapter;
|
||||
|
||||
/**
|
||||
* Throws exceptions from this namespace, and extends from this class.
|
||||
*/
|
||||
use PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* {@link Placebo::getIterator()} returns this object.
|
||||
*/
|
||||
use ArrayObject;
|
||||
|
||||
/**
|
||||
* This adapter is not truly persistent. It is intended to emulate persistence
|
||||
* in non persistent environments, so that upper level applications can use a
|
||||
* single code path for persistent and non persistent code.
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
class Placebo extends SHM
|
||||
{
|
||||
/**
|
||||
* ID of the current storage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $persistentId;
|
||||
|
||||
/**
|
||||
* List of persistent IDs.
|
||||
*
|
||||
* A list of persistent IDs within the current request (as keys) with an int
|
||||
* (as a value) specifying the number of instances in the current request.
|
||||
* Used as an attempt to ensure implicit lock releases on destruction.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $requestInstances = array();
|
||||
|
||||
/**
|
||||
* Array of lock names for each persistent ID.
|
||||
*
|
||||
* Array of lock names (as values) for each persistent ID (as
|
||||
* key) obtained during the current request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $locksBackup = array();
|
||||
|
||||
/**
|
||||
* The data storage.
|
||||
*
|
||||
* Each persistent ID is a key, and the value is an array.
|
||||
* Each such array has data keys as its keys, and an array as a value.
|
||||
* Each such array has as its elements the value, the timeout and the time
|
||||
* the data was set.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $data = array();
|
||||
|
||||
/**
|
||||
* Creates a new shared memory storage.
|
||||
*
|
||||
* Establishes a separate persistent storage.
|
||||
*
|
||||
* @param string $persistentId The ID for the storage. The storage will be
|
||||
* reused if it exists, or created if it doesn't exist. Data and locks
|
||||
* are namespaced by this ID.
|
||||
*/
|
||||
public function __construct($persistentId)
|
||||
{
|
||||
if (isset(static::$requestInstances[$persistentId])) {
|
||||
++static::$requestInstances[$persistentId];
|
||||
} else {
|
||||
static::$requestInstances[$persistentId] = 1;
|
||||
static::$locksBackup[$persistentId] = array();
|
||||
static::$data[$persistentId] = array();
|
||||
}
|
||||
$this->persistentId = $persistentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any unreleased locks.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (0 === --static::$requestInstances[$this->persistentId]) {
|
||||
static::$locksBackup[$this->persistentId] = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the adapter meets its requirements.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public static function isMeetingRequirements()
|
||||
{
|
||||
return 'cli' === PHP_SAPI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretends to obtain a lock.
|
||||
*
|
||||
* @param string $key Ignored.
|
||||
* @param double $timeout Ignored.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function lock($key, $timeout = null)
|
||||
{
|
||||
$key = (string) $key;
|
||||
if (in_array($key, static::$locksBackup[$this->persistentId], true)) {
|
||||
return false;
|
||||
}
|
||||
static::$locksBackup[$this->persistentId][] = $key;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretends to release a lock.
|
||||
*
|
||||
* @param string $key Ignored
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$key = (string) $key;
|
||||
if (!in_array($key, static::$locksBackup[$this->persistentId], true)) {
|
||||
return false;
|
||||
}
|
||||
unset(
|
||||
static::$locksBackup[$this->persistentId][array_search(
|
||||
$key,
|
||||
static::$locksBackup[$this->persistentId],
|
||||
true
|
||||
)]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified key is in the storage.
|
||||
*
|
||||
* @param string $key Name of key to check.
|
||||
*
|
||||
* @return bool TRUE if the key is in the storage, FALSE otherwise.
|
||||
*/
|
||||
public function exists($key)
|
||||
{
|
||||
return array_key_exists($key, static::$data[$this->persistentId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, or fails if it does.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Because "true" adapters purge the cache at the next
|
||||
* request, this setting is ignored.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function add($key, $value, $ttl = 0)
|
||||
{
|
||||
if ($this->exists($key)) {
|
||||
return false;
|
||||
}
|
||||
return $this->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Because "true" adapters purge the cache at the next
|
||||
* request, this setting is ignored.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function set($key, $value, $ttl = 0)
|
||||
{
|
||||
static::$data[$this->persistentId][$key] = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the shared memory storage.
|
||||
*
|
||||
* Gets the current value, or throws an exception if it's not stored.
|
||||
*
|
||||
* @param string $key Name of key to get the value of.
|
||||
*
|
||||
* @return mixed The current value of the specified key.
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if ($this->exists($key)) {
|
||||
return static::$data[$this->persistentId][$key];
|
||||
}
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to fetch key. No such key.',
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the shared memory storage.
|
||||
*
|
||||
* @param string $key Name of key to delete.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
if ($this->exists($key)) {
|
||||
unset(static::$data[$this->persistentId][$key]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases a value from the shared memory storage.
|
||||
*
|
||||
* Increases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)+$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to increase.
|
||||
* @param int $step Value to increase the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function inc($key, $step = 1)
|
||||
{
|
||||
if (!$this->exists($key) || !is_int($value = $this->get($key))
|
||||
|| !$this->set($key, $value + (int) $step)
|
||||
) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to increase the value. Are you sure the value is int?',
|
||||
201
|
||||
);
|
||||
}
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases a value from the shared memory storage.
|
||||
*
|
||||
* Decreases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)-$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to decrease.
|
||||
* @param int $step Value to decrease the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function dec($key, $step = 1)
|
||||
{
|
||||
if (!$this->exists($key) || !is_int($value = $this->get($key))
|
||||
|| !$this->set($key, $value - (int) $step)
|
||||
) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to increase the value. Are you sure the value is int?',
|
||||
202
|
||||
);
|
||||
}
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value if a key has a certain value.
|
||||
*
|
||||
* Sets a new value if a key has a certain value. This function only works
|
||||
* when $old and $new are longs.
|
||||
*
|
||||
* @param string $key Key of the value to compare and set.
|
||||
* @param int $old The value to compare the key against.
|
||||
* @param int $new The value to set the key to.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function cas($key, $old, $new)
|
||||
{
|
||||
return $this->exists($key) && ($this->get($key) === $old)
|
||||
&& is_int($new) && $this->set($key, $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the persistent storage.
|
||||
*
|
||||
* Clears the persistent storage, i.e. removes all keys. Locks are left
|
||||
* intact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
static::$data[$this->persistentId] = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* Returns an external iterator.
|
||||
*
|
||||
* @param string|null $filter A PCRE regular expression.
|
||||
* Only matching keys will be iterated over.
|
||||
* Setting this to NULL matches all keys of this instance.
|
||||
* @param bool $keysOnly Whether to return only the keys,
|
||||
* or return both the keys and values.
|
||||
*
|
||||
* @return ArrayObject An array with all matching keys as array keys,
|
||||
* and values as array values. If $keysOnly is TRUE, the array keys are
|
||||
* numeric, and the array values are key names.
|
||||
*/
|
||||
public function getIterator($filter = null, $keysOnly = false)
|
||||
{
|
||||
if (null === $filter) {
|
||||
return new ArrayObject(
|
||||
$keysOnly
|
||||
? array_keys(static::$data[$this->persistentId])
|
||||
: static::$data[$this->persistentId]
|
||||
);
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach (static::$data[$this->persistentId] as $key => $value) {
|
||||
if (preg_match($filter, $key)) {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return new ArrayObject($keysOnly ? array_keys($result) : $result);
|
||||
}
|
||||
}
|
392
system/autoload/PEAR2/Cache/SHM/Adaptor/Wincache.php
Normal file
392
system/autoload/PEAR2/Cache/SHM/Adaptor/Wincache.php
Normal file
@ -0,0 +1,392 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for shared memory and locking functionality across different extensions.
|
||||
|
||||
*
|
||||
* Allows you to share data across requests as long as the PHP process is running. One of APC or WinCache is required to accomplish this, with other extensions being potentially pluggable as adapters.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 0.2.0
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Cache\SHM\Adapter;
|
||||
|
||||
/**
|
||||
* Throws exceptions from this namespace, and extends from this class.
|
||||
*/
|
||||
use PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* {@link Wincache::getIterator()} returns this object.
|
||||
*/
|
||||
use ArrayObject;
|
||||
|
||||
/**
|
||||
* Shared memory adapter for the WinCache extension.
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
class Wincache extends SHM
|
||||
{
|
||||
/**
|
||||
* ID of the current storage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $persistentId;
|
||||
|
||||
/**
|
||||
* List of persistent IDs.
|
||||
*
|
||||
* A list of persistent IDs within the current request (as keys) with an int
|
||||
* (as a value) specifying the number of instances in the current request.
|
||||
* Used as an attempt to ensure implicit lock releases on destruction.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $requestInstances = array();
|
||||
|
||||
/**
|
||||
* Array of lock names obtained during the current request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $locksBackup = array();
|
||||
|
||||
/**
|
||||
* Creates a new shared memory storage.
|
||||
*
|
||||
* Establishes a separate persistent storage.
|
||||
*
|
||||
* @param string $persistentId The ID for the storage. The storage will be
|
||||
* reused if it exists, or created if it doesn't exist. Data and locks
|
||||
* are namespaced by this ID.
|
||||
*/
|
||||
public function __construct($persistentId)
|
||||
{
|
||||
$this->persistentId
|
||||
= static::encodeLockName(__CLASS__ . ' ' . $persistentId) . ' ';
|
||||
if (isset(static::$requestInstances[$this->persistentId])) {
|
||||
static::$requestInstances[$this->persistentId]++;
|
||||
} else {
|
||||
static::$requestInstances[$this->persistentId] = 1;
|
||||
static::$locksBackup[$this->persistentId] = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a lock name
|
||||
*
|
||||
* Encodes a lock name, so that it can be properly obtained. The scheme used
|
||||
* is a subset of URL encoding, with only the "%" and "\" characters being
|
||||
* escaped. The encoding itself is necessary, since lock names can't contain
|
||||
* the "\" character.
|
||||
*
|
||||
* @param string $name The lock name to encode.
|
||||
*
|
||||
* @return string The encoded name.
|
||||
*
|
||||
* @link http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx
|
||||
*/
|
||||
protected static function encodeLockName($name)
|
||||
{
|
||||
return str_replace(array('%', '\\'), array('%25', '%5C'), $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the adapter meets its requirements.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public static function isMeetingRequirements()
|
||||
{
|
||||
return extension_loaded('wincache')
|
||||
&& version_compare(phpversion('wincache'), '1.1.0', '>=')
|
||||
&& ini_get('wincache.ucenabled')
|
||||
&& ('cli' !== PHP_SAPI || ini_get('wincache.enablecli'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any locks obtained by this instance as soon as there are no more
|
||||
* references to the object's persistent ID.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (0 === --static::$requestInstances[$this->persistentId]) {
|
||||
foreach (static::$locksBackup[$this->persistentId] as $key) {
|
||||
wincache_unlock(
|
||||
$this->persistentId . static::encodeLockName($key)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to obtain. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
* @param double $timeout Ignored with WinCache. Script will always block if
|
||||
* the lock can't be immediately obtained.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function lock($key, $timeout = null)
|
||||
{
|
||||
$result = wincache_lock(
|
||||
$this->persistentId . static::encodeLockName($key)
|
||||
);
|
||||
if ($result) {
|
||||
static::$locksBackup[$this->persistentId] = $key;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases a named lock.
|
||||
*
|
||||
* @param string $key Name of the key to release. Note that $key may
|
||||
* repeat for each distinct $persistentId.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$result = wincache_unlock(
|
||||
$this->persistentId . static::encodeLockName($key)
|
||||
);
|
||||
if ($result) {
|
||||
unset(
|
||||
static::$locksBackup[$this->persistentId][array_search(
|
||||
$key,
|
||||
static::$locksBackup[$this->persistentId],
|
||||
true
|
||||
)]
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified key is in the storage.
|
||||
*
|
||||
* @param string $key Name of key to check.
|
||||
*
|
||||
* @return bool TRUE if the key is in the storage, FALSE otherwise.
|
||||
*/
|
||||
public function exists($key)
|
||||
{
|
||||
return wincache_ucache_exists($this->persistentId . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to the shared memory storage.
|
||||
*
|
||||
* Sets a value to the storage if it doesn't exist, or fails if it does.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function add($key, $value, $ttl = 0)
|
||||
{
|
||||
return wincache_ucache_add($this->persistentId . $key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the shared memory storage.
|
||||
*
|
||||
* Adds a value to the storage if it doesn't exist, overwrites it otherwise.
|
||||
*
|
||||
* @param string $key Name of key to associate the value with.
|
||||
* @param mixed $value Value for the specified key.
|
||||
* @param int $ttl Seconds to store the value. If set to 0 indicates no
|
||||
* time limit.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function set($key, $value, $ttl = 0)
|
||||
{
|
||||
return wincache_ucache_set($this->persistentId . $key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the shared memory storage.
|
||||
*
|
||||
* Gets the current value, or throws an exception if it's not stored.
|
||||
*
|
||||
* @param string $key Name of key to get the value of.
|
||||
*
|
||||
* @return mixed The current value of the specified key.
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
$value = wincache_ucache_get($this->persistentId . $key, $success);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to fetch key. No such key, or key has expired.',
|
||||
300
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the shared memory storage.
|
||||
*
|
||||
* @param string $key Name of key to delete.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return wincache_ucache_delete($this->persistentId . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases a value from the shared memory storage.
|
||||
*
|
||||
* Increases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)+$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to increase.
|
||||
* @param int $step Value to increase the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function inc($key, $step = 1)
|
||||
{
|
||||
$newValue = wincache_ucache_inc(
|
||||
$this->persistentId . $key,
|
||||
(int) $step,
|
||||
$success
|
||||
);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to increase the value. Are you sure the value is int?',
|
||||
301
|
||||
);
|
||||
}
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases a value from the shared memory storage.
|
||||
*
|
||||
* Decreases a value from the shared memory storage. Unlike a plain
|
||||
* set($key, get($key)-$step) combination, this function also implicitly
|
||||
* performs locking.
|
||||
*
|
||||
* @param string $key Name of key to decrease.
|
||||
* @param int $step Value to decrease the key by.
|
||||
*
|
||||
* @return int The new value.
|
||||
*/
|
||||
public function dec($key, $step = 1)
|
||||
{
|
||||
$newValue = wincache_ucache_dec(
|
||||
$this->persistentId . $key,
|
||||
(int) $step,
|
||||
$success
|
||||
);
|
||||
if (!$success) {
|
||||
throw new SHM\InvalidArgumentException(
|
||||
'Unable to decrease the value. Are you sure the value is int?',
|
||||
302
|
||||
);
|
||||
}
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value if a key has a certain value.
|
||||
*
|
||||
* Sets a new value if a key has a certain value. This function only works
|
||||
* when $old and $new are longs.
|
||||
*
|
||||
* @param string $key Key of the value to compare and set.
|
||||
* @param int $old The value to compare the key against.
|
||||
* @param int $new The value to set the key to.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function cas($key, $old, $new)
|
||||
{
|
||||
return wincache_ucache_cas($this->persistentId . $key, $old, $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the persistent storage.
|
||||
*
|
||||
* Clears the persistent storage, i.e. removes all keys. Locks are left
|
||||
* intact.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$info = wincache_ucache_info();
|
||||
foreach ($info['ucache_entries'] as $entry) {
|
||||
if (!$entry['is_session']
|
||||
&& 0 === strpos($entry['key_name'], $this->persistentId)
|
||||
) {
|
||||
wincache_ucache_delete($entry['key_name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
*
|
||||
* Returns an external iterator.
|
||||
*
|
||||
* @param string|null $filter A PCRE regular expression.
|
||||
* Only matching keys will be iterated over.
|
||||
* Setting this to NULL matches all keys of this instance.
|
||||
* @param bool $keysOnly Whether to return only the keys,
|
||||
* or return both the keys and values.
|
||||
*
|
||||
* @return ArrayObject An array with all matching keys as array keys,
|
||||
* and values as array values. If $keysOnly is TRUE, the array keys are
|
||||
* numeric, and the array values are key names.
|
||||
*/
|
||||
public function getIterator($filter = null, $keysOnly = false)
|
||||
{
|
||||
$info = wincache_ucache_info();
|
||||
$result = array();
|
||||
foreach ($info['ucache_entries'] as $entry) {
|
||||
if (!$entry['is_session']
|
||||
&& 0 === strpos($entry['key_name'], $this->persistentId)
|
||||
) {
|
||||
$localKey = strstr($entry['key_name'], $this->persistentId);
|
||||
if (null === $filter || preg_match($filter, $localKey)) {
|
||||
if ($keysOnly) {
|
||||
$result[] = $localKey;
|
||||
} else {
|
||||
$result[$localKey] = apc_fetch($localKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayObject($result);
|
||||
}
|
||||
}
|
35
system/autoload/PEAR2/Cache/SHM/Exception.php
Normal file
35
system/autoload/PEAR2/Cache/SHM/Exception.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for shared memory and locking functionality across different extensions.
|
||||
|
||||
*
|
||||
* Allows you to share data across requests as long as the PHP process is running. One of APC or WinCache is required to accomplish this, with other extensions being potentially pluggable as adapters.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 0.2.0
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* Generic exception class of this package.
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
interface Exception
|
||||
{
|
||||
}
|
36
system/autoload/PEAR2/Cache/SHM/InvalidArgumentException.php
Normal file
36
system/autoload/PEAR2/Cache/SHM/InvalidArgumentException.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Wrapper for shared memory and locking functionality across different extensions.
|
||||
|
||||
*
|
||||
* Allows you to share data across requests as long as the PHP process is running. One of APC or WinCache is required to accomplish this, with other extensions being potentially pluggable as adapters.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 0.2.0
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* Exception thrown when there's something wrong with an argument.
|
||||
*
|
||||
* @category Caching
|
||||
* @package PEAR2_Cache_SHM
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Cache_SHM
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
implements Exception
|
||||
{
|
||||
}
|
40
system/autoload/mail/Exception.php
Normal file
40
system/autoload/mail/Exception.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHPMailer Exception class.
|
||||
* PHP Version 5.5.
|
||||
*
|
||||
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
|
||||
*
|
||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
||||
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
|
||||
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
|
||||
* @author Brent R. Matzelle (original founder)
|
||||
* @copyright 2012 - 2020 Marcus Bointon
|
||||
* @copyright 2010 - 2012 Jim Jagielski
|
||||
* @copyright 2004 - 2009 Andy Prevost
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
namespace PHPMailer\PHPMailer;
|
||||
|
||||
/**
|
||||
* PHPMailer exception handler.
|
||||
*
|
||||
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
/**
|
||||
* Prettify error message output.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function errorMessage()
|
||||
{
|
||||
return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n";
|
||||
}
|
||||
}
|
5252
system/autoload/mail/PHPMailer.php
Normal file
5252
system/autoload/mail/PHPMailer.php
Normal file
File diff suppressed because it is too large
Load Diff
1497
system/autoload/mail/SMTP.php
Normal file
1497
system/autoload/mail/SMTP.php
Normal file
File diff suppressed because it is too large
Load Diff
254
system/controllers/accounts.php
Normal file
254
system/controllers/accounts.php
Normal file
@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
_auth();
|
||||
$ui->assign('_title', Lang::T('My Account'));
|
||||
$ui->assign('_system_menu', 'accounts');
|
||||
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'change-password':
|
||||
run_hook('customer_view_change_password'); #HOOK
|
||||
$ui->display('user-change-password.tpl');
|
||||
break;
|
||||
|
||||
case 'change-password-post':
|
||||
$password = _post('password');
|
||||
run_hook('customer_change_password'); #HOOK
|
||||
if ($password != '') {
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $user['username'])->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
$npass = _post('npass');
|
||||
$cnpass = _post('cnpass');
|
||||
|
||||
if (Password::_uverify($password, $d_pass) == true) {
|
||||
if (!Validator::Length($npass, 15, 2)) {
|
||||
r2(U . 'accounts/change-password', 'e', 'New Password must be 3 to 14 character');
|
||||
}
|
||||
if ($npass != $cnpass) {
|
||||
r2(U . 'accounts/change-password', 'e', 'Both Password should be same');
|
||||
}
|
||||
|
||||
$c = ORM::for_table('tbl_user_recharges')->where('username', $user['username'])->find_one();
|
||||
if ($c) {
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $c['plan_id'])->find_one();
|
||||
if ($p['is_radius']) {
|
||||
if ($c['type'] == 'Hotspot' || ($c['type'] == 'PPPOE' && empty($d['pppoe_password']))) {
|
||||
Radius::customerUpsert($d, $p);
|
||||
}
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($c['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($c['type'] == 'Hotspot') {
|
||||
Mikrotik::setHotspotUser($client, $c['username'], $npass);
|
||||
Mikrotik::removeHotspotActiveUser($client, $user['username']);
|
||||
} else if (empty($d['pppoe_password'])) {
|
||||
// only change when pppoe_password empty
|
||||
Mikrotik::setPpoeUser($client, $c['username'], $npass);
|
||||
Mikrotik::removePpoeActive($client, $user['username']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$d->password = $npass;
|
||||
$d->save();
|
||||
|
||||
_msglog('s', Lang::T('Password changed successfully, Please login again'));
|
||||
_log('[' . $user['username'] . ']: Password changed successfully', 'User', $user['id']);
|
||||
|
||||
r2(U . 'login');
|
||||
} else {
|
||||
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
|
||||
}
|
||||
} else {
|
||||
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
|
||||
}
|
||||
} else {
|
||||
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'profile':
|
||||
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
|
||||
if ($d) {
|
||||
run_hook('customer_view_edit_profile'); #HOOK
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('user-profile.tpl');
|
||||
} else {
|
||||
r2(U . 'home', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit-profile-post':
|
||||
$fullname = _post('fullname');
|
||||
$address = _post('address');
|
||||
$email = _post('email');
|
||||
$phonenumber = _post('phonenumber');
|
||||
run_hook('customer_edit_profile'); #HOOK
|
||||
$msg = '';
|
||||
if (Validator::Length($fullname, 31, 2) == false) {
|
||||
$msg .= 'Full Name should be between 3 to 30 characters' . '<br>';
|
||||
}
|
||||
if (Validator::UnsignedNumber($phonenumber) == false) {
|
||||
$msg .= 'Phone Number must be a number' . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d->fullname = $fullname;
|
||||
$d->address = $address;
|
||||
$d->email = $email;
|
||||
$d->phonenumber = $phonenumber;
|
||||
$d->save();
|
||||
|
||||
_log('[' . $user['username'] . ']: ' . Lang::T('User Updated Successfully'), 'User', $user['id']);
|
||||
r2(U . 'accounts/profile', 's', Lang::T('User Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'accounts/profile', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'phone-update':
|
||||
|
||||
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
|
||||
if ($d) {
|
||||
//run_hook('customer_view_edit_profile'); #HOOK
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('user-phone-update.tpl');
|
||||
} else {
|
||||
r2(U . 'home', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'phone-update-otp':
|
||||
$phone = _post('phone');
|
||||
$username = $user['username'];
|
||||
$otpPath = $CACHE_PATH . '/sms/';
|
||||
|
||||
// Validate the phone number format
|
||||
if (!preg_match('/^[0-9]{10,}$/', $phone)) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
|
||||
}
|
||||
|
||||
if (empty($config['sms_url'])) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not Available, Please try again later'));
|
||||
}
|
||||
|
||||
if (!empty($config['sms_url'])) {
|
||||
if (!empty($phone)) {
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->where('phonenumber', $phone)->find_one();
|
||||
if ($d) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('You cannot use your current phone number'));
|
||||
}
|
||||
if (!file_exists($otpPath)) {
|
||||
mkdir($otpPath);
|
||||
touch($otpPath . 'index.html');
|
||||
}
|
||||
$otpFile = $otpPath . sha1($username . $db_password) . ".txt";
|
||||
$phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
|
||||
|
||||
// expired 10 minutes
|
||||
if (file_exists($otpFile) && time() - filemtime($otpFile) < 1200) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Please wait ' . (1200 - (time() - filemtime($otpFile))) . ' seconds before sending another SMS'));
|
||||
} else {
|
||||
$otp = rand(100000, 999999);
|
||||
file_put_contents($otpFile, $otp);
|
||||
file_put_contents($phoneFile, $phone);
|
||||
// send send OTP to user
|
||||
if ($config['phone_otp_type'] === 'sms') {
|
||||
Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
} elseif ($config['phone_otp_type'] === 'whatsapp') {
|
||||
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
} elseif ($config['phone_otp_type'] === 'both') {
|
||||
Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
}
|
||||
//redirect after sending OTP
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code has been sent to your phone'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'phone-update-post':
|
||||
$phone = _post('phone');
|
||||
$otp_code = _post('otp');
|
||||
$username = $user['username'];
|
||||
$otpPath = $CACHE_PATH . '/sms/';
|
||||
|
||||
// Validate the phone number format
|
||||
if (!preg_match('/^[0-9]{10,}$/', $phone)) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!empty($config['sms_url'])) {
|
||||
$otpFile = $otpPath . sha1($username . $db_password) . ".txt";
|
||||
$phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
|
||||
|
||||
// Check if OTP file exists
|
||||
if (!file_exists($otpFile)) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Please request OTP first'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// expired 10 minutes
|
||||
if (time() - filemtime($otpFile) > 1200) {
|
||||
unlink($otpFile);
|
||||
unlink($phoneFile);
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code expired'));
|
||||
exit();
|
||||
} else {
|
||||
$code = file_get_contents($otpFile);
|
||||
|
||||
// Check if OTP code matches
|
||||
if ($code != $otp_code) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Wrong Verification code'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// Check if the phone number matches the one that requested the OTP
|
||||
$savedPhone = file_get_contents($phoneFile);
|
||||
if ($savedPhone !== $phone) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('The phone number does not match the one that requested the OTP'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// OTP verification successful, delete OTP and phone number files
|
||||
unlink($otpFile);
|
||||
unlink($phoneFile);
|
||||
}
|
||||
} else {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not available'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// Update the phone number in the database
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d->phonenumber = Lang::phoneFormat($phone);
|
||||
$d->save();
|
||||
}
|
||||
|
||||
r2(U . 'accounts/profile', 's', Lang::T('Phone number updated successfully'));
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
57
system/controllers/admin.php
Normal file
57
system/controllers/admin.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
if(Admin::getID()){
|
||||
r2(U.'dashboard', "s", Lang::T("You are already logged in"));
|
||||
}
|
||||
|
||||
if (isset($routes['1'])) {
|
||||
$do = $routes['1'];
|
||||
} else {
|
||||
$do = 'login-display';
|
||||
}
|
||||
|
||||
switch ($do) {
|
||||
case 'post':
|
||||
$username = _post('username');
|
||||
$password = _post('password');
|
||||
run_hook('admin_login'); #HOOK
|
||||
if ($username != '' and $password != '') {
|
||||
$d = ORM::for_table('tbl_users')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
if (Password::_verify($password, $d_pass) == true) {
|
||||
$_SESSION['aid'] = $d['id'];
|
||||
$token = Admin::setCookie($d['id']);
|
||||
$d->last_login = date('Y-m-d H:i:s');
|
||||
$d->save();
|
||||
_log($username . ' ' . Lang::T('Login Successful'), $d['user_type'], $d['id']);
|
||||
if ($isApi) {
|
||||
if ($token) {
|
||||
showResult(true, Lang::T('Login Successful'), ['token' => "a.".$token]);
|
||||
} else {
|
||||
showResult(false, Lang::T('Invalid Username or Password'));
|
||||
}
|
||||
}
|
||||
_alert(Lang::T('Login Successful'),'success', "dashboard");
|
||||
} else {
|
||||
_log($username . ' ' . Lang::T('Failed Login'), $d['user_type']);
|
||||
_alert(Lang::T('Invalid Username or Password').".",'danger', "admin");
|
||||
}
|
||||
} else {
|
||||
_alert(Lang::T('Invalid Username or Password')."..",'danger', "admin");
|
||||
}
|
||||
} else {
|
||||
_alert(Lang::T('Invalid Username or Password')."...",'danger', "admin");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
run_hook('view_login'); #HOOK
|
||||
$ui->display('admin-login.tpl');
|
||||
break;
|
||||
}
|
89
system/controllers/autoload.php
Normal file
89
system/controllers/autoload.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
/**
|
||||
* used for ajax
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Network'));
|
||||
$ui->assign('_system_menu', 'network');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
switch ($action) {
|
||||
case 'pool':
|
||||
$routers = _get('routers');
|
||||
if(empty($routers)){
|
||||
$d = ORM::for_table('tbl_pool')->find_many();
|
||||
}else{
|
||||
$d = ORM::for_table('tbl_pool')->where('routers', $routers)->find_many();
|
||||
}
|
||||
$ui->assign('routers', $routers);
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('autoload-pool.tpl');
|
||||
break;
|
||||
|
||||
case 'server':
|
||||
$d = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
|
||||
$ui->assign('d', $d);
|
||||
|
||||
$ui->display('autoload-server.tpl');
|
||||
break;
|
||||
|
||||
case 'plan':
|
||||
$server = _post('server');
|
||||
$jenis = _post('jenis');
|
||||
if(in_array($admin['user_type'], array('SuperAdmin', 'Admin'))){
|
||||
if($server=='radius'){
|
||||
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many();
|
||||
}else{
|
||||
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many();
|
||||
}
|
||||
}else{
|
||||
if($server=='radius'){
|
||||
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->where('enabled', '1')->find_many();
|
||||
}else{
|
||||
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->where('enabled', '1')->find_many();
|
||||
}
|
||||
}
|
||||
$ui->assign('d', $d);
|
||||
|
||||
$ui->display('autoload.tpl');
|
||||
break;
|
||||
case 'customer_is_active':
|
||||
$d = ORM::for_table('tbl_user_recharges')->where('customer_id', $routes['2'])->findOne();
|
||||
if ($d) {
|
||||
if ($d['status'] == 'on') {
|
||||
die('<span class="label label-success" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">'.$d['namebp'].'</span>');
|
||||
} else {
|
||||
die('<span class="label label-danger" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">'.$d['namebp'].'</span>');
|
||||
}
|
||||
} else {
|
||||
die('<span class="label label-danger">•</span>');
|
||||
}
|
||||
break;
|
||||
case 'customer_select2':
|
||||
|
||||
$s = addslashes(_get('s'));
|
||||
if (empty($s)) {
|
||||
$c = ORM::for_table('tbl_customers')->limit(30)->find_many();
|
||||
} else {
|
||||
$c = ORM::for_table('tbl_customers')->where_raw("(`username` LIKE '%$s%' OR `fullname` LIKE '%$s%' OR `phonenumber` LIKE '%$s%' OR `email` LIKE '%$s%')")->limit(30)->find_many();
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
foreach ($c as $cust) {
|
||||
$json[] = [
|
||||
'id' => $cust['id'],
|
||||
'text' => $cust['username'] . ' - ' . $cust['fullname'] . ' - ' . $cust['email']
|
||||
];
|
||||
}
|
||||
echo json_encode(['results' => $json]);
|
||||
die();
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
37
system/controllers/autoload_user.php
Normal file
37
system/controllers/autoload_user.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
/**
|
||||
* used for ajax
|
||||
**/
|
||||
|
||||
_auth();
|
||||
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
|
||||
switch ($action) {
|
||||
case 'isLogin':
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $routes['2'])->where('username', $user['username'])->findOne();
|
||||
if ($bill['type'] == 'Hotspot' && $bill['status'] == 'on') {
|
||||
$m = Mikrotik::info($bill['routers']);
|
||||
$client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
if (Mikrotik::isUserLogin($client, $user['username'])) {
|
||||
die('<a href="' . U . 'home&mikrotik=logout&id='.$bill['id'].'" onclick="return confirm(\''.Lang::T('Disconnect Internet?').'\')" class="btn btn-success btn-xs btn-block">'.Lang::T('You are Online, Logout?').'</a>');
|
||||
} else {
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
die('<a href="' . U . 'home&mikrotik=login&id='.$bill['id'].'" onclick="return confirm(\''.Lang::T('Connect to Internet?').'\')" class="btn btn-danger btn-xs btn-block">'.Lang::T('Not Online, Login now?').'</a>');
|
||||
}else{
|
||||
die(Lang::T('Your account not connected to internet'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
die('--');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$ui->display('404.tpl');
|
||||
}
|
190
system/controllers/bandwidth.php
Normal file
190
system/controllers/bandwidth.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Bandwidth Plans'));
|
||||
$ui->assign('_system_menu', 'services');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
r2(U . "dashboard", 'e', Lang::T('You do not have permission to access this page'));
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'list':
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/bandwidth.js"></script>');
|
||||
run_hook('view_list_bandwidth'); #HOOK
|
||||
$name = _post('name');
|
||||
if ($name != '') {
|
||||
$query = ORM::for_table('tbl_bandwidth')->where_like('name_bw', '%' . $name . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['name' => $name]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_bandwidth')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('bandwidth.tpl');
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
run_hook('view_add_bandwidth'); #HOOK
|
||||
$ui->display('bandwidth-add.tpl');
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('view_edit_bandwith'); #HOOK
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
|
||||
if ($d) {
|
||||
$ui->assign('burst', explode(" ", $d['burst']));
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('bandwidth-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'bandwidth/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('delete_bandwidth'); #HOOK
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
|
||||
if ($d) {
|
||||
$d->delete();
|
||||
r2(U . 'bandwidth/list', 's', Lang::T('Data Deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add-post':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$name = _post('name');
|
||||
$rate_down = _post('rate_down');
|
||||
$rate_down_unit = _post('rate_down_unit');
|
||||
$rate_up = _post('rate_up');
|
||||
$rate_up_unit = _post('rate_up_unit');
|
||||
run_hook('add_bandwidth'); #HOOK
|
||||
$isBurst = true;
|
||||
$burst = "";
|
||||
if (isset($_POST['burst'])) {
|
||||
foreach ($_POST['burst'] as $b) {
|
||||
if (empty($b)) {
|
||||
$isBurst = false;
|
||||
}
|
||||
}
|
||||
if ($isBurst) {
|
||||
$burst = implode(' ', $_POST['burst']);
|
||||
};
|
||||
}
|
||||
$msg = '';
|
||||
if (Validator::Length($name, 16, 4) == false) {
|
||||
$msg .= 'Name should be between 5 to 15 characters' . '<br>';
|
||||
}
|
||||
|
||||
if ($rate_down_unit == 'Kbps') {
|
||||
$unit_rate_down = $rate_down * 1024;
|
||||
} else {
|
||||
$unit_rate_down = $rate_down * 1048576;
|
||||
}
|
||||
if ($rate_up_unit == 'Kbps') {
|
||||
$unit_rate_up = $min_up * 1024;
|
||||
} else {
|
||||
$unit_rate_up = $min_up * 1048576;
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_bandwidth')->where('name_bw', $name)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Name Bandwidth Already Exist') . '<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d = ORM::for_table('tbl_bandwidth')->create();
|
||||
$d->name_bw = $name;
|
||||
$d->rate_down = $rate_down;
|
||||
$d->rate_down_unit = $rate_down_unit;
|
||||
$d->rate_up = $rate_up;
|
||||
$d->rate_up_unit = $rate_up_unit;
|
||||
$d->burst = $burst;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'bandwidth/list', 's', Lang::T('Data Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'bandwidth/add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit-post':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$name = _post('name');
|
||||
$rate_down = _post('rate_down');
|
||||
$rate_down_unit = _post('rate_down_unit');
|
||||
$rate_up = _post('rate_up');
|
||||
$rate_up_unit = _post('rate_up_unit');
|
||||
run_hook('edit_bandwidth'); #HOOK
|
||||
$isBurst = true;
|
||||
$burst = "";
|
||||
if (isset($_POST['burst'])) {
|
||||
foreach ($_POST['burst'] as $b) {
|
||||
if (empty($b)) {
|
||||
$isBurst = false;
|
||||
}
|
||||
}
|
||||
if ($isBurst) {
|
||||
$burst = implode(' ', $_POST['burst']);
|
||||
};
|
||||
}
|
||||
$msg = '';
|
||||
if (Validator::Length($name, 16, 4) == false) {
|
||||
$msg .= 'Name should be between 5 to 15 characters' . '<br>';
|
||||
}
|
||||
|
||||
$id = _post('id');
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
if ($d['name_bw'] != $name) {
|
||||
$c = ORM::for_table('tbl_bandwidth')->where('name_bw', $name)->find_one();
|
||||
if ($c) {
|
||||
$msg .= Lang::T('Name Bandwidth Already Exist') . '<br>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d->name_bw = $name;
|
||||
$d->rate_down = $rate_down;
|
||||
$d->rate_down_unit = $rate_down_unit;
|
||||
$d->rate_up = $rate_up;
|
||||
$d->rate_up_unit = $rate_up_unit;
|
||||
$d->burst = $burst;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'bandwidth/list', 's', Lang::T('Data Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'bandwidth/edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
22
system/controllers/callback.php
Normal file
22
system/controllers/callback.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
$action = $routes['1'];
|
||||
|
||||
|
||||
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php')) {
|
||||
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php';
|
||||
if (function_exists($action . '_payment_notification')) {
|
||||
run_hook('callback_payment_notification'); #HOOK
|
||||
call_user_func($action . '_payment_notification');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
echo 'Not Found';
|
126
system/controllers/codecanyon.php
Normal file
126
system/controllers/codecanyon.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'CodeCanyon.net');
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
|
||||
$plugin_repository = 'https://hotspotbilling.github.io/Plugin-Repository/repository.json';
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
$cache = File::pathFixer($CACHE_PATH . '/codecanyon.json');
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
if (empty($config['envato_token'])) {
|
||||
r2(U . 'settings/app', 'w', '<a href="' . U . 'settings/app#envato' . '">Envato Personal Access Token</a> is not set');
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'install':
|
||||
if (!is_writeable(File::pathFixer($CACHE_PATH . '/'))) {
|
||||
r2(U . "codecanyon", 'e', 'Folder system/cache/ is not writable');
|
||||
}
|
||||
if (!is_writeable($PLUGIN_PATH)) {
|
||||
r2(U . "codecanyon", 'e', 'Folder plugin/ is not writable');
|
||||
}
|
||||
if (!is_writeable($PAYMENTGATEWAY_PATH)) {
|
||||
r2(U . "codecanyon", 'e', 'Folder paymentgateway/ is not writable');
|
||||
}
|
||||
set_time_limit(-1);
|
||||
$item_id = $routes['2'];
|
||||
$tipe = $routes['3'];
|
||||
$result = Http::getData('https://api.envato.com/v3/market/buyer/download?item_id=' . $item_id, ['Authorization: Bearer ' . $config['envato_token']]);
|
||||
$json = json_decode($result, true);
|
||||
if (!isset($json['download_url'])) {
|
||||
r2(U . 'codecanyon', 'e', 'Failed to get download url. ' . $json['description']);
|
||||
}
|
||||
$file = File::pathFixer($CACHE_PATH . '/codecanyon/');
|
||||
if (!file_exists($file)) {
|
||||
mkdir($file);
|
||||
}
|
||||
$file .= $item_id . '.zip';
|
||||
if (file_exists($file))
|
||||
unlink($file);
|
||||
//download
|
||||
$fp = fopen($file, 'w+');
|
||||
$ch = curl_init($json['download_url']);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
//extract
|
||||
$target = File::pathFixer($CACHE_PATH . '/codecanyon/' . $item_id . '/');
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($file);
|
||||
$zip->extractTo($target);
|
||||
$zip->close();
|
||||
//moving
|
||||
if (file_exists($target . 'plugin')) {
|
||||
File::copyFolder($target . 'plugin', $PLUGIN_PATH . DIRECTORY_SEPARATOR);
|
||||
} else if (file_exists($target . 'paymentgateway')) {
|
||||
File::copyFolder($target . 'paymentgateway', $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR);
|
||||
} else if (file_exists($target . 'theme')) {
|
||||
File::copyFolder($target . 'theme', File::pathFixer('ui/themes/'));
|
||||
}
|
||||
//Cleaning
|
||||
File::deleteFolder($target);
|
||||
unlink($file);
|
||||
r2(U . "codecanyon", 's', 'Installation success');
|
||||
case 'reload':
|
||||
if (file_exists($cache))
|
||||
unlink($cache);
|
||||
default:
|
||||
if (class_exists('ZipArchive')) {
|
||||
$zipExt = true;
|
||||
} else {
|
||||
$zipExt = false;
|
||||
}
|
||||
$ui->assign('zipExt', $zipExt);
|
||||
|
||||
if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
|
||||
$txt = file_get_contents($cache);
|
||||
$plugins = json_decode($txt, true);
|
||||
$ui->assign('chached_until', date($config['date_format'] . ' H:i', filemtime($cache) + (24 * 60 * 60)));
|
||||
if (count($plugins) == 0) {
|
||||
unlink($cache);
|
||||
r2(U . 'codecanyon');
|
||||
}
|
||||
} else {
|
||||
$plugins = [];
|
||||
$page = _get('page', 1);
|
||||
back:
|
||||
$result = Http::getData('https://api.envato.com/v3/market/buyer/list-purchases?&page=' . $page, ['Authorization: Bearer ' . $config['envato_token']]);
|
||||
$items = json_decode($result, true);
|
||||
if ($items && count($items['results']) > 0) {
|
||||
foreach ($items['results'] as $item) {
|
||||
$name = strtolower($item['item']['name']);
|
||||
if (strpos($name, 'phpnuxbill') !== false) {
|
||||
$plugins[] = $item;
|
||||
}
|
||||
}
|
||||
$page++;
|
||||
goto back;
|
||||
}
|
||||
if (count($plugins) > 0) {
|
||||
file_put_contents($cache, json_encode($plugins));
|
||||
if (file_exists($cache)) {
|
||||
$ui->assign('chached_until', date($config['date_format'] . ' H:i', filemtime($cache) + (24 * 60 * 60)));
|
||||
}
|
||||
}
|
||||
}
|
||||
$ui->assign('plugins', $plugins);
|
||||
$ui->display('codecanyon.tpl');
|
||||
}
|
26
system/controllers/community.php
Normal file
26
system/controllers/community.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'Community');
|
||||
$ui->assign('_system_menu', 'community');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
switch ($action) {
|
||||
case 'rollback':
|
||||
$ui->assign('_title', 'Rollback Update');
|
||||
$masters = json_decode(Http::getData("https://api.github.com/repos/hotspotbilling/phpnuxbill/commits?per_page=100",['User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0']), true);
|
||||
$devs = json_decode(Http::getData("https://api.github.com/repos/hotspotbilling/phpnuxbill/commits?sha=Development&per_page=100",['User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0']), true);
|
||||
|
||||
$ui->assign('masters', $masters);
|
||||
$ui->assign('devs', $devs);
|
||||
$ui->display('community-rollback.tpl');
|
||||
break;
|
||||
default:
|
||||
$ui->display('community.tpl');
|
||||
}
|
705
system/controllers/customers.php
Normal file
705
system/controllers/customers.php
Normal file
@ -0,0 +1,705 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Customer'));
|
||||
$ui->assign('_system_menu', 'customers');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'list';
|
||||
}
|
||||
|
||||
$leafletpickerHeader = <<<EOT
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">
|
||||
EOT;
|
||||
|
||||
switch ($action) {
|
||||
case 'csv':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$cs = ORM::for_table('tbl_customers')
|
||||
->select('tbl_customers.id', 'id')
|
||||
->select('tbl_customers.username', 'username')
|
||||
->select('fullname')
|
||||
->select('address')
|
||||
->select('phonenumber')
|
||||
->select('email')
|
||||
->select('balance')
|
||||
->select('service_type')
|
||||
->order_by_asc('tbl_customers.id')
|
||||
->find_array();
|
||||
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="phpnuxbill_customers_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
$headers = [
|
||||
'id',
|
||||
'username',
|
||||
'fullname',
|
||||
'address',
|
||||
'phonenumber',
|
||||
'email',
|
||||
'balance',
|
||||
'service_type',
|
||||
];
|
||||
|
||||
if (!$h) {
|
||||
echo '"' . implode('","', $headers) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
|
||||
foreach ($cs as $c) {
|
||||
$row = [
|
||||
$c['id'],
|
||||
$c['username'],
|
||||
$c['fullname'],
|
||||
$c['address'],
|
||||
$c['phonenumber'],
|
||||
$c['email'],
|
||||
$c['balance'],
|
||||
$c['service_type'],
|
||||
];
|
||||
echo '"' . implode('","', $row) . "\"\n";
|
||||
}
|
||||
break;
|
||||
//case csv-prepaid can be moved later to (plan.php) php file dealing with prepaid users
|
||||
case 'csv-prepaid':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$cs = ORM::for_table('tbl_customers')
|
||||
->select('tbl_customers.id', 'id')
|
||||
->select('tbl_customers.username', 'username')
|
||||
->select('fullname')
|
||||
->select('address')
|
||||
->select('phonenumber')
|
||||
->select('email')
|
||||
->select('balance')
|
||||
->select('service_type')
|
||||
->select('namebp')
|
||||
->select('routers')
|
||||
->select('status')
|
||||
->select('method', 'Payment')
|
||||
->left_outer_join('tbl_user_recharges', array('tbl_customers.id', '=', 'tbl_user_recharges.customer_id'))
|
||||
->order_by_asc('tbl_customers.id')
|
||||
->find_array();
|
||||
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="phpnuxbill_prepaid_users' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
$headers = [
|
||||
'id',
|
||||
'username',
|
||||
'fullname',
|
||||
'address',
|
||||
'phonenumber',
|
||||
'email',
|
||||
'balance',
|
||||
'service_type',
|
||||
'namebp',
|
||||
'routers',
|
||||
'status',
|
||||
'Payment'
|
||||
];
|
||||
|
||||
if (!$h) {
|
||||
echo '"' . implode('","', $headers) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
|
||||
foreach ($cs as $c) {
|
||||
$row = [
|
||||
$c['id'],
|
||||
$c['username'],
|
||||
$c['fullname'],
|
||||
$c['address'],
|
||||
$c['phonenumber'],
|
||||
$c['email'],
|
||||
$c['balance'],
|
||||
$c['service_type'],
|
||||
$c['namebp'],
|
||||
$c['routers'],
|
||||
$c['status'],
|
||||
$c['Payment']
|
||||
];
|
||||
echo '"' . implode('","', $row) . "\"\n";
|
||||
}
|
||||
break;
|
||||
case 'add':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$ui->assign('xheader', $leafletpickerHeader);
|
||||
run_hook('view_add_customer'); #HOOK
|
||||
$ui->display('customers-add.tpl');
|
||||
break;
|
||||
case 'recharge':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id_customer = $routes['2'];
|
||||
$plan_id = $routes['3'];
|
||||
$b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one();
|
||||
if ($b) {
|
||||
$gateway = 'Recharge';
|
||||
$channel = $admin['fullname'];
|
||||
$cust = User::_info($id_customer);
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($b['plan_id']);
|
||||
list($bills, $add_cost) = User::getBills($id_customer);
|
||||
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
|
||||
if (!$cust) {
|
||||
r2(U . 'plan/recharge', 'e', Lang::T('Customer not found'));
|
||||
}
|
||||
if (!$plan) {
|
||||
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
|
||||
}
|
||||
if ($cust['balance'] < ($plan['price'] + $add_cost)) {
|
||||
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
|
||||
}
|
||||
$gateway = 'Recharge Balance';
|
||||
}
|
||||
if ($using == 'zero') {
|
||||
$zero = 1;
|
||||
$gateway = 'Recharge Zero';
|
||||
}
|
||||
$usings = explode(',', $config['payment_usings']);
|
||||
$usings = array_filter(array_unique($usings));
|
||||
if (count($usings) == 0) {
|
||||
$usings[] = Lang::T('Cash');
|
||||
}
|
||||
$ui->assign('usings', $usings);
|
||||
$ui->assign('bills', $bills);
|
||||
$ui->assign('add_cost', $add_cost);
|
||||
$ui->assign('cust', $cust);
|
||||
$ui->assign('gateway', $gateway);
|
||||
$ui->assign('channel', $channel);
|
||||
$ui->assign('server', $b['routers']);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->display('recharge-confirm.tpl');
|
||||
} else {
|
||||
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
|
||||
}
|
||||
break;
|
||||
case 'deactivate':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id_customer = $routes['2'];
|
||||
$plan_id = $routes['3'];
|
||||
$b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one();
|
||||
if ($b) {
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $b['plan_id'])->find_one();
|
||||
if ($p) {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerDeactivate($b['username']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($b['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($b['type'] == 'Hotspot') {
|
||||
Mikrotik::removeHotspotUser($client, $b['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $b['username']);
|
||||
} else if ($b['type'] == 'PPPOE') {
|
||||
Mikrotik::removePpoeUser($client, $b['username']);
|
||||
Mikrotik::removePpoeActive($client, $b['username']);
|
||||
}
|
||||
}
|
||||
$b->status = 'off';
|
||||
$b->expiration = date('Y-m-d');
|
||||
$b->time = date('H:i:s');
|
||||
$b->save();
|
||||
_log('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for ' . $b['username'], 'User', $b['customer_id']);
|
||||
Message::sendTelegram('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for u' . $b['username']);
|
||||
r2(U . 'customers/view/' . $id_customer, 's', 'Success deactivate customer to Mikrotik');
|
||||
}
|
||||
}
|
||||
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
|
||||
break;
|
||||
case 'sync':
|
||||
$id_customer = $routes['2'];
|
||||
$bs = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('status', 'on')->findMany();
|
||||
if ($bs) {
|
||||
$routers = [];
|
||||
foreach ($bs as $b) {
|
||||
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $b['plan_id'])->where('enabled', '1')->find_one();
|
||||
if ($p) {
|
||||
$routers[] = $b['routers'];
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, $p['expiration'] . ' ' . $p['time']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($b['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($b['type'] == 'Hotspot') {
|
||||
Mikrotik::addHotspotUser($client, $p, $c);
|
||||
} else if ($b['type'] == 'PPPOE') {
|
||||
Mikrotik::addPpoeUser($client, $p, $c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
r2(U . 'customers/view/' . $id_customer, 's', 'Sync success to ' . implode(", ", $routers));
|
||||
}
|
||||
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
|
||||
break;
|
||||
case 'viewu':
|
||||
$customer = ORM::for_table('tbl_customers')->where('username', $routes['2'])->find_one();
|
||||
case 'view':
|
||||
$id = $routes['2'];
|
||||
run_hook('view_customer'); #HOOK
|
||||
if (!$customer) {
|
||||
$customer = ORM::for_table('tbl_customers')->find_one($id);
|
||||
}
|
||||
if ($customer) {
|
||||
|
||||
|
||||
// Fetch the Customers Attributes values from the tbl_customer_custom_fields table
|
||||
$customFields = ORM::for_table('tbl_customers_fields')
|
||||
->where('customer_id', $customer['id'])
|
||||
->find_many();
|
||||
$v = $routes['3'];
|
||||
if (empty($v)) {
|
||||
$v = 'activation';
|
||||
}
|
||||
if ($v == 'order') {
|
||||
$v = 'order';
|
||||
$query = ORM::for_table('tbl_transactions')->where('username', $customer['username'])->order_by_desc('id');
|
||||
$order = Paginator::findMany($query);
|
||||
$ui->assign('order', $order);
|
||||
} else if ($v == 'activation') {
|
||||
$query = ORM::for_table('tbl_transactions')->where('username', $customer['username'])->order_by_desc('id');
|
||||
$activation = Paginator::findMany($query);
|
||||
$ui->assign('activation', $activation);
|
||||
}
|
||||
$ui->assign('packages', User::_billing($customer['id']));
|
||||
$ui->assign('v', $v);
|
||||
$ui->assign('d', $customer);
|
||||
$ui->assign('customFields', $customFields);
|
||||
$ui->assign('xheader', $leafletpickerHeader);
|
||||
$ui->display('customers-view.tpl');
|
||||
} else {
|
||||
r2(U . 'customers/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('edit_customer'); #HOOK
|
||||
$d = ORM::for_table('tbl_customers')->find_one($id);
|
||||
// Fetch the Customers Attributes values from the tbl_customers_fields table
|
||||
$customFields = ORM::for_table('tbl_customers_fields')
|
||||
->where('customer_id', $id)
|
||||
->find_many();
|
||||
if ($d) {
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status"));
|
||||
$ui->assign('customFields', $customFields);
|
||||
$ui->assign('xheader', $leafletpickerHeader);
|
||||
$ui->display('customers-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'customers/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('delete_customer'); #HOOK
|
||||
$d = ORM::for_table('tbl_customers')->find_one($id);
|
||||
if ($d) {
|
||||
// Delete the associated Customers Attributes records from tbl_customer_custom_fields table
|
||||
ORM::for_table('tbl_customers_fields')->where('customer_id', $id)->delete_many();
|
||||
$c = ORM::for_table('tbl_user_recharges')->where('username', $d['username'])->find_one();
|
||||
if ($c) {
|
||||
$p = ORM::for_table('tbl_plans')->find_one($c['plan_id']);
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerDelete($d['username']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($c['routers']);
|
||||
if ($c['type'] == 'Hotspot') {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removeHotspotUser($client, $d['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $d['username']);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePpoeUser($client, $d['username']);
|
||||
Mikrotik::removePpoeActive($client, $d['username']);
|
||||
}
|
||||
try {
|
||||
$d->delete();
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
try {
|
||||
$c->delete();
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$d->delete();
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
try {
|
||||
if ($c)
|
||||
$c->delete();
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
}
|
||||
|
||||
r2(U . 'customers/list', 's', Lang::T('User deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add-post':
|
||||
$username = _post('username');
|
||||
$fullname = _post('fullname');
|
||||
$password = _post('password');
|
||||
$pppoe_password = _post('pppoe_password');
|
||||
$email = _post('email');
|
||||
$address = _post('address');
|
||||
$phonenumber = _post('phonenumber');
|
||||
$service_type = _post('service_type');
|
||||
$account_type = _post('account_type');
|
||||
$coordinates = _post('coordinates');
|
||||
//post Customers Attributes
|
||||
$custom_field_names = (array) $_POST['custom_field_name'];
|
||||
$custom_field_values = (array) $_POST['custom_field_value'];
|
||||
//additional information
|
||||
$city = _post('city');
|
||||
$district = _post('district');
|
||||
$state = _post('state');
|
||||
$zip = _post('zip');
|
||||
|
||||
run_hook('add_customer'); #HOOK
|
||||
$msg = '';
|
||||
if (Validator::Length($username, 35, 2) == false) {
|
||||
$msg .= 'Username should be between 3 to 55 characters' . '<br>';
|
||||
}
|
||||
if (Validator::Length($fullname, 36, 2) == false) {
|
||||
$msg .= 'Full Name should be between 3 to 25 characters' . '<br>';
|
||||
}
|
||||
if (!Validator::Length($password, 36, 2)) {
|
||||
$msg .= 'Password should be between 3 to 35 characters' . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Account already axist') . '<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d = ORM::for_table('tbl_customers')->create();
|
||||
$d->username = Lang::phoneFormat($username);
|
||||
$d->password = $password;
|
||||
$d->pppoe_password = $pppoe_password;
|
||||
$d->email = $email;
|
||||
$d->account_type = $account_type;
|
||||
$d->fullname = $fullname;
|
||||
$d->address = $address;
|
||||
$d->created_by = $admin['id'];
|
||||
$d->phonenumber = Lang::phoneFormat($phonenumber);
|
||||
$d->service_type = $service_type;
|
||||
$d->coordinates = $coordinates;
|
||||
$d->city = $city;
|
||||
$d->district = $district;
|
||||
$d->state = $state;
|
||||
$d->zip = $zip;
|
||||
$d->save();
|
||||
|
||||
// Retrieve the customer ID of the newly created customer
|
||||
$customerId = $d->id();
|
||||
// Save Customers Attributes details
|
||||
if (!empty($custom_field_names) && !empty($custom_field_values)) {
|
||||
$totalFields = min(count($custom_field_names), count($custom_field_values));
|
||||
for ($i = 0; $i < $totalFields; $i++) {
|
||||
$name = $custom_field_names[$i];
|
||||
$value = $custom_field_values[$i];
|
||||
|
||||
if (!empty($name)) {
|
||||
$customField = ORM::for_table('tbl_customers_fields')->create();
|
||||
$customField->customer_id = $customerId;
|
||||
$customField->field_name = $name;
|
||||
$customField->field_value = $value;
|
||||
$customField->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
r2(U . 'customers/list', 's', Lang::T('Account Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'customers/add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit-post':
|
||||
$username = Lang::phoneFormat(_post('username'));
|
||||
$fullname = _post('fullname');
|
||||
$account_type = _post('account_type');
|
||||
$password = _post('password');
|
||||
$pppoe_password = _post('pppoe_password');
|
||||
$email = _post('email');
|
||||
$address = _post('address');
|
||||
$phonenumber = Lang::phoneFormat(_post('phonenumber'));
|
||||
$service_type = _post('service_type');
|
||||
$coordinates = _post('coordinates');
|
||||
$status = _post('status');
|
||||
//additional information
|
||||
$city = _post('city');
|
||||
$district = _post('district');
|
||||
$state = _post('state');
|
||||
$zip = _post('zip');
|
||||
run_hook('edit_customer'); #HOOK
|
||||
$msg = '';
|
||||
if (Validator::Length($username, 35, 2) == false) {
|
||||
$msg .= 'Username should be between 3 to 15 characters' . '<br>';
|
||||
}
|
||||
if (Validator::Length($fullname, 36, 1) == false) {
|
||||
$msg .= 'Full Name should be between 2 to 25 characters' . '<br>';
|
||||
}
|
||||
if ($password != '') {
|
||||
if (!Validator::Length($password, 36, 2)) {
|
||||
$msg .= 'Password should be between 3 to 15 characters' . '<br>';
|
||||
}
|
||||
}
|
||||
|
||||
$id = _post('id');
|
||||
$d = ORM::for_table('tbl_customers')->find_one($id);
|
||||
|
||||
//lets find user Customers Attributes using id
|
||||
$customFields = ORM::for_table('tbl_customers_fields')
|
||||
->where('customer_id', $id)
|
||||
->find_many();
|
||||
|
||||
if (!$d) {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
$oldusername = $d['username'];
|
||||
$oldPppoePassword = $d['password'];
|
||||
$oldPassPassword = $d['pppoe_password'];
|
||||
$userDiff = false;
|
||||
$pppoeDiff = false;
|
||||
$passDiff = false;
|
||||
if ($oldusername != $username) {
|
||||
$c = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($c) {
|
||||
$msg .= Lang::T('Account already exist') . '<br>';
|
||||
}
|
||||
$userDiff = true;
|
||||
}
|
||||
if ($oldPppoePassword != $pppoe_password) {
|
||||
$pppoeDiff = true;
|
||||
}
|
||||
if ($password != '' && $oldPassPassword != $password) {
|
||||
$passDiff = true;
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
if ($userDiff) {
|
||||
$d->username = $username;
|
||||
}
|
||||
if ($password != '') {
|
||||
$d->password = $password;
|
||||
}
|
||||
$d->pppoe_password = $pppoe_password;
|
||||
$d->fullname = $fullname;
|
||||
$d->email = $email;
|
||||
$d->account_type = $account_type;
|
||||
$d->address = $address;
|
||||
$d->status = $status;
|
||||
$d->phonenumber = $phonenumber;
|
||||
$d->service_type = $service_type;
|
||||
$d->coordinates = $coordinates;
|
||||
$d->city = $city;
|
||||
$d->district = $district;
|
||||
$d->state = $state;
|
||||
$d->zip = $zip;
|
||||
$d->save();
|
||||
|
||||
|
||||
// Update Customers Attributes values in tbl_customers_fields table
|
||||
foreach ($customFields as $customField) {
|
||||
$fieldName = $customField['field_name'];
|
||||
if (isset($_POST['custom_fields'][$fieldName])) {
|
||||
$customFieldValue = $_POST['custom_fields'][$fieldName];
|
||||
$customField->set('field_value', $customFieldValue);
|
||||
$customField->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Add new Customers Attributess
|
||||
if (isset($_POST['custom_field_name']) && isset($_POST['custom_field_value'])) {
|
||||
$newCustomFieldNames = $_POST['custom_field_name'];
|
||||
$newCustomFieldValues = $_POST['custom_field_value'];
|
||||
|
||||
// Check if the number of field names and values match
|
||||
if (count($newCustomFieldNames) == count($newCustomFieldValues)) {
|
||||
$numNewFields = count($newCustomFieldNames);
|
||||
|
||||
for ($i = 0; $i < $numNewFields; $i++) {
|
||||
$fieldName = $newCustomFieldNames[$i];
|
||||
$fieldValue = $newCustomFieldValues[$i];
|
||||
|
||||
// Insert the new Customers Attributes
|
||||
$newCustomField = ORM::for_table('tbl_customers_fields')->create();
|
||||
$newCustomField->set('customer_id', $id);
|
||||
$newCustomField->set('field_name', $fieldName);
|
||||
$newCustomField->set('field_value', $fieldValue);
|
||||
$newCustomField->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Customers Attributess
|
||||
if (isset($_POST['delete_custom_fields'])) {
|
||||
$fieldsToDelete = $_POST['delete_custom_fields'];
|
||||
foreach ($fieldsToDelete as $fieldName) {
|
||||
// Delete the Customers Attributes with the given field name
|
||||
ORM::for_table('tbl_customers_fields')
|
||||
->where('field_name', $fieldName)
|
||||
->where('customer_id', $id)
|
||||
->delete_many();
|
||||
}
|
||||
}
|
||||
|
||||
if ($userDiff || $pppoeDiff || $passDiff) {
|
||||
$c = ORM::for_table('tbl_user_recharges')->where('username', ($userDiff) ? $oldusername : $username)->find_one();
|
||||
if ($c) {
|
||||
$c->username = $username;
|
||||
$c->save();
|
||||
$p = ORM::for_table('tbl_plans')->find_one($c['plan_id']);
|
||||
if ($p['is_radius']) {
|
||||
if ($userDiff) {
|
||||
Radius::customerChangeUsername($oldusername, $username);
|
||||
}
|
||||
Radius::customerAddPlan($d, $p, $p['expiration'] . ' ' . $p['time']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($c['routers']);
|
||||
if ($c['type'] == 'Hotspot') {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::setHotspotUser($client, $c['username'], $password);
|
||||
Mikrotik::removeHotspotActiveUser($client, $d['username']);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if (!empty($d['pppoe_password'])) {
|
||||
Mikrotik::setPpoeUser($client, $c['username'], $d['pppoe_password']);
|
||||
} else {
|
||||
Mikrotik::setPpoeUser($client, $c['username'], $password);
|
||||
}
|
||||
Mikrotik::removePpoeActive($client, $d['username']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
r2(U . 'customers/view/' . $id, 's', 'User Updated Successfully');
|
||||
} else {
|
||||
r2(U . 'customers/edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
run_hook('list_customers'); #HOOK
|
||||
$search = _post('search');
|
||||
$order = _post('order', 'username');
|
||||
$filter = _post('filter', 'Active');
|
||||
$orderby = _post('orderby', 'asc');
|
||||
$order_pos = [
|
||||
'username' => 0,
|
||||
'created_at' => 8,
|
||||
'balance' => 3,
|
||||
'status' => 7
|
||||
];
|
||||
|
||||
if ($search != '') {
|
||||
$query = ORM::for_table('tbl_customers')
|
||||
->whereRaw("username LIKE '%$search%' OR fullname LIKE '%$search%' OR address LIKE '%$search%' " .
|
||||
"OR phonenumber LIKE '%$search%' OR email LIKE '%$search%' AND status='$filter'");
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_customers');
|
||||
$query->where("status", $filter);
|
||||
}
|
||||
if ($orderby == 'asc') {
|
||||
$query->order_by_asc($order);
|
||||
} else {
|
||||
$query->order_by_desc($order);
|
||||
}
|
||||
$d = $query->findMany();
|
||||
if (_post('export', '') == 'csv') {
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="phpnuxbill_customers_' . $filter . '_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
$headers = [
|
||||
'id',
|
||||
'username',
|
||||
'fullname',
|
||||
'address',
|
||||
'phonenumber',
|
||||
'email',
|
||||
'balance',
|
||||
'service_type',
|
||||
];
|
||||
$fp = fopen('php://output', 'wb');
|
||||
if (!$h) {
|
||||
fputcsv($fp, $headers, ";");
|
||||
$h = true;
|
||||
}
|
||||
foreach ($d as $c) {
|
||||
$row = [
|
||||
$c['id'],
|
||||
$c['username'],
|
||||
$c['fullname'],
|
||||
str_replace("\n", " ", $c['address']),
|
||||
$c['phonenumber'],
|
||||
$c['email'],
|
||||
$c['balance'],
|
||||
$c['service_type'],
|
||||
];
|
||||
fputcsv($fp, $row, ";");
|
||||
}
|
||||
fclose($fp);
|
||||
die();
|
||||
}
|
||||
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">');
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status"));
|
||||
$ui->assign('filter', $filter);
|
||||
$ui->assign('search', $search);
|
||||
$ui->assign('order', $order);
|
||||
$ui->assign('order_pos', $order_pos[$order]);
|
||||
$ui->assign('orderby', $orderby);
|
||||
$ui->display('customers.tpl');
|
||||
break;
|
||||
}
|
204
system/controllers/dashboard.php
Normal file
204
system/controllers/dashboard.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Dashboard'));
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if(isset($_GET['refresh'])){
|
||||
$files = scandir($CACHE_PATH);
|
||||
foreach ($files as $file) {
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
if (is_file($CACHE_PATH . DIRECTORY_SEPARATOR . $file) && $ext == 'temp') {
|
||||
unlink($CACHE_PATH . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
}
|
||||
r2(U . 'dashboard', 's', 'Data Refreshed');
|
||||
}
|
||||
|
||||
$fdate = date('Y-m-01');
|
||||
$tdate = date('Y-m-t');
|
||||
//first day of month
|
||||
$first_day_month = date('Y-m-01');
|
||||
$mdate = date('Y-m-d');
|
||||
$month_n = date('n');
|
||||
|
||||
$iday = ORM::for_table('tbl_transactions')
|
||||
->where('recharged_on', $mdate)
|
||||
->where_not_equal('method', 'Customer - Balance')
|
||||
->where_not_equal('method', 'Recharge Balance - Administrator')
|
||||
->sum('price');
|
||||
|
||||
if ($iday == '') {
|
||||
$iday = '0.00';
|
||||
}
|
||||
$ui->assign('iday', $iday);
|
||||
|
||||
$imonth = ORM::for_table('tbl_transactions')->where_not_equal('method', 'Customer - Balance')->where_not_equal('method', 'Recharge Balance - Administrator')->where_gte('recharged_on', $first_day_month)->where_lte('recharged_on', $mdate)->sum('price');
|
||||
if ($imonth == '') {
|
||||
$imonth = '0.00';
|
||||
}
|
||||
$ui->assign('imonth', $imonth);
|
||||
|
||||
$u_act = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
|
||||
if (empty($u_act)) {
|
||||
$u_act = '0';
|
||||
}
|
||||
$ui->assign('u_act', $u_act);
|
||||
|
||||
$u_all = ORM::for_table('tbl_user_recharges')->count();
|
||||
if (empty($u_all)) {
|
||||
$u_all = '0';
|
||||
}
|
||||
$ui->assign('u_all', $u_all);
|
||||
|
||||
|
||||
$c_all = ORM::for_table('tbl_customers')->count();
|
||||
if (empty($c_all)) {
|
||||
$c_all = '0';
|
||||
}
|
||||
$ui->assign('c_all', $c_all);
|
||||
|
||||
if ($config['hide_uet'] != 'yes') {
|
||||
//user expire
|
||||
$query = ORM::for_table('tbl_user_recharges')
|
||||
->where_lte('expiration', $mdate)
|
||||
->order_by_desc('expiration');
|
||||
$expire = Paginator::findMany($query);
|
||||
|
||||
// Get the total count of expired records for pagination
|
||||
$totalCount = ORM::for_table('tbl_user_recharges')
|
||||
->where_lte('expiration', $mdate)
|
||||
->count();
|
||||
|
||||
// Pass the total count and current page to the paginator
|
||||
$paginator['total_count'] = $totalCount;
|
||||
|
||||
// Assign the pagination HTML to the template variable
|
||||
$ui->assign('expire', $expire);
|
||||
}
|
||||
|
||||
//activity log
|
||||
$dlog = ORM::for_table('tbl_logs')->limit(5)->order_by_desc('id')->find_many();
|
||||
$ui->assign('dlog', $dlog);
|
||||
$log = ORM::for_table('tbl_logs')->count();
|
||||
$ui->assign('log', $log);
|
||||
|
||||
|
||||
if ($config['hide_vs'] != 'yes') {
|
||||
$cacheStocksfile = $CACHE_PATH . File::pathFixer('/VoucherStocks.temp');
|
||||
$cachePlanfile = $CACHE_PATH . File::pathFixer('/VoucherPlans.temp');
|
||||
//Cache for 5 minutes
|
||||
if (file_exists($cacheStocksfile) && time() - filemtime($cacheStocksfile) < 600) {
|
||||
$stocks = json_decode(file_get_contents($cacheStocksfile), true);
|
||||
$plans = json_decode(file_get_contents($cachePlanfile), true);
|
||||
} else {
|
||||
// Count stock
|
||||
$tmp = $v = ORM::for_table('tbl_plans')->select('id')->select('name_plan')->find_many();
|
||||
$plans = array();
|
||||
$stocks = array("used" => 0, "unused" => 0);
|
||||
$n = 0;
|
||||
foreach ($tmp as $plan) {
|
||||
$unused = ORM::for_table('tbl_voucher')
|
||||
->where('id_plan', $plan['id'])
|
||||
->where('status', 0)->count();
|
||||
$used = ORM::for_table('tbl_voucher')
|
||||
->where('id_plan', $plan['id'])
|
||||
->where('status', 1)->count();
|
||||
if ($unused > 0 || $used > 0) {
|
||||
$plans[$n]['name_plan'] = $plan['name_plan'];
|
||||
$plans[$n]['unused'] = $unused;
|
||||
$plans[$n]['used'] = $used;
|
||||
$stocks["unused"] += $unused;
|
||||
$stocks["used"] += $used;
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
file_put_contents($cacheStocksfile, json_encode($stocks));
|
||||
file_put_contents($cachePlanfile, json_encode($plans));
|
||||
}
|
||||
}
|
||||
|
||||
$cacheMRfile = File::pathFixer('/monthlyRegistered.temp');
|
||||
//Cache for 1 hour
|
||||
if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) {
|
||||
$monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true);
|
||||
} else {
|
||||
//Monthly Registered Customers
|
||||
$result = ORM::for_table('tbl_customers')
|
||||
->select_expr('MONTH(created_at)', 'month')
|
||||
->select_expr('COUNT(*)', 'count')
|
||||
->where_raw('YEAR(created_at) = YEAR(NOW())')
|
||||
->group_by_expr('MONTH(created_at)')
|
||||
->find_many();
|
||||
|
||||
$monthlyRegistered = [];
|
||||
foreach ($result as $row) {
|
||||
$monthlyRegistered[] = [
|
||||
'date' => $row->month,
|
||||
'count' => $row->count
|
||||
];
|
||||
}
|
||||
file_put_contents($cacheMRfile, json_encode($monthlyRegistered));
|
||||
}
|
||||
|
||||
$cacheMSfile = $CACHE_PATH . File::pathFixer('/monthlySales.temp');
|
||||
//Cache for 12 hours
|
||||
if (file_exists($cacheMSfile) && time() - filemtime($cacheMSfile) < 43200) {
|
||||
$monthlySales = json_decode(file_get_contents($cacheMSfile), true);
|
||||
} else {
|
||||
// Query to retrieve monthly data
|
||||
$results = ORM::for_table('tbl_transactions')
|
||||
->select_expr('MONTH(recharged_on)', 'month')
|
||||
->select_expr('SUM(price)', 'total')
|
||||
->where_raw("YEAR(recharged_on) = YEAR(CURRENT_DATE())") // Filter by the current year
|
||||
->where_not_equal('method', 'Customer - Balance')
|
||||
->where_not_equal('method', 'Recharge Balance - Administrator')
|
||||
->group_by_expr('MONTH(recharged_on)')
|
||||
->find_many();
|
||||
|
||||
// Create an array to hold the monthly sales data
|
||||
$monthlySales = array();
|
||||
|
||||
// Iterate over the results and populate the array
|
||||
foreach ($results as $result) {
|
||||
$month = $result->month;
|
||||
$totalSales = $result->total;
|
||||
|
||||
$monthlySales[$month] = array(
|
||||
'month' => $month,
|
||||
'totalSales' => $totalSales
|
||||
);
|
||||
}
|
||||
|
||||
// Fill in missing months with zero sales
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
if (!isset($monthlySales[$month])) {
|
||||
$monthlySales[$month] = array(
|
||||
'month' => $month,
|
||||
'totalSales' => 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the array by month
|
||||
ksort($monthlySales);
|
||||
|
||||
// Reindex the array
|
||||
$monthlySales = array_values($monthlySales);
|
||||
file_put_contents($cacheMSfile, json_encode($monthlySales));
|
||||
}
|
||||
|
||||
// Assign the monthly sales data to Smarty
|
||||
$ui->assign('monthlySales', $monthlySales);
|
||||
$ui->assign('xfooter', '');
|
||||
$ui->assign('monthlyRegistered', $monthlyRegistered);
|
||||
$ui->assign('stocks', $stocks);
|
||||
$ui->assign('plans', $plans);
|
||||
|
||||
run_hook('view_dashboard'); #HOOK
|
||||
$ui->display('dashboard.tpl');
|
13
system/controllers/default.php
Normal file
13
system/controllers/default.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
if(Admin::getID()){
|
||||
r2(U.'dashboard');
|
||||
}if(User::getID()){
|
||||
r2(U.'home');
|
||||
}else{
|
||||
r2(U.'login');
|
||||
}
|
356
system/controllers/export.php
Normal file
356
system/controllers/export.php
Normal file
@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Reports'));
|
||||
$ui->assign('_sysfrm_menu', 'reports');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
$mdate = date('Y-m-d');
|
||||
$tdate = date('Y-m-d', strtotime('today - 30 days'));
|
||||
|
||||
//first day of month
|
||||
$first_day_month = date('Y-m-01');
|
||||
//
|
||||
$this_week_start = date('Y-m-d', strtotime('previous sunday'));
|
||||
// 30 days before
|
||||
$before_30_days = date('Y-m-d', strtotime('today - 30 days'));
|
||||
//this month
|
||||
$month_n = date('n');
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'print-by-date':
|
||||
$mdate = date('Y-m-d');
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
$d->where('recharged_on', $mdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
$dr->where('recharged_on', $mdate);
|
||||
$dr->order_by_desc('id');
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$ui->assign('d', $x);
|
||||
$ui->assign('dr', $xy);
|
||||
$ui->assign('mdate', $mdate);
|
||||
$ui->assign('recharged_on', $mdate);
|
||||
run_hook('print_by_date'); #HOOK
|
||||
$ui->display('print-by-date.tpl');
|
||||
break;
|
||||
|
||||
case 'pdf-by-date':
|
||||
$mdate = date('Y-m-d');
|
||||
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
$d->where('recharged_on', $mdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
$dr->where('recharged_on', $mdate);
|
||||
$dr->order_by_desc('id');
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$title = ' Reports [' . $mdate . ']';
|
||||
$title = str_replace('-', ' ', $title);
|
||||
|
||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
||||
} else {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.default.png';
|
||||
}
|
||||
|
||||
if ($x) {
|
||||
$html = '
|
||||
<div id="page-wrap">
|
||||
<div id="address">
|
||||
<h3>' . $config['CompanyName'] . '</h3>
|
||||
' . $config['address'] . '<br>
|
||||
' . Lang::T('Phone Number') . ': ' . $config['phone'] . '<br>
|
||||
</div>
|
||||
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
|
||||
</div>
|
||||
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . date($config['date_format'], strtotime($mdate)) . '</div>
|
||||
<table id="customers">
|
||||
<tr>
|
||||
<th>' . Lang::T('Username') . '</th>
|
||||
<th>' . Lang::T('Plan Name') . '</th>
|
||||
<th>' . Lang::T('Type') . '</th>
|
||||
<th>' . Lang::T('Plan Price') . '</th>
|
||||
<th>' . Lang::T('Created On') . '</th>
|
||||
<th>' . Lang::T('Expires On') . '</th>
|
||||
<th>' . Lang::T('Method') . '</th>
|
||||
<th>' . Lang::T('Routers') . '</th>
|
||||
</tr>';
|
||||
$c = true;
|
||||
foreach ($x as $value) {
|
||||
|
||||
$username = $value['username'];
|
||||
$plan_name = $value['plan_name'];
|
||||
$type = $value['type'];
|
||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||
$recharged_on = date($config['date_format'], strtotime($value['recharged_on']));
|
||||
$expiration = date($config['date_format'], strtotime($value['expiration']));
|
||||
$time = $value['time'];
|
||||
$method = $value['method'];
|
||||
$routers = $value['routers'];
|
||||
|
||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||
<td>$username</td>
|
||||
<td>$plan_name</td>
|
||||
<td>$type</td>
|
||||
<td align='right'>$price</td>
|
||||
<td>$recharged_on</td>
|
||||
<td>$expiration $time </td>
|
||||
<td>$method</td>
|
||||
<td>$routers</td>
|
||||
</tr>";
|
||||
}
|
||||
$html .= '</table>
|
||||
<h4 class="text-uppercase text-bold">' . Lang::T('Total Income') . ':</h4>
|
||||
<h3 class="sum">' . $config['currency_code'] . ' ' . number_format($xy, 2, $config['dec_point'], $config['thousands_sep']) . '</h3>';
|
||||
run_hook('print_pdf_by_date'); #HOOK
|
||||
|
||||
$mpdf = new \Mpdf\Mpdf();
|
||||
$mpdf->SetProtection(array('print'));
|
||||
$mpdf->SetTitle($config['CompanyName'] . ' Reports');
|
||||
$mpdf->SetAuthor($config['CompanyName']);
|
||||
$mpdf->SetWatermarkText($d['price']);
|
||||
$mpdf->showWatermarkText = true;
|
||||
$mpdf->watermark_font = 'Helvetica';
|
||||
$mpdf->watermarkTextAlpha = 0.1;
|
||||
$mpdf->SetDisplayMode('fullpage');
|
||||
|
||||
$style = '<style>
|
||||
#page-wrap { width: 100%; margin: 0 auto; }
|
||||
#header { text-align: center; position: relative; color: black; font: bold 15px Helvetica, Sans-Serif; margin-top: 10px; margin-bottom: 10px;}
|
||||
|
||||
#address { width: 300px; float: left; }
|
||||
#logo { text-align: right; float: right; position: relative; margin-top: 15px; border: 5px solid #fff; overflow: hidden; }
|
||||
|
||||
#customers
|
||||
{
|
||||
font-family: Helvetica, sans-serif;
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
#customers td, #customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
border:1px solid #98bf21;
|
||||
padding:3px 5px 2px 5px;
|
||||
}
|
||||
#customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
text-align:left;
|
||||
padding-top:5px;
|
||||
padding-bottom:4px;
|
||||
background-color:#A7C942;
|
||||
color:#fff;
|
||||
}
|
||||
#customers tr.alt td
|
||||
{
|
||||
color:#000;
|
||||
background-color:#EAF2D3;
|
||||
}
|
||||
</style>';
|
||||
|
||||
$nhtml = <<<EOF
|
||||
$style
|
||||
$html
|
||||
EOF;
|
||||
$mpdf->WriteHTML($nhtml);
|
||||
$mpdf->Output(date('Ymd_His') . '.pdf', 'D');
|
||||
} else {
|
||||
echo 'No Data';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'print-by-period':
|
||||
$fdate = _post('fdate');
|
||||
$tdate = _post('tdate');
|
||||
$stype = _post('stype');
|
||||
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
$d->where_gte('recharged_on', $fdate);
|
||||
$d->where_lte('recharged_on', $tdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$dr->where('type', $stype);
|
||||
}
|
||||
|
||||
$dr->where_gte('recharged_on', $fdate);
|
||||
$dr->where_lte('recharged_on', $tdate);
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$ui->assign('d', $x);
|
||||
$ui->assign('dr', $xy);
|
||||
$ui->assign('fdate', $fdate);
|
||||
$ui->assign('tdate', $tdate);
|
||||
$ui->assign('stype', $stype);
|
||||
run_hook('print_by_period'); #HOOK
|
||||
$ui->display('print-by-period.tpl');
|
||||
break;
|
||||
|
||||
|
||||
case 'pdf-by-period':
|
||||
$fdate = _post('fdate');
|
||||
$tdate = _post('tdate');
|
||||
$stype = _post('stype');
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
|
||||
$d->where_gte('recharged_on', $fdate);
|
||||
$d->where_lte('recharged_on', $tdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$dr->where('type', $stype);
|
||||
}
|
||||
|
||||
$dr->where_gte('recharged_on', $fdate);
|
||||
$dr->where_lte('recharged_on', $tdate);
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$title = ' Reports [' . $mdate . ']';
|
||||
$title = str_replace('-', ' ', $title);
|
||||
|
||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
||||
} else {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.default.png';
|
||||
}
|
||||
|
||||
if ($x) {
|
||||
$html = '
|
||||
<div id="page-wrap">
|
||||
<div id="address">
|
||||
<h3>' . $config['CompanyName'] . '</h3>
|
||||
' . $config['address'] . '<br>
|
||||
' . Lang::T('Phone Number') . ': ' . $config['phone'] . '<br>
|
||||
</div>
|
||||
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
|
||||
</div>
|
||||
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . date($config['date_format'], strtotime($fdate)) . ' - ' . date($config['date_format'], strtotime($tdate)) . '</div>
|
||||
<table id="customers">
|
||||
<tr>
|
||||
<th>' . Lang::T('Username') . '</th>
|
||||
<th>' . Lang::T('Plan Name') . '</th>
|
||||
<th>' . Lang::T('Type') . '</th>
|
||||
<th>' . Lang::T('Plan Price') . '</th>
|
||||
<th>' . Lang::T('Created On') . '</th>
|
||||
<th>' . Lang::T('Expires On') . '</th>
|
||||
<th>' . Lang::T('Method') . '</th>
|
||||
<th>' . Lang::T('Routers') . '</th>
|
||||
</tr>';
|
||||
$c = true;
|
||||
foreach ($x as $value) {
|
||||
|
||||
$username = $value['username'];
|
||||
$plan_name = $value['plan_name'];
|
||||
$type = $value['type'];
|
||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||
$recharged_on = date($config['date_format'], strtotime($value['recharged_on']));
|
||||
$expiration = date($config['date_format'], strtotime($value['expiration']));
|
||||
$time = $value['time'];
|
||||
$method = $value['method'];
|
||||
$routers = $value['routers'];
|
||||
|
||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||
<td>$username</td>
|
||||
<td>$plan_name</td>
|
||||
<td>$type</td>
|
||||
<td align='right'>$price</td>
|
||||
<td>$recharged_on </td>
|
||||
<td>$expiration $time </td>
|
||||
<td>$method</td>
|
||||
<td>$routers</td>
|
||||
</tr>";
|
||||
}
|
||||
$html .= '</table>
|
||||
<h4 class="text-uppercase text-bold">' . Lang::T('Total Income') . ':</h4>
|
||||
<h3 class="sum">' . $config['currency_code'] . ' ' . number_format($xy, 2, $config['dec_point'], $config['thousands_sep']) . '</h3>';
|
||||
|
||||
run_hook('pdf_by_period'); #HOOK
|
||||
$mpdf = new \Mpdf\Mpdf();
|
||||
$mpdf->SetProtection(array('print'));
|
||||
$mpdf->SetTitle($config['CompanyName'] . ' Reports');
|
||||
$mpdf->SetAuthor($config['CompanyName']);
|
||||
$mpdf->SetWatermarkText($d['price']);
|
||||
$mpdf->showWatermarkText = true;
|
||||
$mpdf->watermark_font = 'Helvetica';
|
||||
$mpdf->watermarkTextAlpha = 0.1;
|
||||
$mpdf->SetDisplayMode('fullpage');
|
||||
|
||||
$style = '<style>
|
||||
#page-wrap { width: 100%; margin: 0 auto; }
|
||||
#header { text-align: center; position: relative; color: black; font: bold 15px Helvetica, Sans-Serif; margin-top: 10px; margin-bottom: 10px;}
|
||||
|
||||
#address { width: 300px; float: left; }
|
||||
#logo { text-align: right; float: right; position: relative; margin-top: 15px; border: 5px solid #fff; overflow: hidden; }
|
||||
|
||||
#customers
|
||||
{
|
||||
font-family: Helvetica, sans-serif;
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
#customers td, #customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
border:1px solid #98bf21;
|
||||
padding:3px 5px 2px 5px;
|
||||
}
|
||||
#customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
text-align:left;
|
||||
padding-top:5px;
|
||||
padding-bottom:4px;
|
||||
background-color:#A7C942;
|
||||
color:#fff;
|
||||
}
|
||||
#customers tr.alt td
|
||||
{
|
||||
color:#000;
|
||||
background-color:#EAF2D3;
|
||||
}
|
||||
</style>';
|
||||
|
||||
$nhtml = <<<EOF
|
||||
$style
|
||||
$html
|
||||
EOF;
|
||||
$mpdf->WriteHTML($nhtml);
|
||||
$mpdf->Output(date('Ymd_His') . '.pdf', 'D');
|
||||
} else {
|
||||
echo 'No Data';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
252
system/controllers/home.php
Normal file
252
system/controllers/home.php
Normal file
@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_auth();
|
||||
$ui->assign('_title', Lang::T('Dashboard'));
|
||||
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
if (isset($_GET['renewal'])) {
|
||||
$user->auto_renewal = $_GET['renewal'];
|
||||
$user->save();
|
||||
}
|
||||
|
||||
if (_post('send') == 'balance') {
|
||||
if ($config['enable_balance'] == 'yes' && $config['allow_balance_transfer'] == 'yes') {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$target = ORM::for_table('tbl_customers')->where('username', _post('username'))->find_one();
|
||||
if (!$target) {
|
||||
r2(U . 'home', 'd', Lang::T('Username not found'));
|
||||
}
|
||||
$username = _post('username');
|
||||
$balance = _post('balance');
|
||||
if ($user['balance'] < $balance) {
|
||||
r2(U . 'home', 'd', Lang::T('insufficient balance'));
|
||||
}
|
||||
if (!empty($config['minimum_transfer']) && intval($balance) < intval($config['minimum_transfer'])) {
|
||||
r2(U . 'home', 'd', Lang::T('Minimum Transfer') . ' ' . Lang::moneyFormat($config['minimum_transfer']));
|
||||
}
|
||||
if ($user['username'] == $target['username']) {
|
||||
r2(U . 'home', 'd', Lang::T('Cannot send to yourself'));
|
||||
}
|
||||
if (Balance::transfer($user['id'], $username, $balance)) {
|
||||
//sender
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $target['username'];
|
||||
$d->plan_id = 0;
|
||||
$d->plan_name = 'Send Balance';
|
||||
$d->routers_id = 0;
|
||||
$d->routers = 'balance';
|
||||
$d->price = $balance;
|
||||
$d->payment_method = "Customer";
|
||||
$d->payment_channel = "Balance";
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
//receiver
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $target['username'];
|
||||
$d->gateway = $user['username'];
|
||||
$d->plan_id = 0;
|
||||
$d->plan_name = 'Receive Balance';
|
||||
$d->routers_id = 0;
|
||||
$d->routers = 'balance';
|
||||
$d->payment_method = "Customer";
|
||||
$d->payment_channel = "Balance";
|
||||
$d->price = $balance;
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
Message::sendBalanceNotification($user['phonenumber'], $target['fullname'] . ' (' . $target['username'] . ')', $balance, ($user['balance'] - $balance), Lang::getNotifText('balance_send'), $config['user_notification_payment']);
|
||||
Message::sendBalanceNotification($target['phonenumber'], $user['fullname'] . ' (' . $user['username'] . ')', $balance, ($target['balance'] + $balance), Lang::getNotifText('balance_received'), $config['user_notification_payment']);
|
||||
Message::sendTelegram("#u$user[username] send balance to #u$target[username] \n" . Lang::moneyFormat($balance));
|
||||
r2(U . 'home', 's', Lang::T('Sending balance success'));
|
||||
}
|
||||
} else {
|
||||
r2(U . 'home', 'd', Lang::T('Failed, balance is not available'));
|
||||
}
|
||||
} else if (_post('send') == 'plan') {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$actives = ORM::for_table('tbl_user_recharges')
|
||||
->where('username', _post('username'))
|
||||
->find_many();
|
||||
foreach ($actives as $active) {
|
||||
$router = ORM::for_table('tbl_routers')->where('name', $active['routers'])->find_one();
|
||||
if ($router) {
|
||||
r2(U . "order/send/$router[id]/$active[plan_id]&u=" . trim(_post('username')), 's', Lang::T('Review package before recharge'));
|
||||
}
|
||||
}
|
||||
r2(U . 'home', 'w', Lang::T('Your friend do not have active package'));
|
||||
}
|
||||
|
||||
$ui->assign('_bills', User::_billing());
|
||||
|
||||
if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
if (!empty(App::getTokenValue(_get('stoken')))) {
|
||||
r2(U . "voucher/invoice/");
|
||||
die();
|
||||
}
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['recharge'])->where('username', $user['username'])->findOne();
|
||||
if ($bill) {
|
||||
if ($bill['routers'] == 'radius') {
|
||||
$router = 'radius';
|
||||
} else {
|
||||
$routers = ORM::for_table('tbl_routers')->where('name', $bill['routers'])->find_one();
|
||||
$router = $routers['id'];
|
||||
}
|
||||
if ($config['enable_balance'] == 'yes') {
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($bill['plan_id']);
|
||||
if (!$plan['enabled']) {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
if ($user['balance'] > $plan['price']) {
|
||||
r2(U . "order/pay/$router/$bill[plan_id]&stoken=" . _get('stoken'), 'e', 'Order Plan');
|
||||
} else {
|
||||
r2(U . "order/buy/$router/$bill[plan_id]", 'e', 'Order Plan');
|
||||
}
|
||||
} else {
|
||||
r2(U . "order/buy/$router/$bill[plan_id]", 'e', 'Order Plan');
|
||||
}
|
||||
}
|
||||
} else if (!empty(_get('extend'))) {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
if (!$config['extend_expired']) {
|
||||
r2(U . 'home', 'e', "cannot extend");
|
||||
}
|
||||
if (!empty(App::getTokenValue(_get('stoken')))) {
|
||||
r2(U . 'home', 'e', "You already extend");
|
||||
}
|
||||
$id = _get('extend');
|
||||
$tur = ORM::for_table('tbl_user_recharges')->where('customer_id', $user['id'])->where('id', $id)->find_one();
|
||||
if ($tur) {
|
||||
$m = date("m");
|
||||
$path = $CACHE_PATH . DIRECTORY_SEPARATOR . "extends" . DIRECTORY_SEPARATOR;
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
}
|
||||
$path .= $user['id'] . ".txt";
|
||||
if (file_exists($path)) {
|
||||
// is already extend
|
||||
$last = file_get_contents($path);
|
||||
if ($last == $m) {
|
||||
r2(U . 'home', 'e', "You already extend for this month");
|
||||
}
|
||||
}
|
||||
if ($tur['status'] != 'on') {
|
||||
if ($tur['routers'] != 'radius') {
|
||||
$mikrotik = Mikrotik::info($tur['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$router = $tur['routers'];
|
||||
}
|
||||
$p = ORM::for_table('tbl_plans')->findOne($tur['plan_id']);
|
||||
if (!$p) {
|
||||
r2(U . 'home', '3', "Plan Not Found");
|
||||
}
|
||||
if ($tur['routers'] == 'radius') {
|
||||
Radius::customerAddPlan($user, $p, $tur['expiration'] . ' ' . $tur['time']);
|
||||
} else {
|
||||
if ($tur['type'] == 'Hotspot') {
|
||||
Mikrotik::removeHotspotUser($client, $user['username']);
|
||||
Mikrotik::addHotspotUser($client, $p, $user);
|
||||
} else if ($tur['type'] == 'PPPOE') {
|
||||
Mikrotik::removePpoeUser($client, $user['username']);
|
||||
Mikrotik::addPpoeUser($client, $p, $user);
|
||||
}
|
||||
}
|
||||
// make customer cannot extend again
|
||||
$days = $config['extend_days'];
|
||||
$expiration = date('Y-m-d', strtotime(" +$days day"));
|
||||
$tur->expiration = $expiration;
|
||||
$tur->status = "on";
|
||||
$tur->save();
|
||||
App::setToken(_get('stoken'), $id);
|
||||
file_put_contents($path, $m);
|
||||
_log("Customer $tur[customer_id] $tur[username] extend for $days days", "Customer", $user['id']);
|
||||
Message::sendTelegram("#u$user[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
|
||||
"\nLocation: " . $p['routers'] .
|
||||
"\nCustomer: " . $user['fullname'] .
|
||||
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
|
||||
r2(U . 'home', 's', "Extend until $expiration");
|
||||
} else {
|
||||
r2(U . 'home', 'e', "Plan is not expired");
|
||||
}
|
||||
} else {
|
||||
r2(U . 'home', 'e', "Plan Not Found or Not Active");
|
||||
}
|
||||
} else if (isset($_GET['deactivate']) && !empty($_GET['deactivate'])) {
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['deactivate'])->where('username', $user['username'])->findOne();
|
||||
if ($bill) {
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $bill['plan_id'])->find_one();
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerDeactivate($user['username']);
|
||||
} else {
|
||||
try {
|
||||
$mikrotik = Mikrotik::info($bill['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($bill['type'] == 'Hotspot') {
|
||||
Mikrotik::removeHotspotUser($client, $bill['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $bill['username']);
|
||||
} else if ($bill['type'] == 'PPPOE') {
|
||||
Mikrotik::removePpoeUser($client, $bill['username']);
|
||||
Mikrotik::removePpoeActive($client, $bill['username']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//ignore it maybe mikrotik has been deleted
|
||||
}
|
||||
}
|
||||
$bill->status = 'off';
|
||||
$bill->expiration = date('Y-m-d');
|
||||
$bill->time = date('H:i:s');
|
||||
$bill->save();
|
||||
_log('User ' . $bill['username'] . ' Deactivate ' . $bill['namebp'], 'Customer', $bill['customer_id']);
|
||||
Message::sendTelegram('User u' . $bill['username'] . ' Deactivate ' . $bill['namebp']);
|
||||
r2(U . 'home', 's', 'Success deactivate ' . $bill['namebp']);
|
||||
} else {
|
||||
r2(U . 'home', 'e', 'No Active Plan');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
$ui->assign('nux_mac', $_SESSION['nux-mac']);
|
||||
$ui->assign('nux_ip', $_SESSION['nux-ip']);
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['id'])->where('username', $user['username'])->findOne();
|
||||
if ($_GET['mikrotik'] == 'login') {
|
||||
$m = Mikrotik::info($bill['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeIn($c, $user['username'], $user['password'], $_SESSION['nux-ip'], $_SESSION['nux-mac']);
|
||||
r2(U . 'home', 's', Lang::T('Login Request successfully'));
|
||||
} else if ($_GET['mikrotik'] == 'logout') {
|
||||
$m = Mikrotik::info($bill['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeOut($c, $user['username']);
|
||||
r2(U . 'home', 's', Lang::T('Logout Request successfully'));
|
||||
}
|
||||
}
|
||||
|
||||
$ui->assign('unpaid', ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one());
|
||||
run_hook('view_customer_dashboard'); #HOOK
|
||||
$ui->display('user-dashboard.tpl');
|
8
system/controllers/index.html
Normal file
8
system/controllers/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
195
system/controllers/login.php
Normal file
195
system/controllers/login.php
Normal file
@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
$maintenance_mode = $config['maintenance_mode'];
|
||||
if ($maintenance_mode == true) {
|
||||
displayMaintenanceMessage();
|
||||
}
|
||||
|
||||
if (Admin::getID()) {
|
||||
r2(U . 'dashboard');
|
||||
}
|
||||
|
||||
if (User::getID()) {
|
||||
r2(U . 'home');
|
||||
}
|
||||
|
||||
if (isset($routes['1'])) {
|
||||
$do = $routes['1'];
|
||||
} else {
|
||||
$do = 'login-display';
|
||||
}
|
||||
|
||||
switch ($do) {
|
||||
case 'post':
|
||||
$username = _post('username');
|
||||
$password = _post('password');
|
||||
run_hook('customer_login'); #HOOK
|
||||
if ($username != '' and $password != '') {
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
if ($d['status'] == 'Banned') {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('This account status') . ': ' . Lang::T($d['status']) . '</div>';
|
||||
}
|
||||
if (Password::_uverify($password, $d_pass) == true) {
|
||||
$_SESSION['uid'] = $d['id'];
|
||||
User::setCookie($d['id']);
|
||||
$d->last_login = date('Y-m-d H:i:s');
|
||||
$d->save();
|
||||
_log($username . ' ' . Lang::T('Login Successful'), 'User', $d['id']);
|
||||
echo '<div class="alert alert-success">' . Lang::T('Login Successful') . '</div>';
|
||||
r2(U . 'home');
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
_log($username . ' ' . Lang::T('Failed Login'), 'User');
|
||||
r2(U . 'login');
|
||||
}
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_users')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
if (Password::_verify($password, $d_pass) == true) {
|
||||
$_SESSION['aid'] = $d['id'];
|
||||
$token = Admin::setCookie($d['id']);
|
||||
$d->last_login = date('Y-m-d H:i:s');
|
||||
$d->save();
|
||||
_log($username . ' ' . Lang::T('Login Successful'), $d['user_type'], $d['id']);
|
||||
echo '<div class="alert alert-success">' . Lang::T('Login Successful') . '</div>';
|
||||
r2(U . 'dashboard');
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
_log($username . ' ' . Lang::T('Failed Login'), $d['user_type']);
|
||||
r2(U . 'login');
|
||||
}
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
r2(U . 'login');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
r2(U . 'login');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'activation':
|
||||
$voucher = _post('voucher');
|
||||
$username = _post('username');
|
||||
$v1 = ORM::for_table('tbl_voucher')->where('code', $voucher)->find_one();
|
||||
if ($v1) {
|
||||
// voucher exists, check customer exists or not
|
||||
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if (!$user) {
|
||||
$d = ORM::for_table('tbl_customers')->create();
|
||||
$d->username = alphanumeric($username, "+_.");
|
||||
$d->password = $voucher;
|
||||
$d->fullname = '';
|
||||
$d->address = '';
|
||||
$d->email = '';
|
||||
$d->phonenumber = (strlen($username) < 21) ? $username : '';
|
||||
if ($d->save()) {
|
||||
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one($d->id());
|
||||
if (!$user) {
|
||||
r2(U . 'login', 'e', Lang::T('Voucher activation failed'));
|
||||
}
|
||||
} else {
|
||||
_alert(Lang::T('Login Successful'), 'success', "dashboard");
|
||||
r2(U . 'login', 'e', Lang::T('Voucher activation failed') . '.');
|
||||
}
|
||||
}
|
||||
if ($v1['status'] == 0) {
|
||||
$oldPass = $user['password'];
|
||||
// change customer password to voucher code
|
||||
$user->password = $voucher;
|
||||
$user->save();
|
||||
// voucher activation
|
||||
if (Package::rechargeUser($user['id'], $v1['routers'], $v1['id_plan'], "Voucher", $voucher)) {
|
||||
$v1->status = "1";
|
||||
$v1->user = $user['username'];
|
||||
$v1->save();
|
||||
$user->last_login = date('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
// add customer to mikrotik
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
try {
|
||||
$m = Mikrotik::info($v1['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeIn($c, $user['username'], $user['password'], $_SESSION['nux-ip'], $_SESSION['nux-mac']);
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
} else {
|
||||
// if failed to recharge, restore old password
|
||||
$user->password = $oldPass;
|
||||
$user->save();
|
||||
r2(U . 'login', 'e', Lang::T("Failed to activate voucher"));
|
||||
}
|
||||
} else {
|
||||
// used voucher
|
||||
// check if voucher used by this username
|
||||
if ($v1['user'] == $user['username']) {
|
||||
$user->last_login = date('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
try {
|
||||
$m = Mikrotik::info($v1['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeIn($c, $user['username'], $user['password'], $_SESSION['nux-ip'], $_SESSION['nux-mac']);
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// voucher used by other customer
|
||||
r2(U . 'login', 'e', Lang::T('Voucher Not Valid'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_msglog('e', Lang::T('Invalid Username or Password'));
|
||||
r2(U . 'login');
|
||||
}
|
||||
default:
|
||||
run_hook('customer_view_login'); #HOOK
|
||||
if ($config['disable_registration'] == 'yes') {
|
||||
$ui->display('user-login-noreg.tpl');
|
||||
} else {
|
||||
$ui->display('user-login.tpl');
|
||||
}
|
||||
break;
|
||||
}
|
12
system/controllers/logout.php
Normal file
12
system/controllers/logout.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
run_hook('customer_logout'); #HOOK
|
||||
if (session_status() == PHP_SESSION_NONE) session_start();
|
||||
Admin::removeCookie();
|
||||
User::removeCookie();
|
||||
session_destroy();
|
||||
_alert(Lang::T('Logout Successful'),'warning', "login");
|
125
system/controllers/logs.php
Normal file
125
system/controllers/logs.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'PHPNuxBill Logs');
|
||||
$ui->assign('_system_menu', 'logs');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
|
||||
switch ($action) {
|
||||
case 'list-csv':
|
||||
$logs = ORM::for_table('tbl_logs')
|
||||
->select('id')
|
||||
->select('date')
|
||||
->select('type')
|
||||
->select('description')
|
||||
->select('userid')
|
||||
->select('ip')
|
||||
->order_by_asc('id')->find_array();
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="activity-logs_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
foreach ($logs as $log) {
|
||||
$ks = [];
|
||||
$vs = [];
|
||||
foreach ($log as $k => $v) {
|
||||
$ks[] = $k;
|
||||
$vs[] = $v;
|
||||
}
|
||||
if (!$h) {
|
||||
echo '"' . implode('";"', $ks) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
echo '"' . implode('";"', $vs) . "\"\n";
|
||||
}
|
||||
break;
|
||||
case 'radius-csv':
|
||||
$logs = ORM::for_table('radpostauth')
|
||||
->select('id')
|
||||
->select('username')
|
||||
->select('pass')
|
||||
->select('reply')
|
||||
->select('authdate')
|
||||
->order_by_asc('id')->find_array();
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="radius-logs_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
foreach ($logs as $log) {
|
||||
$ks = [];
|
||||
$vs = [];
|
||||
foreach ($log as $k => $v) {
|
||||
$ks[] = $k;
|
||||
$vs[] = $v;
|
||||
}
|
||||
if (!$h) {
|
||||
echo '"' . implode('";"', $ks) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
echo '"' . implode('";"', $vs) . "\"\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
$q = (_post('q') ? _post('q') : _get('q'));
|
||||
$keep = _post('keep');
|
||||
if (!empty($keep)) {
|
||||
ORM::raw_execute("DELETE FROM tbl_logs WHERE UNIX_TIMESTAMP(date) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
|
||||
r2(U . "logs/list/", 's', "Delete logs older than $keep days");
|
||||
}
|
||||
if ($q != '') {
|
||||
$query = ORM::for_table('tbl_logs')->where_like('description', '%' . $q . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['q' => $q]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_logs')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('q', $q);
|
||||
$ui->display('logs.tpl');
|
||||
break;
|
||||
case 'radius':
|
||||
$q = (_post('q') ? _post('q') : _get('q'));
|
||||
$keep = _post('keep');
|
||||
if (!empty($keep)) {
|
||||
ORM::raw_execute("DELETE FROM radpostauth WHERE UNIX_TIMESTAMP(authdate) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))", [], 'radius');
|
||||
r2(U . "logs/radius/", 's', "Delete logs older than $keep days");
|
||||
}
|
||||
if ($q != '') {
|
||||
$query = ORM::for_table('radpostauth', 'radius')->where_like('username', '%' . $q . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['q' => $q]);
|
||||
} else {
|
||||
$query = ORM::for_table('radpostauth', 'radius')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('q', $q);
|
||||
$ui->display('logs-radius.tpl');
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
r2(U . 'logs/list/', 's', '');
|
||||
}
|
54
system/controllers/map.php
Normal file
54
system/controllers/map.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_system_menu', 'map');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'customer';
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'customer':
|
||||
if(!empty(_req('search'))){
|
||||
$search = _req('search');
|
||||
$query = ORM::for_table('tbl_customers')->whereRaw("coordinates != '' AND fullname LIKE '%$search%' OR username LIKE '%$search%' OR email LIKE '%$search%' OR phonenumber LIKE '%$search%'")->order_by_desc('fullname');
|
||||
$c = Paginator::findMany($query, ['search' => $search], 50);
|
||||
}else{
|
||||
$query = ORM::for_table('tbl_customers')->where_not_equal('coordinates','');
|
||||
$c = Paginator::findMany($query, ['search'=>''], 50);
|
||||
}
|
||||
$customerData = [];
|
||||
|
||||
foreach ($c as $customer) {
|
||||
if (!empty($customer->coordinates)) {
|
||||
$customerData[] = [
|
||||
'id' => $customer->id,
|
||||
'name' => $customer->fullname,
|
||||
'balance' => $customer->balance,
|
||||
'address' => $customer->address,
|
||||
'direction' => $customer->coordinates,
|
||||
'info' => Lang::T("Username") . ": " . $customer->username . " - " . Lang::T("Full Name") . ": " . $customer->fullname . " - " . Lang::T("Email") . ": " . $customer->email . " - " . Lang::T("Phone") . ": " . $customer->phonenumber . " - " . Lang::T("Service Type") . ": " . $customer->service_type,
|
||||
'coordinates' => '[' . $customer->coordinates . ']',
|
||||
];
|
||||
}
|
||||
}
|
||||
$ui->assign('search', $search);
|
||||
$ui->assign('customers', $customerData);
|
||||
$ui->assign('xheader', '<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">');
|
||||
$ui->assign('_title', Lang::T('Customer Geo Location Information'));
|
||||
$ui->assign('xfooter', '<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>');
|
||||
$ui->display('customers-map.tpl');
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'map/customer', 'e', 'action not defined');
|
||||
break;
|
||||
}
|
242
system/controllers/message.php
Normal file
242
system/controllers/message.php
Normal file
@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Send Message'));
|
||||
$ui->assign('_system_menu', 'message');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'send';
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'send':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$select2_customer = <<<EOT
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
$('#personSelect').select2({
|
||||
theme: "bootstrap",
|
||||
ajax: {
|
||||
url: function(params) {
|
||||
if(params.term != undefined){
|
||||
return './index.php?_route=autoload/customer_select2&s='+params.term;
|
||||
}else{
|
||||
return './index.php?_route=autoload/customer_select2';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
EOT;
|
||||
if (isset($routes['2']) && !empty($routes['2'])) {
|
||||
$ui->assign('cust', ORM::for_table('tbl_customers')->find_one($routes['2']));
|
||||
}
|
||||
$id = $routes['2'];
|
||||
$ui->assign('id', $id);
|
||||
$ui->assign('xfooter', $select2_customer);
|
||||
$ui->display('message.tpl');
|
||||
break;
|
||||
|
||||
case 'send-post':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Get form data
|
||||
$id_customer = $_POST['id_customer'];
|
||||
$message = $_POST['message'];
|
||||
$via = $_POST['via'];
|
||||
|
||||
// Check if fields are empty
|
||||
if ($id_customer == '' or $message == '' or $via == '') {
|
||||
r2(U . 'message/send', 'e', Lang::T('All field is required'));
|
||||
} else {
|
||||
// Get customer details from the database
|
||||
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
|
||||
|
||||
// Replace placeholders in the message with actual values
|
||||
$message = str_replace('[[name]]', $c['fullname'], $message);
|
||||
$message = str_replace('[[user_name]]', $c['username'], $message);
|
||||
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
|
||||
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
|
||||
|
||||
// Send the message
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
$smsSent = Message::sendSMS($c['phonenumber'], $message);
|
||||
}
|
||||
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
$waSent = Message::sendWhatsapp($c['phonenumber'], $message);
|
||||
}
|
||||
|
||||
if (isset($smsSent) || isset($waSent)) {
|
||||
r2(U . 'message/send', 's', Lang::T('Message Sent Successfully'));
|
||||
} else {
|
||||
r2(U . 'message/send', 'e', Lang::T('Failed to send message'));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'send_bulk':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Get form data
|
||||
$group = $_POST['group'];
|
||||
$message = $_POST['message'];
|
||||
$via = $_POST['via'];
|
||||
$test = isset($_POST['test']) && $_POST['test'] === 'on' ? 'yes' : 'no';
|
||||
$batch = $_POST['batch'];
|
||||
$delay = $_POST['delay'];
|
||||
|
||||
// Initialize counters
|
||||
$totalSMSSent = 0;
|
||||
$totalSMSFailed = 0;
|
||||
$totalWhatsappSent = 0;
|
||||
$totalWhatsappFailed = 0;
|
||||
$batchStatus = [];
|
||||
|
||||
if (_req('send') == 'now') {
|
||||
// Check if fields are empty
|
||||
if ($group == '' || $message == '' || $via == '') {
|
||||
r2(U . 'message/send_bulk', 'e', Lang::T('All fields are required'));
|
||||
} else {
|
||||
// Get customer details from the database based on the selected group
|
||||
if ($group == 'all') {
|
||||
$customers = ORM::for_table('tbl_customers')->find_many()->as_array();
|
||||
} elseif ($group == 'new') {
|
||||
// Get customers created just a month ago
|
||||
$customers = ORM::for_table('tbl_customers')->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")->find_many()->as_array();
|
||||
} elseif ($group == 'expired') {
|
||||
// Get expired user recharges where status is 'off'
|
||||
$expired = ORM::for_table('tbl_user_recharges')->where('status', 'off')->find_many();
|
||||
$customer_ids = [];
|
||||
foreach ($expired as $recharge) {
|
||||
$customer_ids[] = $recharge->customer_id;
|
||||
}
|
||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many()->as_array();
|
||||
} elseif ($group == 'active') {
|
||||
// Get active user recharges where status is 'on'
|
||||
$active = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
|
||||
$customer_ids = [];
|
||||
foreach ($active as $recharge) {
|
||||
$customer_ids[] = $recharge->customer_id;
|
||||
}
|
||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many()->as_array();
|
||||
} elseif ($group == 'pppoe') {
|
||||
// Get customers with PPPoE service type
|
||||
$customers = ORM::for_table('tbl_customers')->where('service_type', 'PPPoE')->find_many()->as_array();
|
||||
} elseif ($group == 'hotspot') {
|
||||
// Get customers with Hotspot service type
|
||||
$customers = ORM::for_table('tbl_customers')->where('service_type', 'Hotspot')->find_many()->as_array();
|
||||
}
|
||||
|
||||
// Set the batch size
|
||||
$batchSize = $batch;
|
||||
|
||||
// Calculate the number of batches
|
||||
$totalCustomers = count($customers);
|
||||
$totalBatches = ceil($totalCustomers / $batchSize);
|
||||
|
||||
// Loop through batches
|
||||
for ($batchIndex = 0; $batchIndex < $totalBatches; $batchIndex++) {
|
||||
// Get the starting and ending index for the current batch
|
||||
$start = $batchIndex * $batchSize;
|
||||
$end = min(($batchIndex + 1) * $batchSize, $totalCustomers);
|
||||
$batchCustomers = array_slice($customers, $start, $end - $start);
|
||||
|
||||
// Loop through customers in the current batch and send messages
|
||||
foreach ($batchCustomers as $customer) {
|
||||
// Create a copy of the original message for each customer and save it as currentMessage
|
||||
$currentMessage = $message;
|
||||
$currentMessage = str_replace('[[name]]', $customer['fullname'], $currentMessage);
|
||||
$currentMessage = str_replace('[[user_name]]', $customer['username'], $currentMessage);
|
||||
$currentMessage = str_replace('[[phone]]', $customer['phonenumber'], $currentMessage);
|
||||
$currentMessage = str_replace('[[company_name]]', $config['CompanyName'], $currentMessage);
|
||||
|
||||
// Send the message based on the selected method
|
||||
if ($test === 'yes') {
|
||||
// Only for testing, do not send messages to customers
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'Test Mode - Message not sent'
|
||||
];
|
||||
} else {
|
||||
// Send the actual messages
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
$smsSent = Message::sendSMS($customer['phonenumber'], $currentMessage);
|
||||
if ($smsSent) {
|
||||
$totalSMSSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Sent'
|
||||
];
|
||||
} else {
|
||||
$totalSMSFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Failed'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentMessage);
|
||||
if ($waSent) {
|
||||
$totalWhatsappSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'WhatsApp Message Sent'
|
||||
];
|
||||
} else {
|
||||
$totalWhatsappFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'WhatsApp Message Failed'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Introduce a delay between each batch
|
||||
if ($batchIndex < $totalBatches - 1) {
|
||||
sleep($delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$ui->assign('batchStatus', $batchStatus);
|
||||
$ui->assign('totalSMSSent', $totalSMSSent);
|
||||
$ui->assign('totalSMSFailed', $totalSMSFailed);
|
||||
$ui->assign('totalWhatsappSent', $totalWhatsappSent);
|
||||
$ui->assign('totalWhatsappFailed', $totalWhatsappFailed);
|
||||
$ui->display('message-bulk.tpl');
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'message/send_sms', 'e', 'action not defined');
|
||||
}
|
22
system/controllers/messages.php
Normal file
22
system/controllers/messages.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Messaging System
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Messages'));
|
||||
$ui->assign('_system_menu', 'messages');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Fetch all messages
|
||||
$msgs = ORM::for_table('tbl_message')
|
||||
->order_by_desc('date')
|
||||
->find_array();
|
||||
|
||||
$ui->assign('messages', $msgs);
|
||||
$ui->display('message-list.tpl');
|
||||
|
||||
?>
|
130
system/controllers/onlinehotspot.php
Normal file
130
system/controllers/onlinehotspot.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('online'));
|
||||
$ui->assign('_system_menu', 'onlineusers');
|
||||
$ui->assign('onlineusers', $online);
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
function handle_action($action)
|
||||
{
|
||||
switch ($action) {
|
||||
case 'get_hotspot_online_users':
|
||||
mikrotik_get_hotspot_online_users();
|
||||
break;
|
||||
case 'disconnect_online_user':
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
mikrotik_disconnect_online_user($_POST['router'], $_POST['username'], $_POST['userType']);
|
||||
}
|
||||
break;
|
||||
case 'list':
|
||||
// Assuming you have a function to fetch the data for the online hotspot users list
|
||||
$onlineHotspotUsers = fetch_online_hotspot_users();
|
||||
$ui->assign('onlineHotspotUsers', $onlineHotspotUsers);
|
||||
$ui->display('onlinehotspot.tpl');
|
||||
break;
|
||||
default:
|
||||
// Handle default case, maybe return an error or redirect
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function mikrotik_get_hotspot_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$router = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
$hotspotList = [];
|
||||
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$username = $hotspot->getProperty('user');
|
||||
$address = $hotspot->getProperty('address');
|
||||
$uptime = $hotspot->getProperty('uptime');
|
||||
$server = $hotspot->getProperty('server');
|
||||
$mac = $hotspot->getProperty('mac-address');
|
||||
$sessionTime = $hotspot->getProperty('session-time-left');
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'server' => $server,
|
||||
'mac' => $mac,
|
||||
'session_time' => $sessionTime,
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($txBytes + $rxBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Return the Hotspot online user list as JSON
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($hotspotList);
|
||||
}
|
||||
|
||||
function mikrotik_disconnect_online_user($router, $username, $userType)
|
||||
{
|
||||
// Check if the form was submitted
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Retrieve the form data
|
||||
$router = $_POST['router'];
|
||||
$username = $_POST['username'];
|
||||
$userType = $_POST['userType'];
|
||||
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
if (!$mikrotik) {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($userType == 'hotspot') {
|
||||
Mikrotik::removeHotspotActiveUser($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} elseif ($userType == 'pppoe') {
|
||||
Mikrotik::removePpoeActive($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} else {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Handle the error response or redirection
|
||||
} finally {
|
||||
// Disconnect from the MikroTik router
|
||||
if (isset($client)) {
|
||||
$client->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to format bytes
|
||||
function mikrotik_formatBytes($bytes)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$factor = floor((strlen($bytes) - 1) / 3);
|
||||
return sprintf("%.2f %s", $bytes / pow(1024, $factor), @$units[$factor]);
|
||||
}
|
||||
|
||||
// Call the main function with the action provided in the URL
|
||||
$action = $routes['1'] ?? ''; // Assuming $routes is defined elsewhere
|
||||
handle_action($action);
|
||||
|
||||
$ui->assign('onlineusers', $online);
|
||||
|
||||
$ui->display('onlinehotspot.tpl');
|
||||
?>
|
347
system/controllers/onlineusers.php
Normal file
347
system/controllers/onlineusers.php
Normal file
@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
// Include necessary files and functions here
|
||||
// ...
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Online Users'));
|
||||
$ui->assign('_system_menu', 'onlineusers');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
require_once 'system/autoload/PEAR2/Autoload.php';
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Handle cases for hotspot users and PPP users
|
||||
switch ($action) {
|
||||
case 'hotspot':
|
||||
$ui->display('hotspot_users.tpl');
|
||||
break;
|
||||
case 'hotspot_users':
|
||||
$hotspotUsers = mikrotik_get_hotspot_online_users();
|
||||
|
||||
// Filter out entries where all values are null
|
||||
$filteredHotspotUsers = array_filter($hotspotUsers, function($user) {
|
||||
// Check if all specified fields are null
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['server']) &&
|
||||
is_null($user['mac']) &&
|
||||
is_null($user['session_time']) &&
|
||||
$user['rx_bytes'] === '0 B' &&
|
||||
$user['tx_bytes'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($filteredHotspotUsers);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'pppoe':
|
||||
$ui->display('ppp_users.tpl');
|
||||
break;
|
||||
|
||||
case 'ppp_users':
|
||||
$pppUsers = mikrotik_get_ppp_online_users();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($pppUsers);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'disconnect':
|
||||
$routerId = $routes['2'];
|
||||
$username = $routes['3'];
|
||||
$userType = $routes['4'];
|
||||
mikrotik_disconnect_online_user($routerId, $username, $userType);
|
||||
// Redirect or handle the response as needed
|
||||
break;
|
||||
|
||||
case 'summary':
|
||||
// Fetch summary of online users and total bytes used
|
||||
$summary = mikrotik_get_online_users_summary();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($summary);
|
||||
exit;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Handle default case or invalid action
|
||||
break;
|
||||
}
|
||||
|
||||
// Function to round the value and append the appropriate unit
|
||||
function mikrotik_formatBytes($bytes, $precision = 2)
|
||||
{
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= pow(1024, $pow);
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
function filter_null_users($users) {
|
||||
return array_filter($users, function($user) {
|
||||
return array_reduce($user, function($carry, $value) {
|
||||
return $carry || $value !== null;
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
function mikrotik_get_hotspot_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
|
||||
$hotspotList = [];
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$username = $hotspot->getProperty('user');
|
||||
$address = $hotspot->getProperty('address');
|
||||
$uptime = $hotspot->getProperty('uptime');
|
||||
$server = $hotspot->getProperty('server');
|
||||
$mac = $hotspot->getProperty('mac-address');
|
||||
$sessionTime = $hotspot->getProperty('session-time-left');
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'server' => $server,
|
||||
'mac' => $mac,
|
||||
'session_time' => $sessionTime,
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($rxBytes + $txBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out users with all null properties
|
||||
$filteredHotspotList = filter_null_users($hotspotList);
|
||||
|
||||
// Return an empty array if no users are left after filtering
|
||||
return empty($filteredHotspotList) ? [] : $filteredHotspotList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function mikrotik_get_ppp_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
|
||||
$userList = [];
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$username = $pppUser->getProperty('name');
|
||||
$address = $pppUser->getProperty('address');
|
||||
$uptime = $pppUser->getProperty('uptime');
|
||||
$service = $pppUser->getProperty('service');
|
||||
$callerid = $pppUser->getProperty('caller-id');
|
||||
$bytes_in = $pppUser->getProperty('limit-bytes-in');
|
||||
$bytes_out = $pppUser->getProperty('limit-bytes-out');
|
||||
|
||||
$userList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'service' => $service,
|
||||
'caller_id' => $callerid,
|
||||
'bytes_in' => $bytes_in,
|
||||
'bytes_out' => $bytes_out,
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out users with all null properties
|
||||
return filter_null_users($userList);
|
||||
}
|
||||
|
||||
function save_data_usage($username, $bytes_in, $bytes_out, $connection_type) {
|
||||
if (!$username) {
|
||||
error_log("Error: Missing username in save_data_usage()");
|
||||
return;
|
||||
}
|
||||
|
||||
$currentTime = date('Y-m-d H:i:s');
|
||||
$currentDate = date('Y-m-d');
|
||||
|
||||
// Check if there's an existing record for this user today
|
||||
$existingRecord = ORM::for_table('tbl_user_data_usage')
|
||||
->where('username', $username)
|
||||
->where('connection_type', $connection_type)
|
||||
->where_raw('DATE(timestamp) = ?', [$currentDate])
|
||||
->find_one();
|
||||
|
||||
if ($existingRecord) {
|
||||
// Update existing record for today
|
||||
$existingRecord->bytes_in = ($bytes_in ?: 0);
|
||||
$existingRecord->bytes_out = ($bytes_out ?: 0);
|
||||
$existingRecord->last_updated = $currentTime;
|
||||
$existingRecord->save();
|
||||
} else {
|
||||
// Create new record for today
|
||||
$newRecord = ORM::for_table('tbl_user_data_usage')->create();
|
||||
$newRecord->username = $username;
|
||||
$newRecord->bytes_in = ($bytes_in ?: 0);
|
||||
$newRecord->bytes_out = ($bytes_out ?: 0);
|
||||
$newRecord->connection_type = $connection_type;
|
||||
$newRecord->timestamp = $currentTime;
|
||||
$newRecord->last_updated = $currentTime;
|
||||
$newRecord->save();
|
||||
}
|
||||
}
|
||||
|
||||
function mikrotik_get_online_users_summary()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
// Get Hotspot users
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
$hotspotList = [];
|
||||
$totalHotspotUsage = 0;
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
$totalHotspotUsage += $rxBytes + $txBytes;
|
||||
$username = $hotspot->getProperty('user');
|
||||
save_data_usage($username, $rxBytes, $txBytes, 'hotspot');
|
||||
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $hotspot->getProperty('address'),
|
||||
'uptime' => $hotspot->getProperty('uptime'),
|
||||
'server' => $hotspot->getProperty('server'),
|
||||
'mac' => $hotspot->getProperty('mac-address'),
|
||||
'session_time' => $hotspot->getProperty('session-time-left'),
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($rxBytes + $txBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out null hotspot users
|
||||
$hotspotList = array_filter($hotspotList, function($user) {
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['server']) &&
|
||||
is_null($user['mac']) &&
|
||||
is_null($user['session_time']) &&
|
||||
$user['rx_bytes'] === '0 B' &&
|
||||
$user['tx_bytes'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
|
||||
// Get PPPoE users
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
$pppoeList = [];
|
||||
$totalPPPoEUsage = 0;
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$bytes_in = $pppUser->getProperty('limit-bytes-in');
|
||||
$bytes_out = $pppUser->getProperty('limit-bytes-out');
|
||||
$totalPPPoEUsage += $bytes_in + $bytes_out;
|
||||
$username = $pppUser->getProperty('name');
|
||||
save_data_usage($username, $bytes_in, $bytes_out, 'pppoe');
|
||||
|
||||
$pppoeList[] = [
|
||||
'username' => $username,
|
||||
'address' => $pppUser->getProperty('address'),
|
||||
'uptime' => $pppUser->getProperty('uptime'),
|
||||
'service' => $pppUser->getProperty('service'),
|
||||
'caller_id' => $pppUser->getProperty('caller-id'),
|
||||
'bytes_in' => mikrotik_formatBytes($bytes_in),
|
||||
'bytes_out' => mikrotik_formatBytes($bytes_out),
|
||||
'total' => mikrotik_formatBytes($bytes_in + $bytes_out),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out null PPPoE users
|
||||
$pppoeList = array_filter($pppoeList, function($user) {
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['service']) &&
|
||||
is_null($user['caller_id']) &&
|
||||
$user['bytes_in'] === '0 B' &&
|
||||
$user['bytes_out'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
// Calculate total data usage
|
||||
$totalDataUsage = $totalHotspotUsage + $totalPPPoEUsage;
|
||||
|
||||
// Calculate total users
|
||||
$totalHotspotUsers = count($hotspotList);
|
||||
$totalPPPoEUsers = count($pppoeList);
|
||||
$totalUsers = $totalHotspotUsers + $totalPPPoEUsers;
|
||||
|
||||
return [
|
||||
'hotspot_users' => $totalHotspotUsers,
|
||||
'ppoe_users' => $totalPPPoEUsers,
|
||||
'total_users' => $totalUsers,
|
||||
'total_bytes' => mikrotik_formatBytes($totalDataUsage),
|
||||
];
|
||||
}
|
||||
|
||||
function mikrotik_disconnect_online_user($router, $username, $userType)
|
||||
{
|
||||
// Check if the form was submitted
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Retrieve the form data
|
||||
$router = $_POST['router'];
|
||||
$username = $_POST['username'];
|
||||
$userType = $_POST['userType'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
if (!$mikrotik) {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($userType == 'hotspot') {
|
||||
Mikrotik::removeHotspotActiveUser($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} elseif ($userType == 'pppoe') {
|
||||
Mikrotik::removePpoeActive($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} else {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Handle the error response or redirection
|
||||
} finally {
|
||||
// Disconnect from the MikroTik router
|
||||
if (isset($client)) {
|
||||
$client->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
493
system/controllers/order.php
Normal file
493
system/controllers/order.php
Normal file
@ -0,0 +1,493 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_auth();
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
switch ($action) {
|
||||
case 'voucher':
|
||||
$ui->assign('_system_menu', 'voucher');
|
||||
$ui->assign('_title', Lang::T('Order Voucher'));
|
||||
run_hook('customer_view_order'); #HOOK
|
||||
$ui->display('user-order.tpl');
|
||||
break;
|
||||
case 'history':
|
||||
$ui->assign('_system_menu', 'history');
|
||||
$query = ORM::for_table('tbl_payment_gateway')->where('username', $user['username'])->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('_title', Lang::T('Order History'));
|
||||
run_hook('customer_view_order_history'); #HOOK
|
||||
$ui->display('user-orderHistory.tpl');
|
||||
break;
|
||||
case 'balance':
|
||||
if (strpos($user['email'], '@') === false) {
|
||||
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
|
||||
}
|
||||
$ui->assign('_title', 'Top Up');
|
||||
$ui->assign('_system_menu', 'balance');
|
||||
$plans_balance = ORM::for_table('tbl_plans')->where('enabled', '1')->where('type', 'Balance')->where('prepaid', 'yes')->find_many();
|
||||
$ui->assign('plans_balance', $plans_balance);
|
||||
$ui->display('user-orderBalance.tpl');
|
||||
break;
|
||||
case 'package':
|
||||
if (strpos($user['email'], '@') === false) {
|
||||
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
|
||||
}
|
||||
$ui->assign('_title', 'Order Plan');
|
||||
$ui->assign('_system_menu', 'package');
|
||||
$account_type = $user['account_type'];
|
||||
if (empty($account_type)) {
|
||||
$account_type = 'Personal';
|
||||
}
|
||||
if (!empty($_SESSION['nux-router'])) {
|
||||
if ($_SESSION['nux-router'] == 'radius') {
|
||||
$radius_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$radius_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
} else {
|
||||
$routers = ORM::for_table('tbl_routers')->where('id', $_SESSION['nux-router'])->find_many();
|
||||
$rs = [];
|
||||
foreach ($routers as $r) {
|
||||
$rs[] = $r['name'];
|
||||
}
|
||||
$plans_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where_in('routers', $rs)->where('is_radius', 0)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$plans_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where_in('routers', $rs)->where('is_radius', 0)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
}
|
||||
} else {
|
||||
$radius_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$radius_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
|
||||
$routers = ORM::for_table('tbl_routers')->find_many();
|
||||
$plans_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 0)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$plans_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 0)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
}
|
||||
$ui->assign('routers', $routers);
|
||||
$ui->assign('radius_pppoe', $radius_pppoe);
|
||||
$ui->assign('radius_hotspot', $radius_hotspot);
|
||||
$ui->assign('plans_pppoe', $plans_pppoe);
|
||||
$ui->assign('plans_hotspot', $plans_hotspot);
|
||||
run_hook('customer_view_order_plan'); #HOOK
|
||||
$ui->display('user-orderPlan.tpl');
|
||||
break;
|
||||
case 'unpaid':
|
||||
$d = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one();
|
||||
run_hook('custome
|
||||
r_find_unpaid'); #HOOK
|
||||
if ($d) {
|
||||
if (empty($d['pg_url_payment'])) {
|
||||
r2(U . "order/buy/" . $trx['routers_id'] . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
|
||||
} else {
|
||||
r2(U . "order/view/" . $d['id'] . '/check/', 's', Lang::T("You have unpaid transaction"));
|
||||
}
|
||||
} else {
|
||||
r2(U . "order/package/", 's', Lang::T("You have no unpaid transaction"));
|
||||
}
|
||||
break;
|
||||
case 'view':
|
||||
$trxid = $routes['2'];
|
||||
$trx = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->find_one($trxid);
|
||||
run_hook('customer_view_payment'); #HOOK
|
||||
// jika tidak ditemukan, berarti punya orang lain
|
||||
if (empty($trx)) {
|
||||
r2(U . "order/package", 'w', Lang::T("Payment not found"));
|
||||
}
|
||||
// jika url kosong, balikin ke buy, kecuali cancel
|
||||
if (empty($trx['pg_url_payment']) && $routes['3'] != 'cancel') {
|
||||
r2(U . "order/buy/" . (($trx['routers_id'] == 0) ? $trx['routers'] : $trx['routers_id']) . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
|
||||
}
|
||||
if ($routes['3'] == 'check') {
|
||||
if (!file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php')) {
|
||||
r2(U . 'order/view/' . $trxid, 'e', Lang::T("No Payment Gateway Available"));
|
||||
}
|
||||
run_hook('customer_check_payment_status'); #HOOK
|
||||
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php';
|
||||
call_user_func($trx['gateway'] . '_validate_config');
|
||||
call_user_func($trx['gateway'] . '_get_status', $trx, $user);
|
||||
} else if ($routes['3'] == 'cancel') {
|
||||
run_hook('customer_cancel_payment'); #HOOK
|
||||
$trx->pg_paid_response = '{}';
|
||||
$trx->status = 4;
|
||||
$trx->paid_date = date('Y-m-d H:i:s');
|
||||
$trx->save();
|
||||
$trx = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->find_one($trxid);
|
||||
}
|
||||
if (empty($trx)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Transaction Not found"));
|
||||
}
|
||||
|
||||
$router = Mikrotik::info($trx['routers']);
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($trx['plan_id']);
|
||||
$bandw = ORM::for_table('tbl_bandwidth')->find_one($plan['id_bw']);
|
||||
$invoice = ORM::for_table('tbl_transactions')->where("invoice", $trx['trx_invoice'])->find_one();
|
||||
$ui->assign('invoice', $invoice);
|
||||
$ui->assign('trx', $trx);
|
||||
$ui->assign('router', $router);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->assign('bandw', $bandw);
|
||||
$ui->assign('_title', 'TRX #' . $trxid);
|
||||
$ui->display('user-orderView.tpl');
|
||||
break;
|
||||
case 'pay':
|
||||
if ($config['enable_balance'] != 'yes') {
|
||||
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
|
||||
}
|
||||
if (!empty(App::getTokenValue($_GET['stoken']))) {
|
||||
r2(U . "voucher/invoice/");
|
||||
die();
|
||||
}
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
|
||||
if (empty($plan)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
|
||||
}
|
||||
if (!$plan['enabled']) {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
if ($routes['2'] == 'radius') {
|
||||
$router_name = 'radius';
|
||||
} else {
|
||||
$router_name = $plan['routers'];
|
||||
}
|
||||
|
||||
list($bills, $add_cost) = User::getBills($id_customer);
|
||||
|
||||
// Tax calculation start
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
|
||||
if ($tax_enable === 'yes') {
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
} else {
|
||||
$tax = 0;
|
||||
}
|
||||
// Tax calculation stop
|
||||
|
||||
if ($plan && $plan['enabled'] && $user['balance'] >= $plan['price'] + $tax) {
|
||||
if (Package::rechargeUser($user['id'], $router_name, $plan['id'], 'Customer', 'Balance')) {
|
||||
// if success, then get the balance
|
||||
Balance::min($user['id'], $plan['price'] + $add_cost + $tax);
|
||||
App::setToken($_GET['stoken'], "success");
|
||||
r2(U . "voucher/invoice/", 's', Lang::T("Success to buy package"));
|
||||
} else {
|
||||
r2(U . "order/package", 'e', Lang::T("Failed to buy package"));
|
||||
Message::sendTelegram("Buy Package with Balance Failed\n\n#u$c[username] #buy \n" . $plan['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nPrice: " . $plan['price'] + $tax);
|
||||
}
|
||||
} else {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'send':
|
||||
if ($config['enable_balance'] != 'yes') {
|
||||
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
|
||||
}
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$ui->assign('_title', Lang::T('Buy for friend'));
|
||||
$ui->assign('_system_menu', 'package');
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($routes['3']);
|
||||
if (empty($plan)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
|
||||
}
|
||||
if (!$plan['enabled']) {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
if ($routes['2'] == 'radius') {
|
||||
$router_name = 'radius';
|
||||
} else {
|
||||
$router_name = $plan['routers'];
|
||||
}
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
|
||||
if ($tax_enable === 'yes') {
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
$ui->assign('tax', $tax);
|
||||
} else {
|
||||
$tax = 0;
|
||||
}
|
||||
|
||||
// Add tax to plan price
|
||||
$plan['price'] += $tax;
|
||||
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'plan') {
|
||||
$target = ORM::for_table('tbl_customers')->where('username', _post('username'))->find_one();
|
||||
list($bills, $add_cost) = User::getBills($target['id']);
|
||||
if (!empty($add_cost)) {
|
||||
$ui->assign('bills', $bills);
|
||||
$ui->assign('add_cost', $add_cost);
|
||||
$plan['price'] += $add_cost;
|
||||
}
|
||||
|
||||
if (!$target) {
|
||||
r2(U . 'home', 'd', Lang::T('Username not found'));
|
||||
}
|
||||
if ($user['balance'] < $plan['price']) {
|
||||
r2(U . 'home', 'd', Lang::T('insufficient balance'));
|
||||
}
|
||||
if ($user['username'] == $target['username']) {
|
||||
r2(U . "order/pay/$routes[2]/$routes[3]", 's', '^_^ v');
|
||||
}
|
||||
$active = ORM::for_table('tbl_user_recharges')
|
||||
->where('username', _post('username'))
|
||||
->where('status', 'on')
|
||||
->find_one();
|
||||
|
||||
if ($active && $active['plan_id'] != $plan['id']) {
|
||||
r2(U . "order/package", 'e', Lang::T("Target has active plan, different with current plant.") . " [ <b>$active[namebp]</b> ]");
|
||||
}
|
||||
$result = Package::rechargeUser($target['id'], $router_name, $plan['id'], $user['username'], 'Balance');
|
||||
if (!empty($result)) {
|
||||
// if success, then get the balance
|
||||
Balance::min($user['id'], $plan['price']);
|
||||
//sender
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $target['username'];
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $routes['2'];
|
||||
$d->routers = $router_name;
|
||||
$d->price = $plan['price'];
|
||||
$d->payment_method = "Balance";
|
||||
$d->payment_channel = "Send Plan";
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->trx_invoice = $result;
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
$trx_id = $d->id();
|
||||
//receiver
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $target['username'];
|
||||
$d->gateway = $user['username'];
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $routes['2'];
|
||||
$d->routers = $router_name;
|
||||
$d->price = $plan['price'];
|
||||
$d->payment_method = "Balance";
|
||||
$d->payment_channel = "Received Plan";
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->trx_invoice = $result;
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
r2(U . "order/view/$trx_id", 's', Lang::T("Success to send package"));
|
||||
} else {
|
||||
$errorMessage = "Send Package with Balance Failed\n\n#u$user[username] #send \n" . $plan['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nPrice: " . $plan['price'];
|
||||
|
||||
if ($tax_enable === 'yes') {
|
||||
$errorMessage .= "\nTax: " . $tax;
|
||||
}
|
||||
|
||||
r2(U . "order/package", 'e', Lang::T("Failed to Send package"));
|
||||
Message::sendTelegram($errorMessage);
|
||||
}
|
||||
}
|
||||
$ui->assign('username', $_GET['u']);
|
||||
$ui->assign('router', $router_name);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->assign('tax', $tax);
|
||||
$ui->display('user-sendPlan.tpl');
|
||||
break;
|
||||
case 'gateway':
|
||||
$ui->assign('_title', Lang::T('Select Payment Gateway'));
|
||||
$ui->assign('_system_menu', 'package');
|
||||
if (strpos($user['email'], '@') === false) {
|
||||
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
|
||||
}
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($routes['3']);
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
$pgs = array_values(explode(',', $config['payment_gateway']));
|
||||
if (count($pgs) == 0) {
|
||||
sendTelegram("Payment Gateway not set, please set it in Settings");
|
||||
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
|
||||
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
|
||||
}
|
||||
if (count($pgs) > 1) {
|
||||
$ui->assign('pgs', $pgs);
|
||||
if ($tax_enable === 'yes') {
|
||||
$ui->assign('tax', $tax);
|
||||
}
|
||||
$ui->assign('route2', $routes[2]);
|
||||
$ui->assign('route3', $routes[3]);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->display('user-selectGateway.tpl');
|
||||
break;
|
||||
} else {
|
||||
if (empty($pgs[0])) {
|
||||
sendTelegram("Payment Gateway not set, please set it in Settings");
|
||||
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
|
||||
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
|
||||
} else {
|
||||
$_POST['gateway'] = $pgs[0];
|
||||
}
|
||||
}
|
||||
case 'buy':
|
||||
$gateway = _post('gateway');
|
||||
if (empty($gateway) && !empty($_SESSION['gateway'])) {
|
||||
$gateway = $_SESSION['gateway'];
|
||||
} else if (!empty($gateway)) {
|
||||
$_SESSION['gateway'] = $gateway;
|
||||
}
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
if (empty($gateway)) {
|
||||
r2(U . 'order/gateway/' . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please select Payment Gateway"));
|
||||
}
|
||||
run_hook('customer_buy_plan'); #HOOK
|
||||
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $gateway . '.php';
|
||||
call_user_func($gateway . '_validate_config');
|
||||
|
||||
if ($routes['2'] == 'radius') {
|
||||
$router['id'] = 0;
|
||||
$router['name'] = 'radius';
|
||||
} else if ($routes['2'] > 0) {
|
||||
$router = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routes['2']);
|
||||
} else {
|
||||
$router['id'] = 0;
|
||||
$router['name'] = 'balance';
|
||||
}
|
||||
$plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
|
||||
if (empty($router) || empty($plan)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
|
||||
}
|
||||
$d = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one();
|
||||
if ($d) {
|
||||
if ($d['pg_url_payment']) {
|
||||
r2(U . "order/view/" . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
|
||||
} else {
|
||||
if ($gateway == $d['gateway']) {
|
||||
$id = $d['id'];
|
||||
} else {
|
||||
$d->status = 4;
|
||||
$d->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
$add_cost = 0;
|
||||
$tax = 0;
|
||||
if ($router['name'] != 'balance') {
|
||||
list($bills, $add_cost) = User::getBills($id_customer);
|
||||
}
|
||||
// Tax calculation start
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
if ($tax_enable === 'yes') {
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
}
|
||||
// Tax calculation stop
|
||||
if (empty($id)) {
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $gateway;
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $router['id'];
|
||||
$d->routers = $router['name'];
|
||||
if ($plan['validity_unit'] == 'Period') {
|
||||
// Postpaid price from field
|
||||
$add_inv = User::getAttribute("Invoice", $id_customer);
|
||||
if (empty($add_inv) or $add_inv == 0) {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
} else {
|
||||
$d->price = ($add_inv + $add_cost + $tax);
|
||||
}
|
||||
} else {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
}
|
||||
//$d->price = ($plan['price'] + $add_cost);
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->status = 1;
|
||||
$d->save();
|
||||
$id = $d->id();
|
||||
} else {
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $gateway;
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $router['id'];
|
||||
$d->routers = $router['name'];
|
||||
if ($plan['validity_unit'] == 'Period') {
|
||||
// Postpaid price from field
|
||||
$add_inv = User::getAttribute("Invoice", $id_customer);
|
||||
if (empty($add_inv) or $add_inv == 0) {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
} else {
|
||||
$d->price = ($add_inv + $add_cost + $tax);
|
||||
}
|
||||
} else {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
}
|
||||
//$d->price = ($plan['price'] + $add_cost);
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->status = 1;
|
||||
$d->save();
|
||||
}
|
||||
if (!$id) {
|
||||
r2(U . "order/package/" . $d['id'], 'e', Lang::T("Failed to create Transaction.."));
|
||||
} else {
|
||||
call_user_func($gateway . '_create_transaction', $d, $user);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
r2(U . "order/package/", 's', '');
|
||||
}
|
21
system/controllers/page.php
Normal file
21
system/controllers/page.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_auth();
|
||||
$ui->assign('_title', Lang::T('Order Voucher'));
|
||||
$ui->assign('_system_menu', 'order');
|
||||
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
if(file_exists(__DIR__."/../../pages/".str_replace(".","",$action).".html")){
|
||||
$ui->assign("PageFile",$action);
|
||||
$ui->assign("pageHeader",$action);
|
||||
run_hook('customer_view_page'); #HOOK
|
||||
$ui->display('user-pages.tpl');
|
||||
}else
|
||||
$ui->display('404.tpl');
|
71
system/controllers/pages.php
Normal file
71
system/controllers/pages.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'Pages');
|
||||
$ui->assign('_system_menu', 'pages');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if(strpos($action,"-reset")!==false){
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
|
||||
}
|
||||
$action = str_replace("-reset","",$action);
|
||||
$path = "pages/".str_replace(".","",$action).".html";
|
||||
$temp = "pages_template/".str_replace(".","",$action).".html";
|
||||
if(file_exists($temp)){
|
||||
if(!copy($temp, $path)){
|
||||
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/'.$action.'.html'));
|
||||
}
|
||||
}else{
|
||||
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/'.$action.'.html'));
|
||||
}
|
||||
r2(U . 'pages/'.$action);
|
||||
}else if(strpos($action,"-post")===false){
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
|
||||
}
|
||||
$path = "pages/".str_replace(".","",$action).".html";
|
||||
//echo $path;
|
||||
run_hook('view_edit_pages'); #HOOK
|
||||
if(!file_exists($path)){
|
||||
$temp = "pages_template/".str_replace(".","",$action).".html";
|
||||
if(file_exists($temp)){
|
||||
if(!copy($temp, $path)){
|
||||
touch($path);
|
||||
}
|
||||
}else{
|
||||
touch($path);
|
||||
}
|
||||
}
|
||||
if(file_exists($path)){
|
||||
$html = file_get_contents($path);
|
||||
$ui->assign("htmls",str_replace(["<div","</div>"],"",$html));
|
||||
$ui->assign("writeable",is_writable($path));
|
||||
$ui->assign("pageHeader",str_replace('_', ' ', $action));
|
||||
$ui->assign("PageFile",$action);
|
||||
$ui->display('page-edit.tpl');
|
||||
}else
|
||||
$ui->display('a404.tpl');
|
||||
}else{
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
|
||||
}
|
||||
$action = str_replace("-post","",$action);
|
||||
$path = "pages/".str_replace(".","",$action).".html";
|
||||
if(file_exists($path)){
|
||||
$html = _post("html");
|
||||
run_hook('save_pages'); #HOOK
|
||||
if(file_put_contents($path, str_replace(["<div","</div>"],"",$html))){
|
||||
r2(U . 'pages/'.$action, 's', Lang::T("Saving page success"));
|
||||
}else{
|
||||
r2(U . 'pages/'.$action, 'e', Lang::T("Failed to save page, make sure i can write to folder pages, <i>chmod 664 pages/*.html<i>"));
|
||||
}
|
||||
}else
|
||||
$ui->display('a404.tpl');
|
||||
}
|
BIN
ui/.DS_Store
vendored
Normal file
BIN
ui/.DS_Store
vendored
Normal file
Binary file not shown.
8
ui/index.html
Normal file
8
ui/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
BIN
ui/ui/.DS_Store
vendored
Normal file
BIN
ui/ui/.DS_Store
vendored
Normal file
Binary file not shown.
17
ui/ui/404.tpl
Normal file
17
ui/ui/404.tpl
Normal file
@ -0,0 +1,17 @@
|
||||
{include file="sections/user-header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="col-xl-12 col-xxl-12">
|
||||
<div class="col-md-12">
|
||||
<div class="card" style="display: grid; align-content: center;">
|
||||
<div class="card-body text-center ai-icon text-primary">
|
||||
<i class="flaticon-381-error"></i>
|
||||
<h4 class="my-2">404</h4>
|
||||
<a href="{$_url}home" title="Go to Dashboard" class="btn my-2 btn-primary btn-lg px-4"><i
|
||||
class="fa fa-usd"></i> Go to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/user-footer.tpl"}
|
49
ui/ui/a404.tpl
Normal file
49
ui/ui/a404.tpl
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>Admin Dashboard</title>
|
||||
<link rel="stylesheet" href="assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="assets/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body style="background-color:#e9ecef;">
|
||||
<div class="" style="margin-top: 32px;">
|
||||
<div class="container-fluid">
|
||||
<div class="col-xl-12 col-xxl-12">
|
||||
<div class="col-md-12">
|
||||
<div class="card" style="display: grid; align-content: center;">
|
||||
<div class="card-body text-center ai-icon text-primary">
|
||||
<i class="flaticon-381-error"></i>
|
||||
<h4 class="my-2">404</h4>
|
||||
<a href="{$_url}dashboard" title="Go to Dashboard" class="btn my-2 btn-primary btn-lg px-4"><i
|
||||
class="fa fa-usd"></i> Go to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/vendor/global/global.min.js"></script>
|
||||
<script src="assets/vendor/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
|
||||
<script src="assets/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
<!-- Chart piety plugin files -->
|
||||
<script src="assets/vendor/peity/jquery.peity.min.js"></script>
|
||||
|
||||
<!-- Apex Chart -->
|
||||
<script src="assets/vendor/apexchart/apexchart.js"></script>
|
||||
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="assets/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="assets/js/custom.min.js"></script>
|
||||
<script src="assets/js/deznav-init.js"></script>
|
||||
<script src="assets/js/demo.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
53
ui/ui/admin-login.tpl
Normal file
53
ui/ui/admin-login.tpl
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{Lang::T('Login')} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="ui/ui/Css/lineicons.min.css">
|
||||
<link rel="stylesheet" href="ui/ui/Css/Style.css">
|
||||
|
||||
<meta http-equiv="refresh" content="{$time}; url={$url}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login-div">
|
||||
<div class="logo"></div>
|
||||
<div class="title">{$_c['CompanyName']} - Admin</div>
|
||||
{if isset($notify)}
|
||||
{$notify}
|
||||
{/if}
|
||||
<form action="{$_url}admin/post" method="post">
|
||||
<div class="login-in">
|
||||
<div class="username">
|
||||
<i class="lni lni-user"></i>
|
||||
<input type="text" required class="form-control" name="username" placeholder="{Lang::T('Username')}">
|
||||
</div>
|
||||
<div class="password">
|
||||
<i class="lni lni-lock"></i>
|
||||
<input type="password" required class="form-control" name="password" placeholder="{Lang::T('Password')}">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="buttons" value="{Lang::T('Login')}">login</button>
|
||||
</form>
|
||||
</div>
|
||||
<script src="assets/vendor/global/global.min.js"></script>
|
||||
<script src="assets/vendor/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
|
||||
<script src="assets/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
<!-- Chart piety plugin files -->
|
||||
<script src="assets/vendor/peity/jquery.peity.min.js"></script>
|
||||
|
||||
<!-- Apex Chart -->
|
||||
<script src="assets/vendor/apexchart/apexchart.js"></script>
|
||||
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="assets/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="assets/js/custom.min.js"></script>
|
||||
<script src="assets/js/deznav-init.js"></script>
|
||||
<script src="assets/js/demo.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
70
ui/ui/alert.tpl
Normal file
70
ui/ui/alert.tpl
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{Lang::T('Login')} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="ui/ui/assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="ui/ui/assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="ui/ui/assets/css/style.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="ui/ui/styles/sweetalert2.min.css" />
|
||||
<link rel="stylesheet" href="ui/ui/styles/plugins/pace.css" />
|
||||
<script src="ui/ui/scripts/sweetalert2.all.min.js"></script>
|
||||
<meta http-equiv="refresh" content="{$time}; url={$url}">
|
||||
</head>
|
||||
|
||||
<body style="background-color:#e9ecef;">
|
||||
<div class="" style="margin-top: 72px;">
|
||||
<div class="container-fluid">
|
||||
<div class="col-xl-12 col-xxl-12">
|
||||
<div class="col-md-12">
|
||||
<div class="card" style="display: grid; align-content: center;">
|
||||
<div class="card-body text-center ai-icon text-primary">
|
||||
<h4 class="my-2">{ucwords(Lang::T($type))}</h4>
|
||||
<div class="my-2">{$text}</div>
|
||||
<a href="{$url}" id="button" class="btn my-2 btn-primary btn-lg px-4"><i
|
||||
class="fa fa-usd"></i> {Lang::T('Click Here')} ({$time})</a>
|
||||
<div class="lockscreen-footer text-center">
|
||||
{$_c['CompanyName']}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var time = {$time};
|
||||
timer();
|
||||
|
||||
function timer() {
|
||||
setTimeout(() => {
|
||||
time--;
|
||||
if (time > -1) {
|
||||
document.getElementById("button").innerHTML = "{Lang::T('Click Here')} (" + time + ")";
|
||||
timer();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
<script src="assets/vendor/global/global.min.js"></script>
|
||||
<script src="assets/vendor/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
|
||||
<script src="assets/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
<!-- Chart piety plugin files -->
|
||||
<script src="assets/vendor/peity/jquery.peity.min.js"></script>
|
||||
|
||||
<!-- Apex Chart -->
|
||||
<script src="assets/vendor/apexchart/apexchart.js"></script>
|
||||
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="assets/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="assets/js/custom.min.js"></script>
|
||||
<script src="assets/js/deznav-init.js"></script>
|
||||
<script src="assets/js/demo.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
159
ui/ui/app-localisation.tpl
Normal file
159
ui/ui/app-localisation.tpl
Normal file
@ -0,0 +1,159 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xxl-6 col-xxl-12">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Localisation')}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form"
|
||||
action="{$_url}settings/localisation-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Timezone')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="tzone" id="tzone" class="form-control">
|
||||
{foreach $tlist as $value => $label}
|
||||
<option value="{$value}" {if $_c['timezone'] eq
|
||||
$value}selected="selected" {/if}>
|
||||
{$label}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Date Format')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="date_format" id="date_format">
|
||||
<option value="d/m/Y" {if $_c['date_format'] eq 'd/m/Y' }
|
||||
selected="selected" {/if}>
|
||||
{date('d/m/Y')}</option>
|
||||
<option value="d.m.Y" {if $_c['date_format'] eq 'd.m.Y' }
|
||||
selected="selected" {/if}>
|
||||
{date('d.m.Y')}</option>
|
||||
<option value="d-m-Y" {if $_c['date_format'] eq 'd-m-Y' }
|
||||
selected="selected" {/if}>
|
||||
{date('d-m-Y')}</option>
|
||||
<option value="m/d/Y" {if $_c['date_format'] eq 'm/d/Y' }
|
||||
selected="selected" {/if}>
|
||||
{date('m/d/Y')}</option>
|
||||
<option value="Y/m/d" {if $_c['date_format'] eq 'Y/m/d' }
|
||||
selected="selected" {/if}>
|
||||
{date('Y/m/d')}</option>
|
||||
<option value="Y-m-d" {if $_c['date_format'] eq 'Y-m-d' }
|
||||
selected="selected" {/if}>
|
||||
{date('Y-m-d')}</option>
|
||||
<option value="M d Y" {if $_c['date_format'] eq 'M d Y' }
|
||||
selected="selected" {/if}>
|
||||
{date('M d Y')}</option>
|
||||
<option value="d M Y" {if $_c['date_format'] eq 'd M Y' }
|
||||
selected="selected" {/if}>
|
||||
{date('d M Y')}</option>
|
||||
<option value="jS M y" {if $_c['date_format'] eq 'jS M y' }
|
||||
selected="selected" {/if}>
|
||||
{date('jS M y')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Default Language')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="lan" id="lan">
|
||||
{foreach $lani as $lanis}
|
||||
<option value="{$lanis@key}" {if $_c['language'] eq $lanis@key}
|
||||
selected="selected" {/if}>
|
||||
{$lanis@key}
|
||||
</option>
|
||||
{/foreach}
|
||||
<option disabled>_________</option>
|
||||
{foreach $lan as $lans}
|
||||
<option value="{$lans@key}" {if $_c['language'] eq $lans@key}
|
||||
selected="selected" {/if}>
|
||||
{$lans@key}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 help-block">
|
||||
<a href="{$_url}settings/language">{Lang::T('Language Editor')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Decimal Point')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="dec_point" name="dec_point"
|
||||
value="{$_c['dec_point']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Thousands Separator')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="thousands_sep"
|
||||
name="thousands_sep" value="{$_c['thousands_sep']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Currency Code')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="currency_code"
|
||||
name="currency_code" value="{$_c['currency_code']}">
|
||||
</div>
|
||||
<span class="help-block col-md-4">{Lang::T('Keep it blank if you do not want to
|
||||
show currency code')}</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Country Code Phone')}</label>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="basic-addon1">+</span>
|
||||
<input type="text" class="form-control" id="country_code_phone"
|
||||
placeholder="62" name="country_code_phone"
|
||||
value="{$_c['country_code_phone']}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Radius Plan</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="radius_plan" name="radius_plan"
|
||||
value="{if $_c['radius_plan']==''}Radius Plan{else}{$_c['radius_plan']}{/if}">
|
||||
</div>
|
||||
<span class="help-block col-md-4">{Lang::T('Change title in user Plan
|
||||
order')}</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Hotspot Plan</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="hotspot_plan"
|
||||
name="hotspot_plan"
|
||||
value="{if $_c['hotspot_plan']==''}Hotspot Plan{else}{$_c['hotspot_plan']}{/if}">
|
||||
</div>
|
||||
<span class="help-block col-md-4">{Lang::T('Change title in user Plan
|
||||
order')}</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">PPPOE Plan</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="pppoe_plan" name="pppoe_plan"
|
||||
value="{if $_c['pppoe_plan']==''}PPPOE Plan{else}{$_c['pppoe_plan']}{/if}">
|
||||
</div>
|
||||
<span class="help-block col-md-4">{Lang::T('Change title in user Plan
|
||||
order')}</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary" type="submit">{Lang::T('Save
|
||||
Changes')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
192
ui/ui/app-notifications.tpl
Normal file
192
ui/ui/app-notifications.tpl
Normal file
@ -0,0 +1,192 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}settings/notifications-post">
|
||||
<div class="row">
|
||||
<div class="col-xxl-6 col-xxl-12">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('User Notification')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">{Lang::T('Expired Notification
|
||||
Message')}</label>
|
||||
<div class="">
|
||||
<textarea style="overflow: hidden;" class="form-control"
|
||||
id="expired" oninput="autoExpand(this)" name="expired"
|
||||
placeholder="Hello [[name]], your internet package [[package]] has been expired"
|
||||
rows="6">{if $_json['expired']!=''}{Lang::htmlspecialchars($_json['expired'])}{else}Hello [[name]], your internet package [[package]] has been expired.{/if}</textarea>
|
||||
</div>
|
||||
<script>
|
||||
function autoExpand(element) {
|
||||
element.style.height = 'auto';
|
||||
element.style.height = (element.scrollHeight) + 'px';
|
||||
}
|
||||
</script>
|
||||
<p class="help-block">
|
||||
<b>[[name]]</b> will be replaced with Customer Name.
|
||||
<b>[[username]]</b> will be replaced with Customer username.
|
||||
<b>[[package]]</b> will be replaced with Package name.
|
||||
<b>[[price]]</b> will be replaced with Package price.
|
||||
<b>[[bills]]</b> aditional bills for customers
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="control-label">{Lang::T('Reminder 7 days')}</label>
|
||||
<div class="">
|
||||
<textarea class="form-control" id="reminder_7_day"
|
||||
name="reminder_7_day"
|
||||
rows="4">{Lang::htmlspecialchars($_json['reminder_7_day'])}</textarea>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<b>[[name]]</b> will be replaced with Customer Name.
|
||||
<b>[[username]]</b> will be replaced with Customer username.
|
||||
<b>[[package]]</b> will be replaced with Package name.
|
||||
<b>[[price]]</b> will be replaced with Package price.
|
||||
<b>[[expired_date]]</b> will be replaced with Expiration date.
|
||||
<b>[[bills]]</b> aditional bills for customers
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{Lang::T('Reminder 3 days')}</label>
|
||||
<div class="">
|
||||
<textarea class="form-control" id="reminder_3_day"
|
||||
name="reminder_3_day"
|
||||
rows="3">{Lang::htmlspecialchars($_json['reminder_3_day'])}</textarea>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<b>[[name]]</b> will be replaced with Customer Name.
|
||||
<b>[[username]]</b> will be replaced with Customer username.
|
||||
<b>[[package]]</b> will be replaced with Package name.
|
||||
<b>[[price]]</b> will be replaced with Package price.
|
||||
<b>[[expired_date]]</b> will be replaced with Expiration date.
|
||||
<b>[[bills]]</b> aditional bills for customers
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{Lang::T('Reminder 1 day')}</label>
|
||||
<div class="">
|
||||
<textarea class="form-control" id="reminder_1_day"
|
||||
name="reminder_1_day"
|
||||
rows="3">{Lang::htmlspecialchars($_json['reminder_1_day'])}</textarea>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<b>[[name]]</b> will be replaced with Customer Name.
|
||||
<b>[[username]]</b> will be replaced with Customer username.
|
||||
<b>[[package]]</b> will be replaced with Package name.
|
||||
<b>[[price]]</b> will be replaced with Package price.
|
||||
<b>[[expired_date]]</b> will be replaced with Expiration date.
|
||||
<b>[[bills]]</b> aditional bills for customers
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{Lang::T('Invoice Notification
|
||||
Payment')}</label>
|
||||
<div class="">
|
||||
<textarea class="form-control" id="invoice_paid" name="invoice_paid"
|
||||
placeholder="Hello [[name]], your internet package [[package]] has been expired"
|
||||
rows="20">{Lang::htmlspecialchars($_json['invoice_paid'])}</textarea>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<b>[[company_name]]</b> Your Company Name at Settings.
|
||||
<b>[[address]]</b> Your Company Address at Settings.
|
||||
<b>[[phone]]</b> Your Company Phone at Settings.
|
||||
<b>[[invoice]]</b> invoice number.
|
||||
<b>[[date]]</b> Date invoice created.
|
||||
<b>[[payment_gateway]]</b> Payment gateway user paid from.
|
||||
<b>[[payment_channel]]</b> Payment channel user paid from.
|
||||
<b>[[type]]</b> is Hotspot/PPPOE.
|
||||
<b>[[plan_name]]</b> Internet Package.
|
||||
<b>[[plan_price]]</b> Internet Package Prices.
|
||||
<b>[[name]]</b> Receiver name.
|
||||
<b>[[user_name]]</b> Username internet.
|
||||
<b>[[user_password]]</b> User password.
|
||||
<b>[[expired_date]]</b> Expired datetime.
|
||||
<b>[[footer]]</b> Invoice Footer.
|
||||
<b>[[note]]</b> For Notes by admin.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{Lang::T('Balance Notification
|
||||
Payment')}</label>
|
||||
<div class="">
|
||||
<textarea class="form-control" id="invoice_balance"
|
||||
name="invoice_balance"
|
||||
placeholder="Hello [[name]], your internet package [[package]] has been expired"
|
||||
rows="20">{Lang::htmlspecialchars($_json['invoice_balance'])}</textarea>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<b>[[company_name]]</b> Your Company Name at Settings.
|
||||
<b>[[address]]</b> Your Company Address at Settings.
|
||||
<b>[[phone]]</b> Your Company Phone at Settings.
|
||||
<b>[[invoice]]</b> invoice number.
|
||||
<b>[[date]]</b> Date invoice created.
|
||||
<b>[[payment_gateway]]</b> Payment gateway user paid from.
|
||||
<b>[[payment_channel]]</b> Payment channel user paid from.
|
||||
<b>[[type]]</b> is Hotspot/PPPOE.
|
||||
<b>[[plan_name]]</b> Internet Package.
|
||||
<b>[[plan_price]]</b> Internet Package Prices.
|
||||
<b>[[name]]</b> Receiver name.
|
||||
<b>[[user_name]]</b> Username internet.
|
||||
<b>[[user_password]]</b> User password.
|
||||
<b>[[trx_date]]</b> Transaction datetime.
|
||||
<b>[[balance_before]]</b> Balance Before.
|
||||
<b>[[balance]]</b> Balance After.
|
||||
<b>[[footer]]</b> Invoice Footer.
|
||||
</p>
|
||||
</div>
|
||||
{if $_c['enable_balance'] == 'yes'}
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">{Lang::T('Send Balance')}</label>
|
||||
<div class="">
|
||||
<textarea class="form-control" id="balance_send"
|
||||
name="balance_send"
|
||||
rows="3">{if $_json['balance_send']}{Lang::htmlspecialchars($_json['balance_send'])}{else}{Lang::htmlspecialchars($_default['balance_send'])}{/if}</textarea>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<b>[[name]]</b> Receiver name.
|
||||
<b>[[balance]]</b> how much balance have been send.
|
||||
<b>[[current_balance]]</b> Current Balance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">{Lang::T('Received Balance')}</label>
|
||||
<div class="">
|
||||
<textarea class="form-control" id="balance_received"
|
||||
name="balance_received"
|
||||
rows="3">{if $_json['balance_received']}{Lang::htmlspecialchars($_json['balance_received'])}{else}{Lang::htmlspecialchars($_default['balance_received'])}{/if}</textarea>
|
||||
</div>
|
||||
<p class="help-block">
|
||||
<b>[[name]]</b> Sender name.
|
||||
<b>[[balance]]</b> how much balance have been received.
|
||||
<b>[[current_balance]]</b> Current Balance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-success btn-block" type="submit">{Lang::T('Save
|
||||
Changes')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
831
ui/ui/app-settings.tpl
Normal file
831
ui/ui/app-settings.tpl
Normal file
@ -0,0 +1,831 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}settings/app-post"
|
||||
enctype="multipart/form-data">
|
||||
<div class="row">
|
||||
<div class="col-xxl-12">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('General Settings')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Application Name/
|
||||
Company Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" required class="form-control" id="CompanyName"
|
||||
name="CompanyName" value="{$_c['CompanyName']}">
|
||||
</div>
|
||||
<span class="help-block col-md-4">{Lang::T('This Name will be shown on
|
||||
the Title')}</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Company Logo')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="file" class="form-control" id="logo" name="logo"
|
||||
accept="image/*">
|
||||
<span class="help-block">For PDF Reports | Best size 1078 x 200 |
|
||||
uploaded image will be
|
||||
autosize</span>
|
||||
</div>
|
||||
<span class="help-block col-md-4">
|
||||
<a href="./{$logo}" target="_blank"><img src="./{$logo}" height="48"
|
||||
alt="logo for PDF"></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Company
|
||||
Footer')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" required class="form-control" id="CompanyFooter"
|
||||
name="CompanyFooter" value="{$_c['CompanyFooter']}">
|
||||
</div>
|
||||
<span class="help-block col-md-4">{Lang::T('Will show below user
|
||||
pages')}</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Address')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="address" name="address"
|
||||
rows="3">{Lang::htmlspecialchars($_c['address'])}</textarea>
|
||||
</div>
|
||||
<span class="help-block col-md-4">{Lang::T('You can use html
|
||||
tag')}</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Phone Number')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="phone" name="phone"
|
||||
value="{$_c['phone']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Invoice
|
||||
Footer')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="note" name="note"
|
||||
rows="3">{Lang::htmlspecialchars($_c['note'])}</textarea>
|
||||
<span class="help-block">{Lang::T('You can use html tag')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><i
|
||||
class="glyphicon glyphicon-print"></i> Print Max
|
||||
Char</label>
|
||||
<div class="col-md-6">
|
||||
<input type="number" required class="form-control" id="printer_cols"
|
||||
placeholder="37" name="printer_cols"
|
||||
value="{$_c['printer_cols']}">
|
||||
</div>
|
||||
<span class="help-block col-md-4">For invoice print using Thermal
|
||||
Printer</span>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Theme</label>
|
||||
<div class="col-md-6">
|
||||
<select name="theme" id="theme" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="default" {if $_c['theme'] eq 'default'
|
||||
}selected="selected" {/if}>Default
|
||||
</option>
|
||||
{foreach $themes as $theme}
|
||||
<option value="{$theme}" {if $_c['theme'] eq
|
||||
$theme}selected="selected" {/if}>
|
||||
{Lang::ucWords($theme)}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">APP URL</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" readonly class="form-control" value="{$app_url}">
|
||||
</div>
|
||||
<p class="help-block col-md-4">edit at config.php</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Hide Dashboard Content</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label"><input type="checkbox"
|
||||
name="hide_mrc" value="yes" {if $_c['hide_mrc'] eq 'yes'
|
||||
}checked{/if}>
|
||||
{Lang::T('Monthly Registered Customers')}</label>
|
||||
<label class="col-md-2 control-label"><input type="checkbox"
|
||||
name="hide_tms" value="yes" {if $_c['hide_tms'] eq 'yes'
|
||||
}checked{/if}> {Lang::T('Total Monthly Sales')}</label>
|
||||
<label class="col-md-2 control-label"><input type="checkbox"
|
||||
name="hide_aui" value="yes" {if $_c['hide_aui'] eq 'yes'
|
||||
}checked{/if}> {Lang::T('All Users Insights')}</label>
|
||||
<label class="col-md-2 control-label"><input type="checkbox"
|
||||
name="hide_al" value="yes" {if $_c['hide_al'] eq 'yes'
|
||||
}checked{/if}> {Lang::T('Activity Log')}</label>
|
||||
<label class="col-md-2 control-label"><input type="checkbox"
|
||||
name="hide_uet" value="yes" {if $_c['hide_uet'] eq 'yes'
|
||||
}checked{/if}> {Lang::T('User Expired, Today')}</label>
|
||||
<label class="col-md-2 control-label"><input type="checkbox"
|
||||
name="hide_vs" value="yes" {if $_c['hide_vs'] eq 'yes'
|
||||
}checked{/if}> Vouchers Stock</label>
|
||||
<label class="col-md-2 control-label"><input type="checkbox"
|
||||
name="hide_pg" value="yes" {if $_c['hide_pg'] eq 'yes'
|
||||
}checked{/if}> Payment Gateway</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Voucher</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Disable
|
||||
Voucher')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="disable_voucher" id="disable_voucher"
|
||||
class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="no" {if $_c['disable_voucher']=='no'
|
||||
}selected="selected" {/if}>No
|
||||
</option>
|
||||
<option value="yes" {if $_c['disable_voucher']=='yes'
|
||||
}selected="selected" {/if}>Yes
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('Voucher activation menu will be
|
||||
hidden')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Voucher
|
||||
Format')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="voucher_format" id="voucher_format"
|
||||
class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="up" {if $_c['voucher_format']=='up'
|
||||
}selected="selected" {/if}>UPPERCASE
|
||||
</option>
|
||||
<option value="low" {if $_c['voucher_format']=='low'
|
||||
}selected="selected" {/if}>
|
||||
lowercase
|
||||
</option>
|
||||
<option value="rand" {if $_c['voucher_format']=='rand'
|
||||
}selected="selected" {/if}>
|
||||
RaNdoM
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">UPPERCASE lowercase RaNdoM</p>
|
||||
</div>
|
||||
{if $_c['disable_voucher'] != 'yes'}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Disable
|
||||
Registration')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="disable_registration" id="disable_registration"
|
||||
class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="no" {if $_c['disable_registration']=='no'
|
||||
}selected="selected" {/if}>No
|
||||
</option>
|
||||
<option value="yes" {if $_c['disable_registration']=='yes'
|
||||
}selected="selected" {/if}>
|
||||
Yes
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('Customer just Login with Phone number and Voucher Code,
|
||||
Voucher will be
|
||||
password')}
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Redirect after Activation</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="voucher_redirect"
|
||||
name="voucher_redirect"
|
||||
placeholder="https://192.168.88.1/status"
|
||||
value="{$voucher_redirect}">
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('After Customer activate voucher or login, customer will be
|
||||
redirected to this
|
||||
url')}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">FreeRadius</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Enable Radius</label>
|
||||
<div class="col-md-6">
|
||||
<select name="radius_enable" id="radius_enable"
|
||||
class="form-select" style="height: 52px; background-color: white;" text-muted">
|
||||
<option value="0">No</option>
|
||||
<option value="1" {if $_c['radius_enable']}selected="selected"
|
||||
{/if}>Yes</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4"><a
|
||||
href="https://github.com/hotspotbilling/phpnuxbill/wiki/FreeRadius"
|
||||
target="_blank">You can find Free Radius Setup & Configuration
|
||||
Instructions here.</a></p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Radius Client</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="radius_client"
|
||||
value="{$_c['radius_client']}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Balance System')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Enable System')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="enable_balance" id="enable_balance"
|
||||
class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="no" {if $_c['enable_balance']=='no'
|
||||
}selected="selected" {/if}>No
|
||||
</option>
|
||||
<option value="yes" {if $_c['enable_balance']=='yes'
|
||||
}selected="selected" {/if}>Yes
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('Customer can deposit money to
|
||||
buy voucher')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Allow
|
||||
Transfer')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="allow_balance_transfer" id="allow_balance_transfer"
|
||||
class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="no" {if $_c['allow_balance_transfer']=='no'
|
||||
}selected="selected" {/if}>
|
||||
No</option>
|
||||
<option value="yes" {if $_c['allow_balance_transfer']=='yes'
|
||||
}selected="selected" {/if}>
|
||||
Yes</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('Allow balance transfer between
|
||||
customers')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Minimum Balance
|
||||
Transfer')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="number" class="form-control" id="minimum_transfer"
|
||||
name="minimum_transfer" value="{$_c['minimum_transfer']}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Telegram Notification')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-success btn-xs" href="javascript:testTg()">Test
|
||||
TG</a>
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Telegram Bot Token</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="telegram_bot"
|
||||
name="telegram_bot" onmouseleave="this.type = 'password'"
|
||||
onmouseenter="this.type = 'text'" value="{$_c['telegram_bot']}"
|
||||
placeholder="123456:asdasgdkuasghddlashdashldhalskdklasd">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Telegram User/Channel/Group
|
||||
ID</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="telegram_target_id"
|
||||
name="telegram_target_id" value="{$_c['telegram_target_id']}"
|
||||
placeholder="12345678">
|
||||
</div>
|
||||
<small id="emailHelp"
|
||||
class="form-text text-muted help-block col-md-4">You will get
|
||||
Payment and
|
||||
Error
|
||||
notification on you telegram</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('SMS OTP Registration')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-success" href="javascript:testSms()">Test SMS</a>
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">SMS Server URL</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="sms_url" name="sms_url"
|
||||
value="{$_c['sms_url']}"
|
||||
placeholder="https://domain/?param_number=[number]¶m_text=[text]&secret=">
|
||||
</div>
|
||||
<p class="help-block col-md-4">Must include <b>[text]</b> &
|
||||
<b>[number]</b>, it will be
|
||||
replaced.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Or use Mikrotik SMS</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control"
|
||||
onchange="document.getElementById('sms_url').value = this.value">
|
||||
<option value="">Select Router</option>
|
||||
{foreach $r as $rs}
|
||||
<option value="{$rs['name']}" {if
|
||||
$rs['name']==$_c['sms_url']}selected{/if}>
|
||||
{$rs['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">Must include <b>[text]</b> &
|
||||
<b>[number]</b>, it will be
|
||||
replaced.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Whatsapp Notification')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-success" href="javascript:testWa()">Test WA</a>
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Whatsapp Server URL</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="wa_url" name="wa_url"
|
||||
value="{$_c['wa_url']}"
|
||||
placeholder="https://domain/?param_number=[number]¶m_text=[text]&secret=">
|
||||
</div>
|
||||
<p class="help-block col-md-4">Must include <b>[text]</b> &
|
||||
<b>[number]</b>, it will be
|
||||
replaced.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Email Notification')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-success" href="javascript:testEmail()">Test
|
||||
Email</a>
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">SMTP Host : port</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="smtp_host"
|
||||
name="smtp_host" value="{$_c['smtp_host']}"
|
||||
placeholder="smtp.host.tld">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="number" class="form-control" id="smtp_port"
|
||||
name="smtp_port" value="{$_c['smtp_port']}"
|
||||
placeholder="465 587 port">
|
||||
</div>
|
||||
<p class="help-block col-md-4">Empty this to use internal mail() PHP</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">SMTP username</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="smtp_user"
|
||||
name="smtp_user" value="{$_c['smtp_user']}"
|
||||
placeholder="user@host.tld">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">SMTP Password</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="smtp_pass"
|
||||
name="smtp_pass" value="{$_c['smtp_pass']}"
|
||||
onmouseleave="this.type = 'password'"
|
||||
onmouseenter="this.type = 'text'">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">SMTP Security</label>
|
||||
<div class="col-md-6">
|
||||
<select name="smtp_ssltls" id="smtp_ssltls" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="" {if $_c['smtp_ssltls']=='' }selected="selected"
|
||||
{/if}>Not Secure</option>
|
||||
<option value="ssl" {if $_c['smtp_ssltls']=='ssl'
|
||||
}selected="selected" {/if}>SSL</option>
|
||||
<option value="tls" {if $_c['smtp_ssltls']=='tls'
|
||||
}selected="selected" {/if}>TLS</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">UPPERCASE lowercase RaNdoM</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Mail From</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="mail_from"
|
||||
name="mail_from" value="{$_c['mail_from']}"
|
||||
placeholder="noreply@host.tld">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Mail Reply To</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="mail_reply_to"
|
||||
name="mail_reply_to" value="{$_c['mail_reply_to']}"
|
||||
placeholder="support@host.tld">
|
||||
</div>
|
||||
<p class="help-block col-md-4">Customer will reply email to this
|
||||
address, empty if you want to use From Address</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('User Notification')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Expired
|
||||
Notification')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="user_notification_expired"
|
||||
id="user_notification_expired" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="none">None</option>
|
||||
<option value="wa" {if $_c['user_notification_expired']=='wa'
|
||||
}selected="selected" {/if}>Whatsapp</option>
|
||||
<option value="sms" {if $_c['user_notification_expired']=='sms'
|
||||
}selected="selected" {/if}>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('User will get notification when
|
||||
package expired')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Payment
|
||||
Notification')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="user_notification_payment"
|
||||
id="user_notification_payment" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="none">None</option>
|
||||
<option value="wa" {if $_c['user_notification_payment']=='wa'
|
||||
}selected="selected" {/if}>Whatsapp</option>
|
||||
<option value="sms" {if $_c['user_notification_payment']=='sms'
|
||||
}selected="selected" {/if}>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('User will get invoice notification when buy package or
|
||||
package refilled')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Reminder
|
||||
Notification')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="user_notification_reminder"
|
||||
id="user_notification_reminder" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="none">None</option>
|
||||
<option value="wa" {if $_c['user_notification_reminder']=='wa'
|
||||
}selected="selected" {/if}>Whatsapp</option>
|
||||
<option value="sms" {if $_c['user_notification_reminder']=='sms'
|
||||
}selected="selected" {/if}>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Tawk.to Chat Widget')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">https://tawk.to/chat/</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="tawkto" name="tawkto"
|
||||
value="{$_c['tawkto']}"
|
||||
placeholder="62f1ca7037898912e961f5/1ga07df">
|
||||
</div>
|
||||
<p class="help-block col-md-4">For Direct Chat Link with client from
|
||||
their Dashboard</p>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card">
|
||||
<span class="text-warning sub-title fs-14">Copy and paste in
|
||||
Terminal</span>
|
||||
<p class="font-w500">
|
||||
<code>/ip hotspot walled-garden add dst-host=tawk.to add dst-host=*.tawk.to</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Generate Your API Key</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Access Token</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="api_key"
|
||||
name="api_key" value="{$_c['api_key']}"
|
||||
placeholder="Empty this to randomly created API key"
|
||||
onmouseleave="this.type = 'password'"
|
||||
onmouseenter="this.type = 'text'">
|
||||
</div>
|
||||
<p class="col-md-4 help-block">{Lang::T('This Token will act as
|
||||
SuperAdmin/Admin.')}</p>
|
||||
<p class="col-md-12 help-block">Empty this to randomly created API key
|
||||
that you can use to link this application to other services</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Proxy')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Proxy
|
||||
Server')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="http_proxy"
|
||||
name="http_proxy" value="{$_c['http_proxy']}"
|
||||
placeholder="127.0.0.1:3128">
|
||||
</div>
|
||||
<p class="col-md-4 help-block">Enter Your Proxy Server IP Address or DNS IP or URl</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Proxy Server
|
||||
Login')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="http_proxyauth"
|
||||
name="http_proxyauth" autocomplete="off"
|
||||
value="{$_c['http_proxyauth']}" placeholder="username:password"
|
||||
onmouseleave="this.type = 'password'"
|
||||
onmouseenter="this.type = 'text'">
|
||||
</div>
|
||||
<p class="col-md-4 help-block">Enter Your Proxy Server Login Password</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-header"
|
||||
style="display: grid; align-content: center; justify-content: center;">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Miscellaneous')}</h5>
|
||||
<div class="btn-group pull-right">
|
||||
<button class="btn btn-primary" title="save" type="submit"><span
|
||||
class="flaticon-381-save"
|
||||
aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('OTP
|
||||
Required')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="allow_phone_otp" id="allow_phone_otp"
|
||||
class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="no" {if $_c['allow_phone_otp']=='no'
|
||||
}selected="selected" {/if}>
|
||||
No</option>
|
||||
<option value="yes" {if $_c['allow_phone_otp']=='yes'
|
||||
}selected="selected" {/if}>Yes
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('OTP is required
|
||||
when user want
|
||||
to change phone
|
||||
number')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('OTP Method')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="phone_otp_type" id="phone_otp_type"
|
||||
class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="sms" {if $_c['phone_otp_type']=='sms'
|
||||
}selected="selected" {/if}>
|
||||
{Lang::T('SMS')}
|
||||
<option value="whatsapp" {if $_c['phone_otp_type']=='whatsapp'
|
||||
}selected="selected" {/if}>
|
||||
{Lang::T('WhatsApp')}
|
||||
<option value="both" {if $_c['phone_otp_type']=='both'
|
||||
}selected="selected" {/if}>
|
||||
{Lang::T('SMS and WhatsApp')}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('The method which
|
||||
OTP will be
|
||||
sent to user')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<button class="btn btn-success btn-block" type="submit">{Lang::T('Save
|
||||
Changes')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card">
|
||||
<span class="text-warning sub-title fs-14">Copy and paste in
|
||||
Terminal</span>
|
||||
<code>/ip hotspot walled-garden add dst-host={$_domain} add dst-host=*.{$_domain}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card">
|
||||
<span class="text-warning sub-title fs-14">Cron Jobs</span>
|
||||
<p># Expired Cronjob Every 5 Minutes</p>
|
||||
<pre>*/5 * * * * cd {$dir} && {$php} cron.php</pre>
|
||||
<p># Expired Cronjob Every 1 Hour</p>
|
||||
<pre>0 * * * * cd {$dir} && {$php} cron.php</pre>
|
||||
<p># Reminder Cronjob Every 7 AM</p>
|
||||
<pre>0 7 * * * cd {$dir} && {$php} cron_reminder.php</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
function testWa() {
|
||||
var target = prompt("Phone number\nSave First before Test", "");
|
||||
if (target != null) {
|
||||
window.location.href = '{$_url}settings/app&testWa=' + target;
|
||||
}
|
||||
}
|
||||
|
||||
function testSms() {
|
||||
var target = prompt("Phone number\nSave First before Test", "");
|
||||
if (target != null) {
|
||||
window.location.href = '{$_url}settings/app&testSms=' + target;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function testEmail() {
|
||||
var target = prompt("Email\nSave First before Test", "");
|
||||
if (target != null) {
|
||||
window.location.href = '{$_url}settings/app&testEmail=' + target;
|
||||
}
|
||||
}
|
||||
|
||||
function testTg() {
|
||||
window.location.href = '{$_url}settings/app&testTg=test';
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
4
ui/ui/autoload-pool.tpl
Normal file
4
ui/ui/autoload-pool.tpl
Normal file
@ -0,0 +1,4 @@
|
||||
<option value=''>{Lang::T('Select Pool')}</option>
|
||||
{foreach $d as $ds}
|
||||
<option value="{$ds['pool_name']}">{$ds['pool_name']}{if $routers==''} - {$ds['routers']}{/if}</option>
|
||||
{/foreach}
|
7
ui/ui/autoload-server.tpl
Normal file
7
ui/ui/autoload-server.tpl
Normal file
@ -0,0 +1,7 @@
|
||||
<option value=''>{Lang::T('Select Routers')}</option>
|
||||
{if $_c['radius_enable']}
|
||||
<option value="radius">Radius</option>
|
||||
{/if}
|
||||
{foreach $d as $ds}
|
||||
<option value="{$ds['name']}">{$ds['name']}</option>
|
||||
{/foreach}
|
9
ui/ui/autoload.tpl
Normal file
9
ui/ui/autoload.tpl
Normal file
@ -0,0 +1,9 @@
|
||||
<option value="">Select Plans</option>
|
||||
{foreach $d as $ds}
|
||||
<option value="{$ds['id']}">
|
||||
{if $ds['enabled'] neq 1}DISABLED PLAN • {/if}
|
||||
{$ds['name_plan']} •
|
||||
{Lang::moneyFormat($ds['price'])}
|
||||
{if $ds['prepaid'] neq 'yes'} • POSTPAID {/if}
|
||||
</option>
|
||||
{/foreach}
|
51
ui/ui/balance-add.tpl
Normal file
51
ui/ui/balance-add.tpl
Normal file
@ -0,0 +1,51 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xxl-6 col-xxl-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Add Service Plan')}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}services/balance-add-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-8">
|
||||
<label class="radio-inline warning">
|
||||
<input type="radio" checked name="enabled" value="1"> Enable
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="enabled" value="0"> Disable
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Plan Name')}</label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" required class="form-control" id="name" name="name" maxlength="40"
|
||||
placeholder="Topup 100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Plan Price')}</label>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" >{$_c['currency_code']}</span>
|
||||
<input type="number" class="form-control" name="price" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success" type="submit">{Lang::T('Save Changes')}</button>
|
||||
<a class="btn btn-warning" href="{$_url}services/balance">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
51
ui/ui/balance-edit.tpl
Normal file
51
ui/ui/balance-edit.tpl
Normal file
@ -0,0 +1,51 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xxl-6 col-xxl-12">
|
||||
<div class="card card-primary card-hovered panel-stacked mb30">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Edit Service Plan')}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}services/balance-edit-post">
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-8">
|
||||
<label class="radio-inline warning">
|
||||
<input type="radio" checked name="enabled" value="1"> Enable
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="enabled" value="0"> Disable
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Plan Name')}</label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" required class="form-control" id="name" value="{$d['name_plan']}" name="name" maxlength="40" placeholder="Topup 100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Plan Price')}</label>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" style="background-color: white; align-items: center; display: flex; border-radius: 12px 0 0 12px; height: 50px;">{$_c['currency_code']}</span>
|
||||
<input type="number" class="form-control" name="price" value="{$d['price']}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a href="{$_url}services/balance">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
55
ui/ui/balance.tpl
Normal file
55
ui/ui/balance.tpl
Normal file
@ -0,0 +1,55 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xxl-6 col-xxl-12">
|
||||
<div class="card panel-hovered mb20 panel-primary">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Balance Plans')}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center row" style="padding: 15px">
|
||||
<div class="col-md-8">
|
||||
<form id="site-search" method="post" action="{$_url}services/balance/">
|
||||
<div class="input-group">
|
||||
<!-- <div class="input-group-addon">
|
||||
<span class="fa fa-search"></span>
|
||||
</div> -->
|
||||
<input type="text" name="name" class="form-control" placeholder="{Lang::T('Search by Name')}...">
|
||||
<button class="btn btn-success" style="border-radius: 0 12px 12px 0; border: none;" type="submit"><i class="fa fa-search"></i>{Lang::T('Search')}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}services/balance-add" class="btn btn-primary btn-block"><i class="fa fa-plus"> </i> {Lang::T('New Service Plan')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr {if $ds['enabled'] != 1}class="danger" title="disabled"{/if}>
|
||||
<td>{$ds['name_plan']}</td>
|
||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>
|
||||
<a href="{$_url}services/balance-edit/{$ds['id']}" class="btn btn-info btn-xs"><i class="fa fa-edit"></i>{Lang::T('Edit')}</a>
|
||||
<a href="{$_url}services/balance-delete/{$ds['id']}" onclick="return confirm('{Lang::T('Delete')}?')" id="{$ds['id']}" class="btn btn-danger btn-xs"><i class="fa fa-trash" aria-hidden="true"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
84
ui/ui/bandwidth-add.tpl
Normal file
84
ui/ui/bandwidth-add.tpl
Normal file
@ -0,0 +1,84 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Add New Bandwidth')}</h5></div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}bandwidth/add-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Bandwidth Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Rate Download')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="rate_down" name="rate_down">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="rate_down_unit" name="rate_down_unit">
|
||||
<option value="Kbps">Kbps</option>
|
||||
<option value="Mbps">Mbps</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Rate Upload')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="rate_up" name="rate_up">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="rate_up_unit" name="rate_up_unit">
|
||||
<option value="Kbps">Kbps</option>
|
||||
<option value="Mbps">Mbps</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Burst Limit</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Burst/Limit]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Burst Threshold</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Burst/Threshold]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Burst Time</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Burst/Time]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Priority</label>
|
||||
<div class="col-md-6">
|
||||
<input type="number" class="form-control" name="burst[]" placeholder="[Priority]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Limit At</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Limit/At]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary"
|
||||
type="submit">{Lang::T('Submit')}</button>
|
||||
Or <a href="{$_url}bandwidth/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
97
ui/ui/bandwidth-edit.tpl
Normal file
97
ui/ui/bandwidth-edit.tpl
Normal file
@ -0,0 +1,97 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-heading" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Edit Bandwidth')}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}bandwidth/edit-post">
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Bandwidth Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name" value="{$d['name_bw']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Rate Download')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="rate_down" name="rate_down"
|
||||
value="{$d['rate_down']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="rate_down_unit" name="rate_down_unit">
|
||||
<option value="Kbps" {if $d['rate_down_unit'] eq 'Kbps'}selected="selected" {/if}>Kbps
|
||||
</option>
|
||||
<option value="Mbps" {if $d['rate_down_unit'] eq 'Mbps'}selected="selected" {/if}>Mbps
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Rate Upload')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="rate_up" name="rate_up" value="{$d['rate_up']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="rate_up_unit" name="rate_up_unit">
|
||||
<option value="Kbps" {if $d['rate_up_unit'] eq 'Kbps'}selected="selected" {/if}>Kbps
|
||||
</option>
|
||||
<option value="Mbps" {if $d['rate_up_unit'] eq 'Mbps'}selected="selected" {/if}>Mbps
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Burst Limit</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Burst/Limit]" value="{$burst[0]}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Burst Threshold</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Burst/Threshold]" value="{$burst[1]}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Burst Time</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Burst/Time]" value="{$burst[2]}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Priority</label>
|
||||
<div class="col-md-6">
|
||||
<input type="number" class="form-control" name="burst[]" placeholder="[Priority]" value="{$burst[3]}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">Limit At</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="burst[]" placeholder="[Limit/At]" value="{$burst[4]}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<small>{Lang::T('Editing Bandwidth will not automatically update the plan, you need to edit the plan then save again')}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary"
|
||||
type="submit">{Lang::T('Submit')}</button>
|
||||
Or <a href="{$_url}bandwidth/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
58
ui/ui/bandwidth.tpl
Normal file
58
ui/ui/bandwidth.tpl
Normal file
@ -0,0 +1,58 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Bandwidth Plans')}</h5></div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center row" style="padding: 15px">
|
||||
<div class="col-md-8">
|
||||
<form id="site-search" method="post" action="{$_url}bandwidth/list/">
|
||||
<div class="input-group">
|
||||
<!-- <div class="input-group-addon">
|
||||
<span class="fa fa-search"></span>
|
||||
</div> -->
|
||||
<input type="text" name="name" class="form-control" placeholder="{Lang::T('Search by Name')}...">
|
||||
<button class="btn btn-success" type="submit"><i class="fa fa-search"></i>{Lang::T('Search')}</button>
|
||||
<!-- <div class="input-group-btn">
|
||||
</div> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}bandwidth/add" class="btn btn-primary btn-block"><i class="fa fa-plus"> </i> {Lang::T('New Bandwidth')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-condensed table-striped table_mobile">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Bandwidth Name')}</th>
|
||||
<th>{Lang::T('Rate')}</th>
|
||||
<th>{Lang::T('Burst')}</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['name_bw']}</td>
|
||||
<td>{$ds['rate_down']} {$ds['rate_down_unit']} / {$ds['rate_up']} {$ds['rate_up_unit']}</td>
|
||||
<td>{$ds['burst']}</td>
|
||||
<td>
|
||||
<a href="{$_url}bandwidth/edit/{$ds['id']}" class="btn btn-sm btn-warning"><i class="fa fa-edit"></i>{Lang::T('Edit')}</a>
|
||||
<a href="{$_url}bandwidth/delete/{$ds['id']}" id="{$ds['id']}" class="btn btn-danger btn-sm" onclick="return confirm('{Lang::T('Delete')}?')" ><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
42
ui/ui/change-password.tpl
Normal file
42
ui/ui/change-password.tpl
Normal file
@ -0,0 +1,42 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Change Password')}</h5></div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}settings/change-password-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Current Password')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="password" name="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('New Password')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="npass" name="npass">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Confirm New Password')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="cnpass" name="cnpass">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a href="{$_url}dashboard">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
42
ui/ui/community.tpl
Normal file
42
ui/ui/community.tpl
Normal file
@ -0,0 +1,42 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12" id="update">
|
||||
<div class="card card-primary card-hovered mb20 activities">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">MikroPulse</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<b>MikroPulse</b> is a Billing System for Hotspot and PPPOE in Mikrotik using PHP and Mikrotik API to comunicate with router.
|
||||
<hr>
|
||||
<div id="currentVersion">Current Version: </div>
|
||||
<div id="latestVersion">Latest Version: </div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
||||
<a href="./update.php" class="btn btn-success btn-sm btn-block">Install Latest Version</a>
|
||||
<a href="https://github.com/hotspotbilling/phpnuxbill/archive/refs/heads/master.zip" target="_blank"
|
||||
class="btn btn-warning btn-sm btn-block">Download Latest Version</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
If you Download manual the update file, sometime update change database, after uploading, click this button to update database structure.
|
||||
<a href="./update.php?step=4" class="btn btn-default btn-sm btn-block">Update Database</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
$.getJSON("./version.json?" + Math.random(), function(data) {
|
||||
$('#currentVersion').html('Current Version: ' + data.version);
|
||||
});
|
||||
$.getJSON("https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/version.json?" + Math
|
||||
.random(),
|
||||
function(data) {
|
||||
$('#latestVersion').html('Latest Version: ' + data.version);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
215
ui/ui/customers-add.tpl
Normal file
215
ui/ui/customers-add.tpl
Normal file
@ -0,0 +1,215 @@
|
||||
{include file="sections/header.tpl"}
|
||||
{assign var="icmcode" value="ICM{rand(000000,999999)}"}
|
||||
{assign var="itcode" value="IT{rand(500,999)}"}
|
||||
<div class="container-fluid">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}customers/add-post">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Add New Contact')}</h5></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Full Name')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" required class="form-control" id="fullname" name="fullname">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Username')}</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
<input type="text" class="form-control" name="username" value="{$icmcode}" id="username" required
|
||||
placeholder="{if $_c['country_code_phone']!= ''}{$_c['country_code_phone']} {Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Account')}</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
<input type="text" class="form-control" name="account" value="{$itcode}" id="account" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Email')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="email" class="form-control" id="email" name="email" value="icom@net.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Phone Number')}</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
{if $_c['country_code_phone']!= ''}
|
||||
<span class="input-group-text" id="basic-addon1">+</span>
|
||||
{else}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="glyphicon glyphicon-phone-alt"></i></span>
|
||||
{/if}
|
||||
<input type="text" class="form-control" name="phonenumber" value="254"
|
||||
placeholder="{if $_c['country_code_phone']!= ''}{$_c['country_code_phone']}{/if} {Lang::T('Phone Number')}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Password')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" class="form-control" autocomplete="off" required id="password"
|
||||
value="{$icmcode}" name="password" onmouseleave="this.type = 'password'"
|
||||
onmouseenter="this.type = 'text'">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('PPPOE Password')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" class="form-control" id="pppoe_password" name="pppoe_password"
|
||||
value="{$icmcode}" onmouseleave="this.type = 'password'"
|
||||
onmouseenter="this.type = 'text'">
|
||||
<span class="help-block">
|
||||
{Lang::T('User Cannot change this, only admin. if it Empty it will use user password')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Area')}</label>
|
||||
<div class="col-md-9">
|
||||
<textarea name="address" id="address" class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Service Type')}</label>
|
||||
<div class="col-md-9">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="service_type" name="service_type">
|
||||
<option value="Hotspot">Hotspot</option>
|
||||
<option value="PPPoE">PPPoE</option>
|
||||
<option value="Static">Static</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Account Type')}</label>
|
||||
<div class="col-md-9">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="account_type" name="account_type">
|
||||
<option value="Personal">Personal
|
||||
</option>
|
||||
<option value="Business">Business</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Coordinates')}</label>
|
||||
<div class="col-md-9">
|
||||
<input name="coordinates" id="coordinates" class="form-control" value=""
|
||||
placeholder="6.465422, 3.406448">
|
||||
<div id="map" style="width: '100%'; height: 200px; min-height: 150px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card">
|
||||
<div class="card-header">
|
||||
<h5 class="">{Lang::T('Attributes')}</div>
|
||||
<div class="card-body" style="border: none;">
|
||||
<!-- Customers Attributes add start -->
|
||||
<div id="custom-fields-container">
|
||||
</div>
|
||||
<!-- Customers Attributes add end -->
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button class="btn btn-success btn-block" type="button"
|
||||
id="add-custom-field">{Lang::T('Add')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<center>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
{Lang::T('Save Changes')}
|
||||
</button>
|
||||
<br><a href="{$_url}customers/list" class="btn btn-link">{Lang::T('Cancel')}</a>
|
||||
</center>
|
||||
</form>
|
||||
{literal}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var customFieldsContainer = document.getElementById('custom-fields-container');
|
||||
var addCustomFieldButton = document.getElementById('add-custom-field');
|
||||
|
||||
addCustomFieldButton.addEventListener('click', function() {
|
||||
var fieldIndex = customFieldsContainer.children.length;
|
||||
var newField = document.createElement('div');
|
||||
newField.className = 'form-group row';
|
||||
newField.innerHTML = `
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" name="custom_field_name[]" placeholder="Name">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="custom_field_value[]" placeholder="Value">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="button" class="remove-custom-field btn btn-danger btn-sm">-</button>
|
||||
</div>
|
||||
`;
|
||||
customFieldsContainer.appendChild(newField);
|
||||
});
|
||||
|
||||
customFieldsContainer.addEventListener('click', function(event) {
|
||||
if (event.target.classList.contains('remove-custom-field')) {
|
||||
var fieldContainer = event.target.parentNode.parentNode;
|
||||
fieldContainer.parentNode.removeChild(fieldContainer);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>
|
||||
<script>
|
||||
function getLocation() {
|
||||
if (window.location.protocol == "https:" && navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(showPosition);
|
||||
} else {
|
||||
setupMap(51.505, -0.09);
|
||||
}
|
||||
}
|
||||
|
||||
function showPosition(position) {
|
||||
setupMap(position.coords.latitude, position.coords.longitude);
|
||||
}
|
||||
|
||||
function setupMap(lat, lon) {
|
||||
var map = L.map('map').setView([lat, lon], 13);
|
||||
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}.png', {
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
subdomains: 'abcd',
|
||||
maxZoom: 20
|
||||
}).addTo(map);
|
||||
var marker = L.marker([lat, lon]).addTo(map);
|
||||
map.on('click', function(e){
|
||||
var coord = e.latlng;
|
||||
var lat = coord.lat;
|
||||
var lng = coord.lng;
|
||||
var newLatLng = new L.LatLng(lat, lng);
|
||||
marker.setLatLng(newLatLng);
|
||||
$('#coordinates').val(lat + ',' + lng);
|
||||
});
|
||||
}
|
||||
window.onload = function() {
|
||||
getLocation();
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
254
ui/ui/customers-edit.tpl
Normal file
254
ui/ui/customers-edit.tpl
Normal file
@ -0,0 +1,254 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}customers/edit-post">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header" style="display: grid; align-content: center; justify-content: center;">
|
||||
<h5 class="card-title">{Lang::T('Edit Contact')}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Full Name')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" id="fullname" name="fullname"
|
||||
value="{$d['fullname']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Username')}</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group mb-3">
|
||||
{if $_c['country_code_phone']!= ''}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
{else}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
{/if}
|
||||
<input type="text" class="form-control" name="username" value="{$d['username']}"
|
||||
required placeholder="{if $_c['country_code_phone']!= ''}{$_c['country_code_phone']} {Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Account')}</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group mb-3">
|
||||
{if $_c['country_code_phone']!= ''}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
{else}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
{/if}
|
||||
<input type="text" class="form-control" name="username" value="{$d['account']}"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Email')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="email" class="form-control" id="email" name="email" value="{$d['email']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Phone Number')}</label>
|
||||
<div class="col-md-9">
|
||||
<div class="input-group">
|
||||
{if $_c['country_code_phone']!= ''}
|
||||
<span class="input-group-text" id="basic-addon1">+</span>
|
||||
{else}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-phone-alt"></i></span>
|
||||
{/if}
|
||||
<input type="text" class="form-control" name="phonenumber" value="{$d['phonenumber']}"
|
||||
placeholder="{if $_c['country_code_phone']!= ''}{$_c['country_code_phone']}{/if} {Lang::T('Phone Number')}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Password')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" autocomplete="off" class="form-control" id="password" name="password"
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'"
|
||||
value="{$d['password']}">
|
||||
<span class="help-block">{Lang::T('Keep Blank to do not change Password')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('PPPOE Password')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" autocomplete="off" class="form-control" id="pppoe_password"
|
||||
name="pppoe_password" value="{$d['pppoe_password']}"
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'">
|
||||
<span class="help-block">
|
||||
{Lang::T('User Cannot change this, only admin. if it Empty it will use user password')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Area')}</label>
|
||||
<div class="col-md-9">
|
||||
<textarea name="address" id="address" class="form-control">{$d['address']}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Service Type')}</label>
|
||||
<div class="col-md-9">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="service_type" name="service_type">
|
||||
<option value="Hotspot" {if $d['service_type'] eq 'Hotspot' }selected{/if}>Hotspot
|
||||
</option>
|
||||
<option value="PPPoE" {if $d['service_type'] eq 'PPPoE' }selected{/if}>PPPoE</option>
|
||||
<option value="Static" {if $d['service_type'] eq 'Static' }selected{/if}>Static</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Account Type')}</label>
|
||||
<div class="col-md-9">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="account_type" name="account_type">
|
||||
<option value="Personal" {if $d['account_type'] eq 'Personal' }selected{/if}>Personal
|
||||
</option>
|
||||
<option value="Business" {if $d['account_type'] eq 'Business' }selected{/if}>Business
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-3 control-label">{Lang::T('Coordinates')}</label>
|
||||
<div class="col-md-9">
|
||||
<input name="coordinates" id="coordinates" class="form-control" value="{$d['coordinates']}"
|
||||
placeholder="6.465422, 3.406448">
|
||||
<div id="map" style="width: '100%'; height: 200px; min-height: 150px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card">
|
||||
<div class="card-header">
|
||||
<h5>{Lang::T('Attributes')}</h5></div>
|
||||
<div class="card-body">
|
||||
<!--Customers Attributes edit start -->
|
||||
{if $customFields}
|
||||
{foreach $customFields as $customField}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label"
|
||||
for="{$customField.field_name}">{$customField.field_name}</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" type="text" name="custom_fields[{$customField.field_name}]"
|
||||
id="{$customField.field_name}" value="{$customField.field_value}">
|
||||
</div>
|
||||
<label class="col-md-2">
|
||||
<input type="checkbox" name="delete_custom_fields[]" value="{$customField.field_name}">
|
||||
Delete
|
||||
</label>
|
||||
</div>
|
||||
{/foreach}
|
||||
{/if}
|
||||
<!--Customers Attributes edit end -->
|
||||
<!-- Customers Attributes add start -->
|
||||
<div id="custom-fields-container">
|
||||
</div>
|
||||
<!-- Customers Attributes add end -->
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button class="btn btn-success btn-block" type="button"
|
||||
id="add-custom-field">{Lang::T('Add')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
{Lang::T('Save Changes')}
|
||||
</button>
|
||||
<br><a href="{$_url}customers/list" class="btn btn-link">{Lang::T('Cancel')}</a>
|
||||
</center>
|
||||
</form>
|
||||
|
||||
{literal}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var customFieldsContainer = document.getElementById('custom-fields-container');
|
||||
var addCustomFieldButton = document.getElementById('add-custom-field');
|
||||
|
||||
addCustomFieldButton.addEventListener('click', function() {
|
||||
var fieldIndex = customFieldsContainer.children.length;
|
||||
var newField = document.createElement('div');
|
||||
newField.className = 'form-group row';
|
||||
newField.innerHTML = `
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" name="custom_field_name[]" placeholder="Name">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="custom_field_value[]" placeholder="Value">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="button" class="remove-custom-field btn btn-danger btn-sm">-</button>
|
||||
</div>
|
||||
`;
|
||||
customFieldsContainer.appendChild(newField);
|
||||
});
|
||||
|
||||
customFieldsContainer.addEventListener('click', function(event) {
|
||||
if (event.target.classList.contains('remove-custom-field')) {
|
||||
var fieldContainer = event.target.parentNode.parentNode;
|
||||
fieldContainer.parentNode.removeChild(fieldContainer);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>
|
||||
<script>
|
||||
function getLocation() {
|
||||
if (window.location.protocol == "https:" && navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(showPosition);
|
||||
} else {
|
||||
setupMap(51.505, -0.09);
|
||||
}
|
||||
}
|
||||
|
||||
function showPosition(position) {
|
||||
setupMap(position.coords.latitude, position.coords.longitude);
|
||||
}
|
||||
|
||||
function setupMap(lat, lon) {
|
||||
var map = L.map('map').setView([lat, lon], 13);
|
||||
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}.png', {
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
subdomains: 'abcd',
|
||||
maxZoom: 20
|
||||
}).addTo(map);
|
||||
var marker = L.marker([lat, lon]).addTo(map);
|
||||
map.on('click', function(e) {
|
||||
var coord = e.latlng;
|
||||
var lat = coord.lat;
|
||||
var lng = coord.lng;
|
||||
var newLatLng = new L.LatLng(lat, lng);
|
||||
marker.setLatLng(newLatLng);
|
||||
$('#coordinates').val(lat + ',' + lng);
|
||||
});
|
||||
}
|
||||
window.onload = function() {
|
||||
{/literal}{if $d['coordinates']}
|
||||
setupMap({$d['coordinates']});
|
||||
{else}
|
||||
getLocation();
|
||||
{/if}{literal}
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
64
ui/ui/customers-map.tpl
Normal file
64
ui/ui/customers-map.tpl
Normal file
@ -0,0 +1,64 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<!-- Map container div -->
|
||||
<div class="card">
|
||||
<div id="map" class="well card-body" style="width: '100%'; height: 78vh; margin: 20px auto"></div>
|
||||
</div>
|
||||
{literal}
|
||||
<script>
|
||||
function getLocation() {
|
||||
if (window.location.protocol == "https:" && navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(showPosition);
|
||||
} else {
|
||||
setupMap(51.505, -0.09);
|
||||
}
|
||||
}
|
||||
|
||||
function showPosition(position) {
|
||||
setupMap(position.coords.latitude, position.coords.longitude);
|
||||
}
|
||||
|
||||
function setupMap(lat, lon) {
|
||||
var map = L.map('map').setView([lat, lon], 13);
|
||||
var group = L.featureGroup().addTo(map);
|
||||
|
||||
var customers = {/literal}{$customers|json_encode}{literal};
|
||||
|
||||
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}.png', {
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
subdomains: 'abcd',
|
||||
maxZoom: 20
|
||||
}).addTo(map);
|
||||
|
||||
customers.forEach(function(customer) {
|
||||
var name = customer.id;
|
||||
var name = customer.name;
|
||||
var info = customer.info;
|
||||
var direction = customer.direction;
|
||||
var coordinates = customer.coordinates;
|
||||
var balance = customer.balance;
|
||||
var address = customer.address;
|
||||
|
||||
// Create a popup for the marker
|
||||
var popupContent = "<strong>Name</strong>: " + name + "<br>" +
|
||||
"<strong>Info</strong>: " + info + "<br>" +
|
||||
"<strong>Balance</strong>: " + balance + "<br>" +
|
||||
"<strong>Address</strong>: " + address + "<br>" +
|
||||
"<a href='{/literal}{$_url}{literal}customers/view/"+ customer.id +"'>More Info</a> • " +
|
||||
"<a href='https://www.google.com/maps/dir//" + direction + "' target='maps'>Get Direction</a><br>";
|
||||
|
||||
// Add marker to map
|
||||
var marker = L.marker(JSON.parse(coordinates)).addTo(group);
|
||||
marker.bindTooltip(name, { permanent: true }).bindPopup(popupContent);
|
||||
});
|
||||
|
||||
map.fitBounds(group.getBounds());
|
||||
}
|
||||
window.onload = function() {
|
||||
getLocation();
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
263
ui/ui/customers-view.tpl
Normal file
263
ui/ui/customers-view.tpl
Normal file
@ -0,0 +1,263 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 col-md-4">
|
||||
<div class="card card-primary">
|
||||
<div class="card-body card-profile">
|
||||
<img class="user-img img-responsive img-circle"
|
||||
src="https://robohash.org/{$d['id']}?set=set3&size=100x100&bgset=bg1"
|
||||
onerror="this.src='{$UPLOAD_PATH}/user.default.jpg'" alt="avatar">
|
||||
|
||||
<h3 class="profile-username text-center">{$d['fullname']}</h3>
|
||||
|
||||
<ul class="list-group list-group-unbordered">
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Username')}</b> <span class="pull-right">{$d['username']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Phone Number')}</b> <span class="pull-right">{$d['phonenumber']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Email')}</b> <span class="pull-right">{$d['email']}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<!--<p class="text-muted">{Lang::nl2br($d['address'])}</p>-->
|
||||
<ul class="list-group list-group-unbordered">
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Password')}</b> <input type="password" value="{$d['password']}"
|
||||
style=" border: 0px; text-align: right;" class="pull-right"
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'"
|
||||
onclick="this.select()">
|
||||
</li>
|
||||
{if $d['pppoe_password'] != ''}
|
||||
<li class="list-group-item">
|
||||
<b>PPPOE {Lang::T('Password')}</b> <input type="password" value="{$d['pppoe_password']}"
|
||||
style=" border: 0px; text-align: right;" class="pull-right"
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'"
|
||||
onclick="this.select()">
|
||||
</li>
|
||||
{/if}
|
||||
<!--Customers Attributes view start -->
|
||||
{if $customFields}
|
||||
{foreach $customFields as $customField}
|
||||
<li class="list-group-item">
|
||||
<b>{$customField.field_name}</b> <span class="pull-right">
|
||||
{if strpos($customField.field_value, ':0') === false}
|
||||
{$customField.field_value}
|
||||
{else}
|
||||
<b>{Lang::T('Paid')}</b>
|
||||
{/if}
|
||||
</span>
|
||||
</li>
|
||||
{/foreach}
|
||||
{/if}
|
||||
<!--Customers Attributes view end -->
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Service Type')}</b> <span class="pull-right">{Lang::T($d['service_type'])}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Account')}</b> <span class="pull-right">{Lang::T($d['account'])}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Account Type')}</b> <span class="pull-right">{Lang::T($d['account_type'])}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Balance')}</b> <span class="pull-right">{Lang::moneyFormat($d['balance'])}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Auto Renewal')}</b> <span class="pull-right">{if
|
||||
$d['auto_renewal']}yes{else}no
|
||||
{/if}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Created On')}</b> <span
|
||||
class="pull-right">{Lang::dateTimeFormat($d['created_at'])}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Last Login')}</b> <span
|
||||
class="pull-right">{Lang::dateTimeFormat($d['last_login'])}</span>
|
||||
</li>
|
||||
{if $d['coordinates']}
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Coordinates')}</b> <span class="pull-right">
|
||||
<i class="glyphicon glyphicon-road"></i> <a style="color: black;"
|
||||
href="https://www.google.com/maps/dir//{$d['coordinates']}/" target="_blank">Get
|
||||
Directions</a>
|
||||
</span>
|
||||
<div id="map" style="width: '100%'; height: 100px;"></div>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
<div class="row">
|
||||
<div class="col-xs-4 mb-3">
|
||||
<a href="{$_url}customers/delete/{$d['id']}" id="{$d['id']}"
|
||||
class="btn btn-danger btn-block btn-sm"
|
||||
onclick="return confirm('{Lang::T('Delete')}?')"><span class="fa fa-trash"></span></a>
|
||||
</div>
|
||||
<div class="col-xs-8">
|
||||
<a href="{$_url}customers/edit/{$d['id']}"
|
||||
class="btn btn-warning btn-sm btn-block">{Lang::T('Edit')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{foreach $packages as $package}
|
||||
<div class="card card-{if $package['status']=='on'}success{else}danger{/if}">
|
||||
<div class="card-body card-profile">
|
||||
<h4 class="text-center">{$package['type']} - {$package['namebp']}</h4>
|
||||
<ul class="list-group list-group-unbordered">
|
||||
<li class="list-group-item">
|
||||
{Lang::T('Active')} <span class="pull-right">{if
|
||||
$package['status']=='on'}yes{else}no
|
||||
{/if}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
{Lang::T('Type')} <span class="pull-right">
|
||||
{if $package['prepaid'] eq yes}Prepaid{else}<b>Postpaid</b>{/if}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
{Lang::T('Created On')} <span
|
||||
class="pull-right">{Lang::dateAndTimeFormat($package['recharged_on'],$package['recharged_time'])}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
{Lang::T('Expires On')} <span class="pull-right">{Lang::dateAndTimeFormat($package['expiration'],
|
||||
$package['time'])}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
{$package['routers']} <span class="pull-right">{$package['method']}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<a href="{$_url}customers/deactivate/{$d['id']}/{$package['plan_id']}" id="{$d['id']}"
|
||||
class="btn btn-danger btn-block btn-sm"
|
||||
onclick="return confirm('This will deactivate Customer Plan, and make it expired')">{Lang::T('Deactivate')}</a>
|
||||
</div>
|
||||
<div class="col-xs-8">
|
||||
<a href="{$_url}customers/recharge/{$d['id']}/{$package['plan_id']}"
|
||||
class="btn btn-success btn-sm btn-block">{Lang::T('Recharge')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<a href="{$_url}customers/list" class="btn btn-primary btn-sm btn-block">{Lang::T('Back')}</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="{$_url}customers/sync/{$d['id']}"
|
||||
onclick="return confirm('This will sync Customer to Mikrotik?')"
|
||||
class="btn btn-info btn-sm btn-block">{Lang::T('Sync')}</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="{$_url}message/send/{$d['id']}" class="btn btn-success btn-sm btn-block">{Lang::T('Send
|
||||
Message')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs">
|
||||
<li role="presentation" {if $v=='order' }class="nav-item" {/if}><a class="nav-link active"
|
||||
href="{$_url}customers/view/{$d['id']}/order">30 {Lang::T('Order History')}</a></li>
|
||||
<li role="presentation" {if $v=='activation' }class="nav-item" {/if}><a class="nav-link active"
|
||||
href="{$_url}customers/view/{$d['id']}/activation">30 {Lang::T('Activation History')}</a></li>
|
||||
</ul>
|
||||
<div class="table-responsive" style="background-color: white;">
|
||||
<table id="datatable" class="table table-bordered table-striped">
|
||||
{if Lang::arrayCount($activation)}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Invoice')}</th>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Created On')}</th>
|
||||
<th>{Lang::T('Expires On')}</th>
|
||||
<th>{Lang::T('Method')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $activation as $ds}
|
||||
<tr onclick="window.location.href = '{$_url}plan/view/{$ds['id']}'" style="cursor:pointer;">
|
||||
<td>{$ds['invoice']}</td>
|
||||
<td>{$ds['username']}</td>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td class="text-success">{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])}
|
||||
</td>
|
||||
<td class="text-danger">{Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}</td>
|
||||
<td>{$ds['method']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
{/if}
|
||||
{if Lang::arrayCount($order)}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Gateway')}</th>
|
||||
<th>{Lang::T('Routers')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Created On')}</th>
|
||||
<th>{Lang::T('Expires On')}</th>
|
||||
<th>{Lang::T('Date Done')}</th>
|
||||
<th>{Lang::T('Method')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $order as $ds}
|
||||
<tr>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td>{$ds['gateway']}</td>
|
||||
<td>{$ds['routers']}</td>
|
||||
<td>{$ds['payment_channel']}</td>
|
||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td class="text-primary">{Lang::dateTimeFormat($ds['created_date'])}</td>
|
||||
<td class="text-danger">{Lang::dateTimeFormat($ds['expired_date'])}</td>
|
||||
<td class="text-success">{if $ds['status']!=1}{Lang::dateTimeFormat($ds['paid_date'])}{/if}</td>
|
||||
<td>{if $ds['status']==1}{Lang::T('UNPAID')}
|
||||
{elseif $ds['status']==2}{Lang::T('PAID')}
|
||||
{elseif $ds['status']==3}{$_L['FAILED']}
|
||||
{elseif $ds['status']==4}{Lang::T('CANCELED')}
|
||||
{elseif $ds['status']==5}{Lang::T('UNKNOWN')}
|
||||
{/if}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
{/if}
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $d['coordinates']}
|
||||
{literal}
|
||||
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>
|
||||
<script>
|
||||
function setupMap(lat, lon) {
|
||||
var map = L.map('map').setView([lat, lon], 17);
|
||||
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/light_all/{z}/{x}/{y}.png', {
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
subdomains: 'abcd',
|
||||
maxZoom: 20
|
||||
}).addTo(map);
|
||||
var marker = L.marker([lat, lon]).addTo(map);
|
||||
}
|
||||
window.onload = function() {
|
||||
{/literal}setupMap({$d['coordinates']});{literal}
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
{/if}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
97
ui/ui/customers.tpl
Normal file
97
ui/ui/customers.tpl
Normal file
@ -0,0 +1,97 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<style>
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
margin-right: 5px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-xs" title="save" href="{$_url}customers/csv"
|
||||
onclick="return confirm('This will export to CSV?')"><span class="fa fa-download"
|
||||
aria-hidden="true"></span> CSV</a>
|
||||
</div>
|
||||
{/if}
|
||||
{Lang::T('Manage Contact')}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center">
|
||||
<div class="col-md-8">
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}customers/add" class="btn btn-primary btn-block"><i class="fa fa-plus">
|
||||
</i> {Lang::T('Add New Client')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive table_mobile">
|
||||
<table id="customerTable" class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Full Name')}</th>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Account')}</th>
|
||||
<th>{Lang::T('Acc Type')}</th>
|
||||
<th>{Lang::T('Area')}</th>
|
||||
<th>{Lang::T('Package')}</th>
|
||||
<th>{Lang::T('Service')}</th>
|
||||
<!--<th>{Lang::T('Created On')}</th>-->
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr {if $ds['status'] != 'on'}class="danger"{/if}>
|
||||
<td onclick="window.location.href = '{$_url}customers/view/{$ds['id']}'"
|
||||
style="cursor: pointer;">{$ds['fullname']}</td>
|
||||
<td onclick="window.location.href = '{$_url}customers/view/{$ds['id']}'"
|
||||
style="cursor:pointer;">{$ds['username']}</td>
|
||||
<td>{$ds['account']}</td>
|
||||
<td>{$ds['account_type']}</td>
|
||||
<td>{$ds['address']}</td>
|
||||
<td align="center" api-get-text="{$_url}autoload/customer_is_active/{$d['id']}">
|
||||
<span class="label label-default">•</span>
|
||||
</td>
|
||||
<td>{$ds['service_type']}</td>
|
||||
<!--<td>{Lang::dateTimeFormat($ds['created_at'])}</td>-->
|
||||
<td align="center">
|
||||
<a href="{$_url}customers/view/{$ds['id']}" id="{$ds['id']}"
|
||||
style="margin: 0px; color:black"
|
||||
class="btn btn-success btn-xs"> {Lang::T('View')} </a>
|
||||
<a href="{$_url}customers/edit/{$ds['id']}" id="{$ds['id']}"
|
||||
style="margin: 0px; color:black"
|
||||
class="btn btn-info btn-xs"> {Lang::T('Edit')} </a>
|
||||
<a href="{$_url}plan/recharge/{$ds['id']}" id="{$ds['id']}" style="margin: 0px;color:black;"
|
||||
class="btn btn-warning btn-xs">{Lang::T('Recharge')}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
var $j = jQuery.noConflict();
|
||||
$j(document).ready(function () {
|
||||
$j('#customerTable').DataTable({
|
||||
"pagingType": "full_numbers"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
499
ui/ui/dashboard.tpl
Normal file
499
ui/ui/dashboard.tpl
Normal file
@ -0,0 +1,499 @@
|
||||
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<style>
|
||||
.icon i{
|
||||
font-size: 36px;
|
||||
}
|
||||
</style>
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-secondary card-border" style="background:#3444d5 !important;"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class=" num-text text-black font-w200 fs-48">{$_c['currency_code']}{number_format($iday,0,$_c['dec_point'],$_c['thousands_sep'])}</h4>
|
||||
<p class="fs-14">{Lang::T('Income Today')}</p>
|
||||
</div>
|
||||
<div class="icon" style="color: #864AD1;">
|
||||
<i class="fas fa-dollar-sign"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<a href="{$_url}reports/by-date" class="small-box-footer" style="color: #fc5130;">{Lang::T('View Reports')} <i
|
||||
class="fa fa-arrow-circle-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-warning card-border"></div>
|
||||
<div class="card-body">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class="num-text text-black font-w200 fs-48">{$_c['currency_code']}{number_format($imonth,0,$_c['dec_point'],$_c['thousands_sep'])}</h4>
|
||||
<p class="fs-14">{Lang::T('Income This Month')}</p>
|
||||
</div>
|
||||
<!--<div class="icon" style="color: #FFB930;">
|
||||
<i class="fa fa-chart-bar"></i>
|
||||
</div>-->
|
||||
</div>
|
||||
<div class="">
|
||||
<a href="{$_url}reports/by-period" class="small-box-footer" style="color: #fc5130;">{Lang::T('View Reports')} <i
|
||||
class="fa fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-success card-border"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class="num-text text-black font-w200 fs-48">{$u_act}/{$u_all}</h4>
|
||||
<p class="fs-14">{Lang::T('Users Active')}</p>
|
||||
</div>
|
||||
<div class="icon" style="color: #20F174;">
|
||||
<i class="fa fa-users"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<a href="{$_url}plan/list" class="small-box-footer" style="color: #fc5130;">{Lang::T('View All')} <i
|
||||
class="fa fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-info card-border"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class="num-text text-black font-w200 fs-48">{$c_all}</h4>
|
||||
<p class="fs-14">{Lang::T('Total Users')}</p>
|
||||
</div>
|
||||
<div class="icon" style="color: #3ECDFF;">
|
||||
<i class="fa fa-users"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="" >
|
||||
<a href="{$_url}customers/list" class="small-box-footer" style="color: #fc5130;">{Lang::T('View All')} <i
|
||||
class="fa fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-secondary card-border" style="background:#3444d5 !important;"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class=" num-text text-black font-w200 fs-48" id="total-online-users"></h4>
|
||||
<p class="fs-14">{Lang::T('Online Users')}</p>
|
||||
</div>
|
||||
<div class="icon" style="color: #864AD1;">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-warning card-border"></div>
|
||||
<div class="card-body">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class="num-text text-black font-w200 fs-48" id="total_data"></h4>
|
||||
<p class="fs-14">{Lang::T('Data Usage')}</p>
|
||||
</div>
|
||||
<div class="icon" style="color: #FFB930;">
|
||||
<i class="fas fa-signal"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-success card-border"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class="num-text text-black font-w200 fs-48" id="online-hotspot-users"></h4>
|
||||
<p class="fs-14">{Lang::T('Online Hotspot Users')}</p>
|
||||
</div>
|
||||
<div class="icon" style="color: #20F174;">
|
||||
<i class="fas fa-wifi"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-info card-border"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center col">
|
||||
<div class="inner media-body me-3">
|
||||
<h4 class="num-text text-black font-w200 fs-48" id="online-pppoe-users"></h4>
|
||||
<p class="fs-14">{Lang::T('Online PPPoE Users')}</p>
|
||||
</div>
|
||||
<div class="icon" style="color: #3ECDFF;">
|
||||
<i class="fas fa-network-wired"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<!-- solid sales graph -->
|
||||
{if $_c['hide_mrc'] != 'yes'}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-th"></i>
|
||||
<h3 class="card-title">{Lang::T('Monthly Customers')}</h3>
|
||||
<div class="card-tools pull-right">
|
||||
|
||||
<a href="{$_url}dashboard&refresh" class="btn bg-teal btn-xsm"><i
|
||||
class="fa fa-refresh"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="card-body text-center pb-0 px-2 pt-2">
|
||||
<div id="widgetChart1" class="widgetChart1 dashboard-chart"></div>
|
||||
</div> -->
|
||||
<div class="card-body border-radius-none">
|
||||
<canvas class="chart" id="chart" style="height: 200px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<!-- solid sales graph -->
|
||||
{if $_c['hide_tms'] != 'yes'}
|
||||
<div class="card card-solid ">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-inbox"></i>
|
||||
<h3 class="card-title">{Lang::T('Total Monthly Sales')}</h3>
|
||||
<div class="card-tools pull-right">
|
||||
<a href="{$_url}dashboard&refresh" class="btn bg-teal btn-sm"><i
|
||||
class="fa fa-refresh"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="card-body pb-0 px-2 pt-2">
|
||||
<div id="chartTimeline" class="timeline-chart"></div>
|
||||
</div> -->
|
||||
<div class="card-body border-radius-none">
|
||||
<canvas id="salesChart" class="monthly-project-chart" style="height: 200px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
{if $_c['hide_uet'] != 'yes'}
|
||||
<div class="card card-warning card-hovered project-stats table-responsive">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Expired Users')}</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Fullname')}</th>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Account')}</th>
|
||||
<th>{Lang::T('Created On')}</th>
|
||||
<th>{Lang::T('Expires On')}</th>
|
||||
<th>{Lang::T('Router')}</th>
|
||||
<th>{Lang::T('Package')}</th>
|
||||
<th>{Lang::T('Action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $expire as $expired}
|
||||
<tr>
|
||||
<td><a href="{$_url}customers/viewu/{$expired['username']}">{$expired['fullname']}</a></td>
|
||||
<td><a href="{$_url}customers/viewu/{$expired['username']}">{$expired['username']}</a></td>
|
||||
<td><a href="{$_url}customers/viewu/{$expired['username']}">{$expired['account']}</a></td>
|
||||
<td>{Lang::dateAndTimeFormat($expired['recharged_on'],$expired['recharged_time'])}
|
||||
</td>
|
||||
<td>{Lang::dateAndTimeFormat($expired['expiration'],$expired['time'])}
|
||||
</td>
|
||||
<td>{$expired['routers']}</td>
|
||||
<td>{$expired['namebp']}</td>
|
||||
<td>
|
||||
<a href="{$_url}plan/recharge/{$expired['customer_id']}" id="{$expired['customer_id']}" style="margin: 0px;color:black;"
|
||||
class="btn btn-warning btn-xs">{Lang::T('Recharge')}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{/foreach}
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{if $_c['disable_voucher'] != 'yes' && $stocks['unused']>0 || $stocks['used']>0}
|
||||
{if $_c['hide_vs'] != 'yes'}
|
||||
<div class="card card-primary card-hovered project-stats">
|
||||
<div class="card-header">Vouchers Stock</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>unused</th>
|
||||
<th>used</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $plans as $stok}
|
||||
<tr>
|
||||
<td>{$stok['name_plan']}</td>
|
||||
<td>{$stok['unused']}</td>
|
||||
<td>{$stok['used']}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{/foreach}
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td>{$stocks['unused']}</td>
|
||||
<td>{$stocks['used']}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{if $_c['hide_aui'] != 'yes'}
|
||||
<div class="card card-hovered activities">
|
||||
<div class="card-header"><h3 class="card-title">{Lang::T('All Users Insights')}</h3></div>
|
||||
<div class="card-body">
|
||||
<canvas id="userRechargesChart" style="height: 600px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{if $_c['hide_al'] != 'yes'}
|
||||
<div class="card card-info card-hovered activities" style="height: 600px;">
|
||||
<div class="card-header"><a class="card-title" href="{$_url}logs">{Lang::T('Activity Log')}</a></div>
|
||||
<div class="card-body">
|
||||
<div id="DZ_W_TimeLine" class="widget-timeline dz-scroll ps ps--active-y" style="height: 460px;">
|
||||
<ul class="timeline">
|
||||
{foreach $dlog as $dlogs}
|
||||
<li class="primary">
|
||||
<div class="timeline-badge primary"></div>
|
||||
<div class="timeline-panel text-muted">
|
||||
<span class="point"></span>
|
||||
<span class="time small text-muted">{Lang::timeElapsed($dlogs['date'],true)}</span>
|
||||
<p>{$dlogs['description']}</p>
|
||||
</div>
|
||||
</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
{if $_c['hide_mrc'] != 'yes'}
|
||||
{literal}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var counts = JSON.parse('{/literal}{$monthlyRegistered|json_encode}{literal}');
|
||||
var monthNames = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
];
|
||||
var labels = [];
|
||||
var data = [];
|
||||
for (var i = 1; i <= 12; i++) {
|
||||
var month = counts.find(count => count.date === i);
|
||||
labels.push(month ? monthNames[i - 1] : monthNames[i - 1].substring(0, 3));
|
||||
data.push(month ? month.count : 0);
|
||||
}
|
||||
var ctx = document.getElementById('chart').getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: 'Registered Members',
|
||||
data: data,
|
||||
backgroundColor: '#fc5130',
|
||||
borderColor: '#fc5130',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
{/literal}
|
||||
{/if}
|
||||
{if $_c['hide_tmc'] != 'yes'}
|
||||
{literal}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var monthlySales = JSON.parse('{/literal}{$monthlySales|json_encode}{literal}');
|
||||
var monthNames = [
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||
];
|
||||
var labels = [];
|
||||
var data = [];
|
||||
for (var i = 1; i <= 12; i++) {
|
||||
var month = findMonthData(monthlySales, i);
|
||||
labels.push(month ? monthNames[i - 1] : monthNames[i - 1].substring(0, 3));
|
||||
data.push(month ? month.totalSales : 0);
|
||||
}
|
||||
var ctx = document.getElementById('salesChart').getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
//type: 'radialBar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: 'Monthly Sales',
|
||||
data: data,
|
||||
backgroundColor: '#fc5130', // Customize the background color
|
||||
borderColor: '#fc5130', // Customize the border color
|
||||
borderWidth: 2
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
function findMonthData(monthlySales, month) {
|
||||
for (var i = 0; i < monthlySales.length; i++) {
|
||||
if (monthlySales[i].month === month) {
|
||||
return monthlySales[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
{/literal}
|
||||
{/if}
|
||||
{if $_c['hide_aui'] != 'yes'}
|
||||
{literal}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Get the data from PHP and assign it to JavaScript variables
|
||||
var u_act = '{/literal}{$u_act}{literal}';
|
||||
var c_all = '{/literal}{$c_all}{literal}';
|
||||
var u_all = '{/literal}{$u_all}{literal}';
|
||||
//lets calculate the inactive users as reported
|
||||
var expired = u_all - u_act;
|
||||
var inactive = c_all - u_all;
|
||||
// Create the chart data
|
||||
var data = {
|
||||
labels: ['Active Users', 'Expired Users', 'Inactive Users'],
|
||||
datasets: [{
|
||||
label: 'User Recharges',
|
||||
data: [parseInt(u_act), parseInt(expired), parseInt(inactive)],
|
||||
backgroundColor: ['rgba(4, 191, 13)', 'rgba(191, 35, 4)', 'rgba(0, 0, 255, 0.5'],
|
||||
borderColor: ['rgba(0, 255, 0, 1)', 'rgba(255, 99, 132, 1)', 'rgba(0, 0, 255, 0.7'],
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
// Create chart options
|
||||
var options = {
|
||||
responsive: true,
|
||||
aspectRatio: 1,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
boxWidth: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// Get the canvas element and create the chart
|
||||
var ctx = document.getElementById('userRechargesChart').getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: data,
|
||||
options: options
|
||||
});
|
||||
});
|
||||
{/literal}
|
||||
{/if}
|
||||
</script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$.ajax({
|
||||
url: "{$_url}onlineusers/summary", // Adjust this URL to your actual endpoint
|
||||
type: 'GET',
|
||||
dataType: 'json', // Ensure the expected response is JSON
|
||||
success: function(data) {
|
||||
console.log('Data fetched successfully:', data);
|
||||
// Check if data is null or missing properties and set defaults to 0
|
||||
$('#total-online-users').text(data.total_users || 0);
|
||||
$('#online-hotspot-users').text(data.hotspot_users || 0);
|
||||
$('#online-pppoe-users').text(data.ppp_users || 0);
|
||||
$('#total_data').text(data.total_bytes || 0);
|
||||
},
|
||||
error: function(error) {
|
||||
console.log('Error fetching data:', error);
|
||||
// Set all values to 0 in case of an error
|
||||
$('#total-online-users').text(0);
|
||||
$('#online-hotspot-users').text(0);
|
||||
$('#online-pppoe-users').text(0);
|
||||
$('#total_data').text(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
61
ui/ui/dbstatus.tpl
Normal file
61
ui/ui/dbstatus.tpl
Normal file
@ -0,0 +1,61 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">Backup Database</div>
|
||||
<form method="post" action="{$_url}settings/dbbackup">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">{Lang::T('Table Name')}</th>
|
||||
<th>{Lang::T('Rows')}</th>
|
||||
<th>Select</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $tables as $tbl}
|
||||
<tr>
|
||||
<td>{$tbl['name']}</td>
|
||||
<td>{$tbl['rows']}</td>
|
||||
<td><input type="checkbox" checked name="tables[]" value="{$tbl['name']}"></td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">Dont select logs if it failed</div>
|
||||
<div class="col-md-4 text-right">
|
||||
<button type="submit" class="btn btn-primary btn-xs btn-block"><i
|
||||
class="fa fa-download"></i>
|
||||
{Lang::T('Download Database Backup')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">Restore Database</div>
|
||||
<form method="post" action="{$_url}settings/dbrestore" enctype="multipart/form-data">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-7"><input type="file" name="json" accept="application/json"></div>
|
||||
<div class="col-md-5 text-right">
|
||||
<button type="submit" class="btn btn-primary btn-block btn-xs"><i class="fa fa-upload"></i>
|
||||
Restore Dabase</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="card-footer">Restoring database will clean up data and then restore all the data</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
44
ui/ui/deposit.tpl
Normal file
44
ui/ui/deposit.tpl
Normal file
@ -0,0 +1,44 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">{Lang::T('Refill Balance')}</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plan/deposit-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Select Account')}</label>
|
||||
<div class="col-md-6">
|
||||
<select id="personSelect" class="form-control select2" name="id_customer" style="width: 100%"
|
||||
data-placeholder="{Lang::T('Select a customer')}...">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label"><a href="{$_url}services/balance">{Lang::T('Balance Plans')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="planSelect" class="form-control select2" name="id_plan" style="width: 100%"
|
||||
data-placeholder="{Lang::T('Select Plans')}...">
|
||||
<option></option>
|
||||
{foreach $p as $pl}
|
||||
<option value="{$pl['id']}">{if $pl['enabled'] neq 1}DISABLED PLAN • {/if}{$pl['name_plan']} - {Lang::moneyFormat($pl['price'])}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success"
|
||||
type="submit">{Lang::T('Recharge')}</button>
|
||||
Or <a href="{$_url}customers/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
222
ui/ui/hotspot-add.tpl
Normal file
222
ui/ui/hotspot-add.tpl
Normal file
@ -0,0 +1,222 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">{Lang::T('Add Service Plan')}</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}services/add-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="enabled" value="1" checked> Enable
|
||||
<input type="radio" name="enabled" value="0"> Disable
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="prepaid" onclick="prePaid()" value="yes" checked> Prepaid
|
||||
<input type="radio" name="prepaid" onclick="postPaid()" value="no"> Postpaid
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="plan_type" value="Personal" checked> Personal
|
||||
<input type="radio" name="plan_type" value="Business"> Business
|
||||
</div>
|
||||
</div>
|
||||
{if $_c['radius_enable']}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Radius</label>
|
||||
<div class="col-md-6">
|
||||
<label class="radio-inline">
|
||||
<input type="checkbox" name="radius" onclick="isRadius(this)" value="1"> Radius Plan
|
||||
</label>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('Cannot be change after saved')}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name" maxlength="40">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" id="Unlimited" name="typebp" value="Unlimited" checked>
|
||||
{Lang::T('Unlimited')}
|
||||
<input type="radio" id="Limited" name="typebp" value="Limited"> {Lang::T('Limited')}
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:none;" id="Type">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Limit Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" id="Time_Limit" name="limit_type" value="Time_Limit" checked>
|
||||
{Lang::T('Time Limit')}
|
||||
<input type="radio" id="Data_Limit" name="limit_type" value="Data_Limit">
|
||||
{Lang::T('Data Limit')}
|
||||
<input type="radio" id="Both_Limit" name="limit_type" value="Both_Limit">
|
||||
{Lang::T('Both Limit')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:none;" id="TimeLimit">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Time Limit')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="time_limit" name="time_limit" value="0">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select select2" style="height: 52px; background-color: white;" id="time_unit" name="time_unit">
|
||||
<option value="Hrs">{Lang::T('Hrs')}</option>
|
||||
<option value="Mins">{Lang::T('Mins')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:none;" id="DataLimit">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Data Limit')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="data_limit" name="data_limit" value="0">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="data_unit" name="data_unit">
|
||||
<option value="MB">MBs</option>
|
||||
<option value="GB">GBs</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}bandwidth/add">{Lang::T('Bandwidth Name')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="id_bw" name="id_bw" class="form-select select2" style="height: 52px; background-color: white;">
|
||||
<option value="">{Lang::T('Select Bandwidth')}...</option>
|
||||
{foreach $d as $ds}
|
||||
<option value="{$ds['id']}">{$ds['name_bw']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Price')}</label>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="basic-addon1">{$_c['currency_code']}</span>
|
||||
<input type="number" class="form-control" name="price" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Shared Users')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="sharedusers" name="sharedusers" value="1">
|
||||
<p class="help-block">{Lang::T('1 user can be used for many devices?')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Validity')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="validity" name="validity">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="validity_unit" name="validity_unit">
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('1 Period = 1 Month, Expires the 20th of each month')}</p>
|
||||
</div>
|
||||
<span id="routerChoose" class="">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}routers/add">{Lang::T('Router Name')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="routers" name="routers" required class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Routers')}</option>
|
||||
{foreach $r as $rs}
|
||||
<option value="{$rs['name']}">{$rs['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
<p class="help-block">{Lang::T('Cannot be change after saved')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<legend>{Lang::T('Expired Action')}</legend>
|
||||
<div class="form-group row" id="ipPool">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}pool/add">{Lang::T('Expired IP Pool')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="pool_expired" name="pool_expired" class="form-select select2" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Pool')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{* <div class="form-group row" id="AddressList">
|
||||
<label class="col-md-2 control-label">{Lang::T('Address List')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="list_expired" id="list_expired">
|
||||
</div>
|
||||
</div> *}
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button class="btn btn-success" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a href="{$_url}services/hotspot">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var preOpt = `<option value="Mins">{Lang::T('Mins')}</option>
|
||||
<option value="Hrs">{Lang::T('Hrs')}</option>
|
||||
<option value="Days">{Lang::T('Days')}</option>
|
||||
<option value="Months">{Lang::T('Months')}</option>`;
|
||||
var postOpt = `<option value="Period">{Lang::T('Period')}</option>`;
|
||||
function prePaid() {
|
||||
$("#validity_unit").html(preOpt);
|
||||
}
|
||||
|
||||
function postPaid() {
|
||||
$("#validity_unit").html(postOpt);
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
prePaid()
|
||||
})
|
||||
</script>
|
||||
{if $_c['radius_enable']}
|
||||
{literal}
|
||||
<script>
|
||||
function isRadius(cek) {
|
||||
if (cek.checked) {
|
||||
$("#routerChoose").addClass('hidden');
|
||||
document.getElementById("routers").required = false;
|
||||
$("#pool_expired").html('');
|
||||
$.ajax({
|
||||
url: "index.php?_route=autoload/pool",
|
||||
data: "routers=radius",
|
||||
cache: false,
|
||||
success: function(msg) {
|
||||
$("#pool_expired").html(msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
document.getElementById("routers").required = true;
|
||||
$("#routerChoose").removeClass('hidden');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
{/if}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
95
ui/ui/hotspot.tpl
Normal file
95
ui/ui/hotspot.tpl
Normal file
@ -0,0 +1,95 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
{Lang::T('Hotspot Plans')}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-xs" title="save" href="{$_url}services/sync/hotspot"
|
||||
onclick="return confirm('This will sync/send hotspot plan to Mikrotik?')"><span
|
||||
class="fa fa-refresh" aria-hidden="true"></span> sync</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center row" style="padding: 15px">
|
||||
<div class="col-md-8">
|
||||
<form id="site-search" method="post" action="{$_url}services/hotspot/">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text" id="basic-addon1">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="name" class="form-control"
|
||||
placeholder="{Lang::T('Search by Name')}...">
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-success" id="button-addon2" style="height: 52px;" type="submit">{Lang::T('Search')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}services/add" class="btn btn-primary btn-block"><i class="fa fa-plus">
|
||||
</i> {Lang::T('New Service Plan')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Type')}</th>
|
||||
<th>{Lang::T('Bandwidth Plans')}</th>
|
||||
<th>{Lang::T('Plan Category')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Time Limit')}</th>
|
||||
<th>{Lang::T('Data Limit')}</th>
|
||||
<th>{Lang::T('Plan Validity')}</th>
|
||||
<th>{Lang::T('Routers')}</th>
|
||||
<th>{Lang::T('Expired IP Pool')}</th>
|
||||
<th>{Lang::T('ID')}</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr {if $ds['enabled'] !=1}class="danger" title="disabled" {elseif $ds['prepaid'] !='yes'
|
||||
}class="warning" title="Postpaid" {/if}>
|
||||
<td class="headcol">{$ds['name_plan']}</td>
|
||||
<td>{$ds['plan_type']}</td>
|
||||
<td>{$ds['name_bw']}</td>
|
||||
<td>{$ds['typebp']}</td>
|
||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{$ds['time_limit']} {$ds['time_unit']}</td>
|
||||
<td>{$ds['data_limit']} {$ds['data_unit']}</td>
|
||||
<td>{$ds['validity']} {$ds['validity_unit']}</td>
|
||||
<td>
|
||||
{if $ds['is_radius']}
|
||||
<span class="label label-primary">RADIUS</span>
|
||||
{else}
|
||||
{if $ds['routers']!=''}
|
||||
<a href="{$_url}routers/edit/0&name={$ds['routers']}">{$ds['routers']}</a>
|
||||
{/if}
|
||||
{/if}
|
||||
</td>
|
||||
<td>{$ds['pool_expired']}{if $ds['list_expired']}{if $ds['pool_expired']} |
|
||||
{/if}{$ds['list_expired']}{/if}</td>
|
||||
<td>{$ds['id']}</td>
|
||||
<td>
|
||||
<a href="{$_url}services/edit/{$ds['id']}"
|
||||
class="btn btn-info btn-xs">{Lang::T('Edit')}</a>
|
||||
<a href="{$_url}services/delete/{$ds['id']}" id="{$ds['id']}"
|
||||
onclick="return confirm('{Lang::T('Delete')}?')"
|
||||
class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
69
ui/ui/hotspot_users.tpl
Normal file
69
ui/ui/hotspot_users.tpl
Normal file
@ -0,0 +1,69 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card">
|
||||
<div class="card-header">{Lang::T('Hotspot Users')}</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive table_mobile">
|
||||
<table id="hotspot_users_table" class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Address')}</th>
|
||||
<th>{Lang::T('Uptime')}</th>
|
||||
<th>{Lang::T('Server')}</th>
|
||||
<th>{Lang::T('MAC Address')}</th>
|
||||
<th>{Lang::T('Session Time')}</th>
|
||||
<th style="color: red;">{Lang::T('Upload')}</th>
|
||||
<th style="color: green;">{Lang::T('Download')}</th>
|
||||
<th>{Lang::T('Total')}</th>
|
||||
<th>{Lang::T('Action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- DataTables will populate the table body dynamically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
||||
|
||||
<!-- Include jQuery and DataTables JS CDN -->
|
||||
<!--<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>-->
|
||||
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#hotspot_users_table').DataTable({
|
||||
"ajax": {
|
||||
"url": "{$_url}onlineusers/hotspot_users",
|
||||
"dataSrc": ""
|
||||
},
|
||||
"columns": [
|
||||
{ "data": "username", "render": function(data, type, row) {
|
||||
return '<span style="color: blue;">' + data + '</span>';
|
||||
}
|
||||
},
|
||||
{ "data": "address" },
|
||||
{ "data": "uptime" },
|
||||
{ "data": "server" },
|
||||
{ "data": "mac" },
|
||||
{ "data": "session_time" },
|
||||
{ "data": "rx_bytes" },
|
||||
{ "data": "tx_bytes" },
|
||||
{ "data": "total" },
|
||||
{ "data": null, "render": function(data, type, row) {
|
||||
return '<form method="post" action="{$_url}onlineusers/disconnect/{$routerId}/' + row.username + '/hotspot">' +
|
||||
'<button type="submit" class="btn btn-danger btn-sm" title="Disconnect"><i class="fa fa-times"></i></button>' +
|
||||
'</form>';
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user