From 4f8e9c505fdb0b58e6843044dd898f9f661319fc Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 07:59:42 +0200 Subject: [PATCH 01/24] [ADDED] Coin Precision Setting * [UPDATED] Cronjobs to store real values, display with coin precision * [UPDATED] Templates to use new coin precision setting in dashboard * [UPDATED] JS API calls to use new precision setting too * [UPDATED] PPS Value is displayed as coin precision + 8 This should help with #2216 and address issues with coins where rounding of 12 was not enough. --- cronjobs/pplns_payout.php | 3 + cronjobs/pps_payout.php | 17 +- cronjobs/proportional_payout.php | 17 +- include/config/admin_settings.inc.php | 9 +- include/pages/dashboard.inc.php | 1 + include/smarty_globals.inc.php | 3 +- templates/bootstrap/dashboard/js/api.tpl | 18 +- .../round_statistics/pplns/round.tpl | 9 +- .../dashboard/round_statistics/pps/round.tpl | 199 +++++++++++++----- .../dashboard/round_statistics/prop/round.tpl | 9 +- 10 files changed, 199 insertions(+), 86 deletions(-) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 093be4ce0..40fc8f0dd 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -38,6 +38,9 @@ $monitoring->endCronjob($cron_name, 'E0011', 0, true, false); } +// Fetch precision +$precision = $setting->getValue('system_coin_precision', 12); + $log->logDebug('Starting PPLNS payout process'); $count = 0; foreach ($aAllBlocks as $iIndex => $aBlock) { diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 509176d2e..a22461095 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -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')) { @@ -89,10 +89,11 @@ if (!empty($aAccountShares)) { // Runtime information for this payout + $precision = $setting->getValue('system_coin_precision', 12); $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 | %15.15s | %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')); @@ -106,7 +107,7 @@ } // Payout for this user - $aData['payout'] = round($aData['valid'] * $pps_value, 12); + $aData['payout'] = $aData['valid'] * $pps_value; // Defaults $aData['fee' ] = 0; @@ -114,13 +115,13 @@ // 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 diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index 58f47ee9b..8870cd5fe 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -38,6 +38,9 @@ $monitoring->endCronjob($cron_name, 'E0011', 0, true, false); } +// Fetch precision +$precision = $setting->getValue('system_coin_precision', 12); + $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 |"; @@ -87,29 +90,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 diff --git a/include/config/admin_settings.inc.php b/include/config/admin_settings.inc.php index 4f8618305..32cd082e7 100644 --- a/include/config/admin_settings.inc.php +++ b/include/config/admin_settings.inc.php @@ -308,9 +308,16 @@ 'name' => 'system_error_email', 'value' => $setting->getValue('system_error_email'), 'tooltip' => 'The email address for system errors notifications, like cronjobs failures.' ); +$aSettings['system'][] = array( + 'display' => 'Coin Precision', 'type' => 'text', + 'size' => 5, + 'default' => '12', + 'name' => 'system_coin_precision', 'value' => $setting->getValue('system_coin_precision'), + 'tooltip' => 'How do we round any coin values throughout MPOS. Defaults to 12 digits.' +); $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.' diff --git a/include/pages/dashboard.inc.php b/include/pages/dashboard.inc.php index e813c8341..519d76672 100644 --- a/include/pages/dashboard.inc.php +++ b/include/pages/dashboard.inc.php @@ -49,6 +49,7 @@ } // Make it available in Smarty + $smarty->assign('PRECISION', $setting->getValue('system_coin_precision', 12)); $smarty->assign('BLOCKSFOUND', $aLastBlocks); $smarty->assign('DISABLED_DASHBOARD', $setting->getValue('disable_dashboard')); $smarty->assign('DISABLED_DASHBOARD_API', $setting->getValue('disable_dashboard_api')); diff --git a/include/smarty_globals.inc.php b/include/smarty_globals.inc.php index ea13a6d1c..1ecca6c7b 100644 --- a/include/smarty_globals.inc.php +++ b/include/smarty_globals.inc.php @@ -175,7 +175,8 @@ break; case 'pps': $aGlobal['userdata']['pps']['unpaidshares'] = $statistics->getUserUnpaidPPSShares($_SESSION['USERDATA']['username'], $_SESSION['USERDATA']['id'], $setting->getValue('pps_last_share_id')); - $aGlobal['ppsvalue'] = number_format($statistics->getPPSValue(), 12); + // We use coin precision + 8 to display PPS value + $aGlobal['ppsvalue'] = number_format($statistics->getPPSValue(), $setting->getValue('system_coin_precision', 12) + 8); $aGlobal['poolppsvalue'] = $aGlobal['ppsvalue'] * pow(2, $config['difficulty'] - 16); $aGlobal['userdata']['estimates'] = $statistics->getUserEstimates($aGlobal['userdata']['sharerate'], $aGlobal['userdata']['sharedifficulty'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees'], $aGlobal['ppsvalue']); break; diff --git a/templates/bootstrap/dashboard/js/api.tpl b/templates/bootstrap/dashboard/js/api.tpl index e73b48596..6df883ce6 100644 --- a/templates/bootstrap/dashboard/js/api.tpl +++ b/templates/bootstrap/dashboard/js/api.tpl @@ -134,18 +134,18 @@ $(document).ready(function(){ $('#b-nblock').html(data.getdashboarddata.data.network.block); $('#b-roundprogress').html(number_format(parseFloat(data.getdashboarddata.data.pool.shares.progress).toFixed(2), 2) + "%"); {/literal}{if $GLOBAL.config.payout_system != 'pps'}{literal } - $('#b-payout').html(number_format(data.getdashboarddata.data.personal.estimates.payout, 8)); - $('#b-block').html(number_format(data.getdashboarddata.data.personal.estimates.block, 8)); - $('#b-fee').html(number_format(data.getdashboarddata.data.personal.estimates.fee,8 )); - $('#b-donation').html(number_format(data.getdashboarddata.data.personal.estimates.donation, 8)); + $('#b-payout').html(number_format(data.getdashboarddata.data.personal.estimates.payout, {/literal}{$PRECISION}{literal})); + $('#b-block').html(number_format(data.getdashboarddata.data.personal.estimates.block, {/literal}{$PRECISION}{literal})); + $('#b-fee').html(number_format(data.getdashboarddata.data.personal.estimates.fee, {/literal}{$PRECISION}{literal})); + $('#b-donation').html(number_format(data.getdashboarddata.data.personal.estimates.donation, {/literal}{$PRECISION}{literal})); {/literal}{else}{literal} $('#b-ppsunpaid').html(number_format(data.getdashboarddata.data.personal.shares.unpaid)); $('#b-ppsdiff').html(number_format(data.getdashboarddata.data.personal.sharedifficulty, 2)); - $('#b-est1').html(number_format(data.getdashboarddata.data.personal.estimates.hours1, 8)); - $('#b-est24hours').html(number_format(data.getdashboarddata.data.personal.estimates.hours24, 8)); - $('#b-est7days').html(number_format(data.getdashboarddata.data.personal.estimates.days7, 8)); - $('#b-est14days').html(number_format(data.getdashboarddata.data.personal.estimates.days14, 8)); - $('#b-est30days').html(number_format(data.getdashboarddata.data.personal.estimates.days30, 8)); + $('#b-est1').html(number_format(data.getdashboarddata.data.personal.estimates.hours1, {/literal}{$PRECISION}{literal})); + $('#b-est24hours').html(number_format(data.getdashboarddata.data.personal.estimates.hours24, {/literal}{$PRECISION}{literal})); + $('#b-est7days').html(number_format(data.getdashboarddata.data.personal.estimates.days7, {/literal}{$PRECISION}{literal})); + $('#b-est14days').html(number_format(data.getdashboarddata.data.personal.estimates.days14, {/literal}{$PRECISION}{literal})); + $('#b-est30days').html(number_format(data.getdashboarddata.data.personal.estimates.days30, {/literal}{$PRECISION}{literal})); {/literal}{/if}{literal} {/literal}{if $GLOBAL.config.payout_system == 'pplns'}{literal} $('#b-pplns').html({/literal}{$GLOBAL.pplns.target}{literal}); diff --git a/templates/bootstrap/dashboard/round_statistics/pplns/round.tpl b/templates/bootstrap/dashboard/round_statistics/pplns/round.tpl index 8abfe4117..234e31cec 100644 --- a/templates/bootstrap/dashboard/round_statistics/pplns/round.tpl +++ b/templates/bootstrap/dashboard/round_statistics/pplns/round.tpl @@ -37,10 +37,10 @@
-

{$GLOBAL.userdata.estimates.payout|number_format:"8"}

+

{$GLOBAL.userdata.estimates.payout|number_format:$PRECISION}

-

{$GLOBAL.config.currency} Est Earnings

+

{$GLOBAL.config.currency} Est. Earnings

@@ -67,11 +67,10 @@
-

{if $GLOBAL.nethashrate > 0}{$NETWORK.EstNextDifficulty|number_format:"8"}{else}n/a{/if}

-

{if $GLOBAL.nethashrate > 0}Change in {$NETWORK.BlocksUntilDiffChange} Blocks{else}No Estimates{/if}

+

{if $GLOBAL.nethashrate > 0}{$NETWORK.EstNextDifficulty|number_format:"8"}{else}n/a{/if}

-

Est Next Difficulty

+

Est Next Difficulty{if $GLOBAL.nethashrate > 0}
Change in {$NETWORK.BlocksUntilDiffChange} Blocks{else}No Estimates{/if}

diff --git a/templates/bootstrap/dashboard/round_statistics/pps/round.tpl b/templates/bootstrap/dashboard/round_statistics/pps/round.tpl index e11186cb1..7fc95e6c5 100644 --- a/templates/bootstrap/dashboard/round_statistics/pps/round.tpl +++ b/templates/bootstrap/dashboard/round_statistics/pps/round.tpl @@ -1,55 +1,154 @@
-

{$GLOBAL.userdata.estimates.payout|number_format:"8"}

+

{$GLOBAL.userdata.estimates.payout|number_format:$PRECISION}

-

{$GLOBAL.config.currency} Est Earnings

+

{$GLOBAL.config.currency} Est. Earnings

@@ -67,11 +67,10 @@
-

{if $GLOBAL.nethashrate > 0}{$NETWORK.EstNextDifficulty|number_format:"8"}{else}n/a{/if}

-

{if $GLOBAL.nethashrate > 0}Change in {$NETWORK.BlocksUntilDiffChange} Blocks{else}No Estimates{/if}

+

{if $GLOBAL.nethashrate > 0}{$NETWORK.EstNextDifficulty|number_format:"8"}{else}n/a{/if}

-

Est Next Difficulty

+

Est Next Difficulty{if $GLOBAL.nethashrate > 0}
Change in {$NETWORK.BlocksUntilDiffChange} Blocks{else}No Estimates{/if}

From 4cfab278e85a5feb2aaac25faa737b9b73f3da98 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 08:14:22 +0200 Subject: [PATCH 02/24] [FIX] +3 on table widths --- cronjobs/pplns_payout.php | 3 ++- cronjobs/pps_payout.php | 7 ++++--- cronjobs/proportional_payout.php | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 40fc8f0dd..dc81fa7a2 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -40,6 +40,7 @@ // Fetch precision $precision = $setting->getValue('system_coin_precision', 12); +$table_precision = $setting->getValue('system_coin_precision', 12) + 3; $log->logDebug('Starting PPLNS payout process'); $count = 0; @@ -184,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 diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index a22461095..deeeb095e 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -90,13 +90,14 @@ if (!empty($aAccountShares)) { // Runtime information for this payout $precision = $setting->getValue('system_coin_precision', 12); + $table_precision = $setting->getValue('system_coin_precision', 12) + 3; $log->logInfo('Runtime information for this payout'); - $strLogMask = "| %-15.15s | %15.15s | %15.15s | %15.15s | %3.3s |"; + $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) { diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index 8870cd5fe..95daf0ffb 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -40,10 +40,11 @@ // Fetch precision $precision = $setting->getValue('system_coin_precision', 12); +$table_precision = $setting->getValue('system_coin_precision', 12) + 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 From 1a3d5e65250473f93450e0161cefd3c61f117d54 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 08:52:18 +0200 Subject: [PATCH 03/24] [FIX] Allow higher precision in MySQL DOUBLE --- include/version.inc.php | 2 +- upgrade/definitions/0.0.11_to_0.0.12.inc.php | 30 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 upgrade/definitions/0.0.11_to_0.0.12.inc.php diff --git a/include/version.inc.php b/include/version.inc.php index 91d93ee8d..a4e0d533f 100644 --- a/include/version.inc.php +++ b/include/version.inc.php @@ -2,7 +2,7 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; define('MPOS_VERSION', '0.0.4'); -define('DB_VERSION', '0.0.11'); +define('DB_VERSION', '0.0.12'); define('CONFIG_VERSION', '0.0.8'); define('HASH_VERSION', 1); diff --git a/upgrade/definitions/0.0.11_to_0.0.12.inc.php b/upgrade/definitions/0.0.11_to_0.0.12.inc.php new file mode 100644 index 000000000..7d8d7c6dc --- /dev/null +++ b/upgrade/definitions/0.0.11_to_0.0.12.inc.php @@ -0,0 +1,30 @@ +getValue('DB_VERSION'); // Our actual version installed + + // Upgrade specific variables + $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DOUBLE(60,30) NULL DEFAULT '0'"; + $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.12' WHERE name = 'DB_VERSION'"; + + if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { + // Run the upgrade + echo '- Starting database migration to version ' . $db_version_new . PHP_EOL; + foreach ($aSql as $sql) { + echo '- Preparing: ' . $sql . PHP_EOL; + $stmt = $mysqli->prepare($sql); + if ($stmt && $stmt->execute()) { + echo '- success' . PHP_EOL; + } else { + echo '- failed: ' . $mysqli->error . PHP_EOL; + exit(1); + } + } + } +} +?> From b9873583552d8e2890e79a61d71bd3e9eb48f46b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 08:54:12 +0200 Subject: [PATCH 04/24] [UPDATE] Base Structure with new DOUBLE --- sql/000_base_structure.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index 85e3e67cf..e919afd66 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -134,7 +134,7 @@ CREATE TABLE IF NOT EXISTS `settings` ( UNIQUE KEY `setting` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.11'); +INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.12'); CREATE TABLE IF NOT EXISTS `shares` ( `id` bigint(30) NOT NULL AUTO_INCREMENT, @@ -216,7 +216,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( `account_id` int(255) unsigned NOT NULL, `type` varchar(25) DEFAULT NULL, `coin_address` varchar(255) DEFAULT NULL, - `amount` double DEFAULT '0', + `amount` double(60,30) DEFAULT '0', `block_id` int(255) DEFAULT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `txid` varchar(256) DEFAULT NULL, From 0a09c78e271305c03392d8b9ef063061090471e5 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 09:02:51 +0200 Subject: [PATCH 05/24] Revert "[UPDATE] Base Structure with new DOUBLE" This reverts commit b9873583552d8e2890e79a61d71bd3e9eb48f46b. --- sql/000_base_structure.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index e919afd66..85e3e67cf 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -134,7 +134,7 @@ CREATE TABLE IF NOT EXISTS `settings` ( UNIQUE KEY `setting` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.12'); +INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.11'); CREATE TABLE IF NOT EXISTS `shares` ( `id` bigint(30) NOT NULL AUTO_INCREMENT, @@ -216,7 +216,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( `account_id` int(255) unsigned NOT NULL, `type` varchar(25) DEFAULT NULL, `coin_address` varchar(255) DEFAULT NULL, - `amount` double(60,30) DEFAULT '0', + `amount` double DEFAULT '0', `block_id` int(255) DEFAULT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `txid` varchar(256) DEFAULT NULL, From b9c38fe9fff54643744a8668b7e1a4c8ffd635cb Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 09:02:58 +0200 Subject: [PATCH 06/24] Revert "[FIX] Allow higher precision in MySQL DOUBLE" This reverts commit 1a3d5e65250473f93450e0161cefd3c61f117d54. --- include/version.inc.php | 2 +- upgrade/definitions/0.0.11_to_0.0.12.inc.php | 30 -------------------- 2 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 upgrade/definitions/0.0.11_to_0.0.12.inc.php diff --git a/include/version.inc.php b/include/version.inc.php index a4e0d533f..91d93ee8d 100644 --- a/include/version.inc.php +++ b/include/version.inc.php @@ -2,7 +2,7 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; define('MPOS_VERSION', '0.0.4'); -define('DB_VERSION', '0.0.12'); +define('DB_VERSION', '0.0.11'); define('CONFIG_VERSION', '0.0.8'); define('HASH_VERSION', 1); diff --git a/upgrade/definitions/0.0.11_to_0.0.12.inc.php b/upgrade/definitions/0.0.11_to_0.0.12.inc.php deleted file mode 100644 index 7d8d7c6dc..000000000 --- a/upgrade/definitions/0.0.11_to_0.0.12.inc.php +++ /dev/null @@ -1,30 +0,0 @@ -getValue('DB_VERSION'); // Our actual version installed - - // Upgrade specific variables - $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DOUBLE(60,30) NULL DEFAULT '0'"; - $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.12' WHERE name = 'DB_VERSION'"; - - if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { - // Run the upgrade - echo '- Starting database migration to version ' . $db_version_new . PHP_EOL; - foreach ($aSql as $sql) { - echo '- Preparing: ' . $sql . PHP_EOL; - $stmt = $mysqli->prepare($sql); - if ($stmt && $stmt->execute()) { - echo '- success' . PHP_EOL; - } else { - echo '- failed: ' . $mysqli->error . PHP_EOL; - exit(1); - } - } - } -} -?> From 52dcd05c9916d2856e6d60cc7818fe71822fa80e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 09:03:21 +0200 Subject: [PATCH 07/24] [UPDATE] Fixed PPLNS to not round values --- cronjobs/pplns_payout.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index dc81fa7a2..5f1840b7b 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -201,7 +201,7 @@ // 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; @@ -209,19 +209,19 @@ // 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( From 2f2b3f578cd085f567e01fabd54594522e0a0d0f Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 30 Jun 2014 09:07:16 +0200 Subject: [PATCH 08/24] [UPDATE] Do not round balances from SQL --- include/classes/transaction.class.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/include/classes/transaction.class.php b/include/classes/transaction.class.php index d01b53e34..990a6555f 100644 --- a/include/classes/transaction.class.php +++ b/include/classes/transaction.class.php @@ -296,11 +296,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 @@ -319,19 +319,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 @@ -357,12 +357,10 @@ public function getAPQueue($limit=250) { a.ap_threshold, a.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 @@ -449,12 +447,10 @@ public function getMPQueue($limit=250) { a.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 From 6ddca7466c0dcc38813546715ea10694bbd1de03 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 7 Jul 2014 14:31:17 +0200 Subject: [PATCH 09/24] [ADDED] Properly insert coin values --- include/classes/transaction.class.php | 4 ++- include/version.inc.php | 2 +- sql/000_base_structure.sql | 4 +-- upgrade/definitions/0.0.11_to_0.0.12.inc.php | 30 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 upgrade/definitions/0.0.11_to_0.0.12.inc.php diff --git a/include/classes/transaction.class.php b/include/classes/transaction.class.php index 990a6555f..01d5fc143 100644 --- a/include/classes/transaction.class.php +++ b/include/classes/transaction.class.php @@ -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->setting->getValue('system_coin_precision', 12), '.', ''); $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; } @@ -473,6 +474,7 @@ public function getMPQueue($limit=250) { $transaction = new Transaction(); $transaction->setMemcache($memcache); $transaction->setNotification($notification); +$transaction->setSetting($setting); $transaction->setDebug($debug); $transaction->setMysql($mysqli); $transaction->setConfig($config); diff --git a/include/version.inc.php b/include/version.inc.php index 91d93ee8d..a4e0d533f 100644 --- a/include/version.inc.php +++ b/include/version.inc.php @@ -2,7 +2,7 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; define('MPOS_VERSION', '0.0.4'); -define('DB_VERSION', '0.0.11'); +define('DB_VERSION', '0.0.12'); define('CONFIG_VERSION', '0.0.8'); define('HASH_VERSION', 1); diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index 85e3e67cf..c9773c5f4 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -134,7 +134,7 @@ CREATE TABLE IF NOT EXISTS `settings` ( UNIQUE KEY `setting` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.11'); +INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.12'); CREATE TABLE IF NOT EXISTS `shares` ( `id` bigint(30) NOT NULL AUTO_INCREMENT, @@ -216,7 +216,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( `account_id` int(255) unsigned NOT NULL, `type` varchar(25) DEFAULT NULL, `coin_address` varchar(255) DEFAULT NULL, - `amount` double DEFAULT '0', + `amount` double(50,30) DEFAULT '0', `block_id` int(255) DEFAULT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `txid` varchar(256) DEFAULT NULL, diff --git a/upgrade/definitions/0.0.11_to_0.0.12.inc.php b/upgrade/definitions/0.0.11_to_0.0.12.inc.php new file mode 100644 index 000000000..7d8d7c6dc --- /dev/null +++ b/upgrade/definitions/0.0.11_to_0.0.12.inc.php @@ -0,0 +1,30 @@ +getValue('DB_VERSION'); // Our actual version installed + + // Upgrade specific variables + $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DOUBLE(60,30) NULL DEFAULT '0'"; + $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.12' WHERE name = 'DB_VERSION'"; + + if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { + // Run the upgrade + echo '- Starting database migration to version ' . $db_version_new . PHP_EOL; + foreach ($aSql as $sql) { + echo '- Preparing: ' . $sql . PHP_EOL; + $stmt = $mysqli->prepare($sql); + if ($stmt && $stmt->execute()) { + echo '- success' . PHP_EOL; + } else { + echo '- failed: ' . $mysqli->error . PHP_EOL; + exit(1); + } + } + } +} +?> From 227d9d73ff7b58376994a57723a06c0ceb5a2c1e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 7 Jul 2014 14:36:38 +0200 Subject: [PATCH 10/24] [REMOVED] Precision from estimates --- templates/bootstrap/dashboard/js/api.tpl | 10 +++++----- .../bootstrap/dashboard/round_statistics/pps/round.tpl | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/templates/bootstrap/dashboard/js/api.tpl b/templates/bootstrap/dashboard/js/api.tpl index 6df883ce6..cc991f384 100644 --- a/templates/bootstrap/dashboard/js/api.tpl +++ b/templates/bootstrap/dashboard/js/api.tpl @@ -141,11 +141,11 @@ $(document).ready(function(){ {/literal}{else}{literal} $('#b-ppsunpaid').html(number_format(data.getdashboarddata.data.personal.shares.unpaid)); $('#b-ppsdiff').html(number_format(data.getdashboarddata.data.personal.sharedifficulty, 2)); - $('#b-est1').html(number_format(data.getdashboarddata.data.personal.estimates.hours1, {/literal}{$PRECISION}{literal})); - $('#b-est24hours').html(number_format(data.getdashboarddata.data.personal.estimates.hours24, {/literal}{$PRECISION}{literal})); - $('#b-est7days').html(number_format(data.getdashboarddata.data.personal.estimates.days7, {/literal}{$PRECISION}{literal})); - $('#b-est14days').html(number_format(data.getdashboarddata.data.personal.estimates.days14, {/literal}{$PRECISION}{literal})); - $('#b-est30days').html(number_format(data.getdashboarddata.data.personal.estimates.days30, {/literal}{$PRECISION}{literal})); + $('#b-est1').html(number_format(data.getdashboarddata.data.personal.estimates.hours1, 12)); + $('#b-est24hours').html(number_format(data.getdashboarddata.data.personal.estimates.hours24, 12)); + $('#b-est7days').html(number_format(data.getdashboarddata.data.personal.estimates.days7, 12)); + $('#b-est14days').html(number_format(data.getdashboarddata.data.personal.estimates.days14, 12)); + $('#b-est30days').html(number_format(data.getdashboarddata.data.personal.estimates.days30, 12)); {/literal}{/if}{literal} {/literal}{if $GLOBAL.config.payout_system == 'pplns'}{literal} $('#b-pplns').html({/literal}{$GLOBAL.pplns.target}{literal}); diff --git a/templates/bootstrap/dashboard/round_statistics/pps/round.tpl b/templates/bootstrap/dashboard/round_statistics/pps/round.tpl index 7fc95e6c5..14ab1584f 100644 --- a/templates/bootstrap/dashboard/round_statistics/pps/round.tpl +++ b/templates/bootstrap/dashboard/round_statistics/pps/round.tpl @@ -7,7 +7,7 @@
-

{$GLOBAL.userdata.estimates.hours1|number_format:$PRECISION}

+

{$GLOBAL.userdata.estimates.hours1|number_format:12}

{$GLOBAL.config.currency} 1 Hour Estimated Earnings

@@ -22,7 +22,7 @@
-

{$GLOBAL.userdata.estimates.hours24|number_format:$PRECISION}

+

{$GLOBAL.userdata.estimates.hours24|number_format:12}

{$GLOBAL.config.currency} 24 Hour Estimated Earnings

@@ -37,7 +37,7 @@
-

{$GLOBAL.userdata.estimates.days7|number_format:$PRECISION}

+

{$GLOBAL.userdata.estimates.days7|number_format:12}

{$GLOBAL.config.currency} 7 Days Estimated Earnings

@@ -52,7 +52,7 @@
-

{$GLOBAL.userdata.estimates.days14|number_format:$PRECISION}

+

{$GLOBAL.userdata.estimates.days14|number_format:12}

{$GLOBAL.config.currency} 14 Days Estimated Earnings

@@ -67,7 +67,7 @@
-

{$GLOBAL.userdata.estimates.days30|number_format:$PRECISION}

+

{$GLOBAL.userdata.estimates.days30|number_format:12}

{$GLOBAL.config.currency} 30 Days Estimated Earnings

From 8928b339393d2b51263ad3b601d140110f9c8d96 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 19 Jul 2014 08:20:22 +0200 Subject: [PATCH 11/24] [SQL] Moved to 0.0.13 SQL structure --- include/version.inc.php | 2 +- sql/000_base_structure.sql | 2 +- .../{0.0.11_to_0.0.12.inc.php => 0.0.12_to_0.0.13.inc.php} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename upgrade/definitions/{0.0.11_to_0.0.12.inc.php => 0.0.12_to_0.0.13.inc.php} (85%) diff --git a/include/version.inc.php b/include/version.inc.php index a4e0d533f..2b7ea18af 100644 --- a/include/version.inc.php +++ b/include/version.inc.php @@ -2,7 +2,7 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; define('MPOS_VERSION', '0.0.4'); -define('DB_VERSION', '0.0.12'); +define('DB_VERSION', '0.0.13'); define('CONFIG_VERSION', '0.0.8'); define('HASH_VERSION', 1); diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index c9773c5f4..742b36349 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -134,7 +134,7 @@ CREATE TABLE IF NOT EXISTS `settings` ( UNIQUE KEY `setting` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.12'); +INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.13'); CREATE TABLE IF NOT EXISTS `shares` ( `id` bigint(30) NOT NULL AUTO_INCREMENT, diff --git a/upgrade/definitions/0.0.11_to_0.0.12.inc.php b/upgrade/definitions/0.0.12_to_0.0.13.inc.php similarity index 85% rename from upgrade/definitions/0.0.11_to_0.0.12.inc.php rename to upgrade/definitions/0.0.12_to_0.0.13.inc.php index 7d8d7c6dc..b81b87ce3 100644 --- a/upgrade/definitions/0.0.11_to_0.0.12.inc.php +++ b/upgrade/definitions/0.0.12_to_0.0.13.inc.php @@ -4,13 +4,13 @@ function run_0012() { global $setting, $config, $user, $mysqli, $transaction; // Version information - $db_version_old = '0.0.11'; // What version do we expect - $db_version_new = '0.0.12'; // What is the new version we wish to upgrade to + $db_version_old = '0.0.12'; // What version do we expect + $db_version_new = '0.0.13'; // What is the new version we wish to upgrade to $db_version_now = $setting->getValue('DB_VERSION'); // Our actual version installed // Upgrade specific variables $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DOUBLE(60,30) NULL DEFAULT '0'"; - $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.12' WHERE name = 'DB_VERSION'"; + $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.13' WHERE name = 'DB_VERSION'"; if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { // Run the upgrade From a7a731dcf1c8c33bdda3a3b8a17755171f631df8 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sat, 19 Jul 2014 09:10:21 +0200 Subject: [PATCH 12/24] [UPDATE] Working coin precision system * [ADDED] New option to coin class to change coin value precision * [UPDATE] SQL Transactions table from double to decimal(50,30) * [REMOVED] Admin setting for coin value precision * [UPDATE] JS files to honor coin precision --- cronjobs/pplns_payout.php | 4 ++-- cronjobs/pps_payout.php | 4 ++-- cronjobs/proportional_payout.php | 4 ++-- include/classes/coins/coin_base.class.php | 10 ++++++++++ include/classes/coins/coin_sha256d.class.php | 1 + include/classes/transaction.class.php | 3 ++- include/config/admin_settings.inc.php | 7 ------- include/pages/dashboard.inc.php | 2 +- include/smarty_globals.inc.php | 2 +- include/version.inc.php | 2 +- sql/000_base_structure.sql | 2 +- templates/bootstrap/dashboard/js/api.tpl | 10 +++++----- .../bootstrap/dashboard/round_statistics/pps/round.tpl | 10 +++++----- upgrade/definitions/0.0.12_to_0.0.13.inc.php | 4 ++-- 14 files changed, 35 insertions(+), 30 deletions(-) diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 5f1840b7b..832f6e59d 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -39,8 +39,8 @@ } // Fetch precision -$precision = $setting->getValue('system_coin_precision', 12); -$table_precision = $setting->getValue('system_coin_precision', 12) + 3; +$precision = $coin->getCoinValuePrevision(); +$table_precision = $coin->getCoinValuePrevision() + 3; $log->logDebug('Starting PPLNS payout process'); $count = 0; diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index deeeb095e..f35f15605 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -89,8 +89,8 @@ if (!empty($aAccountShares)) { // Runtime information for this payout - $precision = $setting->getValue('system_coin_precision', 12); - $table_precision = $setting->getValue('system_coin_precision', 12) + 3; + $precision = $coin->getCoinValuePrevision(); + $table_precision = $coin->getCoinValuePrevision() + 3; $log->logInfo('Runtime information for this payout'); $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')); diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index 95daf0ffb..faf4e53ae 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -39,8 +39,8 @@ } // Fetch precision -$precision = $setting->getValue('system_coin_precision', 12); -$table_precision = $setting->getValue('system_coin_precision', 12) + 3; +$precision = $coin->getCoinValuePrevision(); +$table_precision = $coin->getCoinValuePrevision() + 3; $count = 0; // Table header for account shares diff --git a/include/classes/coins/coin_base.class.php b/include/classes/coins/coin_base.class.php index b92428224..ab84c311a 100644 --- a/include/classes/coins/coin_base.class.php +++ b/include/classes/coins/coin_base.class.php @@ -15,6 +15,9 @@ 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 **/ @@ -22,6 +25,13 @@ 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 **/ diff --git a/include/classes/coins/coin_sha256d.class.php b/include/classes/coins/coin_sha256d.class.php index 1147770ef..2b8302636 100644 --- a/include/classes/coins/coin_sha256d.class.php +++ b/include/classes/coins/coin_sha256d.class.php @@ -8,6 +8,7 @@ **/ class Coin extends CoinBase { protected $target_bits = 32; + protected $coin_value_precision = 20; } ?> diff --git a/include/classes/transaction.class.php b/include/classes/transaction.class.php index 0b3abf3e2..194ca4867 100644 --- a/include/classes/transaction.class.php +++ b/include/classes/transaction.class.php @@ -16,7 +16,7 @@ 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->setting->getValue('system_coin_precision', 12), '.', ''); + $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("isisss", $account_id, $amount, $block_id, $type, $coin_address, $txid) && $stmt->execute()) { $this->insert_id = $stmt->insert_id; @@ -480,6 +480,7 @@ public function getMPQueue($limit=250) { $transaction->setNotification($notification); $transaction->setSetting($setting); $transaction->setDebug($debug); +$transaction->setCoin($coin); $transaction->setCoinAddress($coin_address); $transaction->setMysql($mysqli); $transaction->setConfig($config); diff --git a/include/config/admin_settings.inc.php b/include/config/admin_settings.inc.php index 32cd082e7..475c2448d 100644 --- a/include/config/admin_settings.inc.php +++ b/include/config/admin_settings.inc.php @@ -308,13 +308,6 @@ 'name' => 'system_error_email', 'value' => $setting->getValue('system_error_email'), 'tooltip' => 'The email address for system errors notifications, like cronjobs failures.' ); -$aSettings['system'][] = array( - 'display' => 'Coin Precision', 'type' => 'text', - 'size' => 5, - 'default' => '12', - 'name' => 'system_coin_precision', 'value' => $setting->getValue('system_coin_precision'), - 'tooltip' => 'How do we round any coin values throughout MPOS. Defaults to 12 digits.' -); $aSettings['system'][] = array( 'display' => 'Date format string', 'type' => 'text', 'size' => 25, diff --git a/include/pages/dashboard.inc.php b/include/pages/dashboard.inc.php index 519d76672..56747bea1 100644 --- a/include/pages/dashboard.inc.php +++ b/include/pages/dashboard.inc.php @@ -49,7 +49,7 @@ } // Make it available in Smarty - $smarty->assign('PRECISION', $setting->getValue('system_coin_precision', 12)); + $smarty->assign('PRECISION', $coin->getCoinValuePrevision()); $smarty->assign('BLOCKSFOUND', $aLastBlocks); $smarty->assign('DISABLED_DASHBOARD', $setting->getValue('disable_dashboard')); $smarty->assign('DISABLED_DASHBOARD_API', $setting->getValue('disable_dashboard_api')); diff --git a/include/smarty_globals.inc.php b/include/smarty_globals.inc.php index e2785a4eb..8cd45a776 100644 --- a/include/smarty_globals.inc.php +++ b/include/smarty_globals.inc.php @@ -177,7 +177,7 @@ case 'pps': $aGlobal['userdata']['pps']['unpaidshares'] = $statistics->getUserUnpaidPPSShares($_SESSION['USERDATA']['username'], $_SESSION['USERDATA']['id'], $setting->getValue('pps_last_share_id')); // We use coin precision + 8 to display PPS value - $aGlobal['ppsvalue'] = number_format($statistics->getPPSValue(), $setting->getValue('system_coin_precision', 12) + 8); + $aGlobal['ppsvalue'] = number_format($statistics->getPPSValue(), $coin->getCoinValuePrevision() + 8); $aGlobal['poolppsvalue'] = $aGlobal['ppsvalue'] * pow(2, $config['difficulty'] - 16); $aGlobal['userdata']['estimates'] = $statistics->getUserEstimates($aGlobal['userdata']['sharerate'], $aGlobal['userdata']['sharedifficulty'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees'], $aGlobal['ppsvalue']); break; diff --git a/include/version.inc.php b/include/version.inc.php index a4e0d533f..2b7ea18af 100644 --- a/include/version.inc.php +++ b/include/version.inc.php @@ -2,7 +2,7 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; define('MPOS_VERSION', '0.0.4'); -define('DB_VERSION', '0.0.12'); +define('DB_VERSION', '0.0.13'); define('CONFIG_VERSION', '0.0.8'); define('HASH_VERSION', 1); diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index f007ca55b..f4ddebcfa 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -226,7 +226,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( `account_id` int(255) unsigned NOT NULL, `type` varchar(25) DEFAULT NULL, `coin_address` varchar(255) DEFAULT NULL, - `amount` double(50,30) DEFAULT '0', + `amount` decimal(50,30) DEFAULT '0', `block_id` int(255) DEFAULT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `txid` varchar(256) DEFAULT NULL, diff --git a/templates/bootstrap/dashboard/js/api.tpl b/templates/bootstrap/dashboard/js/api.tpl index 07344d74b..9e4a9d542 100644 --- a/templates/bootstrap/dashboard/js/api.tpl +++ b/templates/bootstrap/dashboard/js/api.tpl @@ -141,11 +141,11 @@ $(document).ready(function(){ {/literal}{else}{literal} $('#b-ppsunpaid').html(number_format(data.getdashboarddata.data.personal.shares.unpaid)); $('#b-ppsdiff').html(number_format(data.getdashboarddata.data.personal.sharedifficulty, 2)); - $('#b-est1').html(number_format(data.getdashboarddata.data.personal.estimates.hours1, 12)); - $('#b-est24hours').html(number_format(data.getdashboarddata.data.personal.estimates.hours24, 12)); - $('#b-est7days').html(number_format(data.getdashboarddata.data.personal.estimates.days7, 12)); - $('#b-est14days').html(number_format(data.getdashboarddata.data.personal.estimates.days14, 12)); - $('#b-est30days').html(number_format(data.getdashboarddata.data.personal.estimates.days30, 12)); + $('#b-est1').html(number_format(data.getdashboarddata.data.personal.estimates.hours1, {/literal}{$PRECISION}{literal})); + $('#b-est24hours').html(number_format(data.getdashboarddata.data.personal.estimates.hours24, {/literal}{$PRECISION}{literal})); + $('#b-est7days').html(number_format(data.getdashboarddata.data.personal.estimates.days7, {/literal}{$PRECISION}{literal})); + $('#b-est14days').html(number_format(data.getdashboarddata.data.personal.estimates.days14, {/literal}{$PRECISION}{literal})); + $('#b-est30days').html(number_format(data.getdashboarddata.data.personal.estimates.days30, {/literal}{$PRECISION}{literal})); {/literal}{/if}{literal} {/literal}{if $GLOBAL.config.payout_system == 'pplns'}{literal} $('#b-pplns').html({/literal}{$GLOBAL.pplns.target}{literal}); diff --git a/templates/bootstrap/dashboard/round_statistics/pps/round.tpl b/templates/bootstrap/dashboard/round_statistics/pps/round.tpl index 14ab1584f..7fc95e6c5 100644 --- a/templates/bootstrap/dashboard/round_statistics/pps/round.tpl +++ b/templates/bootstrap/dashboard/round_statistics/pps/round.tpl @@ -7,7 +7,7 @@
-

{$GLOBAL.userdata.estimates.hours1|number_format:12}

+

{$GLOBAL.userdata.estimates.hours1|number_format:$PRECISION}

{$GLOBAL.config.currency} 1 Hour Estimated Earnings

@@ -22,7 +22,7 @@
-

{$GLOBAL.userdata.estimates.hours24|number_format:12}

+

{$GLOBAL.userdata.estimates.hours24|number_format:$PRECISION}

{$GLOBAL.config.currency} 24 Hour Estimated Earnings

@@ -37,7 +37,7 @@
-

{$GLOBAL.userdata.estimates.days7|number_format:12}

+

{$GLOBAL.userdata.estimates.days7|number_format:$PRECISION}

{$GLOBAL.config.currency} 7 Days Estimated Earnings

@@ -52,7 +52,7 @@
-

{$GLOBAL.userdata.estimates.days14|number_format:12}

+

{$GLOBAL.userdata.estimates.days14|number_format:$PRECISION}

{$GLOBAL.config.currency} 14 Days Estimated Earnings

@@ -67,7 +67,7 @@
-

{$GLOBAL.userdata.estimates.days30|number_format:12}

+

{$GLOBAL.userdata.estimates.days30|number_format:$PRECISION}

{$GLOBAL.config.currency} 30 Days Estimated Earnings

diff --git a/upgrade/definitions/0.0.12_to_0.0.13.inc.php b/upgrade/definitions/0.0.12_to_0.0.13.inc.php index b81b87ce3..6c198bb99 100644 --- a/upgrade/definitions/0.0.12_to_0.0.13.inc.php +++ b/upgrade/definitions/0.0.12_to_0.0.13.inc.php @@ -1,5 +1,5 @@ getValue('DB_VERSION'); // Our actual version installed // Upgrade specific variables - $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DOUBLE(60,30) NULL DEFAULT '0'"; + $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DECIMAL(50,30) NULL DEFAULT '0'"; $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.13' WHERE name = 'DB_VERSION'"; if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { From 2b49325ff547c880b5b4c6f9b200c1d01dcd5045 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 22 Jul 2014 10:37:55 +0200 Subject: [PATCH 13/24] [FIX] DB_VERSION to 0.0.14 after update --- upgrade/definitions/0.0.13_to_0.0.14.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade/definitions/0.0.13_to_0.0.14.inc.php b/upgrade/definitions/0.0.13_to_0.0.14.inc.php index 4ed45bb1d..7eb6ce211 100644 --- a/upgrade/definitions/0.0.13_to_0.0.14.inc.php +++ b/upgrade/definitions/0.0.13_to_0.0.14.inc.php @@ -10,7 +10,7 @@ function run_0014() { // Upgrade specific variables $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DECIMAL(50,30) NULL DEFAULT '0'"; - $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.13' WHERE name = 'DB_VERSION'"; + $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.14' WHERE name = 'DB_VERSION'"; if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { // Run the upgrade From 277c31af587e3847ef8350af8494ad109cc90481 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 25 Jul 2014 09:52:45 +0200 Subject: [PATCH 14/24] [FIX] Merge conflict with development --- include/version.inc.php | 2 +- ...{0.0.13_to_0.0.14.inc.php => 0.0.14_to_0.0.15.inc.php} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename upgrade/definitions/{0.0.13_to_0.0.14.inc.php => 0.0.14_to_0.0.15.inc.php} (83%) diff --git a/include/version.inc.php b/include/version.inc.php index 46e122fb9..1007908c0 100644 --- a/include/version.inc.php +++ b/include/version.inc.php @@ -2,7 +2,7 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; define('MPOS_VERSION', '0.0.4'); -define('DB_VERSION', '0.0.14'); +define('DB_VERSION', '0.0.15'); define('CONFIG_VERSION', '0.0.8'); define('HASH_VERSION', 1); diff --git a/upgrade/definitions/0.0.13_to_0.0.14.inc.php b/upgrade/definitions/0.0.14_to_0.0.15.inc.php similarity index 83% rename from upgrade/definitions/0.0.13_to_0.0.14.inc.php rename to upgrade/definitions/0.0.14_to_0.0.15.inc.php index 7eb6ce211..e1ddc4c7f 100644 --- a/upgrade/definitions/0.0.13_to_0.0.14.inc.php +++ b/upgrade/definitions/0.0.14_to_0.0.15.inc.php @@ -1,16 +1,16 @@ getValue('DB_VERSION'); // Our actual version installed // Upgrade specific variables $aSql[] = "ALTER TABLE " . $transaction->getTableName() . " CHANGE `amount` `amount` DECIMAL(50,30) NULL DEFAULT '0'"; - $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.14' WHERE name = 'DB_VERSION'"; + $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.15' WHERE name = 'DB_VERSION'"; if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { // Run the upgrade From a3cf5ecb2be0f3a983cd11d3ed6f227041084164 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 28 Aug 2014 15:41:15 +0200 Subject: [PATCH 15/24] Newbie hint to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61ea994b9..c031cc0af 100644 --- a/README.md +++ b/README.md @@ -1,7 +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 +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! **NOTE**: This project is still under development and commits are happening on a daily basis. From a6c587cefefe68cb97a4b738afe63307ff7efa86 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 29 Aug 2014 09:08:21 +0200 Subject: [PATCH 16/24] [RE-ADDED] Mobile detection in PHP * Allow users to define a seperate mobile template * Defaults to bootstrap * Disconnects desktop sites from mobile sites Fixes #2329 once merged --- include/autoloader.inc.php | 9 +- include/config/admin_settings.inc.php | 7 + include/lib/Mobile_Detect.php | 1248 +++++++++++++++++++++++++ 3 files changed, 1262 insertions(+), 2 deletions(-) create mode 100644 include/lib/Mobile_Detect.php diff --git a/include/autoloader.inc.php b/include/autoloader.inc.php index eb44f86f0..f67eeb75f 100644 --- a/include/autoloader.inc.php +++ b/include/autoloader.inc.php @@ -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); diff --git a/include/config/admin_settings.inc.php b/include/config/admin_settings.inc.php index 2ae6716e3..68ea79153 100644 --- a/include/config/admin_settings.inc.php +++ b/include/config/admin_settings.inc.php @@ -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 default theme used on your pool.' +); $aSettings['website'][] = array( 'display' => 'Website Design', 'type' => 'select', 'options' => $aDesigns, diff --git a/include/lib/Mobile_Detect.php b/include/lib/Mobile_Detect.php new file mode 100644 index 000000000..b6eb0da21 --- /dev/null +++ b/include/lib/Mobile_Detect.php @@ -0,0 +1,1248 @@ +, Nick Ilyin + * Original author: Victor Stanciu + * + * @license Code and contributions have 'MIT License' + * More details: https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt + * + * @link Homepage: http://mobiledetect.net + * GitHub Repo: https://github.com/serbanghita/Mobile-Detect + * Google Code: http://code.google.com/p/php-mobile-detect/ + * README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md + * HOWTO: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples + * + * @version 2.8.3 + */ + +class Mobile_Detect +{ + /** + * Mobile detection type. + * + * @deprecated since version 2.6.9 + */ + const DETECTION_TYPE_MOBILE = 'mobile'; + + /** + * Extended detection type. + * + * @deprecated since version 2.6.9 + */ + const DETECTION_TYPE_EXTENDED = 'extended'; + + /** + * A frequently used regular expression to extract version #s. + * + * @deprecated since version 2.6.9 + */ + const VER = '([\w._\+]+)'; + + /** + * Top-level device. + */ + const MOBILE_GRADE_A = 'A'; + + /** + * Mid-level device. + */ + const MOBILE_GRADE_B = 'B'; + + /** + * Low-level device. + */ + const MOBILE_GRADE_C = 'C'; + + /** + * Stores the version number of the current release. + */ + const VERSION = '2.8.3'; + + /** + * A type for the version() method indicating a string return value. + */ + const VERSION_TYPE_STRING = 'text'; + + /** + * A type for the version() method indicating a float return value. + */ + const VERSION_TYPE_FLOAT = 'float'; + + /** + * The User-Agent HTTP header is stored in here. + * @var string + */ + protected $userAgent = null; + + /** + * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE. + * @var array + */ + protected $httpHeaders = array(); + + /** + * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED. + * + * @deprecated since version 2.6.9 + * + * @var string + */ + protected $detectionType = self::DETECTION_TYPE_MOBILE; + + /** + * HTTP headers that trigger the 'isMobile' detection + * to be true. + * + * @var array + */ + protected static $mobileHeaders = array( + + 'HTTP_ACCEPT' => array('matches' => array( + // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/ + 'application/x-obml2d', + // BlackBerry devices. + 'application/vnd.rim.html', + 'text/vnd.wap.wml', + 'application/vnd.wap.xhtml+xml' + )), + 'HTTP_X_WAP_PROFILE' => null, + 'HTTP_X_WAP_CLIENTID' => null, + 'HTTP_WAP_CONNECTION' => null, + 'HTTP_PROFILE' => null, + // Reported by Opera on Nokia devices (eg. C3). + 'HTTP_X_OPERAMINI_PHONE_UA' => null, + 'HTTP_X_NOKIA_GATEWAY_ID' => null, + 'HTTP_X_ORANGE_ID' => null, + 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null, + 'HTTP_X_HUAWEI_USERID' => null, + // Reported by Windows Smartphones. + 'HTTP_UA_OS' => null, + // Reported by Verizon, Vodafone proxy system. + 'HTTP_X_MOBILE_GATEWAY' => null, + // Seend this on HTC Sensation. @ref: SensationXE_Beats_Z715e. + 'HTTP_X_ATT_DEVICEID' => null, + // Seen this on a HTC. + 'HTTP_UA_CPU' => array('matches' => array('ARM')), + ); + + /** + * List of mobile devices (phones). + * + * @var array + */ + protected static $phoneDevices = array( + 'iPhone' => '\biPhone.*(Mobile|PhoneGap)|\biPod', // |\biTunes + 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+', + 'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m', + 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile', + // @todo: Is 'Dell Streak' a tablet or a phone? ;) + 'Dell' => 'Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b', + 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925', + 'Samsung' => 'Samsung|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E', + 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802)', + 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i', + 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile', + // @ref: http://www.micromaxinfo.com/mobiles/smartphones + // Added because the codes might conflict with Acer Tablets. + 'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b', + 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ; @todo - complete the regex. + 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;) + // @ref: http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH) + // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android. + 'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790', + // @ref: http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones. + 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250', + 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)', + // Added simvalley mobile just for fun. They have some interesting devices. + // @ref: http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html + 'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b', + // @Tapatalk is a mobile app; @ref: http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039 + 'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser' + ); + + /** + * List of tablet devices. + * + * @var array + */ + protected static $tabletDevices = array( + 'iPad' => 'iPad|iPad.*Mobile', // @todo: check for mobile friendly emails topic. + 'NexusTablet' => 'Android.*Nexus[\s]+(7|10)|^.*Android.*Nexus(?:(?!Mobile).)*$', + 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-I9205|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705C|SM-T535|SM-T331', // SCH-P709|SCH-P729|SM-T2558 - Samsung Mega - treat them like a regular phone. + // @reference: http://www.labnol.org/software/kindle-user-agent-string/20378/ + 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE)\b', + // Only the Surface tablets with Windows RT are considered mobile. + // @ref: http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx + 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;', + // @ref: http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT + 'HPTablet' => 'HP Slate 7|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8', + // @note: watch out for PadFone, see #132 + 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|TX201LA', + 'BlackBerryTablet' => 'PlayBook|RIM Tablet', + 'HTCtablet' => 'HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200', + 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617', + 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2', + // @ref: http://www.acer.ro/ac/ro/RO/content/drivers + // @ref: http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer) + // @ref: http://us.acer.com/ac/en/US/content/group/tablets + // @note: Can conflict with Micromax and Motorola phones codes. + 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-830)\b|W3-810|\bA3-A10\b', + // @ref: http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/ + // @ref: http://us.toshiba.com/tablets/tablet-finder + // @ref: http://www.toshiba.co.jp/regza/tablet/ + 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO', + // @ref: http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html + // @ref: http://www.lg.com/us/tablets + 'LGTablet' => '\bL-06C|LG-V900|LG-V500|LG-V909|LG-V500|LG-V510|LG-VK810\b', + 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b', + // Prestigio Tablets http://www.prestigio.com/support + 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD', + // @ref: http://support.lenovo.com/en_GB/downloads/default.page?# + 'LenovoTablet' => 'IdeaTab|ThinkPad([ ]+)?Tablet|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A1000|A2107|A2109|A1107|B6000|B8000|B8080-F)', + // @ref: http://www.yarvik.com/en/matrix/tablets/ + 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b', + 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB', + 'ArnovaTablet' => 'AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT', + // http://www.intenso.de/kategorie_en.php?kategorie=33 + // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate + 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab', + // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/ + 'IRUTablet' => 'M702pro', + 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b', + // @ref: http://www.e-boda.ro/tablete-pc.html + 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)', + // @ref: http://www.allview.ro/produse/droseries/lista-tablete-pc/ + 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)', + // @reference: http://wiki.archosfans.com/index.php?title=Main_Page + 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|ARCHOS 101G10|Archos 101 Neon', + // @ref: http://www.ainol.com/plugin.php?identifier=ainol&module=product + 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark', + // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER + // @ref: Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser + // @ref: http://www.sony.jp/support/tablet/ + 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551', + // @ref: db + http://www.cube-tablet.com/buy-products.html + 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT', + // @ref: http://www.cobyusa.com/?p=pcat&pcat_id=3001 + 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010', + // @ref: http://www.match.net.cn/products.asp + 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733', + // @ref: http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets) + // @ref: http://www.imp3.net/14/show.php?itemid=20454 + 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)', + // @ref: http://www.rock-chips.com/index.php?do=prod&pid=2 + 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A', + // @ref: http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/ + 'FlyTablet' => 'IQ310|Fly Vision', + // @ref: http://www.bqreaders.com/gb/tablets-prices-sale.html + 'bqTablet' => 'bq.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant)|Maxwell.*Lite|Maxwell.*Plus', + // @ref: http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290 + // @ref: http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets) + 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim', + // Nec or Medias Tab + 'NecTablet' => '\bN-06D|\bN-08D', + // Pantech Tablets: http://www.pantechusa.com/phones/ + 'PantechTablet' => 'Pantech.*P4100', + // Broncho Tablets: http://www.broncho.cn/ (hard to find) + 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)', + // @ref: http://versusuk.com/support.html + 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b', + // @ref: http://www.zync.in/index.php/our-products/tablet-phablets + 'ZyncTablet' => 'z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900', + // @ref: http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/ + 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA', + // @ref: https://www.nabitablet.com/ + 'NabiTablet' => 'Android.*\bNabi', + 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build', + // French Danew Tablets http://www.danew.com/produits-tablette.php + 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b', + // Texet Tablets and Readers http://www.texet.ru/tablet/ + 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE', + // @note: Avoid detecting 'PLAYSTATION 3' as mobile. + 'PlaystationTablet' => 'Playstation.*(Portable|Vita)', + // @ref: http://www.trekstor.de/surftabs.html + 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A', + // @ref: http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets + 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b', + // @ref: http://www.advandigital.com/index.php?link=content-product&jns=JP001 + // @Note: because of the short codenames we have to include whitespaces to reduce the possible conflicts. + 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ', + // @ref: http://www.danytech.com/category/tablet-pc + 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1', + // @ref: http://www.galapad.net/product.html + 'GalapadTablet' => 'Android.*\bG1\b', + // @ref: http://www.micromaxinfo.com/tablet/funbook + 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b', + // http://www.karbonnmobiles.com/products_tablet.php + 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b', + // @ref: http://www.myallfine.com/Products.asp + 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide', + // @ref: http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr= + 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b', + // @ref: http://www.yonesnav.com/products/products.php + 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026', + // @ref: http://www.cjshowroom.com/eproducts.aspx?classcode=004001001 + // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html) + 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503', + // @ref: http://www.gloryunion.cn/products.asp + // @ref: http://www.allwinnertech.com/en/apply/mobile.html + // @ref: http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB) + // @todo: Softwiner tablets? + // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions. + 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G + // @ref: http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118 + 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10', + // @ref: http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/ + // @todo: add more tests. + 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)', + // @ref: http://hclmetablet.com/India/index.php + 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync', + // @ref: http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html + 'DPSTablet' => 'DPS Dream 9|DPS Dual 7', + // @ref: http://www.visture.com/index.asp + 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10', + // @ref: http://www.mijncresta.nl/tablet + 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989', + // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309 + 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b', + // Concorde tab + 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan', + // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/ + 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042', + // Modecom Tablets - http://www.modecom.eu/tablets/portal/ + 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003', + // Vonino Tablets - http://www.vonino.eu/tablets + 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b', + // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0 + 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1', + // Storex Tablets - http://storex.fr/espace_client/support.html + // @note: no need to add all the tablet codes since they are guided by the first regex. + 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab', + // Generic Vodafone tablets. + 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10', + // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb + // Aka: http://www.essentielb.fr/ + 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2', + // Ross & Moor - http://ross-moor.ru/ + 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711', + // i-mobile http://product.i-mobilephone.com/Mobile_Device + 'iMobileTablet' => 'i-mobile i-note', + // @ref: http://www.tolino.de/de/vergleichen/ + 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine', + // AudioSonic - a Kmart brand + // http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72¤tPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1 + 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b', + // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/ + // @todo: add them gradually to avoid conflicts. + 'AMPETablet' => 'Android.* A78 ', + // Skk Mobile - http://skkmobile.com.ph/product_tablets.php + 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)', + // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1 + 'TecnoTablet' => 'TECNO P9', + // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3 + 'JXDTablet' => 'Android.*\b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b', + // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/ + 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)', + // http://www.intracon.eu/tablet + 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10', + // http://www.xoro.de/produkte/ + // @note: Might be the same brand with 'Simply tablets' + 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151', + // http://www1.viewsonic.com/products/computing/tablets/ + 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a', + // http://www.odys.de/web/internet-tablet_en.html + 'OdysTablet' => 'LOOX|XENO10|ODYS Space', + // http://www.captiva-power.de/products.html#tablets-en + 'CaptivaTablet' => 'CAPTIVA PAD', + // IconBIT - http://www.iconbit.com/products/tablets/ + 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S', + // @ref: http://www.tesco.com/direct/hudl/ + 'Hudl' => 'Hudl HT7S3', + // @ref: http://www.telstra.com.au/home-phone/thub-2/ + 'TelstraTablet' => 'T-Hub2', + 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4', + ); + + /** + * List of mobile Operating Systems. + * + * @var array + */ + protected static $operatingSystems = array( + 'AndroidOS' => 'Android', + 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os', + 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino', + 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b', + // @reference: http://en.wikipedia.org/wiki/Windows_Mobile + 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;', + // @reference: http://en.wikipedia.org/wiki/Windows_Phone + // http://wifeng.cn/?r=blog&a=view&id=106 + // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx + 'WindowsPhoneOS' => 'Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7', + 'iOS' => '\biPhone.*Mobile|\biPod|\biPad', + // http://en.wikipedia.org/wiki/MeeGo + // @todo: research MeeGo in UAs + 'MeeGoOS' => 'MeeGo', + // http://en.wikipedia.org/wiki/Maemo + // @todo: research Maemo in UAs + 'MaemoOS' => 'Maemo', + 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135 + 'webOS' => 'webOS|hpwOS', + 'badaOS' => '\bBada\b', + 'BREWOS' => 'BREW', + ); + + /** + * List of mobile User Agents. + * + * @var array + */ + protected static $browsers = array( + // @reference: https://developers.google.com/chrome/mobile/docs/user-agent + 'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?', + 'Dolfin' => '\bDolfin\b', + 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+|Coast/[0-9.]+', + 'Skyfire' => 'Skyfire', + 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+ + 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile', + 'Bolt' => 'bolt', + 'TeaShark' => 'teashark', + 'Blazer' => 'Blazer', + // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3 + 'Safari' => 'Version.*Mobile.*Safari|Safari.*Mobile', + // @ref: http://en.wikipedia.org/wiki/Midori_(web_browser) + //'Midori' => 'midori', + 'Tizen' => 'Tizen', + 'UCBrowser' => 'UC.*Browser|UCWEB', + // @ref: https://github.com/serbanghita/Mobile-Detect/issues/7 + 'DiigoBrowser' => 'DiigoBrowser', + // http://www.puffinbrowser.com/index.php + 'Puffin' => 'Puffin', + // @ref: http://mercury-browser.com/index.html + 'Mercury' => '\bMercury\b', + // @reference: http://en.wikipedia.org/wiki/Minimo + // http://en.wikipedia.org/wiki/Vision_Mobile_Browser + 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger' + ); + + /** + * Utilities. + * + * @var array + */ + protected static $utilities = array( + // Experimental. When a mobile device wants to switch to 'Desktop Mode'. + // @ref: http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/ + // @ref: https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011 + 'DesktopMode' => 'WPDesktop', + 'TV' => 'SonyDTV|HbbTV', // experimental + 'WebKit' => '(webkit)[ /]([\w.]+)', + 'Bot' => 'Googlebot|DoCoMo|YandexBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|facebookexternalhit', + 'MobileBot' => 'Googlebot-Mobile|DoCoMo|YahooSeeker/M1A1-R2D2', + // @todo: Include JXD consoles. + 'Console' => '\b(Nintendo|Nintendo WiiU|PLAYSTATION|Xbox)\b', + 'Watch' => 'SM-V700', + ); + + /** + * All possible HTTP headers that represent the + * User-Agent string. + * + * @var array + */ + protected static $uaHttpHeaders = array( + // The default User-Agent string. + 'HTTP_USER_AGENT', + // Header can occur on devices using Opera Mini. + 'HTTP_X_OPERAMINI_PHONE_UA', + // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/ + 'HTTP_X_DEVICE_USER_AGENT', + 'HTTP_X_ORIGINAL_USER_AGENT', + 'HTTP_X_SKYFIRE_PHONE', + 'HTTP_X_BOLT_PHONE_UA', + 'HTTP_DEVICE_STOCK_UA', + 'HTTP_X_UCBROWSER_DEVICE_UA' + ); + + /** + * The individual segments that could exist in a User-Agent string. VER refers to the regular + * expression defined in the constant self::VER. + * + * @var array + */ + protected static $properties = array( + + // Build + 'Mobile' => 'Mobile/[VER]', + 'Build' => 'Build/[VER]', + 'Version' => 'Version/[VER]', + 'VendorID' => 'VendorID/[VER]', + + // Devices + 'iPad' => 'iPad.*CPU[a-z ]+[VER]', + 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]', + 'iPod' => 'iPod.*CPU[a-z ]+[VER]', + //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'), + 'Kindle' => 'Kindle/[VER]', + + // Browser + 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'), + 'Coast' => array('Coast/[VER]'), + 'Dolfin' => 'Dolfin/[VER]', + // @reference: https://developer.mozilla.org/en-US/docs/User_Agent_Strings_Reference + 'Firefox' => 'Firefox/[VER]', + 'Fennec' => 'Fennec/[VER]', + // @reference: http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx + 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];'), + // http://en.wikipedia.org/wiki/NetFront + 'NetFront' => 'NetFront/[VER]', + 'NokiaBrowser' => 'NokiaBrowser/[VER]', + 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ), + 'Opera Mini' => 'Opera Mini/[VER]', + 'Opera Mobi' => 'Version/[VER]', + 'UC Browser' => 'UC Browser[VER]', + 'MQQBrowser' => 'MQQBrowser/[VER]', + 'MicroMessenger' => 'MicroMessenger/[VER]', + // @note: Safari 7534.48.3 is actually Version 5.1. + // @note: On BlackBerry the Version is overwriten by the OS. + 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ), + 'Skyfire' => 'Skyfire/[VER]', + 'Tizen' => 'Tizen/[VER]', + 'Webkit' => 'webkit[ /][VER]', + + // Engine + 'Gecko' => 'Gecko/[VER]', + 'Trident' => 'Trident/[VER]', + 'Presto' => 'Presto/[VER]', + + // OS + 'iOS' => ' \bOS\b [VER] ', + 'Android' => 'Android [VER]', + 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'), + 'BREW' => 'BREW [VER]', + 'Java' => 'Java/[VER]', + // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx + // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases + 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'), + 'Windows Phone' => 'Windows Phone [VER]', + 'Windows CE' => 'Windows CE/[VER]', + // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd + 'Windows NT' => 'Windows NT [VER]', + 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'), + 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'), + ); + + /** + * Construct an instance of this class. + * + * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored. + * If left empty, will use the global _SERVER['HTTP_*'] vars instead. + * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT + * from the $headers array instead. + */ + public function __construct( + array $headers = null, + $userAgent = null + ){ + $this->setHttpHeaders($headers); + $this->setUserAgent($userAgent); + } + + /** + * Get the current script version. + * This is useful for the demo.php file, + * so people can check on what version they are testing + * for mobile devices. + * + * @return string The version number in semantic version format. + */ + public static function getScriptVersion() + { + return self::VERSION; + } + + /** + * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers. + * + * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract + * the headers. The default null is left for backwards compatibilty. + */ + public function setHttpHeaders($httpHeaders = null) + { + //use global _SERVER if $httpHeaders aren't defined + if (!is_array($httpHeaders) || !count($httpHeaders)) { + $httpHeaders = $_SERVER; + } + + //clear existing headers + $this->httpHeaders = array(); + + //Only save HTTP headers. In PHP land, that means only _SERVER vars that + //start with HTTP_. + foreach ($httpHeaders as $key => $value) { + if (substr($key,0,5) == 'HTTP_') { + $this->httpHeaders[$key] = $value; + } + } + } + + /** + * Retrieves the HTTP headers. + * + * @return array + */ + public function getHttpHeaders() + { + return $this->httpHeaders; + } + + /** + * Retrieves a particular header. If it doesn't exist, no exception/error is caused. + * Simply null is returned. + * + * @param string $header The name of the header to retrieve. Can be HTTP compliant such as + * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the + * all-caps, HTTP_ prefixed, underscore seperated awesomeness. + * + * @return string|null The value of the header. + */ + public function getHttpHeader($header) + { + //are we using PHP-flavored headers? + if (strpos($header, '_') === false) { + $header = str_replace('-', '_', $header); + $header = strtoupper($header); + } + + //test the alternate, too + $altHeader = 'HTTP_' . $header; + + //Test both the regular and the HTTP_ prefix + if (isset($this->httpHeaders[$header])) { + return $this->httpHeaders[$header]; + } elseif (isset($this->httpHeaders[$altHeader])) { + return $this->httpHeaders[$altHeader]; + } + + return null; + } + + public function getMobileHeaders() + { + return self::$mobileHeaders; + } + + /** + * Get all possible HTTP headers that + * can contain the User-Agent string. + * + * @return array List of HTTP headers. + */ + public function getUaHttpHeaders() + { + return self::$uaHttpHeaders; + } + + /** + * Set the User-Agent to be used. + * + * @param string $userAgent The user agent string to set. + * + * @return string|null + */ + public function setUserAgent($userAgent = null) + { + if (!empty($userAgent)) { + return $this->userAgent = $userAgent; + } else { + + $this->userAgent = null; + + foreach($this->getUaHttpHeaders() as $altHeader){ + if(!empty($this->httpHeaders[$altHeader])){ // @todo: should use getHttpHeader(), but it would be slow. (Serban) + $this->userAgent .= $this->httpHeaders[$altHeader] . " "; + } + } + + return $this->userAgent = (!empty($this->userAgent) ? trim($this->userAgent) : null); + + } + } + + /** + * Retrieve the User-Agent. + * + * @return string|null The user agent if it's set. + */ + public function getUserAgent() + { + return $this->userAgent; + } + + /** + * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or + * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set. + * + * @deprecated since version 2.6.9 + * + * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default + * parameter is null which will default to self::DETECTION_TYPE_MOBILE. + */ + public function setDetectionType($type = null) + { + if ($type === null) { + $type = self::DETECTION_TYPE_MOBILE; + } + + if ($type != self::DETECTION_TYPE_MOBILE && $type != self::DETECTION_TYPE_EXTENDED) { + return; + } + + $this->detectionType = $type; + } + + /** + * Retrieve the list of known phone devices. + * + * @return array List of phone devices. + */ + public static function getPhoneDevices() + { + return self::$phoneDevices; + } + + /** + * Retrieve the list of known tablet devices. + * + * @return array List of tablet devices. + */ + public static function getTabletDevices() + { + return self::$tabletDevices; + } + + /** + * Alias for getBrowsers() method. + * + * @return array List of user agents. + */ + public static function getUserAgents() + { + return self::getBrowsers(); + } + + /** + * Retrieve the list of known browsers. Specifically, the user agents. + * + * @return array List of browsers / user agents. + */ + public static function getBrowsers() + { + return self::$browsers; + } + + /** + * Retrieve the list of known utilities. + * + * @return array List of utilities. + */ + public static function getUtilities() + { + return self::$utilities; + } + + /** + * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*(). + * + * @deprecated since version 2.6.9 + * + * @return array All the rules (but not extended). + */ + public static function getMobileDetectionRules() + { + static $rules; + + if (!$rules) { + $rules = array_merge( + self::$phoneDevices, + self::$tabletDevices, + self::$operatingSystems, + self::$browsers + ); + } + + return $rules; + + } + + /** + * Method gets the mobile detection rules + utilities. + * The reason this is separate is because utilities rules + * don't necessary imply mobile. This method is used inside + * the new $detect->is('stuff') method. + * + * @deprecated since version 2.6.9 + * + * @return array All the rules + extended. + */ + public function getMobileDetectionRulesExtended() + { + static $rules; + + if (!$rules) { + // Merge all rules together. + $rules = array_merge( + self::$phoneDevices, + self::$tabletDevices, + self::$operatingSystems, + self::$browsers, + self::$utilities + ); + } + + return $rules; + } + + /** + * Retrieve the current set of rules. + * + * @deprecated since version 2.6.9 + * + * @return array + */ + public function getRules() + { + if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) { + return self::getMobileDetectionRulesExtended(); + } else { + return self::getMobileDetectionRules(); + } + } + + /** + * Retrieve the list of mobile operating systems. + * + * @return array The list of mobile operating systems. + */ + public static function getOperatingSystems() + { + return self::$operatingSystems; + } + + /** + * Check the HTTP headers for signs of mobile. + * This is the fastest mobile check possible; it's used + * inside isMobile() method. + * + * @return bool + */ + public function checkHttpHeadersForMobile() + { + + foreach($this->getMobileHeaders() as $mobileHeader => $matchType){ + if( isset($this->httpHeaders[$mobileHeader]) ){ + if( is_array($matchType['matches']) ){ + foreach($matchType['matches'] as $_match){ + if( strpos($this->httpHeaders[$mobileHeader], $_match) !== false ){ + return true; + } + } + return false; + } else { + return true; + } + } + } + + return false; + + } + + /** + * Magic overloading method. + * + * @method boolean is[...]() + * @param string $name + * @param array $arguments + * @return mixed + * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is' + */ + public function __call($name, $arguments) + { + //make sure the name starts with 'is', otherwise + if (substr($name, 0, 2) != 'is') { + throw new BadMethodCallException("No such method exists: $name"); + } + + $this->setDetectionType(self::DETECTION_TYPE_MOBILE); + + $key = substr($name, 2); + + return $this->matchUAAgainstKey($key); + } + + /** + * Find a detection rule that matches the current User-agent. + * + * @param null $userAgent deprecated + * @return boolean + */ + protected function matchDetectionRulesAgainstUA($userAgent = null) + { + // Begin general search. + foreach ($this->getRules() as $_regex) { + if (empty($_regex)) { + continue; + } + if ($this->match($_regex, $userAgent)) { + return true; + } + } + + return false; + } + + /** + * Search for a certain key in the rules array. + * If the key is found the try to match the corresponding + * regex agains the User-Agent. + * + * @param string $key + * @param null $userAgent deprecated + * @return mixed + */ + protected function matchUAAgainstKey($key, $userAgent = null) + { + // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc. + $key = strtolower($key); + + //change the keys to lower case + $_rules = array_change_key_case($this->getRules()); + + if (array_key_exists($key, $_rules)) { + if (empty($_rules[$key])) { + return null; + } + + return $this->match($_rules[$key], $userAgent); + } + + return false; + } + + /** + * Check if the device is mobile. + * Returns true if any type of mobile device detected, including special ones + * @param null $userAgent deprecated + * @param null $httpHeaders deprecated + * @return bool + */ + public function isMobile($userAgent = null, $httpHeaders = null) + { + + if ($httpHeaders) { + $this->setHttpHeaders($httpHeaders); + } + + if ($userAgent) { + $this->setUserAgent($userAgent); + } + + $this->setDetectionType(self::DETECTION_TYPE_MOBILE); + + if ($this->checkHttpHeadersForMobile()) { + return true; + } else { + return $this->matchDetectionRulesAgainstUA(); + } + + } + + /** + * Check if the device is a tablet. + * Return true if any type of tablet device is detected. + * + * @param string $userAgent deprecated + * @param array $httpHeaders deprecated + * @return bool + */ + public function isTablet($userAgent = null, $httpHeaders = null) + { + $this->setDetectionType(self::DETECTION_TYPE_MOBILE); + + foreach (self::$tabletDevices as $_regex) { + if ($this->match($_regex, $userAgent)) { + return true; + } + } + + return false; + } + + /** + * This method checks for a certain property in the + * userAgent. + * @todo: The httpHeaders part is not yet used. + * + * @param string $key + * @param string $userAgent deprecated + * @param string $httpHeaders deprecated + * @return bool|int|null + */ + public function is($key, $userAgent = null, $httpHeaders = null) + { + // Set the UA and HTTP headers only if needed (eg. batch mode). + if ($httpHeaders) { + $this->setHttpHeaders($httpHeaders); + } + + if ($userAgent) { + $this->setUserAgent($userAgent); + } + + $this->setDetectionType(self::DETECTION_TYPE_EXTENDED); + + return $this->matchUAAgainstKey($key); + } + + /** + * Some detection rules are relative (not standard), + * because of the diversity of devices, vendors and + * their conventions in representing the User-Agent or + * the HTTP headers. + * + * This method will be used to check custom regexes against + * the User-Agent string. + * + * @param $regex + * @param string $userAgent + * @return bool + * + * @todo: search in the HTTP headers too. + */ + public function match($regex, $userAgent = null) + { + // Escape the special character which is the delimiter. + $regex = str_replace('/', '\/', $regex); + + return (bool) preg_match('/'.$regex.'/is', (!empty($userAgent) ? $userAgent : $this->userAgent)); + } + + /** + * Get the properties array. + * + * @return array + */ + public static function getProperties() + { + return self::$properties; + } + + /** + * Prepare the version number. + * + * @todo Remove the error supression from str_replace() call. + * + * @param string $ver The string version, like "2.6.21.2152"; + * + * @return float + */ + public function prepareVersionNo($ver) + { + $ver = str_replace(array('_', ' ', '/'), '.', $ver); + $arrVer = explode('.', $ver, 2); + + if (isset($arrVer[1])) { + $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions. + } + + return (float) implode('.', $arrVer); + } + + /** + * Check the version of the given property in the User-Agent. + * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31) + * + * @param string $propertyName The name of the property. See self::getProperties() array + * keys for all possible properties. + * @param string $type Either self::VERSION_TYPE_STRING to get a string value or + * self::VERSION_TYPE_FLOAT indicating a float value. This parameter + * is optional and defaults to self::VERSION_TYPE_STRING. Passing an + * invalid parameter will default to the this type as well. + * + * @return string|float The version of the property we are trying to extract. + */ + public function version($propertyName, $type = self::VERSION_TYPE_STRING) + { + if (empty($propertyName)) { + return false; + } + + //set the $type to the default if we don't recognize the type + if ($type != self::VERSION_TYPE_STRING && $type != self::VERSION_TYPE_FLOAT) { + $type = self::VERSION_TYPE_STRING; + } + + $properties = self::getProperties(); + + // Check if the property exists in the properties array. + if (array_key_exists($propertyName, $properties)) { + + // Prepare the pattern to be matched. + // Make sure we always deal with an array (string is converted). + $properties[$propertyName] = (array) $properties[$propertyName]; + + foreach ($properties[$propertyName] as $propertyMatchString) { + + $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString); + + // Escape the special character which is the delimiter. + $propertyPattern = str_replace('/', '\/', $propertyPattern); + + // Identify and extract the version. + preg_match('/'.$propertyPattern.'/is', $this->userAgent, $match); + + if (!empty($match[1])) { + $version = ( $type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1] ); + + return $version; + } + + } + + } + + return false; + } + + /** + * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants. + * + * @return string One of the self::MOBILE_GRADE_* constants. + */ + public function mobileGrade() + { + $isMobile = $this->isMobile(); + + if ( + // Apple iOS 3.2-5.1 - Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3), iPad 3 (5.1), original iPhone (3.1), iPhone 3 (3.2), 3GS (4.3), 4 (4.3 / 5.0), and 4S (5.1) + $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)>=4.3 || + $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)>=3.1 || + $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)>=3.1 || + + // Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5) + // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM + // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices + // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7 + ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) || + + // Windows Phone 7-7.5 - Tested on the HTC Surround (7.0) HTC Trophy (7.5), LG-E900 (7.5), Nokia Lumia 800 + $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT)>=7.0 || + + // Blackberry 7 - Tested on BlackBerry Torch 9810 + // Blackberry 6.0 - Tested on the Torch 9800 and Style 9670 + $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=6.0 || + // Blackberry Playbook (1.0-2.0) - Tested on PlayBook + $this->match('Playbook.*Tablet') || + + // Palm WebOS (1.4-2.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0) + ( $this->version('webOS', self::VERSION_TYPE_FLOAT)>=1.4 && $this->match('Palm|Pre|Pixi') ) || + // Palm WebOS 3.0 - Tested on HP TouchPad + $this->match('hp.*TouchPad') || + + // Firefox Mobile (12 Beta) - Tested on Android 2.3 device + ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=12 ) || + + // Chrome for Android - Tested on Android 4.0, 4.1 device + ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=4.0 ) || + + // Skyfire 4.1 - Tested on Android 2.3 device + ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT)>=4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) || + + // Opera Mobile 11.5-12: Tested on Android 2.3 + ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>11 && $this->is('AndroidOS') ) || + + // Meego 1.2 - Tested on Nokia 950 and N9 + $this->is('MeeGoOS') || + + // Tizen (pre-release) - Tested on early hardware + $this->is('Tizen') || + + // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser + // @todo: more tests here! + $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT)>=2.0 || + + // UC Browser - Tested on Android 2.3 device + ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) || + + // Kindle 3 and Fire - Tested on the built-in WebKit browser for each + ( $this->match('Kindle Fire') || + $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT)>=3.0 ) || + + // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet + $this->is('AndroidOS') && $this->is('NookTablet') || + + // Chrome Desktop 11-21 - Tested on OS X 10.7 and Windows 7 + $this->version('Chrome', self::VERSION_TYPE_FLOAT)>=11 && !$isMobile || + + // Safari Desktop 4-5 - Tested on OS X 10.7 and Windows 7 + $this->version('Safari', self::VERSION_TYPE_FLOAT)>=5.0 && !$isMobile || + + // Firefox Desktop 4-13 - Tested on OS X 10.7 and Windows 7 + $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=4.0 && !$isMobile || + + // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7 + $this->version('MSIE', self::VERSION_TYPE_FLOAT)>=7.0 && !$isMobile || + + // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7 + // @reference: http://my.opera.com/community/openweb/idopera/ + $this->version('Opera', self::VERSION_TYPE_FLOAT)>=10 && !$isMobile + + ){ + return self::MOBILE_GRADE_A; + } + + if ( + $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 || + $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<3.1 || + $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)<3.1 || + + // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770 + $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 || + + //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3 + ( $this->version('Opera Mini', self::VERSION_TYPE_FLOAT)>=5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT)<=6.5 && + ($this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 || $this->is('iOS')) ) || + + // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1) + $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') || + + // @todo: report this (tested on Nokia N71) + $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>=11 && $this->is('SymbianOS') + ){ + return self::MOBILE_GRADE_B; + } + + if ( + // Blackberry 4.x - Tested on the Curve 8330 + $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<5.0 || + // Windows Mobile - Tested on the HTC Leo (WinMo 5.2) + $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT)<=5.2 + + ){ + return self::MOBILE_GRADE_C; + } + + //All older smartphone platforms and featurephones - Any device that doesn't support media queries + //will receive the basic, C grade experience. + return self::MOBILE_GRADE_C; + } +} From 67066efd7a5512e311ec102b013d4d8a4246a3b9 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 29 Aug 2014 09:10:21 +0200 Subject: [PATCH 17/24] [FIX] Description --- include/config/admin_settings.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/admin_settings.inc.php b/include/config/admin_settings.inc.php index 68ea79153..3c0b525d1 100644 --- a/include/config/admin_settings.inc.php +++ b/include/config/admin_settings.inc.php @@ -74,7 +74,7 @@ 'options' => $aThemes, 'default' => 'bootstrap', 'name' => 'website_mobile_theme', 'value' => $setting->getValue('website_mobile_theme'), - 'tooltip' => 'The default theme used on your pool.' + 'tooltip' => 'The mobile theme used on your pool.' ); $aSettings['website'][] = array( 'display' => 'Website Design', 'type' => 'select', From 8e2dd34f9c0d45d204415c63267ef6aa3ea13cab Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 29 Aug 2014 12:27:13 +0200 Subject: [PATCH 18/24] [UPDATE] Remove fees from templates if disabled via config --- templates/bootstrap/account/edit/cashout.tpl | 2 +- templates/bootstrap/account/edit/detail.tpl | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/templates/bootstrap/account/edit/cashout.tpl b/templates/bootstrap/account/edit/cashout.tpl index 17ab106f4..6be309c8f 100644 --- a/templates/bootstrap/account/edit/cashout.tpl +++ b/templates/bootstrap/account/edit/cashout.tpl @@ -13,7 +13,7 @@

- Please note: a {if $GLOBAL.config.txfee_manual > 0.00001}{$GLOBAL.config.txfee_manual}{else}{$GLOBAL.config.txfee_manual|number_format:"8"}{/if} {$GLOBAL.config.currency} transaction will apply when processing "On-Demand" manual payments + {if $GLOBAL.config.txfee_manual > 0}Please note: a {if $GLOBAL.config.txfee_manual > 0.00001}{$GLOBAL.config.txfee_manual}{else}{$GLOBAL.config.txfee_manual|number_format:"8"}{/if} {$GLOBAL.config.currency} transaction will apply when processing "On-Demand" manual payments {/if}

Minimum Cashout: {$GLOBAL.config.mp_threshold} {$GLOBAL.config.currency} diff --git a/templates/bootstrap/account/edit/detail.tpl b/templates/bootstrap/account/edit/detail.tpl index a59574b1c..b13e3672f 100644 --- a/templates/bootstrap/account/edit/detail.tpl +++ b/templates/bootstrap/account/edit/detail.tpl @@ -50,7 +50,7 @@


- {$GLOBAL.config.ap_threshold.min} - {$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout. A {if $GLOBAL.config.txfee_auto > 0.00001}{$GLOBAL.config.txfee_auto}{else}{$GLOBAL.config.txfee_auto|number_format:"8"}{/if} {$GLOBAL.config.currency} TX fee will apply + {$GLOBAL.config.ap_threshold.min} - {$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout.{if $GLOBAL.config.txfee_auto > 0} A {if $GLOBAL.config.txfee_auto > 0.00001}{$GLOBAL.config.txfee_auto}{else}{$GLOBAL.config.txfee_auto|number_format:"8"}{/if} {$GLOBAL.config.currency} TX fee will apply {/if}
@@ -66,7 +66,6 @@ The 4 digit PIN you chose when registering
-
From 939cb805c86e2845281205b450a80a57cab02e2c Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Fri, 29 Aug 2014 12:28:34 +0200 Subject: [PATCH 19/24] [FIX] Properly detect mobile --- include/autoloader.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/autoloader.inc.php b/include/autoloader.inc.php index f67eeb75f..8f0e6e6e4 100644 --- a/include/autoloader.inc.php +++ b/include/autoloader.inc.php @@ -37,7 +37,7 @@ require_once(INCLUDE_DIR . '/lib/Mobile_Detect.php'); $detect = new Mobile_Detect; -if (!$detect->isMobile()) { +if ($detect->isMobile()) { $theme = $setting->getValue('website_mobile_theme', 'bootstrap'); } else if ( PHP_SAPI == 'cli') { // Create a new compile folder just for crons From 8cf7c9912434a82c53694306916495343a93577a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 1 Sep 2014 20:15:33 +0200 Subject: [PATCH 20/24] [FIX] Added 0.0.14 to base structure --- sql/000_base_structure.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index b9887a519..78d65ce86 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -144,7 +144,7 @@ CREATE TABLE IF NOT EXISTS `settings` ( UNIQUE KEY `setting` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.13'); +INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.14'); CREATE TABLE IF NOT EXISTS `shares` ( `id` bigint(30) NOT NULL AUTO_INCREMENT, @@ -242,7 +242,7 @@ CREATE TABLE IF NOT EXISTS `transactions` ( CREATE TABLE `statistics_users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `account_id` int(11) NOT NULL, - `hashrate` int(11) NOT NULL, + `hashrate` bigint(20) unsigned NOT NULL, `workers` int(11) NOT NULL, `sharerate` float NOT NULL, `timestamp` int(11) NOT NULL, From 4147ef583b54aea89b7ee4036d228516923aa0c1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 1 Sep 2014 20:23:40 +0200 Subject: [PATCH 21/24] [FIX] New base SQL version --- sql/000_base_structure.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index 125b4c661..aeed52f6c 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -144,7 +144,7 @@ CREATE TABLE IF NOT EXISTS `settings` ( UNIQUE KEY `setting` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.14'); +INSERT INTO `settings` (`name`, `value`) VALUES ('DB_VERSION', '0.0.15'); CREATE TABLE IF NOT EXISTS `shares` ( `id` bigint(30) NOT NULL AUTO_INCREMENT, From ecbda36e6c2b8a313fe73b413bf00c8e603a3a2d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 4 Sep 2014 23:18:39 +0200 Subject: [PATCH 22/24] Update README.md --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index c031cc0af..d721dc6af 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,6 @@ 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 - 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! - -**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! - Donations ========= From 44ab7c986d539f696cc4e1e3e7ba615d83ff9b1a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 4 Sep 2014 23:23:58 +0200 Subject: [PATCH 23/24] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d721dc6af..c8c01563a 100644 --- a/README.md +++ b/README.md @@ -19,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 ====== @@ -104,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 @@ -120,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 ============= @@ -172,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: From 751f247a25990754bc1ccd82fca1982521dac5ac Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 6 Oct 2014 10:55:25 +0200 Subject: [PATCH 24/24] [FIX] Allow < 1 max/min thresholds --- templates/bootstrap/account/edit/detail.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/bootstrap/account/edit/detail.tpl b/templates/bootstrap/account/edit/detail.tpl index b13e3672f..f892348a4 100644 --- a/templates/bootstrap/account/edit/detail.tpl +++ b/templates/bootstrap/account/edit/detail.tpl @@ -50,9 +50,9 @@

- {$GLOBAL.config.ap_threshold.min} - {$GLOBAL.config.ap_threshold.max} {$GLOBAL.config.currency}. Set to '0' for no auto payout.{if $GLOBAL.config.txfee_auto > 0} A {if $GLOBAL.config.txfee_auto > 0.00001}{$GLOBAL.config.txfee_auto}{else}{$GLOBAL.config.txfee_auto|number_format:"8"}{/if} {$GLOBAL.config.currency} TX fee will apply {/if} + {if $GLOBAL.config.ap_threshold.min < 0.0001}{$GLOBAL.config.ap_threshold.min|number_format:"8"}{else}{$GLOBAL.config.ap_threshold.min}{/if} - {if $GLOBAL.config.ap_threshold.max < 0.0001}{$GLOBAL.config.ap_threshold.max|number_format:"8"}{else}{$GLOBAL.config.ap_threshold.max}{/if} {$GLOBAL.config.currency}. Set to '0' for no auto payout.{if $GLOBAL.config.txfee_auto > 0} A {if $GLOBAL.config.txfee_auto > 0.00001}{$GLOBAL.config.txfee_auto}{else}{$GLOBAL.config.txfee_auto|number_format:"8"}{/if} {$GLOBAL.config.currency} TX fee will apply {/if}
- +