Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4f8e9c5
[ADDED] Coin Precision Setting
MPOS123 Jun 30, 2014
4cfab27
[FIX] +3 on table widths
MPOS123 Jun 30, 2014
1a3d5e6
[FIX] Allow higher precision in MySQL DOUBLE
MPOS123 Jun 30, 2014
b987358
[UPDATE] Base Structure with new DOUBLE
MPOS123 Jun 30, 2014
0a09c78
Revert "[UPDATE] Base Structure with new DOUBLE"
MPOS123 Jun 30, 2014
b9c38fe
Revert "[FIX] Allow higher precision in MySQL DOUBLE"
MPOS123 Jun 30, 2014
52dcd05
[UPDATE] Fixed PPLNS to not round values
MPOS123 Jun 30, 2014
2f2b3f5
[UPDATE] Do not round balances from SQL
MPOS123 Jun 30, 2014
6ddca74
[ADDED] Properly insert coin values
MPOS123 Jul 7, 2014
227d9d7
[REMOVED] Precision from estimates
MPOS123 Jul 7, 2014
8928b33
[SQL] Moved to 0.0.13 SQL structure
MPOS123 Jul 19, 2014
eeb0481
[MERGE] Conflicts with development
MPOS123 Jul 19, 2014
674b12e
Merge branch 'development' into coin-rounding-setting
MPOS123 Jul 19, 2014
a7a731d
[UPDATE] Working coin precision system
MPOS123 Jul 19, 2014
105d772
[MERGE] Development
MPOS123 Jul 22, 2014
2b49325
[FIX] DB_VERSION to 0.0.14 after update
MPOS123 Jul 22, 2014
277c31a
[FIX] Merge conflict with development
MPOS123 Jul 25, 2014
9c73e01
[MERGE] Merge conflict with development
MPOS123 Jul 25, 2014
a3cf5ec
Newbie hint to readme
TheSerapher Aug 28, 2014
a6c587c
[RE-ADDED] Mobile detection in PHP
MPOS123 Aug 29, 2014
67066ef
[FIX] Description
MPOS123 Aug 29, 2014
09f681b
Merge pull request #2337 from MPOS/mobile-detection
TheSerapher Aug 29, 2014
8e2dd34
[UPDATE] Remove fees from templates if disabled via config
MPOS123 Aug 29, 2014
939cb80
[FIX] Properly detect mobile
MPOS123 Aug 29, 2014
8cf7c99
[FIX] Added 0.0.14 to base structure
MPOS123 Sep 1, 2014
22e8877
Merge branch 'development' into coin-rounding-setting
MPOS123 Sep 1, 2014
4147ef5
[FIX] New base SQL version
MPOS123 Sep 1, 2014
1552fc2
Merge pull request #2290 from MPOS/coin-rounding-setting
TheSerapher Sep 1, 2014
ecbda36
Update README.md
TheSerapher Sep 4, 2014
44ab7c9
Update README.md
TheSerapher Sep 4, 2014
751f247
[FIX] Allow < 1 max/min thresholds
MPOS123 Oct 6, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
Description
===========

MPOS is a web based Mining Portal for various crypto currencies. It was created by [TheSerapher](https://github.com/TheSerapher) and has hence grown quite large. Recently it was migrated into a Github Organization to make development easier. It's a community driven open source project. Support can be requested on IRC at https://webchat.freenode.net/?channels=#mpos


**NOTE**: This project is still under development and commits are happening on a daily basis.
I do not recommend using this for a live setup as of yet. Wait for the later Release Candidate
if you wish to run your pool with it. Testing pools are much appreciated though!
MPOS is a web based Mining Portal for various crypto currencies. It was created by [TheSerapher](https://github.com/TheSerapher) and has hence grown quite large. Recently it was migrated into a Github Organization to make development easier. It's a community driven open source project. Support can be requested on IRC at https://webchat.freenode.net/?channels=#mpos - Be **PATIENT** ... People listed in this channel may currently be inactive but most users there have offline logging of messages. They **will** see your questions and answer if they can. Don't join, ask the question and leave. Sit around if you want answers to your questions!

Donations
=========
Expand All @@ -24,7 +19,7 @@ Donations to this project are going directly to [TheSerapher](https://github.com
Website Footer
==============

When you decide to use `MPOS` please be so kind and leave the footer intact. You are not the author of the software and should honor those that have worked on it. I don't mind changing the LTC donation address at the bottom, but keep in mind who really wrote this software and would deserve those ;-).
When you decide to use `MPOS` please be so kind and leave the footer intact. You are not the author of the software and should honor those that have worked on it. Keeping the footer intact helps spreading the word. Leaving the donation address untouched allows miners to donate to the author.

Donors
======
Expand Down Expand Up @@ -109,7 +104,6 @@ The following feature have been implemented so far:
* Pool Settings
* Pool Workers
* User Reports
* Template Overwrite
* Notification system
* IDLE Workers
* New blocks found in pool
Expand All @@ -125,7 +119,7 @@ The following feature have been implemented so far:
Installation
============

Please take a look at the [Quick Start Guide](https://github.com/TheSerapher/php-mpos/wiki/Quick-Start-Guide). This will give you an idea how to setup `MPOS`.
Please take a look at the [Quick Start Guide](https://github.com/TheSerapher/php-mpos/wiki/Quick-Start-Guide). This will give you an idea how to setup `MPOS`. Please be aware that the `master` branch is our currently considered stable system while `development` is used as a test bed for all upcoming changes for `master`. If you wish to run a stable, well tested system ensure you run `git checkout master`. If you decide to stick to the `development` branch with bleeding edge code and potential bugs, just `git clone` the project.

Customization
=============
Expand Down Expand Up @@ -177,7 +171,7 @@ You can find the team on Freenode.net, #MPOS.
Team Members
============

Author and Github Owner: [TheSerapher](https://github.com/TheSerapher) aka Sebastian Grewe
Author and Project Owner: [TheSerapher](https://github.com/TheSerapher) aka Sebastian Grewe

Developers:

Expand Down
16 changes: 10 additions & 6 deletions cronjobs/pplns_payout.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
$monitoring->endCronjob($cron_name, 'E0011', 0, true, false);
}

// Fetch precision
$precision = $coin->getCoinValuePrevision();
$table_precision = $coin->getCoinValuePrevision() + 3;

$log->logDebug('Starting PPLNS payout process');
$count = 0;
foreach ($aAllBlocks as $iIndex => $aBlock) {
Expand Down Expand Up @@ -181,7 +185,7 @@
}

// Table header for account shares
$strLogMask = "| %5.5s | %-15.15s | %15.15s | %15.15s | %12.12s | %15.15s | %15.15s | %15.15s | %15.15s |";
$strLogMask = "| %5.5s | %-15.15s | %15.15s | %15.15s | %12.12s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s |";
$log->logInfo(sprintf($strLogMask, 'ID', 'Username', 'Valid', 'Invalid', 'Percentage', 'Payout', 'Donation', 'Fee', 'Bonus'));

// Loop through all accounts that have found shares for this round
Expand All @@ -197,27 +201,27 @@

// Payout based on PPLNS target shares, proportional payout for all users
$aData['percentage'] = round(( 100 / $iRoundShares) * $aData['pplns_valid'], 8);
$aData['payout'] = round(( $aData['percentage'] / 100 ) * $dReward, 8);
$aData['payout'] = ( $aData['percentage'] / 100 ) * $dReward;
// Defaults
$aData['fee' ] = 0;
$aData['donation'] = 0;
$aData['pool_bonus'] = 0;

// Calculate pool fees
if ($config['fees'] > 0 && $aData['no_fees'] == 0)
$aData['fee'] = round($config['fees'] / 100 * $aData['payout'], 8);
$aData['fee'] = $config['fees'] / 100 * $aData['payout'];

// Calculate pool bonus if it applies, will be paid from liquid assets!
if ($config['pool_bonus'] > 0) {
if ($config['pool_bonus_type'] == 'block') {
$aData['pool_bonus'] = round(( $config['pool_bonus'] / 100 ) * $dReward, 8);
$aData['pool_bonus'] = ( $config['pool_bonus'] / 100 ) * $dReward;
} else {
$aData['pool_bonus'] = round(( $config['pool_bonus'] / 100 ) * $aData['payout'], 8);
$aData['pool_bonus'] = ( $config['pool_bonus'] / 100 ) * $aData['payout'];
}
}

// Calculate donation amount, fees not included
$aData['donation'] = round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8);
$aData['donation'] = $user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']);

// Verbose output of this users calculations
$log->logInfo(
Expand Down
22 changes: 12 additions & 10 deletions cronjobs/pps_payout.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
}

// Per-share value to be paid out to users
$pps_value = round($coin->calcPPSValue($pps_reward, $dDifficulty), 12);
$pps_value = $coin->calcPPSValue($pps_reward, $dDifficulty);

// Find our last share accounted and last inserted share for PPS calculations
if (!$iPreviousShareId = $setting->getValue('pps_last_share_id')) {
Expand All @@ -89,13 +89,15 @@

if (!empty($aAccountShares)) {
// Runtime information for this payout
$precision = $coin->getCoinValuePrevision();
$table_precision = $coin->getCoinValuePrevision() + 3;
$log->logInfo('Runtime information for this payout');
$strLogMask = "| %-15.15s | %15.15s | %15.15s | %15.15s |";
$log->logInfo(sprintf($strLogMask, 'PPS reward type', 'Reward Base', 'Difficulty', 'PPS Value'));
$log->logInfo(sprintf($strLogMask, $strRewardType, $pps_reward, $dDifficulty, $pps_value));
$strLogMask = "| %-15.15s | %15.15s | %15.15s | %${table_precision}.${table_precision}s | %3.3s |";
$log->logInfo(sprintf($strLogMask, 'PPS reward type', 'Reward Base', 'Difficulty', 'PPS Value', 'Precision'));
$log->logInfo(sprintf($strLogMask, $strRewardType, $pps_reward, $dDifficulty, $pps_value, $precision));
$log->logInfo('Per-user payout information');
$strLogMask = "| %8.8s | %25.25s | %15.15s | %15.15s | %18.18s | %18.18s | %18.18s |";
$log->logInfo(sprintf($strLogMask, 'User ID', 'Username', 'Invalid', 'Valid', ' * PPS Value', ' = Payout', 'Donation', 'Fee'));
$strLogMask = "| %8.8s | %25.25s | %15.15s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s |";
$log->logInfo(sprintf($strLogMask, 'User ID', 'Username', 'Invalid', 'Valid', '* PPS Value', ' = Payout', 'Donation', 'Fee'));
}

foreach ($aAccountShares as $aData) {
Expand All @@ -106,21 +108,21 @@
}

// Payout for this user
$aData['payout'] = round($aData['valid'] * $pps_value, 12);
$aData['payout'] = $aData['valid'] * $pps_value;

// Defaults
$aData['fee' ] = 0;
$aData['donation'] = 0;

// Calculate block fees
if ($config['fees'] > 0 && $aData['no_fees'] == 0)
$aData['fee'] = round($config['fees'] / 100 * $aData['payout'], 12);
$aData['fee'] = $config['fees'] / 100 * $aData['payout'];
// Calculate donation amount
$aData['donation'] = round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 12);
$aData['donation'] = $user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']);

$log->logInfo(sprintf(
$strLogMask, $aData['id'], $aData['username'], $aData['invalid'], $aData['valid'],
number_format($pps_value, 12), number_format($aData['payout'], 12), number_format($aData['donation'], 12), number_format($aData['fee'], 12)
number_format($pps_value, $precision), number_format($aData['payout'], $precision), number_format($aData['donation'], $precision), number_format($aData['fee'], $precision)
));

// Add new credit transaction
Expand Down
20 changes: 12 additions & 8 deletions cronjobs/proportional_payout.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@
$monitoring->endCronjob($cron_name, 'E0011', 0, true, false);
}

// Fetch precision
$precision = $coin->getCoinValuePrevision();
$table_precision = $coin->getCoinValuePrevision() + 3;

$count = 0;
// Table header for account shares
$strLogMask = "| %10.10s | %-5.5s | %15.15s | %15.15s | %12.12s | %12.12s | %15.15s | %15.15s | %15.15s | %15.15s |";
$strLogMask = "| %10.10s | %-5.5s | %15.15s | %15.15s | %12.12s | %12.12s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s | %${table_precision}.${table_precision}s |";
$log->logInfo(sprintf($strLogMask, 'Block', 'ID', 'Username', 'Valid', 'Invalid', 'Percentage', 'Payout', 'Donation', 'Fee', 'Bonus'));
foreach ($aAllBlocks as $iIndex => $aBlock) {
// If we have unaccounted blocks without share_ids, they might not have been inserted yet
Expand Down Expand Up @@ -87,29 +91,29 @@
$aData['fee' ] = 0;
$aData['donation'] = 0;
$aData['pool_bonus'] = 0;
$aData['percentage'] = round(( 100 / $iRoundShares ) * $aData['valid'], 8);
$aData['payout'] = round(( $aData['percentage'] / 100 ) * $dReward, 8);
$aData['percentage'] = ( 100 / $iRoundShares ) * $aData['valid'];
$aData['payout'] = ( $aData['percentage'] / 100 ) * $dReward;

// Calculate pool fees if they apply
if ($config['fees'] > 0 && $aData['no_fees'] == 0)
$aData['fee'] = round($config['fees'] / 100 * $aData['payout'], 8);
$aData['fee'] = $config['fees'] / 100 * $aData['payout'];

// Calculate pool bonus if it applies, will be paid from liquid assets!
if ($config['pool_bonus'] > 0) {
if ($config['pool_bonus_type'] == 'block') {
$aData['pool_bonus'] = round(( $config['pool_bonus'] / 100 ) * $dReward, 8);
$aData['pool_bonus'] = ( $config['pool_bonus'] / 100 ) * $dReward;
} else {
$aData['pool_bonus'] = round(( $config['pool_bonus'] / 100 ) * $aData['payout'], 8);
$aData['pool_bonus'] = ( $config['pool_bonus'] / 100 ) * $aData['payout'];
}
}

// Calculate donation amount, fees not included
$aData['donation'] = round($user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']), 8);
$aData['donation'] = $user->getDonatePercent($user->getUserId($aData['username'])) / 100 * ( $aData['payout'] - $aData['fee']);

// Verbose output of this users calculations
$log->logInfo(
sprintf($strLogMask, $aBlock['height'], $aData['id'], $aData['username'], $aData['valid'], $aData['invalid'],
number_format($aData['percentage'], 8), number_format($aData['payout'], 8), number_format($aData['donation'], 8), number_format($aData['fee'], 8), number_format($aData['pool_bonus'], 8))
number_format($aData['percentage'], $precision), number_format($aData['payout'], $precision), number_format($aData['donation'], $precision), number_format($aData['fee'], $precision), number_format($aData['pool_bonus'], $precision))
);

// Update user share statistics
Expand Down
9 changes: 7 additions & 2 deletions include/autoloader.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,18 @@
require_once(INCLUDE_DIR . '/lib/swiftmailer/swift_required.php');

// Detect device
if ( PHP_SAPI == 'cli') {
require_once(INCLUDE_DIR . '/lib/Mobile_Detect.php');
$detect = new Mobile_Detect;

if ($detect->isMobile()) {
$theme = $setting->getValue('website_mobile_theme', 'bootstrap');
} else if ( PHP_SAPI == 'cli') {
// Create a new compile folder just for crons
// We call mail templates directly anyway
$theme = 'cron';
} else {
// Use configured theme, fallback to default theme
$setting->getValue('website_theme') ? $theme = $setting->getValue('website_theme') : $theme = 'bootstrap';
$theme = $setting->getValue('website_theme', 'bootstrap');
}
define('THEME', $theme);

Expand Down
10 changes: 10 additions & 0 deletions include/classes/coins/coin_base.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@ class CoinBase extends Base {
// Our coins share difficulty precision
protected $share_difficulty_precision = 0;

// Our coin value precision, mostly used on frontend
protected $coin_value_precision = 8;

/**
* Read our target bits
**/
public function getTargetBits() {
return $this->target_bits;
}

/**
* Read our coin value precision
**/
public function getCoinValuePrevision() {
return $this->coin_value_precision;
}

/**
* Read our share difficulty precision
**/
Expand Down
1 change: 1 addition & 0 deletions include/classes/coins/coin_sha256d.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
**/
class Coin extends CoinBase {
protected $target_bits = 32;
protected $coin_value_precision = 20;
}

?>
25 changes: 12 additions & 13 deletions include/classes/transaction.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ class Transaction extends Base {
* @return bool
**/
public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL, $txid=NULL) {
$amount = number_format($amount, $this->coin->getCoinValuePrevision(), '.', '');
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address, txid) VALUES (?, ?, ?, ?, ?, ?)");
if ($this->checkStmt($stmt) && $stmt->bind_param("idisss", $account_id, $amount, $block_id, $type, $coin_address, $txid) && $stmt->execute()) {
if ($this->checkStmt($stmt) && $stmt->bind_param("isisss", $account_id, $amount, $block_id, $type, $coin_address, $txid) && $stmt->execute()) {
$this->insert_id = $stmt->insert_id;
return true;
}
Expand Down Expand Up @@ -296,11 +297,11 @@ public function getLockedBalance() {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT
ROUND((
(
SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
), 8) AS balance
) AS balance
FROM $this->table AS t
LEFT JOIN " . $this->block->getTableName() . " AS b
ON t.block_id = b.id
Expand All @@ -319,19 +320,19 @@ public function getBalance($account_id) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("
SELECT
IFNULL(ROUND((
IFNULL((
SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= ? ) OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= ? ) OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
), 8), 0) AS confirmed,
IFNULL(ROUND((
), 0) AS confirmed,
IFNULL((
SUM( IF( t.type IN ('Credit','Bonus') AND b.confirmations < ? AND b.confirmations >= 0, t.amount, 0 ) ) -
SUM( IF( t.type IN ('Donation','Fee') AND b.confirmations < ? AND b.confirmations >= 0, t.amount, 0 ) )
), 8), 0) AS unconfirmed,
IFNULL(ROUND((
), 0) AS unconfirmed,
IFNULL((
SUM( IF( t.type IN ('Credit','Bonus') AND b.confirmations = -1, t.amount, 0) ) -
SUM( IF( t.type IN ('Donation','Fee') AND b.confirmations = -1, t.amount, 0) )
), 8), 0) AS orphaned
), 0) AS orphaned
FROM $this->table AS t
LEFT JOIN " . $this->block->getTableName() . " AS b
ON t.block_id = b.id
Expand All @@ -357,12 +358,10 @@ public function getAPQueue($limit=250) {
a.ap_threshold,
ca.coin_address,
IFNULL(
ROUND(
(
SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= " . $this->config['confirmations'] . ") OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= " . $this->config['confirmations'] . ") OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
), 8
), 0
) AS confirmed
FROM $this->table AS t
Expand Down Expand Up @@ -451,12 +450,10 @@ public function getMPQueue($limit=250) {
ca.coin_address,
p.id AS payout_id,
IFNULL(
ROUND(
(
SUM( IF( ( t.type IN ('Credit','Bonus') AND b.confirmations >= " . $this->config['confirmations'] . ") OR t.type = 'Credit_PPS', t.amount, 0 ) ) -
SUM( IF( t.type IN ('Debit_MP', 'Debit_AP'), t.amount, 0 ) ) -
SUM( IF( ( t.type IN ('Donation','Fee') AND b.confirmations >= " . $this->config['confirmations'] . ") OR ( t.type IN ('Donation_PPS', 'Fee_PPS', 'TXFee') ), t.amount, 0 ) )
), 8
), 0
) AS confirmed
FROM " . $this->payout->getTableName() . " AS p
Expand All @@ -481,7 +478,9 @@ public function getMPQueue($limit=250) {
$transaction = new Transaction();
$transaction->setMemcache($memcache);
$transaction->setNotification($notification);
$transaction->setSetting($setting);
$transaction->setDebug($debug);
$transaction->setCoin($coin);
$transaction->setCoinAddress($coin_address);
$transaction->setMysql($mysqli);
$transaction->setConfig($config);
Expand Down
9 changes: 8 additions & 1 deletion include/config/admin_settings.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@
'name' => 'website_theme', 'value' => $setting->getValue('website_theme'),
'tooltip' => 'The default theme used on your pool.'
);
$aSettings['website'][] = array(
'display' => 'Website mobile theme', 'type' => 'select',
'options' => $aThemes,
'default' => 'bootstrap',
'name' => 'website_mobile_theme', 'value' => $setting->getValue('website_mobile_theme'),
'tooltip' => 'The mobile theme used on your pool.'
);
$aSettings['website'][] = array(
'display' => 'Website Design', 'type' => 'select',
'options' => $aDesigns,
Expand Down Expand Up @@ -317,7 +324,7 @@
);
$aSettings['system'][] = array(
'display' => 'Date format string', 'type' => 'text',
'site' => 25,
'size' => 25,
'default' => '%m/%d/%Y %H:%M:%S',
'name' => 'system_date_format', 'value' => $setting->getValue('system_date_format'),
'tooltip' => 'Date format to be used throughout the site. Please check PHP strftime for details.'
Expand Down
Loading