Initial commit
This commit is contained in:
commit
1ebcd999a6
77 changed files with 6256 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/vendor/*
|
||||||
|
/config/app.php
|
||||||
|
/tmp/*
|
||||||
|
/logs/*
|
||||||
|
lbryexplorer.zip
|
||||||
|
|
11
.htaccess
Normal file
11
.htaccess
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Uncomment the following to prevent the httpoxy vulnerability
|
||||||
|
# See: https://httpoxy.org/
|
||||||
|
#<IfModule mod_headers.c>
|
||||||
|
# RequestHeader unset Proxy
|
||||||
|
#</IfModule>
|
||||||
|
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteRule ^$ webroot/ [L]
|
||||||
|
RewriteRule (.*) webroot/$1 [L]
|
||||||
|
</IfModule>
|
46
bin/cake
Executable file
46
bin/cake
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Cake is a shell script for invoking CakePHP shell commands
|
||||||
|
#
|
||||||
|
# CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
#
|
||||||
|
# Licensed under The MIT License
|
||||||
|
# For full copyright and license information, please see the LICENSE.txt
|
||||||
|
# Redistributions of files must retain the above copyright notice.
|
||||||
|
#
|
||||||
|
# @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
# @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
# @since 1.2.0
|
||||||
|
# @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Canonicalize by following every symlink of the given name recursively
|
||||||
|
canonicalize() {
|
||||||
|
NAME="$1"
|
||||||
|
if [ -f "$NAME" ]
|
||||||
|
then
|
||||||
|
DIR=$(dirname -- "$NAME")
|
||||||
|
NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME")
|
||||||
|
fi
|
||||||
|
while [ -h "$NAME" ]; do
|
||||||
|
DIR=$(dirname -- "$NAME")
|
||||||
|
SYM=$(readlink "$NAME")
|
||||||
|
NAME=$(cd "$DIR" > /dev/null && cd $(dirname -- "$SYM") > /dev/null && pwd)/$(basename -- "$SYM")
|
||||||
|
done
|
||||||
|
echo "$NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
CONSOLE=$(dirname -- "$(canonicalize "$0")")
|
||||||
|
APP=$(dirname "$CONSOLE")
|
||||||
|
|
||||||
|
if [ $(basename $0) != 'cake' ]
|
||||||
|
then
|
||||||
|
exec php "$CONSOLE"/cake.php $(basename $0) "$@"
|
||||||
|
else
|
||||||
|
exec php "$CONSOLE"/cake.php "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit
|
27
bin/cake.bat
Normal file
27
bin/cake.bat
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::
|
||||||
|
:: Cake is a Windows batch script for invoking CakePHP shell commands
|
||||||
|
::
|
||||||
|
:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
::
|
||||||
|
:: Licensed under The MIT License
|
||||||
|
:: Redistributions of files must retain the above copyright notice.
|
||||||
|
::
|
||||||
|
:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
:: @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
:: @since 2.0.0
|
||||||
|
:: @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
SET app=%0
|
||||||
|
SET lib=%~dp0
|
||||||
|
|
||||||
|
php "%lib%cake.php" %*
|
||||||
|
|
||||||
|
echo.
|
||||||
|
|
||||||
|
exit /B %ERRORLEVEL%
|
34
bin/cake.php
Normal file
34
bin/cake.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/php -q
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Command-line code generation utility to automate programmer chores.
|
||||||
|
*
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 2.0.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
$minVersion = '5.6.0';
|
||||||
|
if (file_exists('composer.json')) {
|
||||||
|
$composer = json_decode(file_get_contents('composer.json'));
|
||||||
|
if (isset($composer->require->php)) {
|
||||||
|
$minVersion = preg_replace('/([^0-9\.])/', '', $composer->require->php);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (version_compare(phpversion(), $minVersion, '<')) {
|
||||||
|
fwrite(STDERR, sprintf("Minimum PHP version: %s. You are using: %s.\n", $minVersion, phpversion()));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||||
|
include dirname(__DIR__) . '/config/bootstrap.php';
|
||||||
|
|
||||||
|
exit(Cake\Console\ShellDispatcher::run($argv));
|
346
config/app.default.php
Normal file
346
config/app.default.php
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
/**
|
||||||
|
* Debug Level:
|
||||||
|
*
|
||||||
|
* Production Mode:
|
||||||
|
* false: No error messages, errors, or warnings shown.
|
||||||
|
*
|
||||||
|
* Development Mode:
|
||||||
|
* true: Errors and warnings shown.
|
||||||
|
*/
|
||||||
|
'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure basic information about the application.
|
||||||
|
*
|
||||||
|
* - namespace - The namespace to find app classes under.
|
||||||
|
* - defaultLocale - The default locale for translation, formatting currencies and numbers, date and time.
|
||||||
|
* - encoding - The encoding used for HTML + database connections.
|
||||||
|
* - base - The base directory the app resides in. If false this
|
||||||
|
* will be auto detected.
|
||||||
|
* - dir - Name of app directory.
|
||||||
|
* - webroot - The webroot directory.
|
||||||
|
* - wwwRoot - The file path to webroot.
|
||||||
|
* - baseUrl - To configure CakePHP to *not* use mod_rewrite and to
|
||||||
|
* use CakePHP pretty URLs, remove these .htaccess
|
||||||
|
* files:
|
||||||
|
* /.htaccess
|
||||||
|
* /webroot/.htaccess
|
||||||
|
* And uncomment the baseUrl key below.
|
||||||
|
* - fullBaseUrl - A base URL to use for absolute links.
|
||||||
|
* - imageBaseUrl - Web path to the public images directory under webroot.
|
||||||
|
* - cssBaseUrl - Web path to the public css directory under webroot.
|
||||||
|
* - jsBaseUrl - Web path to the public js directory under webroot.
|
||||||
|
* - paths - Configure paths for non class based resources. Supports the
|
||||||
|
* `plugins`, `templates`, `locales` subkeys, which allow the definition of
|
||||||
|
* paths for plugins, view templates and locale files respectively.
|
||||||
|
*/
|
||||||
|
'App' => [
|
||||||
|
'namespace' => 'App',
|
||||||
|
'encoding' => env('APP_ENCODING', 'UTF-8'),
|
||||||
|
'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'),
|
||||||
|
'base' => false,
|
||||||
|
'dir' => 'src',
|
||||||
|
'webroot' => 'webroot',
|
||||||
|
'wwwRoot' => WWW_ROOT,
|
||||||
|
// 'baseUrl' => env('SCRIPT_NAME'),
|
||||||
|
'fullBaseUrl' => false,
|
||||||
|
'imageBaseUrl' => 'img/',
|
||||||
|
'cssBaseUrl' => 'css/',
|
||||||
|
'jsBaseUrl' => 'js/',
|
||||||
|
'paths' => [
|
||||||
|
'plugins' => [ROOT . DS . 'plugins' . DS],
|
||||||
|
'templates' => [APP . 'Template' . DS],
|
||||||
|
'locales' => [APP . 'Locale' . DS],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security and encryption configuration
|
||||||
|
*
|
||||||
|
* - salt - A random string used in security hashing methods.
|
||||||
|
* The salt value is also used as the encryption key.
|
||||||
|
* You should treat it as extremely sensitive data.
|
||||||
|
*/
|
||||||
|
'Security' => [
|
||||||
|
'salt' => env('SECURITY_SALT', '__SALT__'),
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply timestamps with the last modified time to static assets (js, css, images).
|
||||||
|
* Will append a querystring parameter containing the time the file was modified.
|
||||||
|
* This is useful for busting browser caches.
|
||||||
|
*
|
||||||
|
* Set to true to apply timestamps when debug is true. Set to 'force' to always
|
||||||
|
* enable timestamping regardless of debug value.
|
||||||
|
*/
|
||||||
|
'Asset' => [
|
||||||
|
// 'timestamp' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the cache adapters.
|
||||||
|
*/
|
||||||
|
'Cache' => [
|
||||||
|
'default' => [
|
||||||
|
'className' => 'File',
|
||||||
|
'path' => CACHE,
|
||||||
|
'url' => env('CACHE_DEFAULT_URL', null),
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the cache used for general framework caching.
|
||||||
|
* Translation cache files are stored with this configuration.
|
||||||
|
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
|
||||||
|
* If you set 'className' => 'Null' core cache will be disabled.
|
||||||
|
*/
|
||||||
|
'_cake_core_' => [
|
||||||
|
'className' => 'File',
|
||||||
|
'prefix' => 'myapp_cake_core_',
|
||||||
|
'path' => CACHE . 'persistent/',
|
||||||
|
'serialize' => true,
|
||||||
|
'duration' => '+1 years',
|
||||||
|
'url' => env('CACHE_CAKECORE_URL', null),
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the cache for model and datasource caches. This cache
|
||||||
|
* configuration is used to store schema descriptions, and table listings
|
||||||
|
* in connections.
|
||||||
|
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
|
||||||
|
*/
|
||||||
|
'_cake_model_' => [
|
||||||
|
'className' => 'File',
|
||||||
|
'prefix' => 'myapp_cake_model_',
|
||||||
|
'path' => CACHE . 'models/',
|
||||||
|
'serialize' => true,
|
||||||
|
'duration' => '+1 years',
|
||||||
|
'url' => env('CACHE_CAKEMODEL_URL', null),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the Error and Exception handlers used by your application.
|
||||||
|
*
|
||||||
|
* By default errors are displayed using Debugger, when debug is true and logged
|
||||||
|
* by Cake\Log\Log when debug is false.
|
||||||
|
*
|
||||||
|
* In CLI environments exceptions will be printed to stderr with a backtrace.
|
||||||
|
* In web environments an HTML page will be displayed for the exception.
|
||||||
|
* With debug true, framework errors like Missing Controller will be displayed.
|
||||||
|
* When debug is false, framework errors will be coerced into generic HTTP errors.
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
*
|
||||||
|
* - `errorLevel` - int - The level of errors you are interested in capturing.
|
||||||
|
* - `trace` - boolean - Whether or not backtraces should be included in
|
||||||
|
* logged errors/exceptions.
|
||||||
|
* - `log` - boolean - Whether or not you want exceptions logged.
|
||||||
|
* - `exceptionRenderer` - string - The class responsible for rendering
|
||||||
|
* uncaught exceptions. If you choose a custom class you should place
|
||||||
|
* the file for that class in src/Error. This class needs to implement a
|
||||||
|
* render method.
|
||||||
|
* - `skipLog` - array - List of exceptions to skip for logging. Exceptions that
|
||||||
|
* extend one of the listed exceptions will also be skipped for logging.
|
||||||
|
* E.g.:
|
||||||
|
* `'skipLog' => ['Cake\Network\Exception\NotFoundException', 'Cake\Network\Exception\UnauthorizedException']`
|
||||||
|
* - `extraFatalErrorMemory` - int - The number of megabytes to increase
|
||||||
|
* the memory limit by when a fatal error is encountered. This allows
|
||||||
|
* breathing room to complete logging or error handling.
|
||||||
|
*/
|
||||||
|
'Error' => [
|
||||||
|
'errorLevel' => E_ALL,
|
||||||
|
'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
|
||||||
|
'skipLog' => [],
|
||||||
|
'log' => true,
|
||||||
|
'trace' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email configuration.
|
||||||
|
*
|
||||||
|
* By defining transports separately from delivery profiles you can easily
|
||||||
|
* re-use transport configuration across multiple profiles.
|
||||||
|
*
|
||||||
|
* You can specify multiple configurations for production, development and
|
||||||
|
* testing.
|
||||||
|
*
|
||||||
|
* Each transport needs a `className`. Valid options are as follows:
|
||||||
|
*
|
||||||
|
* Mail - Send using PHP mail function
|
||||||
|
* Smtp - Send using SMTP
|
||||||
|
* Debug - Do not send the email, just return the result
|
||||||
|
*
|
||||||
|
* You can add custom transports (or override existing transports) by adding the
|
||||||
|
* appropriate file to src/Mailer/Transport. Transports should be named
|
||||||
|
* 'YourTransport.php', where 'Your' is the name of the transport.
|
||||||
|
*/
|
||||||
|
'EmailTransport' => [
|
||||||
|
'default' => [
|
||||||
|
'className' => 'Mail',
|
||||||
|
// The following keys are used in SMTP transports
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 25,
|
||||||
|
'timeout' => 30,
|
||||||
|
'username' => 'user',
|
||||||
|
'password' => 'secret',
|
||||||
|
'client' => null,
|
||||||
|
'tls' => null,
|
||||||
|
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email delivery profiles
|
||||||
|
*
|
||||||
|
* Delivery profiles allow you to predefine various properties about email
|
||||||
|
* messages from your application and give the settings a name. This saves
|
||||||
|
* duplication across your application and makes maintenance and development
|
||||||
|
* easier. Each profile accepts a number of keys. See `Cake\Mailer\Email`
|
||||||
|
* for more information.
|
||||||
|
*/
|
||||||
|
'Email' => [
|
||||||
|
'default' => [
|
||||||
|
'transport' => 'default',
|
||||||
|
'from' => 'you@localhost',
|
||||||
|
//'charset' => 'utf-8',
|
||||||
|
//'headerCharset' => 'utf-8',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection information used by the ORM to connect
|
||||||
|
* to your application's datastores.
|
||||||
|
* Do not use periods in database name - it may lead to error.
|
||||||
|
* See https://github.com/cakephp/cakephp/issues/6471 for details.
|
||||||
|
* Drivers include Mysql Postgres Sqlite Sqlserver
|
||||||
|
* See vendor\cakephp\cakephp\src\Database\Driver for complete list
|
||||||
|
*/
|
||||||
|
'Datasources' => [
|
||||||
|
'default' => [
|
||||||
|
'className' => 'Cake\Database\Connection',
|
||||||
|
'driver' => 'Cake\Database\Driver\Mysql',
|
||||||
|
'persistent' => false,
|
||||||
|
'host' => 'localhost',
|
||||||
|
/**
|
||||||
|
* CakePHP will use the default DB port based on the driver selected
|
||||||
|
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
|
||||||
|
* the following line and set the port accordingly
|
||||||
|
*/
|
||||||
|
//'port' => 'non_standard_port_number',
|
||||||
|
'username' => 'my_app',
|
||||||
|
'password' => 'secret',
|
||||||
|
'database' => 'my_app',
|
||||||
|
'encoding' => 'utf8',
|
||||||
|
'timezone' => 'UTC',
|
||||||
|
'flags' => [],
|
||||||
|
'cacheMetadata' => true,
|
||||||
|
'log' => false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set identifier quoting to true if you are using reserved words or
|
||||||
|
* special characters in your table or column names. Enabling this
|
||||||
|
* setting will result in queries built using the Query Builder having
|
||||||
|
* identifiers quoted when creating SQL. It should be noted that this
|
||||||
|
* decreases performance because each query needs to be traversed and
|
||||||
|
* manipulated before being executed.
|
||||||
|
*/
|
||||||
|
'quoteIdentifiers' => false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* During development, if using MySQL < 5.6, uncommenting the
|
||||||
|
* following line could boost the speed at which schema metadata is
|
||||||
|
* fetched from the database. It can also be set directly with the
|
||||||
|
* mysql configuration directive 'innodb_stats_on_metadata = 0'
|
||||||
|
* which is the recommended value in production environments
|
||||||
|
*/
|
||||||
|
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
|
||||||
|
|
||||||
|
'url' => env('DATABASE_URL', null),
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The test connection is used during the test suite.
|
||||||
|
*/
|
||||||
|
'test' => [
|
||||||
|
'className' => 'Cake\Database\Connection',
|
||||||
|
'driver' => 'Cake\Database\Driver\Mysql',
|
||||||
|
'persistent' => false,
|
||||||
|
'host' => 'localhost',
|
||||||
|
//'port' => 'non_standard_port_number',
|
||||||
|
'username' => 'my_app',
|
||||||
|
'password' => 'secret',
|
||||||
|
'database' => 'test_myapp',
|
||||||
|
'encoding' => 'utf8',
|
||||||
|
'timezone' => 'UTC',
|
||||||
|
'cacheMetadata' => true,
|
||||||
|
'quoteIdentifiers' => false,
|
||||||
|
'log' => false,
|
||||||
|
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
|
||||||
|
'url' => env('DATABASE_TEST_URL', null),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures logging options
|
||||||
|
*/
|
||||||
|
'Log' => [
|
||||||
|
'debug' => [
|
||||||
|
'className' => 'Cake\Log\Engine\FileLog',
|
||||||
|
'path' => LOGS,
|
||||||
|
'file' => 'debug',
|
||||||
|
'levels' => ['notice', 'info', 'debug'],
|
||||||
|
'url' => env('LOG_DEBUG_URL', null),
|
||||||
|
],
|
||||||
|
'error' => [
|
||||||
|
'className' => 'Cake\Log\Engine\FileLog',
|
||||||
|
'path' => LOGS,
|
||||||
|
'file' => 'error',
|
||||||
|
'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
|
||||||
|
'url' => env('LOG_ERROR_URL', null),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session configuration.
|
||||||
|
*
|
||||||
|
* Contains an array of settings to use for session configuration. The
|
||||||
|
* `defaults` key is used to define a default preset to use for sessions, any
|
||||||
|
* settings declared here will override the settings of the default config.
|
||||||
|
*
|
||||||
|
* ## Options
|
||||||
|
*
|
||||||
|
* - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'.
|
||||||
|
* - `cookiePath` - The url path for which session cookie is set. Maps to the
|
||||||
|
* `session.cookie_path` php.ini config. Defaults to base path of app.
|
||||||
|
* - `timeout` - The time in minutes the session should be valid for.
|
||||||
|
* Pass 0 to disable checking timeout.
|
||||||
|
* Please note that php.ini's session.gc_maxlifetime must be equal to or greater
|
||||||
|
* than the largest Session['timeout'] in all served websites for it to have the
|
||||||
|
* desired effect.
|
||||||
|
* - `defaults` - The default configuration set to use as a basis for your session.
|
||||||
|
* There are four built-in options: php, cake, cache, database.
|
||||||
|
* - `handler` - Can be used to enable a custom session handler. Expects an
|
||||||
|
* array with at least the `engine` key, being the name of the Session engine
|
||||||
|
* class to use for managing the session. CakePHP bundles the `CacheSession`
|
||||||
|
* and `DatabaseSession` engines.
|
||||||
|
* - `ini` - An associative array of additional ini values to set.
|
||||||
|
*
|
||||||
|
* The built-in `defaults` options are:
|
||||||
|
*
|
||||||
|
* - 'php' - Uses settings defined in your php.ini.
|
||||||
|
* - 'cake' - Saves session files in CakePHP's /tmp directory.
|
||||||
|
* - 'database' - Uses CakePHP's database sessions.
|
||||||
|
* - 'cache' - Use the Cache class to save sessions.
|
||||||
|
*
|
||||||
|
* To define a custom session handler, save it at src/Network/Session/<name>.php.
|
||||||
|
* Make sure the class implements PHP's `SessionHandlerInterface` and set
|
||||||
|
* Session.handler to <name>
|
||||||
|
*
|
||||||
|
* To use database sessions, load the SQL file located at config/Schema/sessions.sql
|
||||||
|
*/
|
||||||
|
'Session' => [
|
||||||
|
'defaults' => 'php',
|
||||||
|
],
|
||||||
|
];
|
222
config/bootstrap.php
Normal file
222
config/bootstrap.php
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.8
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
// You can remove this if you are confident that your PHP version is sufficient.
|
||||||
|
if (version_compare(PHP_VERSION, '5.6.0') < 0) {
|
||||||
|
trigger_error('Your PHP version must be equal or higher than 5.6.0 to use CakePHP.', E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You can remove this if you are confident you have intl installed.
|
||||||
|
*/
|
||||||
|
if (!extension_loaded('intl')) {
|
||||||
|
trigger_error('You must enable the intl extension to use CakePHP.', E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You can remove this if you are confident you have mbstring installed.
|
||||||
|
*/
|
||||||
|
if (!extension_loaded('mbstring')) {
|
||||||
|
trigger_error('You must enable the mbstring extension to use CakePHP.', E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure paths required to find CakePHP + general filepath
|
||||||
|
* constants
|
||||||
|
*/
|
||||||
|
require __DIR__ . '/paths.php';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bootstrap CakePHP.
|
||||||
|
*
|
||||||
|
* Does the various bits of setup that CakePHP needs to do.
|
||||||
|
* This includes:
|
||||||
|
*
|
||||||
|
* - Registering the CakePHP autoloader.
|
||||||
|
* - Setting the default application paths.
|
||||||
|
*/
|
||||||
|
require CORE_PATH . 'config' . DS . 'bootstrap.php';
|
||||||
|
|
||||||
|
use Cake\Cache\Cache;
|
||||||
|
use Cake\Console\ConsoleErrorHandler;
|
||||||
|
use Cake\Core\App;
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Core\Configure\Engine\PhpConfig;
|
||||||
|
use Cake\Core\Plugin;
|
||||||
|
use Cake\Database\Type;
|
||||||
|
use Cake\Datasource\ConnectionManager;
|
||||||
|
use Cake\Error\ErrorHandler;
|
||||||
|
use Cake\Log\Log;
|
||||||
|
use Cake\Mailer\Email;
|
||||||
|
use Cake\Network\Request;
|
||||||
|
use Cake\Utility\Inflector;
|
||||||
|
use Cake\Utility\Security;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read configuration file and inject configuration into various
|
||||||
|
* CakePHP classes.
|
||||||
|
*
|
||||||
|
* By default there is only one configuration file. It is often a good
|
||||||
|
* idea to create multiple configuration files, and separate the configuration
|
||||||
|
* that changes from configuration that does not. This makes deployment simpler.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
Configure::config('default', new PhpConfig());
|
||||||
|
Configure::load('app', 'default', false);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
exit($e->getMessage() . "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load an environment local configuration file.
|
||||||
|
* You can use a file like app_local.php to provide local overrides to your
|
||||||
|
* shared configuration.
|
||||||
|
*/
|
||||||
|
//Configure::load('app_local', 'default');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When debug = true the metadata cache should only last
|
||||||
|
* for a short time.
|
||||||
|
*/
|
||||||
|
if (Configure::read('debug')) {
|
||||||
|
Configure::write('Cache._cake_model_.duration', '+2 minutes');
|
||||||
|
Configure::write('Cache._cake_core_.duration', '+2 minutes');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set server timezone to UTC. You can change it to another timezone of your
|
||||||
|
* choice but using UTC makes time calculations / conversions easier.
|
||||||
|
*/
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure the mbstring extension to use the correct encoding.
|
||||||
|
*/
|
||||||
|
mb_internal_encoding(Configure::read('App.encoding'));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the default locale. This controls how dates, number and currency is
|
||||||
|
* formatted and sets the default language to use for translations.
|
||||||
|
*/
|
||||||
|
ini_set('intl.default_locale', Configure::read('App.defaultLocale'));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register application error and exception handlers.
|
||||||
|
*/
|
||||||
|
$isCli = PHP_SAPI === 'cli';
|
||||||
|
if ($isCli) {
|
||||||
|
(new ConsoleErrorHandler(Configure::read('Error')))->register();
|
||||||
|
} else {
|
||||||
|
(new ErrorHandler(Configure::read('Error')))->register();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Include the CLI bootstrap overrides.
|
||||||
|
*/
|
||||||
|
if ($isCli) {
|
||||||
|
require __DIR__ . '/bootstrap_cli.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the full base URL.
|
||||||
|
* This URL is used as the base of all absolute links.
|
||||||
|
*
|
||||||
|
* If you define fullBaseUrl in your config file you can remove this.
|
||||||
|
*/
|
||||||
|
if (!Configure::read('App.fullBaseUrl')) {
|
||||||
|
$s = null;
|
||||||
|
if (env('HTTPS')) {
|
||||||
|
$s = 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
$httpHost = env('HTTP_HOST');
|
||||||
|
if (isset($httpHost)) {
|
||||||
|
Configure::write('App.fullBaseUrl', 'http' . $s . '://' . $httpHost);
|
||||||
|
}
|
||||||
|
unset($httpHost, $s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::setConfig(Configure::consume('Cache'));
|
||||||
|
ConnectionManager::setConfig(Configure::consume('Datasources'));
|
||||||
|
Email::setConfigTransport(Configure::consume('EmailTransport'));
|
||||||
|
Email::setConfig(Configure::consume('Email'));
|
||||||
|
Log::setConfig(Configure::consume('Log'));
|
||||||
|
Security::salt(Configure::consume('Security.salt'));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The default crypto extension in 3.0 is OpenSSL.
|
||||||
|
* If you are migrating from 2.x uncomment this code to
|
||||||
|
* use a more compatible Mcrypt based implementation
|
||||||
|
*/
|
||||||
|
//Security::engine(new \Cake\Utility\Crypto\Mcrypt());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup detectors for mobile and tablet.
|
||||||
|
*/
|
||||||
|
Request::addDetector('mobile', function ($request) {
|
||||||
|
$detector = new \Detection\MobileDetect();
|
||||||
|
|
||||||
|
return $detector->isMobile();
|
||||||
|
});
|
||||||
|
Request::addDetector('tablet', function ($request) {
|
||||||
|
$detector = new \Detection\MobileDetect();
|
||||||
|
|
||||||
|
return $detector->isTablet();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable immutable time objects in the ORM.
|
||||||
|
*
|
||||||
|
* You can enable default locale format parsing by adding calls
|
||||||
|
* to `useLocaleParser()`. This enables the automatic conversion of
|
||||||
|
* locale specific date formats. For details see
|
||||||
|
* @link http://book.cakephp.org/3.0/en/core-libraries/internationalization-and-localization.html#parsing-localized-datetime-data
|
||||||
|
*/
|
||||||
|
Type::build('time')
|
||||||
|
->useImmutable();
|
||||||
|
Type::build('date')
|
||||||
|
->useImmutable();
|
||||||
|
Type::build('datetime')
|
||||||
|
->useImmutable();
|
||||||
|
Type::build('timestamp')
|
||||||
|
->useImmutable();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Custom Inflector rules, can be set to correctly pluralize or singularize
|
||||||
|
* table, model, controller names or whatever other string is passed to the
|
||||||
|
* inflection functions.
|
||||||
|
*/
|
||||||
|
//Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']);
|
||||||
|
//Inflector::rules('irregular', ['red' => 'redlings']);
|
||||||
|
//Inflector::rules('uninflected', ['dontinflectme']);
|
||||||
|
//Inflector::rules('transliteration', ['/å/' => 'aa']);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Plugins need to be loaded manually, you can either load them one by one or all of them in a single call
|
||||||
|
* Uncomment one of the lines below, as you need. make sure you read the documentation on Plugin to use more
|
||||||
|
* advanced ways of loading plugins
|
||||||
|
*
|
||||||
|
* Plugin::loadAll(); // Loads all plugins at once
|
||||||
|
* Plugin::load('Migrations'); //Loads a single plugin named Migrations
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only try to load DebugKit in development mode
|
||||||
|
* Debug Kit should not be installed on a production system
|
||||||
|
*/
|
||||||
|
if (Configure::read('debug')) {
|
||||||
|
Plugin::load('DebugKit', ['bootstrap' => true]);
|
||||||
|
}
|
38
config/bootstrap_cli.php
Normal file
38
config/bootstrap_cli.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.0.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Core\Exception\MissingPluginException;
|
||||||
|
use Cake\Core\Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional bootstrapping and configuration for CLI environments should
|
||||||
|
* be put here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Set the fullBaseUrl to allow URLs to be generated in shell tasks.
|
||||||
|
// This is useful when sending email from shells.
|
||||||
|
//Configure::write('App.fullBaseUrl', php_uname('n'));
|
||||||
|
|
||||||
|
// Set logs to different files so they don't have permission conflicts.
|
||||||
|
Configure::write('Log.debug.file', 'cli-debug');
|
||||||
|
Configure::write('Log.error.file', 'cli-error');
|
||||||
|
|
||||||
|
try {
|
||||||
|
Plugin::load('Bake');
|
||||||
|
} catch (MissingPluginException $e) {
|
||||||
|
// Do not halt if the plugin is missing
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin::load('Migrations');
|
85
config/paths.php
Normal file
85
config/paths.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.0.0
|
||||||
|
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the DS to separate the directories in other defines
|
||||||
|
*/
|
||||||
|
if (!defined('DS')) {
|
||||||
|
define('DS', DIRECTORY_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These defines should only be edited if you have cake installed in
|
||||||
|
* a directory layout other than the way it is distributed.
|
||||||
|
* When using custom settings be sure to use the DS and do not add a trailing DS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full path to the directory which holds "src", WITHOUT a trailing DS.
|
||||||
|
*/
|
||||||
|
define('ROOT', dirname(__DIR__));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual directory name for the application directory. Normally
|
||||||
|
* named 'src'.
|
||||||
|
*/
|
||||||
|
define('APP_DIR', 'src');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the application's directory.
|
||||||
|
*/
|
||||||
|
define('APP', ROOT . DS . APP_DIR . DS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the config directory.
|
||||||
|
*/
|
||||||
|
define('CONFIG', ROOT . DS . 'config' . DS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File path to the webroot directory.
|
||||||
|
*/
|
||||||
|
define('WWW_ROOT', ROOT . DS . 'webroot' . DS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the tests directory.
|
||||||
|
*/
|
||||||
|
define('TESTS', ROOT . DS . 'tests' . DS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the temporary files directory.
|
||||||
|
*/
|
||||||
|
define('TMP', ROOT . DS . 'tmp' . DS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the logs directory.
|
||||||
|
*/
|
||||||
|
define('LOGS', ROOT . DS . 'logs' . DS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the cache files directory. It can be shared between hosts in a multi-server setup.
|
||||||
|
*/
|
||||||
|
define('CACHE', TMP . 'cache' . DS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The absolute path to the "cake" directory, WITHOUT a trailing DS.
|
||||||
|
*
|
||||||
|
* CakePHP should always be installed with composer, so look there.
|
||||||
|
*/
|
||||||
|
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the cake directory.
|
||||||
|
*/
|
||||||
|
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
|
||||||
|
define('CAKE', CORE_PATH . 'src' . DS);
|
72
config/routes.php
Normal file
72
config/routes.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Routes configuration
|
||||||
|
*
|
||||||
|
* In this file, you set up routes to your controllers and their actions.
|
||||||
|
* Routes are very important mechanism that allows you to freely connect
|
||||||
|
* different URLs to chosen controllers and their actions (functions).
|
||||||
|
*
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Cake\Core\Plugin;
|
||||||
|
use Cake\Routing\RouteBuilder;
|
||||||
|
use Cake\Routing\Router;
|
||||||
|
use Cake\Routing\Route\DashedRoute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default class to use for all routes
|
||||||
|
*
|
||||||
|
* The following route classes are supplied with CakePHP and are appropriate
|
||||||
|
* to set as the default:
|
||||||
|
*
|
||||||
|
* - Route
|
||||||
|
* - InflectedRoute
|
||||||
|
* - DashedRoute
|
||||||
|
*
|
||||||
|
* If no call is made to `Router::defaultRouteClass()`, the class used is
|
||||||
|
* `Route` (`Cake\Routing\Route\Route`)
|
||||||
|
*
|
||||||
|
* Note that `Route` does not do any inflections on URLs which will result in
|
||||||
|
* inconsistently cased URLs when used with `:plugin`, `:controller` and
|
||||||
|
* `:action` markers.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Router::defaultRouteClass(DashedRoute::class);
|
||||||
|
|
||||||
|
Router::scope('/', function (RouteBuilder $routes) {
|
||||||
|
$routes->connect('/', ['controller' => 'Main', 'action' => 'index']);
|
||||||
|
$routes->connect('/address/*', ['controller' => 'Main', 'action' => 'address']);
|
||||||
|
$routes->connect('/blocks/*', ['controller' => 'Main', 'action' => 'blocks']);
|
||||||
|
$routes->connect('/find', ['controller' => 'Main', 'action' => 'find']);
|
||||||
|
$routes->connect('/realtime', ['controller' => 'Main', 'action' => 'realtime']);
|
||||||
|
$routes->connect('/tx/*', ['controller' => 'Main', 'action' => 'tx']);
|
||||||
|
$routes->connect('/qr/*', ['controller' => 'Main', 'action' => 'qr']);
|
||||||
|
|
||||||
|
$routes->connect('/api/v1/address/:addr/tag', ['controller' => 'Main', 'action' => 'apiaddrtag'], ['addr' => '[A-Za-z0-9,]+', 'pass' => ['addr']]);
|
||||||
|
$routes->connect('/api/v1/address/:addr/utxo', ['controller' => 'Main', 'action' => 'apiaddrutxo'], ['addr' => '[A-Za-z0-9,]+', 'pass' => ['addr']]);
|
||||||
|
|
||||||
|
$routes->connect('/api/v1/realtime/blocks', ['controller' => 'Main', 'action' => 'apirealtimeblocks']);
|
||||||
|
$routes->connect('/api/v1/realtime/tx', ['controller' => 'Main', 'action' => 'apirealtimetx']);
|
||||||
|
$routes->connect('/api/v1/recentblocks', ['controller' => 'Main', 'action' => 'apirecentblocks']);
|
||||||
|
$routes->connect('/api/v1/status', ['controller' => 'Main', 'action' => 'apistatus']);
|
||||||
|
//$routes->connect('/api/v1/recenttxs', ['controller' => 'Main', 'action' => 'apirecenttxs']);
|
||||||
|
|
||||||
|
//$routes->fallbacks(DashedRoute::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all plugin routes. See the Plugin documentation on
|
||||||
|
* how to customize the loading of plugin routes.
|
||||||
|
*/
|
||||||
|
Plugin::routes();
|
||||||
|
|
18
config/schema/i18n.sql
Normal file
18
config/schema/i18n.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
#
|
||||||
|
# Licensed under The MIT License
|
||||||
|
# For full copyright and license information, please see the LICENSE.txt
|
||||||
|
# Redistributions of files must retain the above copyright notice.
|
||||||
|
# MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
|
||||||
|
CREATE TABLE i18n (
|
||||||
|
id int NOT NULL auto_increment,
|
||||||
|
locale varchar(6) NOT NULL,
|
||||||
|
model varchar(255) NOT NULL,
|
||||||
|
foreign_key int(10) NOT NULL,
|
||||||
|
field varchar(255) NOT NULL,
|
||||||
|
content text,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
|
||||||
|
INDEX I18N_FIELD(model, foreign_key, field)
|
||||||
|
);
|
13
config/schema/sessions.sql
Normal file
13
config/schema/sessions.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
#
|
||||||
|
# Licensed under The MIT License
|
||||||
|
# For full copyright and license information, please see the LICENSE.txt
|
||||||
|
# Redistributions of files must retain the above copyright notice.
|
||||||
|
# MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
|
||||||
|
CREATE TABLE sessions (
|
||||||
|
id char(40) NOT NULL,
|
||||||
|
data text,
|
||||||
|
expires INT(11) NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
4
cron/addrtx.sh
Executable file
4
cron/addrtx.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake block addrtxamounts
|
||||||
|
|
4
cron/blocks.sh
Executable file
4
cron/blocks.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake block parsenewblocks
|
||||||
|
|
134
cron/blockstuff.php
Normal file
134
cron/blockstuff.php
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
define('TMP', '/tmp/');
|
||||||
|
define('DS', '/');
|
||||||
|
|
||||||
|
class BlockSyncThread extends \Thread {
|
||||||
|
private $_startHeight;
|
||||||
|
private $_endHeight;
|
||||||
|
private $_maxHeight;
|
||||||
|
|
||||||
|
public function __construct($startBlock, $endBlock, $maxHeight) {
|
||||||
|
$this->_startHeight = $startBlock;
|
||||||
|
$this->_endHeight = $endBlock;
|
||||||
|
$this->_maxHeight = $maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run() {
|
||||||
|
$conn = new \PDO("mysql:host=localhost;dbname=lbry", 'lbry-admin', '46D861aX#!yQ');
|
||||||
|
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$data_error = false;
|
||||||
|
$conn->beginTransaction();
|
||||||
|
for ($curr_height = $this->_startHeight; $curr_height <= $this->_endHeight; $curr_height++) {
|
||||||
|
$idx_str = str_pad($curr_height, strlen($this->_maxHeight), '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
// get the block hash
|
||||||
|
$req = ['method' => 'getblockhash', 'params' => [$curr_height]];
|
||||||
|
$response = BlockStuff::curl_json_post(BlockStuff::rpcurl, json_encode($req));
|
||||||
|
$json = json_decode($response);
|
||||||
|
$curr_block_hash = $json->result;
|
||||||
|
|
||||||
|
$req = ['method' => 'getblock', 'params' => [$curr_block_hash]];
|
||||||
|
$response = BlockStuff::curl_json_post(BlockStuff::rpcurl, json_encode($req));
|
||||||
|
$json = json_decode($response);
|
||||||
|
$curr_block = $json->result;
|
||||||
|
|
||||||
|
$stmt = $conn->prepare('UPDATE Blocks SET Confirmations = ? WHERE Height = ?');
|
||||||
|
try {
|
||||||
|
$stmt->execute([$curr_block->confirmations, $curr_height]);
|
||||||
|
echo "[$idx_str/$this->_maxHeight] Updated block height: $curr_height with confirmations $curr_block->confirmations.\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$data_error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data_error) {
|
||||||
|
echo "Rolling back changes.\n";
|
||||||
|
$conn->rollBack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Committing data.\n";
|
||||||
|
$conn->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlockStuff {
|
||||||
|
const rpcurl = 'http://lrpc:lrpc@127.0.0.1:9245';
|
||||||
|
public static function blocksync() {
|
||||||
|
self::lock('blocksync');
|
||||||
|
|
||||||
|
$conn = new \PDO("mysql:host=localhost;dbname=lbry", 'lbry-admin', '46D861aX#!yQ');
|
||||||
|
|
||||||
|
$stmt = $conn->prepare('SELECT Height FROM Blocks ORDER BY Height DESC LIMIT 1');
|
||||||
|
$stmt->execute([]);
|
||||||
|
$max_block = $stmt->fetch(PDO::FETCH_OBJ);
|
||||||
|
if ($max_block) {
|
||||||
|
$chunk_limit = 10;
|
||||||
|
$curr_height = 0;
|
||||||
|
$chunks = floor($max_block->Height / $chunk_limit);
|
||||||
|
$threads = [];
|
||||||
|
for ($i = 0; $i < $chunk_limit; $i++) {
|
||||||
|
$start = $curr_height;
|
||||||
|
$end = ($i == ($chunk_limit - 1)) ? $max_block->Height : $start + $chunks;
|
||||||
|
$curr_height += $chunks + 1;
|
||||||
|
$thread = new BlockSyncThread($start, $end, $max_block->Height);
|
||||||
|
$threads[] = $thread;
|
||||||
|
|
||||||
|
$thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($threads); $i++) {
|
||||||
|
$threads[$i]->join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::unlock('blocksync');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function curl_json_post($url, $data, $headers = []) {
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
//Log::debug('Request execution completed.');
|
||||||
|
if ($response === false) {
|
||||||
|
$error = curl_error($ch);
|
||||||
|
$errno = curl_errno($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
throw new \Exception(sprintf('The request failed: %s', $error), $errno);
|
||||||
|
} else {
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close any open file handle
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function lock($process_name) {
|
||||||
|
if (!is_dir(TMP . 'lock')) {
|
||||||
|
mkdir(TMP . 'lock');
|
||||||
|
}
|
||||||
|
$lock_file = TMP . 'lock' . DS . $process_name;
|
||||||
|
if (file_exists($lock_file)) {
|
||||||
|
echo "$process_name is already running.\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
file_put_contents($lock_file, '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function unlock($process_name) {
|
||||||
|
$lock_file = TMP . 'lock' . DS . $process_name;
|
||||||
|
if (file_exists($lock_file)) {
|
||||||
|
unlink($lock_file);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockStuff::blocksync();
|
4
cron/blocktx.sh
Executable file
4
cron/blocktx.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake block parsetxs
|
||||||
|
|
3
cron/confirmations.sh
Executable file
3
cron/confirmations.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng/cron
|
||||||
|
php blockstuff.php
|
4
cron/fixzero.sh
Executable file
4
cron/fixzero.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake block fixzerooutputs
|
||||||
|
|
4
cron/forevermempool.sh
Executable file
4
cron/forevermempool.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake block forevermempool &
|
||||||
|
|
4
cron/livetx.sh
Executable file
4
cron/livetx.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake block parsemempool
|
||||||
|
|
4
cron/spends.sh
Executable file
4
cron/spends.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake block updatespends
|
||||||
|
|
4
cron/verifytags.sh
Executable file
4
cron/verifytags.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd /var/www/lbry.block.ng
|
||||||
|
bin/cake aux verifytags
|
||||||
|
|
16
index.php
Normal file
16
index.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';
|
52
src/Application.php
Normal file
52
src/Application.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.3.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Error\Middleware\ErrorHandlerMiddleware;
|
||||||
|
use Cake\Http\BaseApplication;
|
||||||
|
use Cake\Routing\Middleware\AssetMiddleware;
|
||||||
|
use Cake\Routing\Middleware\RoutingMiddleware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application setup class.
|
||||||
|
*
|
||||||
|
* This defines the bootstrapping logic and middleware layers you
|
||||||
|
* want to use in your application.
|
||||||
|
*/
|
||||||
|
class Application extends BaseApplication
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Setup the middleware your application will use.
|
||||||
|
*
|
||||||
|
* @param \Cake\Http\MiddlewareQueue $middleware The middleware queue to setup.
|
||||||
|
* @return \Cake\Http\MiddlewareQueue The updated middleware.
|
||||||
|
*/
|
||||||
|
public function middleware($middleware)
|
||||||
|
{
|
||||||
|
$middleware
|
||||||
|
// Catch any exceptions in the lower layers,
|
||||||
|
// and make an error page/response
|
||||||
|
->add(ErrorHandlerMiddleware::class)
|
||||||
|
|
||||||
|
// Handle plugin/theme assets like CakePHP normally does.
|
||||||
|
->add(AssetMiddleware::class)
|
||||||
|
|
||||||
|
// Apply routing
|
||||||
|
->add(RoutingMiddleware::class);
|
||||||
|
|
||||||
|
return $middleware;
|
||||||
|
}
|
||||||
|
}
|
195
src/Console/Installer.php
Normal file
195
src/Console/Installer.php
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.0.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\Console;
|
||||||
|
|
||||||
|
use Cake\Utility\Security;
|
||||||
|
use Composer\Script\Event;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides installation hooks for when this application is installed via
|
||||||
|
* composer. Customize this class to suit your needs.
|
||||||
|
*/
|
||||||
|
class Installer
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does some routine installation tasks so people don't have to.
|
||||||
|
*
|
||||||
|
* @param \Composer\Script\Event $event The composer event object.
|
||||||
|
* @throws \Exception Exception raised by validator.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function postInstall(Event $event)
|
||||||
|
{
|
||||||
|
$io = $event->getIO();
|
||||||
|
|
||||||
|
$rootDir = dirname(dirname(__DIR__));
|
||||||
|
|
||||||
|
static::createAppConfig($rootDir, $io);
|
||||||
|
static::createWritableDirectories($rootDir, $io);
|
||||||
|
|
||||||
|
// ask if the permissions should be changed
|
||||||
|
if ($io->isInteractive()) {
|
||||||
|
$validator = function ($arg) {
|
||||||
|
if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
|
||||||
|
return $arg;
|
||||||
|
}
|
||||||
|
throw new Exception('This is not a valid answer. Please choose Y or n.');
|
||||||
|
};
|
||||||
|
$setFolderPermissions = $io->askAndValidate(
|
||||||
|
'<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
|
||||||
|
$validator,
|
||||||
|
10,
|
||||||
|
'Y'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (in_array($setFolderPermissions, ['Y', 'y'])) {
|
||||||
|
static::setFolderPermissions($rootDir, $io);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static::setFolderPermissions($rootDir, $io);
|
||||||
|
}
|
||||||
|
|
||||||
|
static::setSecuritySalt($rootDir, $io);
|
||||||
|
|
||||||
|
if (class_exists('\Cake\Codeception\Console\Installer')) {
|
||||||
|
\Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the config/app.php file if it does not exist.
|
||||||
|
*
|
||||||
|
* @param string $dir The application's root directory.
|
||||||
|
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function createAppConfig($dir, $io)
|
||||||
|
{
|
||||||
|
$appConfig = $dir . '/config/app.php';
|
||||||
|
$defaultConfig = $dir . '/config/app.default.php';
|
||||||
|
if (!file_exists($appConfig)) {
|
||||||
|
copy($defaultConfig, $appConfig);
|
||||||
|
$io->write('Created `config/app.php` file');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the `logs` and `tmp` directories.
|
||||||
|
*
|
||||||
|
* @param string $dir The application's root directory.
|
||||||
|
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function createWritableDirectories($dir, $io)
|
||||||
|
{
|
||||||
|
$paths = [
|
||||||
|
'logs',
|
||||||
|
'tmp',
|
||||||
|
'tmp/cache',
|
||||||
|
'tmp/cache/models',
|
||||||
|
'tmp/cache/persistent',
|
||||||
|
'tmp/cache/views',
|
||||||
|
'tmp/sessions',
|
||||||
|
'tmp/tests'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
$path = $dir . '/' . $path;
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
mkdir($path);
|
||||||
|
$io->write('Created `' . $path . '` directory');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set globally writable permissions on the "tmp" and "logs" directory.
|
||||||
|
*
|
||||||
|
* This is not the most secure default, but it gets people up and running quickly.
|
||||||
|
*
|
||||||
|
* @param string $dir The application's root directory.
|
||||||
|
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setFolderPermissions($dir, $io)
|
||||||
|
{
|
||||||
|
// Change the permissions on a path and output the results.
|
||||||
|
$changePerms = function ($path, $perms, $io) {
|
||||||
|
// Get permission bits from stat(2) result.
|
||||||
|
$currentPerms = fileperms($path) & 0777;
|
||||||
|
if (($currentPerms & $perms) == $perms) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = chmod($path, $currentPerms | $perms);
|
||||||
|
if ($res) {
|
||||||
|
$io->write('Permissions set on ' . $path);
|
||||||
|
} else {
|
||||||
|
$io->write('Failed to set permissions on ' . $path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$walker = function ($dir, $perms, $io) use (&$walker, $changePerms) {
|
||||||
|
$files = array_diff(scandir($dir), ['.', '..']);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$path = $dir . '/' . $file;
|
||||||
|
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$changePerms($path, $perms, $io);
|
||||||
|
$walker($path, $perms, $io);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$worldWritable = bindec('0000000111');
|
||||||
|
$walker($dir . '/tmp', $worldWritable, $io);
|
||||||
|
$changePerms($dir . '/tmp', $worldWritable, $io);
|
||||||
|
$changePerms($dir . '/logs', $worldWritable, $io);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the security.salt value in the application's config file.
|
||||||
|
*
|
||||||
|
* @param string $dir The application's root directory.
|
||||||
|
* @param \Composer\IO\IOInterface $io IO interface to write to console.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setSecuritySalt($dir, $io)
|
||||||
|
{
|
||||||
|
$config = $dir . '/config/app.php';
|
||||||
|
$content = file_get_contents($config);
|
||||||
|
|
||||||
|
$newKey = hash('sha256', Security::randomBytes(64));
|
||||||
|
$content = str_replace('__SALT__', $newKey, $content, $count);
|
||||||
|
|
||||||
|
if ($count == 0) {
|
||||||
|
$io->write('No Security.salt placeholder to replace.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = file_put_contents($config, $content);
|
||||||
|
if ($result) {
|
||||||
|
$io->write('Updated Security.salt value in config/app.php');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$io->write('Unable to update Security.salt value.');
|
||||||
|
}
|
||||||
|
}
|
69
src/Controller/AppController.php
Normal file
69
src/Controller/AppController.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.2.9
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Cake\Controller\Controller;
|
||||||
|
use Cake\Event\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application Controller
|
||||||
|
*
|
||||||
|
* Add your application-wide methods in the class below, your controllers
|
||||||
|
* will inherit them.
|
||||||
|
*
|
||||||
|
* @link http://book.cakephp.org/3.0/en/controllers.html#the-app-controller
|
||||||
|
*/
|
||||||
|
class AppController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization hook method.
|
||||||
|
*
|
||||||
|
* Use this method to add common initialization code like loading components.
|
||||||
|
*
|
||||||
|
* e.g. `$this->loadComponent('Security');`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
parent::initialize();
|
||||||
|
|
||||||
|
$this->loadComponent('RequestHandler');
|
||||||
|
$this->loadComponent('Flash');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable the following components for recommended CakePHP security settings.
|
||||||
|
* see http://book.cakephp.org/3.0/en/controllers/components/security.html
|
||||||
|
*/
|
||||||
|
//$this->loadComponent('Security');
|
||||||
|
//$this->loadComponent('Csrf');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before render callback.
|
||||||
|
*
|
||||||
|
* @param \Cake\Event\Event $event The beforeRender event.
|
||||||
|
* @return \Cake\Network\Response|null|void
|
||||||
|
*/
|
||||||
|
public function beforeRender(Event $event)
|
||||||
|
{
|
||||||
|
if (!array_key_exists('_serialize', $this->viewVars) &&
|
||||||
|
in_array($this->response->type(), ['application/json', 'application/xml'])
|
||||||
|
) {
|
||||||
|
$this->set('_serialize', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
src/Controller/ErrorController.php
Normal file
68
src/Controller/ErrorController.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.3.4
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Cake\Event\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error Handling Controller
|
||||||
|
*
|
||||||
|
* Controller used by ExceptionRenderer to render error responses.
|
||||||
|
*/
|
||||||
|
class ErrorController extends AppController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initialization hook method.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
$this->loadComponent('RequestHandler');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* beforeFilter callback.
|
||||||
|
*
|
||||||
|
* @param \Cake\Event\Event $event Event.
|
||||||
|
* @return \Cake\Network\Response|null|void
|
||||||
|
*/
|
||||||
|
public function beforeFilter(Event $event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* beforeRender callback.
|
||||||
|
*
|
||||||
|
* @param \Cake\Event\Event $event Event.
|
||||||
|
* @return \Cake\Network\Response|null|void
|
||||||
|
*/
|
||||||
|
public function beforeRender(Event $event)
|
||||||
|
{
|
||||||
|
parent::beforeRender($event);
|
||||||
|
|
||||||
|
$this->viewBuilder()->setTemplatePath('Error');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* afterFilter callback.
|
||||||
|
*
|
||||||
|
* @param \Cake\Event\Event $event Event.
|
||||||
|
* @return \Cake\Network\Response|null|void
|
||||||
|
*/
|
||||||
|
public function afterFilter(Event $event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
617
src/Controller/MainController.php
Normal file
617
src/Controller/MainController.php
Normal file
|
@ -0,0 +1,617 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Mdanter\Ecc\EccFactory;
|
||||||
|
use Mdanter\Ecc\Crypto\Signature\Signer;
|
||||||
|
use Mdanter\Ecc\Serializer\PublicKey\PemPublicKeySerializer;
|
||||||
|
use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
|
||||||
|
use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
|
||||||
|
use Cake\Datasource\ConnectionManager;
|
||||||
|
use Cake\Log\Log;
|
||||||
|
use Endroid\QrCode\ErrorCorrectionLevel;
|
||||||
|
use Endroid\QrCode\LabelAlignment;
|
||||||
|
use Endroid\QrCode\QrCode;
|
||||||
|
|
||||||
|
class MainController extends AppController {
|
||||||
|
|
||||||
|
const rpcurl = 'http://lrpc:lrpc@127.0.0.1:9245';
|
||||||
|
|
||||||
|
const lbcPriceKey = 'lbc.price';
|
||||||
|
|
||||||
|
const bittrexMarketUrl = 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LBC';
|
||||||
|
|
||||||
|
const blockchainTickerUrl = 'https://blockchain.info/ticker';
|
||||||
|
|
||||||
|
const tagReceiptAddress = 'bLockNgmfvnnnZw7bM6SPz6hk5BVzhevEp';
|
||||||
|
|
||||||
|
protected $redis;
|
||||||
|
|
||||||
|
public function initialize() {
|
||||||
|
parent::initialize();
|
||||||
|
|
||||||
|
$this->redis = new \Predis\Client('tcp://127.0.0.1:6379');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _getLatestPrice() {
|
||||||
|
$now = new \DateTime('now', new \DateTimeZone('UTC'));
|
||||||
|
$priceInfo = new \stdClass();
|
||||||
|
$priceInfo->time = $now->format('c');
|
||||||
|
|
||||||
|
$shouldRefreshPrice = false;
|
||||||
|
if (!$this->redis->exists(self::lbcPriceKey)) {
|
||||||
|
$shouldRefreshPrice = true;
|
||||||
|
} else {
|
||||||
|
$priceInfo = json_decode($this->redis->get(self::lbcPriceKey));
|
||||||
|
$lastPriceDt = new \DateTime($priceInfo->time);
|
||||||
|
$diff = $now->diff($lastPriceDt);
|
||||||
|
$diffHours = $diff->h;
|
||||||
|
$diffHours = $diffHours + ($diff->days * 24);
|
||||||
|
if ($diffHours >= 3) {
|
||||||
|
$shouldRefreshPrice = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($shouldRefreshPrice) {
|
||||||
|
$btrxjson = json_decode(self::curl_get(self::bittrexMarketUrl));
|
||||||
|
$blckjson = json_decode(self::curl_get(self::blockchainTickerUrl));
|
||||||
|
|
||||||
|
if ($btrxjson->success) {
|
||||||
|
$onelbc = $btrxjson->result->Ask;
|
||||||
|
$lbcPrice = 0;
|
||||||
|
if (isset($blckjson->USD)) {
|
||||||
|
$lbcPrice = $onelbc * $blckjson->USD->buy;
|
||||||
|
$priceInfo->price = number_format($lbcPrice, 2, '.', '');
|
||||||
|
$priceInfo->time = $now->format('c');
|
||||||
|
$this->redis->set(self::lbcPriceKey, json_encode($priceInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$lbcUsdPrice = isset($priceInfo->price) ? '$' . $priceInfo->price : 'N/A';
|
||||||
|
return $lbcUsdPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
|
||||||
|
$lbcUsdPrice = $this->_getLatestPrice();
|
||||||
|
$this->set('lbcUsdPrice', $lbcUsdPrice);
|
||||||
|
|
||||||
|
$blocks = $this->Blocks->find()->select(['Chainwork', 'Confirmations', 'Difficulty', 'Hash', 'Height', 'TransactionHashes', 'BlockTime', 'BlockSize'])->
|
||||||
|
order(['Height' => 'desc'])->limit(6)->toArray();
|
||||||
|
for ($i = 0; $i < count($blocks); $i++) {
|
||||||
|
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||||
|
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to calculate the hashrate based on the last 12 blocks found
|
||||||
|
$diffBlocks = $this->Blocks->find()->select(['Chainwork', 'BlockTime', 'Difficulty'])->order(['Height' => 'desc'])->limit(12)->toArray();
|
||||||
|
$hashRate = 'N/A';
|
||||||
|
if (count($diffBlocks) > 1) {
|
||||||
|
$highestBlock = $diffBlocks[0];
|
||||||
|
$lowestBlock = $diffBlocks[count($diffBlocks) - 1];
|
||||||
|
|
||||||
|
$maxTime = max($highestBlock->BlockTime, $lowestBlock->BlockTime);
|
||||||
|
$minTime = min($highestBlock->BlockTime, $lowestBlock->BlockTime);
|
||||||
|
$timeDiff = $maxTime - $minTime;
|
||||||
|
$math = EccFactory::getAdapter();
|
||||||
|
$workDiff = bcsub($math->hexDec($highestBlock->Chainwork), $math->hexDec($lowestBlock->Chainwork));
|
||||||
|
if ($timeDiff > 0) {
|
||||||
|
$hashRate = $this->_formatHashRate(bcdiv($workDiff, $timeDiff)) . '/s';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->set('recentBlocks', $blocks);
|
||||||
|
$this->set('hashRate', $hashRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function realtime() {
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
$this->loadModel('Transactions');
|
||||||
|
|
||||||
|
// load 10 blocks and transactions
|
||||||
|
$conn = ConnectionManager::get('default');
|
||||||
|
$blocks = $this->Blocks->find()->select(['Height', 'BlockTime', 'TransactionHashes'])->order(['Height' => 'desc'])->limit(10)->toArray();
|
||||||
|
for ($i = 0; $i < count($blocks); $i++) {
|
||||||
|
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||||
|
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $conn->execute('SELECT T.Hash, T.InputCount, T.OutputCount, T.Value, IFNULL(T.TransactionTime, T.CreatedTime) AS TxTime ' .
|
||||||
|
'FROM Transactions T ORDER BY CreatedTime DESC LIMIT 10');
|
||||||
|
$txs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
$this->set('blocks', $blocks);
|
||||||
|
$this->set('txs', $txs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apirealtimeblocks() {
|
||||||
|
// load 10 blocks
|
||||||
|
$this->autoRender = false;
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
$blocks = $this->Blocks->find()->select(['Height', 'BlockTime', 'TransactionHashes'])->order(['Height' => 'desc'])->limit(10)->toArray();
|
||||||
|
for ($i = 0; $i < count($blocks); $i++) {
|
||||||
|
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||||
|
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||||
|
unset($blocks[$i]->TransactionHashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_jsonResponse(['success' => true, 'blocks' => $blocks]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apirealtimetx() {
|
||||||
|
// load 10 transactions
|
||||||
|
$this->autoRender = false;
|
||||||
|
$conn = ConnectionManager::get('default');
|
||||||
|
$stmt = $conn->execute('SELECT T.Hash, T.InputCount, T.OutputCount, T.Value, IFNULL(T.TransactionTime, T.CreatedTime) AS TxTime ' .
|
||||||
|
'FROM Transactions T ORDER BY CreatedTime DESC LIMIT 10');
|
||||||
|
$txs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
$this->_jsonResponse(['success' => true, 'txs' => $txs]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _formatHashRate($value) {
|
||||||
|
/*if ($value > 1000000000000) {
|
||||||
|
return number_format( $value / 1000000000000, 2, '.', '' ) . ' TH';
|
||||||
|
}*/
|
||||||
|
if ($value > 1000000000) {
|
||||||
|
return number_format( $value / 1000000000, 2, '.', '' ) . ' GH';
|
||||||
|
}
|
||||||
|
if ($value > 1000000) {
|
||||||
|
return number_format( $value / 1000000, 2, '.', '' ) . ' MH';
|
||||||
|
}
|
||||||
|
if ($value > 1000) {
|
||||||
|
return number_format( $value / 1000, 2, '.', '' ) . ' KH';
|
||||||
|
}
|
||||||
|
|
||||||
|
return number_format($value) . ' H';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find() {
|
||||||
|
$criteria = $this->request->query('q');
|
||||||
|
if ($criteria === null || strlen(trim($criteria)) == 0) {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
$this->loadModel('Addresses');
|
||||||
|
$this->loadModel('Transactions');
|
||||||
|
|
||||||
|
if (is_numeric($criteria)) {
|
||||||
|
$height = (int) $criteria;
|
||||||
|
$block = $this->Blocks->find()->select(['Id'])->where(['Height' => $height])->first();
|
||||||
|
if ($block) {
|
||||||
|
return $this->redirect('/blocks/' . $height);
|
||||||
|
}
|
||||||
|
} else if (strlen(trim($criteria)) <= 40) {
|
||||||
|
// Address
|
||||||
|
$address = $this->Addresses->find()->select(['Id', 'Address'])->where(['Address' => $criteria])->first();
|
||||||
|
if ($address) {
|
||||||
|
return $this->redirect('/address/' . $address->Address);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Try block hash first
|
||||||
|
$block = $this->Blocks->find()->select(['Height'])->where(['Hash' => $criteria])->first();
|
||||||
|
if ($block) {
|
||||||
|
return $this->redirect('/blocks/' . $block->Height);
|
||||||
|
} else {
|
||||||
|
$tx = $this->Transactions->find()->select(['Hash'])->where(['Hash' => $criteria])->first();
|
||||||
|
if ($tx) {
|
||||||
|
return $this->redirect('/tx/' . $tx->Hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found, redirect to index
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function blocks($height = null) {
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
|
||||||
|
if ($height === null) {
|
||||||
|
// paginate blocks
|
||||||
|
return $this->redirect('/');
|
||||||
|
} else {
|
||||||
|
$this->loadModel('Transactions');
|
||||||
|
$height = intval($height);
|
||||||
|
if ($height < 0) {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$block = $this->Blocks->find()->where(['Height' => $height])->first();
|
||||||
|
if (!$block) {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// update the block confirmations
|
||||||
|
$req = ['method' => 'getblock', 'params' => [$block->Hash]];
|
||||||
|
$response = self::curl_json_post(self::rpcurl, json_encode($req));
|
||||||
|
$json = json_decode($response);
|
||||||
|
$rpc_block = $json->result;
|
||||||
|
if (isset($rpc_block->confirmations)) {
|
||||||
|
$block->Confirmations = $rpc_block->confirmations;
|
||||||
|
$conn = ConnectionManager::get('default');
|
||||||
|
$conn->execute('UPDATE Blocks SET Confirmations = ? WHERE Id = ?', [$rpc_block->confirmations, $block->Id]);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// try again next time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the basic block transaction info
|
||||||
|
$txs = $this->Transactions->find()->select(['InputCount', 'OutputCount', 'Hash', 'Value', 'Version'])->where(['BlockHash' => $block->Hash])->toArray();
|
||||||
|
|
||||||
|
$this->set('block', $block);
|
||||||
|
$this->set('blockTxs', $txs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tx($hash = null) {
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
$this->loadModel('Transactions');
|
||||||
|
$this->loadModel('Inputs');
|
||||||
|
$this->loadModel('Outputs');
|
||||||
|
$sourceAddress = $this->request->query('address');
|
||||||
|
|
||||||
|
if (!$hash) {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$tx = $this->Transactions->find()->select(
|
||||||
|
['Id', 'BlockHash', 'InputCount', 'OutputCount', 'Hash', 'Value', 'TransactionTime', 'TransactionSize', 'Created', 'Version', 'LockTime', 'Raw'])->where(['Hash' => $hash])->first();
|
||||||
|
if (!$tx) {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tx->TransactionSize == 0) {
|
||||||
|
$tx->TransactionSize = (strlen($tx->Raw) / 2);
|
||||||
|
$conn = ConnectionManager::get('default');
|
||||||
|
$conn->execute('UPDATE Transactions SET TransactionSize = ? WHERE Id = ?', [$tx->TransactionSize, $tx->Id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$block = $this->Blocks->find()->select(['Confirmations', 'Height'])->where(['Hash' => $tx->BlockHash])->first();
|
||||||
|
$inputs = $this->Inputs->find()->contain(['InputAddresses'])->where(['TransactionId' => $tx->Id])->order(['PrevoutN' => 'asc'])->toArray();
|
||||||
|
$outputs = $this->Outputs->find()->contain(['OutputAddresses', 'SpendInput' => ['fields' => ['Id', 'TransactionHash', 'PrevoutN', 'PrevoutHash']]])->where(['Outputs.TransactionId' => $tx->Id])->order(['Vout' => 'asc'])->toArray();
|
||||||
|
for ($i = 0; $i < count($outputs); $i++) {
|
||||||
|
$outputs[$i]->IsClaim = (strpos($outputs[$i]->ScriptPubKeyAsm, 'CLAIM') > -1);
|
||||||
|
$outputs[$i]->IsSupportClaim = (strpos($outputs[$i]->ScriptPubKeyAsm, 'SUPPORT_CLAIM') > -1);
|
||||||
|
$outputs[$i]->IsUpdateClaim = (strpos($outputs[$i]->ScriptPubKeyAsm, 'UPDATE_CLAIM') > -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalIn = 0;
|
||||||
|
$totalOut = 0;
|
||||||
|
$fee = 0;
|
||||||
|
foreach ($inputs as $in) {
|
||||||
|
$totalIn = bcadd($totalIn, $in->Value, 8);
|
||||||
|
}
|
||||||
|
foreach ($outputs as $out) {
|
||||||
|
$totalOut = bcadd($totalOut, $out->Value, 8);
|
||||||
|
}
|
||||||
|
$fee = bcsub($totalIn, $totalOut, 8);
|
||||||
|
|
||||||
|
$this->set('tx', $tx);
|
||||||
|
$this->set('block', $block);
|
||||||
|
$this->set('confirmations', $block ? number_format($block->Confirmations, 0, '', ',') : '0');
|
||||||
|
$this->set('fee', $fee);
|
||||||
|
$this->set('inputs', $inputs);
|
||||||
|
$this->set('outputs', $outputs);
|
||||||
|
$this->set('sourceAddress', $sourceAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function address($addr = null) {
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
$this->loadModel('Addresses');
|
||||||
|
$this->loadModel('Transactions');
|
||||||
|
$this->loadModel('Inputs');
|
||||||
|
$this->loadModel('Outputs');
|
||||||
|
|
||||||
|
if (!$addr) {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$canTag = false;
|
||||||
|
$totalRecvAmount = 0;
|
||||||
|
$totalSentAmount = 0;
|
||||||
|
$balanceAmount = 0;
|
||||||
|
$recentTxs = [];
|
||||||
|
|
||||||
|
$tagRequestAmount = 0;
|
||||||
|
// Check for pending tag request
|
||||||
|
$this->loadModel('TagAddressRequests');
|
||||||
|
$pending = $this->TagAddressRequests->find()->where(['Address' => $addr, 'IsVerified <>' => 1])->first();
|
||||||
|
if (!$pending) {
|
||||||
|
$tagRequestAmount = '25.' . rand(11111111, 99999999);
|
||||||
|
}
|
||||||
|
|
||||||
|
$address = $this->Addresses->find()->where(['Address' => $addr])->first();
|
||||||
|
if (!$address) {
|
||||||
|
if (strlen($addr) === 34) {
|
||||||
|
$address = new \stdClass();
|
||||||
|
$address->Address = $addr;
|
||||||
|
} else {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$conn = ConnectionManager::get('default');
|
||||||
|
|
||||||
|
$canTag = true;
|
||||||
|
$addressId = $address->Id;
|
||||||
|
|
||||||
|
$stmt = $conn->execute('SELECT A.TotalReceived, A.TotalSent FROM Addresses A WHERE A.Id = ?', [$address->Id]);
|
||||||
|
$totals = $stmt->fetch(\PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
$stmt = $conn->execute('SELECT T.Id, T.Hash, T.InputCount, T.OutputCount, T.Value, ' .
|
||||||
|
'TA.DebitAmount, TA.CreditAmount, ' .
|
||||||
|
'B.Height, B.Confirmations, IFNULL(T.TransactionTime, T.CreatedTime) AS TxTime ' .
|
||||||
|
'FROM Transactions T ' .
|
||||||
|
'LEFT JOIN Blocks B ON T.BlockHash = B.Hash ' .
|
||||||
|
'RIGHT JOIN (SELECT TransactionId, DebitAmount, CreditAmount FROM TransactionsAddresses ' .
|
||||||
|
' WHERE AddressId = ? ORDER BY TransactionTime DESC LIMIT 0, 20) TA ON TA.TransactionId = T.Id', [$addressId]);
|
||||||
|
$recentTxs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
$totalRecvAmount = $totals->TotalReceived == 0 ? '0' : $totals->TotalReceived + 0;
|
||||||
|
$totalSentAmount = $totals->TotalSent == 0 ? '0' : $totals->TotalSent + 0;
|
||||||
|
$balanceAmount = bcsub($totalRecvAmount, $totalSentAmount, 8) + 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->set('canTag', $canTag);
|
||||||
|
$this->set('pending', $pending);
|
||||||
|
$this->set('tagRequestAmount', $tagRequestAmount);
|
||||||
|
$this->set('address', $address);
|
||||||
|
$this->set('totalReceived', $totalRecvAmount);
|
||||||
|
$this->set('totalSent', $totalSentAmount);
|
||||||
|
$this->set('balanceAmount', $balanceAmount);
|
||||||
|
$this->set('recentTxs', $recentTxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function qr($data = null) {
|
||||||
|
$this->autoRender = false;
|
||||||
|
|
||||||
|
if (!$data || strlen(trim($data)) == 0 || strlen(trim($data)) > 50) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$qrCode = new QrCode($data);
|
||||||
|
$qrCode->setSize(300);
|
||||||
|
|
||||||
|
// Set advanced options
|
||||||
|
$qrCode
|
||||||
|
->setWriterByName('png')
|
||||||
|
->setMargin(10)
|
||||||
|
->setEncoding('UTF-8')
|
||||||
|
->setErrorCorrectionLevel(ErrorCorrectionLevel::LOW)
|
||||||
|
->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0])
|
||||||
|
->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255])
|
||||||
|
->setLogoWidth(150)
|
||||||
|
->setValidateResult(false);
|
||||||
|
|
||||||
|
header('Content-Type: '.$qrCode->getContentType());
|
||||||
|
echo $qrCode->writeString();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function curl_get($url) {
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
Log::debug('Request execution completed.');
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
$error = curl_error($ch);
|
||||||
|
$errno = curl_errno($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
throw new \Exception(sprintf('The request failed: %s', $error), $errno);
|
||||||
|
} else {
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apistatus() {
|
||||||
|
$this->autoRender = false;
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
|
||||||
|
// Get the max height block
|
||||||
|
$height = 0;
|
||||||
|
$difficulty = 0;
|
||||||
|
$highestBlock = $this->Blocks->find()->select(['Height', 'Difficulty'])->order(['Height' => 'desc'])->first();
|
||||||
|
$height = $highestBlock->Height;
|
||||||
|
$difficulty = $highestBlock->Difficulty;
|
||||||
|
$lbcUsdPrice = $this->_getLatestPrice();
|
||||||
|
|
||||||
|
// Calculate hash rate
|
||||||
|
$diffBlocks = $this->Blocks->find()->select(['Chainwork', 'BlockTime', 'Difficulty'])->order(['Height' => 'desc'])->limit(12)->toArray();
|
||||||
|
$hashRate = 'N/A';
|
||||||
|
if (count($diffBlocks) > 1) {
|
||||||
|
$highestBlock = $diffBlocks[0];
|
||||||
|
$lowestBlock = $diffBlocks[count($diffBlocks) - 1];
|
||||||
|
|
||||||
|
$maxTime = max($highestBlock->BlockTime, $lowestBlock->BlockTime);
|
||||||
|
$minTime = min($highestBlock->BlockTime, $lowestBlock->BlockTime);
|
||||||
|
$timeDiff = $maxTime - $minTime;
|
||||||
|
$math = EccFactory::getAdapter();
|
||||||
|
$workDiff = bcsub($math->hexDec($highestBlock->Chainwork), $math->hexDec($lowestBlock->Chainwork));
|
||||||
|
if ($timeDiff > 0) {
|
||||||
|
$hashRate = $this->_formatHashRate(bcdiv($workDiff, $timeDiff)) . '/s';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_jsonResponse(['success' => true, 'status' => [
|
||||||
|
'height' => $height,
|
||||||
|
'difficulty' => number_format($difficulty, 2, '.', ''),
|
||||||
|
'price' => $lbcUsdPrice,
|
||||||
|
'hashrate' => $hashRate
|
||||||
|
]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apirecentblocks() {
|
||||||
|
$this->autoRender = false;
|
||||||
|
$this->loadModel('Blocks');
|
||||||
|
$blocks = $this->Blocks->find()->select(['Difficulty', 'Hash', 'Height', 'TransactionHashes', 'BlockTime', 'BlockSize'])->
|
||||||
|
order(['Height' => 'desc'])->limit(6)->toArray();
|
||||||
|
for ($i = 0; $i < count($blocks); $i++) {
|
||||||
|
$tx_hashes = json_decode($blocks[$i]->TransactionHashes);
|
||||||
|
$blocks[$i]->TransactionCount = count($tx_hashes);
|
||||||
|
$blocks[$i]->Difficulty = number_format($blocks[$i]->Difficulty, 2, '.', '');
|
||||||
|
unset($blocks[$i]->TransactionHashes);
|
||||||
|
}
|
||||||
|
return $this->_jsonResponse(['success' => true, 'blocks' => $blocks]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apiaddrtag($base58address = null) {
|
||||||
|
$this->autoRender = false;
|
||||||
|
if (!isset($base58address) || strlen(trim($base58address)) !== 34) {
|
||||||
|
return $this->_jsonError('Invalid base58 address not specified.', 400);
|
||||||
|
}
|
||||||
|
if (!$this->request->is('post')) {
|
||||||
|
return $this->_jsonError('Invalid HTTP request method.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trim($base58address) == self::tagReceiptAddress) {
|
||||||
|
return $this->_jsonError('You cannot submit a tag request for this address.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadModel('Addresses');
|
||||||
|
$this->loadModel('TagAddressRequests');
|
||||||
|
$data = [
|
||||||
|
'Address' => $base58address,
|
||||||
|
'Tag' => trim($this->request->data('tag')),
|
||||||
|
'TagUrl' => trim($this->request->data('url')),
|
||||||
|
'VerificationAmount' => $this->request->data('vamount')
|
||||||
|
];
|
||||||
|
|
||||||
|
// verify
|
||||||
|
$entity = $this->TagAddressRequests->newEntity($data);
|
||||||
|
if (strlen($entity->Tag) === 0 || strlen($entity->Tag) > 30) {
|
||||||
|
return $this->_jsonError('Oops! Please specify a valid tag. It should be no more than 30 characters long.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($entity->TagUrl) > 0) {
|
||||||
|
if (strlen($entity->TagUrl) > 200) {
|
||||||
|
return $this->_jsonError('Oops! The link should be no more than 200 characters long.', 400);
|
||||||
|
}
|
||||||
|
if (!filter_var($entity->TagUrl, FILTER_VALIDATE_URL)) {
|
||||||
|
return $this->_jsonError('Oops! The link should be a valid URL.', 400);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unset($entity->TagUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entity->VerificationAmount < 25.1 || $entity->VerificationAmount > 25.99999999) {
|
||||||
|
return $this->_jsonError('Oops! The verification amount is invalid. Please refresh the page and try again.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the tag is taken
|
||||||
|
$addrTag = $this->Addresses->find()->select(['Id'])->where(['LOWER(Tag)' => strtolower($entity->Tag)])->first();
|
||||||
|
if ($addrTag) {
|
||||||
|
return $this->_jsonError('Oops! The tag is already taken. Please specify a different tag.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for existing verification
|
||||||
|
$exist = $this->TagAddressRequests->find()->select(['Id'])->where(['Address' => $base58address, 'IsVerified' => 0])->first();
|
||||||
|
if ($exist) {
|
||||||
|
return $this->_jsonError('Oops! There is a pending tag verification for this address.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the request
|
||||||
|
if (!$this->TagAddressRequests->save($entity)) {
|
||||||
|
return $this->_jsonError('Oops! The verification request could not be saved. If this problem persists, please send an email to hello@aureolin.co');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_jsonResponse(['success' => true, 'tag' => $entity->Tag]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apiaddrutxo($base58address = null) {
|
||||||
|
$this->autoRender = false;
|
||||||
|
$this->loadModel('Addresses');
|
||||||
|
|
||||||
|
if (!isset($base58address)) {
|
||||||
|
return $this->_jsonError('Base58 address not specified.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$arr = explode(',', $base58address);
|
||||||
|
$addresses = $this->Addresses->find()->select(['Id'])->where(['Address IN' => $arr])->toArray();
|
||||||
|
if (count($addresses) == 0) {
|
||||||
|
return $this->_jsonError('No base58 address matching the specified parameter was found.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$addressIds = [];
|
||||||
|
$params = [];
|
||||||
|
foreach ($addresses as $address) {
|
||||||
|
$addressIds[] = $address->Id;
|
||||||
|
$params[] = '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the unspent outputs for the address
|
||||||
|
$conn = ConnectionManager::get('default');
|
||||||
|
$stmt = $conn->execute(sprintf(
|
||||||
|
'SELECT T.Hash AS TransactionHash, O.Vout, O.Value, O.Addresses, O.ScriptPubKeyAsm, O.ScriptPubKeyHex, O.Type, O.RequiredSignatures, B.Confirmations ' .
|
||||||
|
'FROM Transactions T ' .
|
||||||
|
'JOIN Outputs O ON O.TransactionId = T.Id ' .
|
||||||
|
'JOIN Blocks B ON B.Hash = T.BlockHash ' .
|
||||||
|
'WHERE O.Id IN (SELECT OutputId FROM OutputsAddresses WHERE AddressId IN (%s)) AND O.IsSpent <> 1 ORDER BY T.TransactionTime ASC', implode(',', $params)), $addressIds);
|
||||||
|
$outputs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
$utxo = [];
|
||||||
|
foreach ($outputs as $out) {
|
||||||
|
$utxo[] = [
|
||||||
|
'transaction_hash' => $out->TransactionHash,
|
||||||
|
'output_index' => $out->Vout,
|
||||||
|
'value' => (int) bcmul($out->Value, 100000000),
|
||||||
|
'addresses' => json_decode($out->Addresses),
|
||||||
|
'script' => $out->ScriptPubKeyAsm,
|
||||||
|
'script_hex' => $out->ScriptPubKeyHex,
|
||||||
|
'script_type' => $out->Type,
|
||||||
|
'required_signatures' => (int) $out->RequiredSignatures,
|
||||||
|
'spent' => false,
|
||||||
|
'confirmations' => (int) $out->Confirmations
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_jsonResponse(['success' => true, 'utxo' => $utxo]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _jsonResponse($object = [], $statusCode = null)
|
||||||
|
{
|
||||||
|
$this->response->statusCode($statusCode);
|
||||||
|
$this->response->type('json');
|
||||||
|
$this->response->body(json_encode($object));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _jsonError($message, $statusCode = null) {
|
||||||
|
return $this->_jsonResponse(['error' => true, 'message' => $message], $statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function curl_json_post($url, $data, $headers = []) {
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
if ($response === false) {
|
||||||
|
$error = curl_error($ch);
|
||||||
|
$errno = curl_errno($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
throw new \Exception(sprintf('The request failed: %s', $error), $errno);
|
||||||
|
} else {
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close any open file handle
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
69
src/Controller/PagesController.php
Normal file
69
src/Controller/PagesController.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.2.9
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Network\Exception\ForbiddenException;
|
||||||
|
use Cake\Network\Exception\NotFoundException;
|
||||||
|
use Cake\View\Exception\MissingTemplateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static content controller
|
||||||
|
*
|
||||||
|
* This controller will render views from Template/Pages/
|
||||||
|
*
|
||||||
|
* @link http://book.cakephp.org/3.0/en/controllers/pages-controller.html
|
||||||
|
*/
|
||||||
|
class PagesController extends AppController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a view
|
||||||
|
*
|
||||||
|
* @param string ...$path Path segments.
|
||||||
|
* @return void|\Cake\Network\Response
|
||||||
|
* @throws \Cake\Network\Exception\ForbiddenException When a directory traversal attempt.
|
||||||
|
* @throws \Cake\Network\Exception\NotFoundException When the view file could not
|
||||||
|
* be found or \Cake\View\Exception\MissingTemplateException in debug mode.
|
||||||
|
*/
|
||||||
|
public function display(...$path)
|
||||||
|
{
|
||||||
|
$count = count($path);
|
||||||
|
if (!$count) {
|
||||||
|
return $this->redirect('/');
|
||||||
|
}
|
||||||
|
if (in_array('..', $path, true) || in_array('.', $path, true)) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
$page = $subpage = null;
|
||||||
|
|
||||||
|
if (!empty($path[0])) {
|
||||||
|
$page = $path[0];
|
||||||
|
}
|
||||||
|
if (!empty($path[1])) {
|
||||||
|
$subpage = $path[1];
|
||||||
|
}
|
||||||
|
$this->set(compact('page', 'subpage'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->render(implode('/', $path));
|
||||||
|
} catch (MissingTemplateException $e) {
|
||||||
|
if (Configure::read('debug')) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/Model/Behavior/SimpleAuditBehavior.php
Normal file
67
src/Model/Behavior/SimpleAuditBehavior.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Behavior;
|
||||||
|
|
||||||
|
use Cake\ORM\Behavior;
|
||||||
|
use Cake\ORM\Entity;
|
||||||
|
use Cake\Event\Event;
|
||||||
|
use Cake\Routing\Router;
|
||||||
|
|
||||||
|
class SimpleAuditBehavior extends Behavior {
|
||||||
|
private static $DefaultUser = '0';
|
||||||
|
|
||||||
|
private $_userField = 'Id';
|
||||||
|
|
||||||
|
protected $_defaultConfig = [
|
||||||
|
'abortOnUserInvalid' => false,
|
||||||
|
'implementedMethods' => ['audit' => 'audit'],
|
||||||
|
'fieldMap' => [
|
||||||
|
'CreatedOn' => 'Created',
|
||||||
|
'ModifiedOn' => 'Modified',
|
||||||
|
'CreatedBy' => 'CreatedBy',
|
||||||
|
'ModifiedBy' => 'ModifiedBy'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
public function audit(Entity $entity, $systemOperation = false) {
|
||||||
|
$time = $this->_currentUtcTime()->format('Y-m-d H:i:s');
|
||||||
|
$user = ($systemOperation) ? self::$DefaultUser : $this->_currentUser();
|
||||||
|
|
||||||
|
if (!$systemOperation
|
||||||
|
&& $this->_config['abortOnUserInvalid']
|
||||||
|
&& $user == self::$DefaultUser)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldMap = $this->_config['fieldMap'];
|
||||||
|
|
||||||
|
if ($entity->isNew()) {
|
||||||
|
$entity->set($fieldMap['CreatedOn'], $time);
|
||||||
|
$entity->set($fieldMap['CreatedBy'], $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity->set($fieldMap['ModifiedOn'], $time);
|
||||||
|
$entity->set($fieldMap['ModifiedBy'], $user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beforeSave(Event $event, Entity $entity) {
|
||||||
|
return $this->audit($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _currentUtcTime() {
|
||||||
|
return new \DateTime('now', new \DateTimeZone('UTC'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _currentUser() {
|
||||||
|
$request = Router::getRequest(true);
|
||||||
|
if ($request) {
|
||||||
|
$session = $request->session();
|
||||||
|
$fieldValue = $session->read(sprintf('Auth.User.' . $this->_userField));
|
||||||
|
return (intval($fieldValue) > 0) ? $fieldValue : self::$DefaultUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
11
src/Model/Entity/Address.php
Normal file
11
src/Model/Entity/Address.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Entity;
|
||||||
|
|
||||||
|
use Cake\ORM\Entity;
|
||||||
|
|
||||||
|
class Address extends Entity {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
11
src/Model/Entity/Block.php
Normal file
11
src/Model/Entity/Block.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Entity;
|
||||||
|
|
||||||
|
use Cake\ORM\Entity;
|
||||||
|
|
||||||
|
class Block extends Entity {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
11
src/Model/Entity/Input.php
Normal file
11
src/Model/Entity/Input.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Entity;
|
||||||
|
|
||||||
|
use Cake\ORM\Entity;
|
||||||
|
|
||||||
|
class Input extends Entity {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
11
src/Model/Entity/Output.php
Normal file
11
src/Model/Entity/Output.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Entity;
|
||||||
|
|
||||||
|
use Cake\ORM\Entity;
|
||||||
|
|
||||||
|
class Output extends Entity {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
11
src/Model/Entity/TagAddressRequest.php
Normal file
11
src/Model/Entity/TagAddressRequest.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Entity;
|
||||||
|
|
||||||
|
use Cake\ORM\Entity;
|
||||||
|
|
||||||
|
class TagAddressRequest extends Entity {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
11
src/Model/Entity/Transaction.php
Normal file
11
src/Model/Entity/Transaction.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Entity;
|
||||||
|
|
||||||
|
use Cake\ORM\Entity;
|
||||||
|
|
||||||
|
class Transaction extends Entity {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
18
src/Model/Table/AddressesTable.php
Normal file
18
src/Model/Table/AddressesTable.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Table;
|
||||||
|
|
||||||
|
use Cake\ORM\Table;
|
||||||
|
|
||||||
|
class AddressesTable extends Table {
|
||||||
|
public function initialize(array $config) {
|
||||||
|
parent::initialize($config);
|
||||||
|
|
||||||
|
$this->primaryKey('Id');
|
||||||
|
$this->table('Addresses');
|
||||||
|
|
||||||
|
$this->addBehavior('SimpleAudit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
18
src/Model/Table/BlocksTable.php
Normal file
18
src/Model/Table/BlocksTable.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Table;
|
||||||
|
|
||||||
|
use Cake\ORM\Table;
|
||||||
|
|
||||||
|
class BlocksTable extends Table {
|
||||||
|
public function initialize(array $config) {
|
||||||
|
parent::initialize($config);
|
||||||
|
|
||||||
|
$this->primaryKey('Id');
|
||||||
|
$this->table('Blocks');
|
||||||
|
|
||||||
|
$this->addBehavior('SimpleAudit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
30
src/Model/Table/InputsTable.php
Normal file
30
src/Model/Table/InputsTable.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Table;
|
||||||
|
|
||||||
|
use Cake\ORM\Table;
|
||||||
|
|
||||||
|
class InputsTable extends Table {
|
||||||
|
public function initialize(array $config) {
|
||||||
|
parent::initialize($config);
|
||||||
|
|
||||||
|
$this->primaryKey('Id');
|
||||||
|
$this->table('Inputs');
|
||||||
|
|
||||||
|
$this->addBehavior('SimpleAudit');
|
||||||
|
|
||||||
|
$this->addAssociations([
|
||||||
|
'belongsToMany' => [
|
||||||
|
'InputAddresses' => [
|
||||||
|
'className' => 'App\Model\Table\AddressesTable',
|
||||||
|
'joinTable' => 'InputsAddresses',
|
||||||
|
'foreignKey' => 'InputId',
|
||||||
|
'targetForeignKey' => 'AddressId',
|
||||||
|
'propertyName' => 'InputAddresses'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
37
src/Model/Table/OutputsTable.php
Normal file
37
src/Model/Table/OutputsTable.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Table;
|
||||||
|
|
||||||
|
use Cake\ORM\Table;
|
||||||
|
|
||||||
|
class OutputsTable extends Table {
|
||||||
|
public function initialize(array $config) {
|
||||||
|
parent::initialize($config);
|
||||||
|
|
||||||
|
$this->primaryKey('Id');
|
||||||
|
$this->table('Outputs');
|
||||||
|
|
||||||
|
$this->addBehavior('SimpleAudit');
|
||||||
|
|
||||||
|
$this->addAssociations([
|
||||||
|
'belongsTo' => [
|
||||||
|
'SpendInput' => [
|
||||||
|
'className' => 'App\Model\Table\InputsTable',
|
||||||
|
'foreignKey' => 'SpentByInputId',
|
||||||
|
'propertyName' => 'SpendInput'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'belongsToMany' => [
|
||||||
|
'OutputAddresses' => [
|
||||||
|
'className' => 'App\Model\Table\AddressesTable',
|
||||||
|
'joinTable' => 'OutputsAddresses',
|
||||||
|
'foreignKey' => 'OutputId',
|
||||||
|
'targetForeignKey' => 'AddressId',
|
||||||
|
'propertyName' => 'OutputAddresses'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
18
src/Model/Table/TagAddressRequestsTable.php
Normal file
18
src/Model/Table/TagAddressRequestsTable.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Table;
|
||||||
|
|
||||||
|
use Cake\ORM\Table;
|
||||||
|
|
||||||
|
class TagAddressRequestsTable extends Table {
|
||||||
|
public function initialize(array $config) {
|
||||||
|
parent::initialize($config);
|
||||||
|
|
||||||
|
$this->primaryKey('Id');
|
||||||
|
$this->table('TagAddressRequests');
|
||||||
|
|
||||||
|
$this->addBehavior('SimpleAudit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
18
src/Model/Table/TransactionsTable.php
Normal file
18
src/Model/Table/TransactionsTable.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Table;
|
||||||
|
|
||||||
|
use Cake\ORM\Table;
|
||||||
|
|
||||||
|
class TransactionsTable extends Table {
|
||||||
|
public function initialize(array $config) {
|
||||||
|
parent::initialize($config);
|
||||||
|
|
||||||
|
$this->primaryKey('Id');
|
||||||
|
$this->table('Transactions');
|
||||||
|
|
||||||
|
$this->addBehavior('SimpleAudit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
140
src/Shell/AuxShell.php
Normal file
140
src/Shell/AuxShell.php
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Shell;
|
||||||
|
|
||||||
|
use Cake\Console\ConsoleOutput;
|
||||||
|
use Cake\Console\Shell;
|
||||||
|
use Cake\Datasource\ConnectionManager;
|
||||||
|
use Cake\Log\Log;
|
||||||
|
use Mdanter\Ecc\EccFactory;
|
||||||
|
|
||||||
|
class AuxShell extends Shell {
|
||||||
|
|
||||||
|
const pubKeyAddress = [0, 85];
|
||||||
|
|
||||||
|
const scriptAddress = [5, 122];
|
||||||
|
|
||||||
|
const rpcurl = 'http://lrpc:lrpc@127.0.0.1:9245';
|
||||||
|
|
||||||
|
const tagrcptaddress = 'bLockNgmfvnnnZw7bM6SPz6hk5BVzhevEp';
|
||||||
|
|
||||||
|
public function initialize() {
|
||||||
|
parent::initialize();
|
||||||
|
$this->loadModel('Addresses');
|
||||||
|
$this->loadModel('Inputs');
|
||||||
|
$this->loadModel('Outputs');
|
||||||
|
$this->loadModel('Transactions');
|
||||||
|
$this->loadModel('TagAddressRequests');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main() {
|
||||||
|
echo "No arguments specified.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verifytags() {
|
||||||
|
self::lock('auxverify');
|
||||||
|
|
||||||
|
$conn = ConnectionManager::get('default');
|
||||||
|
$requests = $this->TagAddressRequests->find()->where(['IsVerified <>' => 1])->toArray();
|
||||||
|
foreach ($requests as $req) {
|
||||||
|
echo "Verifying tag for $req->Address, amount: $req->VerificationAmount... ";
|
||||||
|
|
||||||
|
$req_date = $req->Created;
|
||||||
|
$src_addr = $req->Address;
|
||||||
|
$dst_addr = self::tagrcptaddress;
|
||||||
|
|
||||||
|
// find a transaction with the corresponding inputs created on or after the date
|
||||||
|
// look for the address ids
|
||||||
|
$address = $this->Addresses->find()->select(['Id'])->where(['Address' => $src_addr])->first();
|
||||||
|
$veri_address = $this->Addresses->find()->select(['Id'])->where(['Address' => $dst_addr])->first(); // TODO: Redis cache?
|
||||||
|
if (!$address || !$veri_address) {
|
||||||
|
echo "could not find source nor verification addresses. Skipping.\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$src_addr_id = $address->Id;
|
||||||
|
$dst_addr_id = $veri_address->Id;
|
||||||
|
|
||||||
|
// find the inputs for the source address that were created after $req->Created - 1 hour
|
||||||
|
$req_date->sub(new \DateInterval('PT1H'));
|
||||||
|
$stmt = $conn->execute('SELECT DISTINCT I.TransactionId FROM Inputs I ' .
|
||||||
|
'RIGHT JOIN (SELECT IIA.InputId FROM InputsAddresses IIA WHERE IIA.AddressId = ?) IA ON IA.InputId = I.Id ' .
|
||||||
|
'JOIN Transactions T ON T.Id = I.TransactionId ' .
|
||||||
|
'LEFT JOIN Blocks B ON B.Hash = T.BlockHash ' .
|
||||||
|
'WHERE B.Confirmations > 0 AND DATE(I.Created) >= ?', [$src_addr_id, $req_date->format('Y-m-d')]);
|
||||||
|
$tx_inputs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||||
|
|
||||||
|
|
||||||
|
$param_values = [$dst_addr_id];
|
||||||
|
$params = [];
|
||||||
|
foreach ($tx_inputs as $in) {
|
||||||
|
$params[] = '?';
|
||||||
|
$param_values[] = $in->TransactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$num_inputs = count($tx_inputs);
|
||||||
|
echo "***found $num_inputs inputs from address $src_addr.\n";
|
||||||
|
|
||||||
|
if ($num_inputs == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// check the outputs with the dst address
|
||||||
|
$total_amount = 0;
|
||||||
|
$stmt = $conn->execute(sprintf('SELECT O.Value FROM Outputs O ' .
|
||||||
|
'RIGHT JOIN (SELECT IOA.OutputId, IOA.AddressId FROM OutputsAddresses IOA WHERE IOA.AddressId = ?) OA ON OA.OutputId = O.Id ' .
|
||||||
|
'WHERE O.TransactionId IN (%s)', implode(', ', $params)), $param_values);
|
||||||
|
$tx_outputs = $stmt->fetchAll(\PDO::FETCH_OBJ);
|
||||||
|
foreach ($tx_outputs as $out) {
|
||||||
|
echo "***found output to verification address with value " . $out->Value . "\n";
|
||||||
|
$total_amount = bcadd($total_amount, $out->Value, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($total_amount >= $req->VerificationAmount) {
|
||||||
|
$conn->begin();
|
||||||
|
echo "***$total_amount is gte verification amount: $req->VerificationAmount.\n";
|
||||||
|
|
||||||
|
// Update the tag in the DB
|
||||||
|
$conn->execute('UPDATE Addresses SET Tag = ?, TagUrl = ? WHERE Address = ?', [$req->Tag, $req->TagUrl, $src_addr]);
|
||||||
|
|
||||||
|
// Set the request as verified
|
||||||
|
$conn->execute('UPDATE TagAddressRequests SET IsVerified = 1 WHERE Id = ?', [$req->Id]);
|
||||||
|
|
||||||
|
$conn->commit();
|
||||||
|
echo "Data committed.\n";
|
||||||
|
} else {
|
||||||
|
echo "***$total_amount is NOT up to verification amount: $req->VerificationAmount.\n";
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
print_r($e);
|
||||||
|
echo "Rolling back.\n";
|
||||||
|
$conn->rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::unlock('auxverify');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function lock($process_name) {
|
||||||
|
if (!is_dir(TMP . 'lock')) {
|
||||||
|
mkdir(TMP . 'lock');
|
||||||
|
}
|
||||||
|
$lock_file = TMP . 'lock' . DS . $process_name;
|
||||||
|
if (file_exists($lock_file)) {
|
||||||
|
echo "$process_name is already running.\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
file_put_contents($lock_file, '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function unlock($process_name) {
|
||||||
|
$lock_file = TMP . 'lock' . DS . $process_name;
|
||||||
|
if (file_exists($lock_file)) {
|
||||||
|
unlink($lock_file);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
1707
src/Shell/BlockShell.php
Normal file
1707
src/Shell/BlockShell.php
Normal file
File diff suppressed because it is too large
Load diff
81
src/Shell/ConsoleShell.php
Normal file
81
src/Shell/ConsoleShell.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.0.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\Shell;
|
||||||
|
|
||||||
|
use Cake\Console\ConsoleOptionParser;
|
||||||
|
use Cake\Console\Shell;
|
||||||
|
use Cake\Log\Log;
|
||||||
|
use Psy\Shell as PsyShell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple console wrapper around Psy\Shell.
|
||||||
|
*/
|
||||||
|
class ConsoleShell extends Shell
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the shell and interactive console.
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function main()
|
||||||
|
{
|
||||||
|
if (!class_exists('Psy\Shell')) {
|
||||||
|
$this->err('<error>Unable to load Psy\Shell.</error>');
|
||||||
|
$this->err('');
|
||||||
|
$this->err('Make sure you have installed psysh as a dependency,');
|
||||||
|
$this->err('and that Psy\Shell is registered in your autoloader.');
|
||||||
|
$this->err('');
|
||||||
|
$this->err('If you are using composer run');
|
||||||
|
$this->err('');
|
||||||
|
$this->err('<info>$ php composer.phar require --dev psy/psysh</info>');
|
||||||
|
$this->err('');
|
||||||
|
|
||||||
|
return self::CODE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->out("You can exit with <info>`CTRL-C`</info> or <info>`exit`</info>");
|
||||||
|
$this->out('');
|
||||||
|
|
||||||
|
Log::drop('debug');
|
||||||
|
Log::drop('error');
|
||||||
|
$this->_io->setLoggers(false);
|
||||||
|
restore_error_handler();
|
||||||
|
restore_exception_handler();
|
||||||
|
|
||||||
|
$psy = new PsyShell();
|
||||||
|
$psy->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display help for this console.
|
||||||
|
*
|
||||||
|
* @return \Cake\Console\ConsoleOptionParser
|
||||||
|
*/
|
||||||
|
public function getOptionParser()
|
||||||
|
{
|
||||||
|
$parser = new ConsoleOptionParser('console');
|
||||||
|
$parser->setDescription(
|
||||||
|
'This shell provides a REPL that you can use to interact ' .
|
||||||
|
'with your application in an interactive fashion. You can use ' .
|
||||||
|
'it to run adhoc queries with your models, or experiment ' .
|
||||||
|
'and explore the features of CakePHP and your application.' .
|
||||||
|
"\n\n" .
|
||||||
|
'You will need to have psysh installed for this Shell to work.'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $parser;
|
||||||
|
}
|
||||||
|
}
|
10
src/Template/Element/Flash/default.ctp
Normal file
10
src/Template/Element/Flash/default.ctp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
$class = 'message';
|
||||||
|
if (!empty($params['class'])) {
|
||||||
|
$class .= ' ' . $params['class'];
|
||||||
|
}
|
||||||
|
if (!isset($params['escape']) || $params['escape'] !== false) {
|
||||||
|
$message = h($message);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="<?= h($class) ?>" onclick="this.classList.add('hidden');"><?= $message ?></div>
|
6
src/Template/Element/Flash/error.ctp
Normal file
6
src/Template/Element/Flash/error.ctp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
if (!isset($params['escape']) || $params['escape'] !== false) {
|
||||||
|
$message = h($message);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="message error" onclick="this.classList.add('hidden');"><?= $message ?></div>
|
6
src/Template/Element/Flash/success.ctp
Normal file
6
src/Template/Element/Flash/success.ctp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
if (!isset($params['escape']) || $params['escape'] !== false) {
|
||||||
|
$message = h($message);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="message success" onclick="this.classList.add('hidden')"><?= $message ?></div>
|
14
src/Template/Element/header.ctp
Normal file
14
src/Template/Element/header.ctp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="header">
|
||||||
|
<div class="title">
|
||||||
|
<a href="/">LBRY Block Explorer</a>
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<form method="get" action="/find">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" name="q" placeholder="Enter a block height or hash, transaction hash or address" />
|
||||||
|
<button class="btn btn-inline-search">Search</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
22
src/Template/Email/html/default.ctp
Normal file
22
src/Template/Email/html/default.ctp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php
|
||||||
|
$content = explode("\n", $content);
|
||||||
|
|
||||||
|
foreach ($content as $line):
|
||||||
|
echo '<p> ' . $line . "</p>\n";
|
||||||
|
endforeach;
|
||||||
|
?>
|
16
src/Template/Email/text/default.ctp
Normal file
16
src/Template/Email/text/default.ctp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?= $content ?>
|
38
src/Template/Error/error400.ctp
Normal file
38
src/Template/Error/error400.ctp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Error\Debugger;
|
||||||
|
|
||||||
|
$this->layout = 'error';
|
||||||
|
|
||||||
|
if (Configure::read('debug')):
|
||||||
|
$this->layout = 'dev_error';
|
||||||
|
|
||||||
|
$this->assign('title', $message);
|
||||||
|
$this->assign('templateName', 'error400.ctp');
|
||||||
|
|
||||||
|
$this->start('file');
|
||||||
|
?>
|
||||||
|
<?php if (!empty($error->queryString)) : ?>
|
||||||
|
<p class="notice">
|
||||||
|
<strong>SQL Query: </strong>
|
||||||
|
<?= h($error->queryString) ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($error->params)) : ?>
|
||||||
|
<strong>SQL Query Params: </strong>
|
||||||
|
<?php Debugger::dump($error->params) ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?= $this->element('auto_table_warning') ?>
|
||||||
|
<?php
|
||||||
|
if (extension_loaded('xdebug')):
|
||||||
|
xdebug_print_function_stack();
|
||||||
|
endif;
|
||||||
|
|
||||||
|
$this->end();
|
||||||
|
endif;
|
||||||
|
?>
|
||||||
|
<h2><?= h($message) ?></h2>
|
||||||
|
<p class="error">
|
||||||
|
<strong><?= __d('cake', 'Error') ?>: </strong>
|
||||||
|
<?= __d('cake', 'The requested address {0} was not found on this server.', "<strong>'{$url}'</strong>") ?>
|
||||||
|
</p>
|
43
src/Template/Error/error500.ctp
Normal file
43
src/Template/Error/error500.ctp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Error\Debugger;
|
||||||
|
|
||||||
|
$this->layout = 'error';
|
||||||
|
|
||||||
|
if (Configure::read('debug')):
|
||||||
|
$this->layout = 'dev_error';
|
||||||
|
|
||||||
|
$this->assign('title', $message);
|
||||||
|
$this->assign('templateName', 'error500.ctp');
|
||||||
|
|
||||||
|
$this->start('file');
|
||||||
|
?>
|
||||||
|
<?php if (!empty($error->queryString)) : ?>
|
||||||
|
<p class="notice">
|
||||||
|
<strong>SQL Query: </strong>
|
||||||
|
<?= h($error->queryString) ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($error->params)) : ?>
|
||||||
|
<strong>SQL Query Params: </strong>
|
||||||
|
<?php Debugger::dump($error->params) ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($error instanceof Error) : ?>
|
||||||
|
<strong>Error in: </strong>
|
||||||
|
<?= sprintf('%s, line %s', str_replace(ROOT, 'ROOT', $error->getFile()), $error->getLine()) ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
echo $this->element('auto_table_warning');
|
||||||
|
|
||||||
|
if (extension_loaded('xdebug')):
|
||||||
|
xdebug_print_function_stack();
|
||||||
|
endif;
|
||||||
|
|
||||||
|
$this->end();
|
||||||
|
endif;
|
||||||
|
?>
|
||||||
|
<h2><?= __d('cake', 'An Internal Error Has Occurred') ?></h2>
|
||||||
|
<p class="error">
|
||||||
|
<strong><?= __d('cake', 'Error') ?>: </strong>
|
||||||
|
<?= h($message) ?>
|
||||||
|
</p>
|
24
src/Template/Layout/Email/html/default.ctp
Normal file
24
src/Template/Layout/Email/html/default.ctp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><?= $this->fetch('title') ?></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?= $this->fetch('content') ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
src/Template/Layout/Email/text/default.ctp
Normal file
16
src/Template/Layout/Email/text/default.ctp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?= $this->fetch('content') ?>
|
16
src/Template/Layout/ajax.ctp
Normal file
16
src/Template/Layout/ajax.ctp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?= $this->fetch('content') ?>
|
54
src/Template/Layout/default.ctp
Normal file
54
src/Template/Layout/default.ctp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<?= $this->Html->charset(); ?>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>
|
||||||
|
LBRY Block Explorer • <?= $this->fetch('title') ?>
|
||||||
|
</title>
|
||||||
|
<?php echo ''; /* $this->Html->meta('icon') */?>
|
||||||
|
|
||||||
|
<?php echo $this->Html->script('jquery.js') ?>
|
||||||
|
<?php echo $this->Html->script('moment.js') ?>
|
||||||
|
|
||||||
|
<?php echo $this->Html->css('main.css') ?>
|
||||||
|
|
||||||
|
<script src="https://use.typekit.net/yst3vhs.js"></script>
|
||||||
|
<script>try{Typekit.load({ async: true });}catch(e){}</script>
|
||||||
|
|
||||||
|
<?php if ($_SERVER['HTTP_HOST'] !== 'local.lbry.block.ng'): ?>
|
||||||
|
<!-- Analytics -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var _paq = _paq || [];
|
||||||
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="//analytics.aureolin.co/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||||
|
_paq.push(['setSiteId', '1']);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<!-- End Analytics Code -->
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php echo $this->fetch('meta') ?>
|
||||||
|
<?php echo $this->fetch('css') ?>
|
||||||
|
<?php echo $this->fetch('script') ?>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php echo $this->fetch('content') ?>
|
||||||
|
<footer>
|
||||||
|
<div class="content">
|
||||||
|
<a href="/">LBRY Block Explorer</a> is an alternative blockchain explorer for the <a href="https://lbry.io/get?r=RzJNi">LBRY</a> blockchain. Please report issues or send feedback to <a href="mailto:hello@aureolin.co?Subject=LBRY%20Block%20Explorer">hello@aureolin.co</a>.<br />
|
||||||
|
If you like this explorer, LBC donations to <a href="/address/bacon25yAnhH2YZxKDt4G7DcygXD8xpM1a">bacon25yAnhH2YZxKDt4G7DcygXD8xpM1a</a> are appreciated.<br />
|
||||||
|
© 2017 <a href="https://www.aureolin.co">Aureolin</a>.
|
||||||
|
|
||||||
|
<div class="page-time">Page took <?php echo round((microtime(true) - TIME_START) * 1000, 0) ?>ms</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
47
src/Template/Layout/error.ctp
Normal file
47
src/Template/Layout/error.ctp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<?= $this->Html->charset() ?>
|
||||||
|
<title>
|
||||||
|
<?= $this->fetch('title') ?>
|
||||||
|
</title>
|
||||||
|
<?= $this->Html->meta('icon') ?>
|
||||||
|
|
||||||
|
<?= $this->Html->css('base.css') ?>
|
||||||
|
<?= $this->Html->css('cake.css') ?>
|
||||||
|
|
||||||
|
<?= $this->fetch('meta') ?>
|
||||||
|
<?= $this->fetch('css') ?>
|
||||||
|
<?= $this->fetch('script') ?>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="header">
|
||||||
|
<h1><?= __('Error') ?></h1>
|
||||||
|
</div>
|
||||||
|
<div id="content">
|
||||||
|
<?= $this->Flash->render() ?>
|
||||||
|
|
||||||
|
<?= $this->fetch('content') ?>
|
||||||
|
</div>
|
||||||
|
<div id="footer">
|
||||||
|
<?= $this->Html->link(__('Back'), 'javascript:history.back()') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
14
src/Template/Layout/rss/default.ctp
Normal file
14
src/Template/Layout/rss/default.ctp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!isset($channel)):
|
||||||
|
$channel = [];
|
||||||
|
endif;
|
||||||
|
if (!isset($channel['title'])):
|
||||||
|
$channel['title'] = $this->fetch('title');
|
||||||
|
endif;
|
||||||
|
|
||||||
|
echo $this->Rss->document(
|
||||||
|
$this->Rss->channel(
|
||||||
|
[], $channel, $this->fetch('content')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
186
src/Template/Main/address.ctp
Normal file
186
src/Template/Main/address.ctp
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
<?php $this->assign('title', 'Address ' . $address->Address) ?>
|
||||||
|
|
||||||
|
<?php $this->start('script') ?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var buildTagRequest = function() {
|
||||||
|
return {
|
||||||
|
tag: $.trim($('input[name="tag_value"]').val()),
|
||||||
|
url: $.trim($('input[name="tag_url"]').val()),
|
||||||
|
vamount: parseFloat($('input[name="tag_verify_amount"]').val())
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('.tag-link').on('click', function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var container = $('.tag-address-container');
|
||||||
|
if (!container.is(':visible')) {
|
||||||
|
container.slideDown(200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.btn-tag').on('click', function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var btn = $(this);
|
||||||
|
var req = buildTagRequest();
|
||||||
|
|
||||||
|
var err = $('.tag-address-container .error-message');
|
||||||
|
err.css({ color: '#ff0000' }).text('');
|
||||||
|
if (req.tag.length === 0 || req.tag.length > 30) {
|
||||||
|
return err.text('Oops! Please specify a valid tag. It should be no more than 30 characters long.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.url.length > 200) {
|
||||||
|
return err.text('Oops! The link should be no more than 200 characters long.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(req.vamount)) {
|
||||||
|
return err.text('Oops! Invalid verification amount. Please refresh the page and try again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
var btnClose = $('.btn-close');
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/address/<?php echo $address->Address ?>/tag',
|
||||||
|
type: 'post',
|
||||||
|
dataType: 'json',
|
||||||
|
data: req,
|
||||||
|
beforeSend: function() {
|
||||||
|
btn.prop('disabled', true);
|
||||||
|
btnClose.prop('disabled', true);
|
||||||
|
btn.text('Loading...');
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
err.css({ color: '#00aa00'}).html('Your request for the tag, <strong>' + response.tag + '</strong> was successfully submitted. The tag will become active upon automatic transaction verification.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
var error = 'An error occurred with the request. If this problem persists, please send an email to hello@aureolin.co.';
|
||||||
|
try {
|
||||||
|
var json = JSON.parse(xhr.responseText);
|
||||||
|
if (json.error) {
|
||||||
|
error = json.message ? json.message : error;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// return default error
|
||||||
|
}
|
||||||
|
err.css({ color: '#ff0000' }).text(error);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
btn.text('Tag address');
|
||||||
|
btn.prop('disabled', false);
|
||||||
|
btnClose.prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.btn-close').on('click', function() {
|
||||||
|
$('input[name="tag_value"]').val('');
|
||||||
|
$('input[name="tag_url"]').val('');
|
||||||
|
$('.tag-address-container').slideUp(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php $this->end() ?>
|
||||||
|
|
||||||
|
<?php echo $this->element('header') ?>
|
||||||
|
|
||||||
|
<div class="address-head">
|
||||||
|
<h3>LBRY Address</h3>
|
||||||
|
<h4><?php echo $address->Address ?></h4> <div class="tag">
|
||||||
|
<?php if (isset($address->Tag) && strlen(trim($address->Tag)) > 0): ?>
|
||||||
|
<?php if (strlen(trim($address->TagUrl)) > 0): ?><a href="<?php echo $address->TagUrl ?>" target="_blank" rel="nofollow"><?php echo $address->Tag ?></a><?php else: echo $address->Tag; endif; ?>
|
||||||
|
<?php else: ?><a href="#" class="tag-link">Tag this address</a><?php endif; ?></div>
|
||||||
|
|
||||||
|
<div class="tag-address-container">
|
||||||
|
<h4>Tag adddress</h4>
|
||||||
|
<?php if ($pending): ?>
|
||||||
|
<div class="desc">This address has a pending tag request. If you made this request, please send exactly <strong><?php echo $pending->VerificationAmount ?> LBC</strong> from <strong><?php echo $address->Address ?></strong> to <strong><a href="/address/bLockNgmfvnnnZw7bM6SPz6hk5BVzhevEp" target="_blank">bLockNgmfvnnnZw7bM6SPz6hk5BVzhevEp</a></strong>. Incomplete requests will be automatically deleted 7 days from the request creation date.</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="desc">Label your public LBRY address with a name and an optional link. In order to tag this address, please send exactly <strong><?php echo $tagRequestAmount ?> LBC</strong>
|
||||||
|
from <strong><?php echo $address->Address ?></strong> to <strong><a href="/address/bLockNgmfvnnnZw7bM6SPz6hk5BVzhevEp" target="_blank">bLockNgmfvnnnZw7bM6SPz6hk5BVzhevEp</a></strong>
|
||||||
|
and then specify the desired tag (maximum 30 characters) and link in the fields below. The transaction will be verified after at least 1 confirmation. The <strong><?php echo $tagRequestAmount ?> LBC</strong> fee is a measure to prevent spam and low-effort submissions. Verification is an automatic process, but any tags or URLs that may be considered illegal when brought to attention will be removed.</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="hidden" name="tag_verify_amount" value="<?php echo $tagRequestAmount ?>" />
|
||||||
|
<div class="error-message"></div>
|
||||||
|
<div class="col">
|
||||||
|
<input name="tag_value" maxlength="30" placeholder="Tag (max. 30 characters)" />
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<input name="tag_url" maxlength="200" placeholder="Link (max. 200 characters)" />
|
||||||
|
</div>
|
||||||
|
<div class="col buttons">
|
||||||
|
<button class="btn btn-tag">Tag address</button>
|
||||||
|
<button class="btn btn-close">Close</button>
|
||||||
|
</div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="address-subhead">
|
||||||
|
<div class="address-qr">
|
||||||
|
<img src="/qr/lbry%3A<?php echo $address->Address ?>" alt="lbry:<?php echo $address->Address ?>" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="address-summary">
|
||||||
|
<div class="box">
|
||||||
|
<div class="title">Received (LBC)</div>
|
||||||
|
<div class="value"><?php echo $this->Amount->format($totalReceived) ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="title">Sent (LBC)</div>
|
||||||
|
<div class="value"><?php echo $this->Amount->format($totalSent) ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box last">
|
||||||
|
<div class="title">Balance (LBC)</div>
|
||||||
|
<div class="value"><?php echo $this->Amount->format($balanceAmount) ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="recent-transactions">
|
||||||
|
<h3>Recent Transactions</h3>
|
||||||
|
<table class="table tx-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w125 left">Height</th>
|
||||||
|
<th class="w250 left">Transaction Hash</th>
|
||||||
|
<th class="left">Timestamp</th>
|
||||||
|
<th class="w125 right">Confirmations</th>
|
||||||
|
<th class="w80 right">Inputs</th>
|
||||||
|
<th class="w80 right">Outputs</th>
|
||||||
|
<th class="w225 right">Amount</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<?php if (count($recentTxs) == 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="nodata" colspan="7">There are no recent transactions to display for this wallet.</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php foreach ($recentTxs as $tx): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="w125"><?php if ($tx->Height === null): ?><em>Unconfirmed</em><?php else: ?><a href="/blocks/<?php echo $tx->Height ?>"><?php echo $tx->Height ?></a><?php endif; ?></td>
|
||||||
|
<td class="w250"><div><a href="/tx/<?php echo $tx->Hash ?>?address=<?php echo $address->Address ?>#<?php echo $address->Address ?>"><?php echo $tx->Hash ?></a></div></td>
|
||||||
|
<td><?php echo \DateTime::createFromFormat('U', $tx->TxTime)->format('j M Y H:i:s') . ' UTC'; ?></td>
|
||||||
|
<td class="right"><?php echo number_format($tx->Confirmations, 0, '', ',') ?></td>
|
||||||
|
<td class="right"><?php echo $tx->InputCount ?></td>
|
||||||
|
<td class="right"><?php echo $tx->OutputCount ?></td>
|
||||||
|
<td class="right<?php echo ' ' . (($tx->DebitAmount > 0) ? 'debit' : 'credit') ?>">
|
||||||
|
<?php echo (($tx->DebitAmount > 0) ? '-' : '+'); ?><?php echo number_format((($tx->DebitAmount > 0) ? $tx->DebitAmount : $tx->CreditAmount), 8, '.', '') ?> LBC
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
127
src/Template/Main/blocks.ctp
Normal file
127
src/Template/Main/blocks.ctp
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<?php echo $this->element('header') ?>
|
||||||
|
|
||||||
|
<?php if(isset($block)): ?>
|
||||||
|
<?php $this->start('script'); ?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var resizeCards = function() {
|
||||||
|
var bSummary = $('.block-summary');
|
||||||
|
var bTransactions = $('.block-transactions');
|
||||||
|
if (bTransactions.outerHeight() < bSummary.outerHeight()) {
|
||||||
|
bTransactions.outerHeight(bSummary.outerHeight());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
resizeCards();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
resizeCards();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<?php $this->end(); ?>
|
||||||
|
|
||||||
|
<?php $this->assign('title', 'Block Height ' . $block->Height) ?>
|
||||||
|
|
||||||
|
<div class="block-head">
|
||||||
|
<h3>LBRY Block <?php echo $block->Height ?></h3>
|
||||||
|
<h4><?php echo $block->Hash ?></h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block-nav">
|
||||||
|
<?php if (strlen(trim($block->PreviousBlockHash)) > 0): ?>
|
||||||
|
<a class="btn btn-prev" href="/blocks/<?php echo ($block->Height - 1); ?>">« Previous Block</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (strlen(trim($block->NextBlockHash)) > 0): ?>
|
||||||
|
<a class="btn btn-next" href="/blocks/<?php echo ($block->Height + 1); ?>">Next Block »</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block-info">
|
||||||
|
<div class="block-summary">
|
||||||
|
<h3>Overview</h3>
|
||||||
|
|
||||||
|
<div class="label half-width">Block Size (bytes)</div>
|
||||||
|
<div class="label half-width">Block Time</div>
|
||||||
|
|
||||||
|
<div class="value half-width"><?php echo number_format($block->BlockSize, 0, '', ',') ?></div>
|
||||||
|
<div class="value half-width"><?php echo \DateTime::createFromFormat('U', $block->BlockTime)->format('j M Y H:i:s') . ' UTC' ?></div>
|
||||||
|
|
||||||
|
<div class="clear spacer"></div>
|
||||||
|
|
||||||
|
<div class="label half-width">Bits</div>
|
||||||
|
<div class="label half-width">Confirmations</div>
|
||||||
|
|
||||||
|
<div class="value half-width"><?php echo $block->Bits ?></div>
|
||||||
|
<div class="value half-width"><?php echo number_format($block->Confirmations, 0, '', ',') ?></div>
|
||||||
|
|
||||||
|
<div class="clear spacer"></div>
|
||||||
|
|
||||||
|
<div class="label half-width">Difficulty</div>
|
||||||
|
<div class="label half-width">Nonce</div>
|
||||||
|
|
||||||
|
<div class="value half-width"><?php echo $this->Amount->format($block->Difficulty) ?></div>
|
||||||
|
<div class="value half-width"><?php echo $block->Nonce ?></div>
|
||||||
|
|
||||||
|
<div class="clear spacer"></div>
|
||||||
|
|
||||||
|
<div class="label">Chainwork</div> <div class="value"><?php echo $block->Chainwork ?></div>
|
||||||
|
|
||||||
|
<div class="spacer"></div>
|
||||||
|
|
||||||
|
<div class="label">MerkleRoot</div> <div class="value"><?php echo $block->MerkleRoot ?></div>
|
||||||
|
|
||||||
|
<div class="spacer"></div>
|
||||||
|
|
||||||
|
<div class="label">NameClaimRoot</div> <div class="value"><?php echo $block->NameClaimRoot ?></div>
|
||||||
|
|
||||||
|
<div class="spacer"></div>
|
||||||
|
|
||||||
|
<div class="label">Target</div> <div class="value"><?php echo $block->Target ?></div>
|
||||||
|
|
||||||
|
<div class="spacer"></div>
|
||||||
|
|
||||||
|
<div class="label">Version</div> <div class="value"><?php echo $block->Version ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block-transactions">
|
||||||
|
<h3><?php echo count($blockTxs); ?> Transaction<?php echo (count($blockTxs) == 1 ? '' : 's'); ?></h3>
|
||||||
|
|
||||||
|
<div class="transactions-list">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Hash</th>
|
||||||
|
<th class="w100 right">Inputs</th>
|
||||||
|
<th class="w100 right">Outputs</th>
|
||||||
|
<th class="w200 right">Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (count($blockTxs) == 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="nodata" colspan="4">There are no transactions to display at this time.</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php foreach ($blockTxs as $tx): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="w300"><div><a href="/tx/<?php echo $tx->Hash ?>"><?php echo $tx->Hash ?></a></div></td>
|
||||||
|
<td class="right"><?php echo $tx->InputCount ?></td>
|
||||||
|
<td class="right"><?php echo $tx->OutputCount ?></td>
|
||||||
|
<td class="right"><div title="<?php echo $tx->Value ?> LBC"><?php echo $this->Amount->formatCurrency($tx->Value) ?> LBC</div></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear"></div>
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
147
src/Template/Main/index.ctp
Normal file
147
src/Template/Main/index.ctp
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
<?php $this->assign('title', 'Home') ?>
|
||||||
|
|
||||||
|
<?php $this->start('script'); ?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var updateInterval = 120000;
|
||||||
|
|
||||||
|
var updateStatus = function() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/status',
|
||||||
|
type: 'get',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
var status = response.status;
|
||||||
|
var stats = $('.stats');
|
||||||
|
stats.find('.box:eq(0) > .value').text(status.height);
|
||||||
|
stats.find('.box:eq(1) > .value').text(status.difficulty);
|
||||||
|
stats.find('.box:eq(2) > .value').text(status.hashrate);
|
||||||
|
stats.find('.box:eq(3) > .value').text(status.price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateRecentBlocks = function() {
|
||||||
|
var tbody = $('.recent-blocks .table tbody');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/recentblocks',
|
||||||
|
type: 'get',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
var blocks = response.blocks;
|
||||||
|
for (var i = blocks.length - 1; i >= 0; i--) {
|
||||||
|
var block = blocks[i];
|
||||||
|
var prevRow = tbody.find('tr[data-height="' + block.Height + '"]');
|
||||||
|
if (prevRow.length > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockTime = moment(block.BlockTime * 1000);
|
||||||
|
tbody.prepend(
|
||||||
|
$('<tr></tr>').attr({'data-height': block.Height, 'data-time': block.BlockTime}).append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<a></a>').attr({'href': '/blocks/' + block.Height}).text(block.Height)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').text(blockTime.fromNow())
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'right'}).text((block.BlockSize / 1024).toFixed(2) + 'KB')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'right'}).text(block.TransactionCount)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'right'}).text(block.Difficulty)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'last-cell'}).text(blockTime.utc().format('D MMM YYYY HH:mm:ss') + ' UTC')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove the last row
|
||||||
|
tbody.find('tr:last').remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
tbody.find('tr').each(function() {
|
||||||
|
var row = $(this);
|
||||||
|
var blockTime = moment(row.attr('data-time') * 1000);
|
||||||
|
row.find('td:eq(1)').text(blockTime.fromNow());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
setInterval(updateStatus, updateInterval);
|
||||||
|
setInterval(updateRecentBlocks, updateInterval);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php $this->end(); ?>
|
||||||
|
|
||||||
|
<div class="home-container">
|
||||||
|
<div class="home-container-cell">
|
||||||
|
<div class="main">
|
||||||
|
<div class="title">LBRY Block Explorer</div>
|
||||||
|
<form method="get" action="/find">
|
||||||
|
<input class="search-input" name="q" type="text" placeholder="Enter a block height or hash, transaction hash or address" />
|
||||||
|
<div class="ctls"><button class="btn btn-search">Search</button> <a href="/realtime">Realtime</a></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<div class="box">
|
||||||
|
<div class="title">Block Height</div>
|
||||||
|
<div class="value"><?php echo $recentBlocks[0]->Height ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="title">Difficulty</div>
|
||||||
|
<div class="value" title="<?php echo $recentBlocks[0]->Difficulty ?>"><?php echo number_format($recentBlocks[0]->Difficulty, 2, '.', '') ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="title">Network</div>
|
||||||
|
<div class="value"><?php echo $hashRate ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box last">
|
||||||
|
<div class="title">Price</div>
|
||||||
|
<div class="value"><?php echo $lbcUsdPrice ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="recent-blocks">
|
||||||
|
<h3>Recent Blocks</h3>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left w125">Height</th>
|
||||||
|
<th class="left w125">Age</th>
|
||||||
|
<th class="right w150">Block Size</th>
|
||||||
|
<th class="right w150">Transactions</th>
|
||||||
|
<th class="right w150">Difficulty</th>
|
||||||
|
<th class="left w250 last-cell">Timestamp</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($recentBlocks as $block): ?>
|
||||||
|
<tr data-height="<?php echo $block->Height ?>" data-time="<?php echo $block->BlockTime ?>">
|
||||||
|
<td><a href="/blocks/<?php echo $block->Height ?>"><?php echo $block->Height ?></a></td>
|
||||||
|
<td><?php echo \Carbon\Carbon::createFromTimestamp($block->BlockTime)->diffForHumans(); ?></td>
|
||||||
|
<td class="right"><?php echo round($block->BlockSize / 1024, 2) . 'KB' ?></td>
|
||||||
|
<td class="right"><?php echo $block->TransactionCount ?></td>
|
||||||
|
<td class="right"><?php echo number_format($block->Difficulty, 2, '.', '') ?></td>
|
||||||
|
<td class="last-cell"><?php echo DateTime::createFromFormat('U', $block->BlockTime)->format('j M Y H:i:s') . ' UTC' ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
170
src/Template/Main/realtime.ctp
Normal file
170
src/Template/Main/realtime.ctp
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
<?php $this->assign('title', 'Realtime Explorer') ?>
|
||||||
|
|
||||||
|
<?php $this->start('script'); ?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var updateBlocksInterval = 120000;
|
||||||
|
var updateTxInterval = 30000;
|
||||||
|
|
||||||
|
var updateRealtimeBlocks = function() {
|
||||||
|
var tbody = $('.realtime-blocks .table tbody');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/realtime/blocks',
|
||||||
|
type: 'get',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
var blocks = response.blocks;
|
||||||
|
for (var i = blocks.length - 1; i >= 0; i--) {
|
||||||
|
var block = blocks[i];
|
||||||
|
var prevRow = tbody.find('tr[data-height="' + block.Height + '"]');
|
||||||
|
if (prevRow.length > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockTime = moment(block.BlockTime * 1000);
|
||||||
|
tbody.prepend(
|
||||||
|
$('<tr></tr>').attr({'data-height': block.Height, 'data-time': block.BlockTime}).append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<a></a>').attr({'href': ('/blocks/' + block.Height), 'target': '_blank'}).text(block.Height)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').text(blockTime.fromNow())
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'right'}).text(block.TransactionCount)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove the last row
|
||||||
|
tbody.find('tr:last').remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
tbody.find('tr').each(function() {
|
||||||
|
var row = $(this);
|
||||||
|
var blockTime = moment(row.attr('data-time') * 1000);
|
||||||
|
row.find('td:eq(1)').text(blockTime.fromNow());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateRealtimeTransactions = function() {
|
||||||
|
var tbody = $('.realtime-tx .table tbody');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/realtime/tx',
|
||||||
|
type: 'get',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
var txs = response.txs;
|
||||||
|
for (var i = txs.length - 1; i >= 0; i--) {
|
||||||
|
var tx = txs[i];
|
||||||
|
var prevRow = tbody.find('tr[data-hash="' + tx.Hash + '"]');
|
||||||
|
if (prevRow.length > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var txTime = moment(tx.TxTime * 1000);
|
||||||
|
tbody.prepend(
|
||||||
|
$('<tr></tr>').attr({'data-hash': tx.Hash, 'data-time': tx.TxTime}).append(
|
||||||
|
$('<td></td>').attr({'class': 'w200'}).append(
|
||||||
|
$('<div></div>').append(
|
||||||
|
$('<a></a>').attr({'href': ('/tx/' + tx.Hash), 'target': '_blank'}).text(tx.Hash)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').text(txTime.fromNow())
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'right'}).text(tx.InputCount)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'right'}).text(tx.OutputCount)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr({'class': 'right'}).text(Number(tx.Value).toFixed(8) + ' LBC')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove the last row
|
||||||
|
tbody.find('tr:last').remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
tbody.find('tr').each(function() {
|
||||||
|
var row = $(this);
|
||||||
|
var blockTime = moment(row.attr('data-time') * 1000);
|
||||||
|
row.find('td:eq(1)').text(blockTime.fromNow());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
setInterval(updateRealtimeBlocks, updateBlocksInterval);
|
||||||
|
setInterval(updateRealtimeTransactions, updateTxInterval);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php $this->end(); ?>
|
||||||
|
|
||||||
|
<?php echo $this->element('header') ?>
|
||||||
|
|
||||||
|
<div class="realtime-head">
|
||||||
|
<h3>Realtime Explorer</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="realtime-main">
|
||||||
|
<div class="realtime-blocks">
|
||||||
|
<h3>Recent Blocks</h3>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="left">Height</th>
|
||||||
|
<th class="left">Age</th>
|
||||||
|
<th class="right"># TXs</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($blocks as $block): ?>
|
||||||
|
<tr data-height="<?php echo $block->Height ?>" data-time="<?php echo $block->BlockTime ?>">
|
||||||
|
<td><a href="/blocks/<?php echo $block->Height ?>" target="_blank"><?php echo $block->Height ?></a></td>
|
||||||
|
<td><?php echo \Carbon\Carbon::createFromTimestamp($block->BlockTime)->diffForHumans(); ?></td>
|
||||||
|
<td class="right"><?php echo $block->TransactionCount ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="realtime-tx">
|
||||||
|
<h3>Recent Transactions</h3>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w200 left">Hash</th>
|
||||||
|
<th class="left">Time</th>
|
||||||
|
<th class="w100 right">Inputs</th>
|
||||||
|
<th class="w100 right">Outputs</th>
|
||||||
|
<th class="w200 right">Amount</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($txs as $tx): ?>
|
||||||
|
<tr data-hash="<?php echo $tx->Hash ?>" data-time="<?php echo $tx->TxTime ?>">
|
||||||
|
<td class="w200"><div><a href="/tx/<?php echo $tx->Hash ?>" target="_blank"><?php echo $tx->Hash ?></a></div></td>
|
||||||
|
<td><?php echo \Carbon\Carbon::createFromTimestamp($tx->TxTime)->diffForHumans(); ?></td>
|
||||||
|
<td class="right"><?php echo $tx->InputCount ?></td>
|
||||||
|
<td class="right"><?php echo $tx->OutputCount ?></td>
|
||||||
|
<td class="right"><?php echo number_format($tx->Value, 8, '.', '') ?> LBC</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear"></div>
|
||||||
|
|
||||||
|
</div>
|
142
src/Template/Main/tx.ctp
Normal file
142
src/Template/Main/tx.ctp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
<?php $this->assign('title', 'Transaction ' . $tx->Hash) ?>
|
||||||
|
|
||||||
|
<?php $this->start('script'); ?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
if (location.hash && (location.hash.indexOf('input-') > -1 || location.hash.indexOf('output-') > -1)) {
|
||||||
|
$(location.hash).addClass('highlighted');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php $this->end(); ?>
|
||||||
|
|
||||||
|
<?php echo $this->element('header') ?>
|
||||||
|
|
||||||
|
<div class="tx-head">
|
||||||
|
<h3>LBRY Transaction</h3>
|
||||||
|
<h4><?php echo $tx->Hash ?></h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tx-summary">
|
||||||
|
<div class="box p25">
|
||||||
|
<div class="title">Amount (LBC)</div>
|
||||||
|
<div class="value"><?php echo $this->Amount->format($tx->Value) ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box p15">
|
||||||
|
<div class="title">Block Height</div>
|
||||||
|
<?php if (!isset($tx->BlockHash) || strlen(trim($tx->BlockHash)) === 0): ?>
|
||||||
|
<div class="value" title="Unconfirmed">Unconf.</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="value" title="<?php echo $tx->BlockHash ?>"><a href="/blocks/<?php echo $block->Height ?>"><?php echo $block->Height ?></a></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box p15">
|
||||||
|
<div class="title">Confirmations</div>
|
||||||
|
<div class="value"><?php echo $confirmations ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box p15">
|
||||||
|
<div class="title">Size (bytes)</div>
|
||||||
|
<div class="value"><?php echo number_format($tx->TransactionSize, 0, '', ',') ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box p15">
|
||||||
|
<div class="title">Inputs</div>
|
||||||
|
<div class="value"><?php echo $tx->InputCount ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box p15 last">
|
||||||
|
<div class="title">Outputs</div>
|
||||||
|
<div class="value"><?php echo $tx->OutputCount ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tx-details">
|
||||||
|
<h3>Details</h3>
|
||||||
|
<div class="tx-details-layout">
|
||||||
|
<div class="inputs">
|
||||||
|
<div class="subtitle"><?php echo $tx->InputCount ?> input<?php echo $tx->InputCount === 1 ? '' : 's'; ?></div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$setAddressIds = [];
|
||||||
|
foreach ($inputs as $in):
|
||||||
|
?>
|
||||||
|
<div id="input-<?php echo $in->Id ?>" class="input <?php if (isset($in['InputAddresses']) && count($in['InputAddresses']) > 0 && $in['InputAddresses'][0]->Address == $sourceAddress): ?>is-source<?php endif; ?>">
|
||||||
|
<?php if ($in['IsCoinbase']): ?>
|
||||||
|
<div>Block Reward (New Coins)</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php if (strlen(trim($in->Value)) == 0): ?>
|
||||||
|
<div>Incomplete data</div>
|
||||||
|
<?php else:
|
||||||
|
$addr = $in['InputAddresses'][0];
|
||||||
|
|
||||||
|
if (!isset($setAddressIds[$addr->Address])):
|
||||||
|
$setAddressIds[$addr->Address] = 1; ?>
|
||||||
|
<a id="<?php echo $addr->Address ?>"></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div><span class="value"><?php echo $this->Amount->format($in['Value']) ?> LBC</span> from</div>
|
||||||
|
<div class="address"><a href="/address/<?php echo $addr->Address ?>"><?php echo $addr->Address ?></a>
|
||||||
|
(<a class="output-link" href="/tx/<?php echo $in->PrevoutHash ?>#output-<?php echo $in->PrevoutN ?>">output</a>)
|
||||||
|
<?php if (isset($addr->Tag) && strlen(trim($addr->Tag)) > 0): ?>
|
||||||
|
<div class="tag">
|
||||||
|
<?php if (strlen(trim($addr->TagUrl)) > 0): ?><a href="<?php echo $addr->TagUrl ?>" target="_blank" rel="nofollow"><?php echo $addr->Tag ?></a><?php else: echo $addr->Tag; endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider">
|
||||||
|
<img src="/img/right-arrow.png" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="outputs">
|
||||||
|
<div class="subtitle"><?php echo $tx->OutputCount ?> output<?php echo $tx->OutputCount === 1 ? '' : 's'; ?>
|
||||||
|
|
||||||
|
<?php if ($fee > 0): ?>
|
||||||
|
<span class="fee"><span class="label">Fee</span> <span class="value"><?php echo $this->Amount->format($fee) ?> LBC</span></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
foreach ($outputs as $out): ?>
|
||||||
|
<div id="output-<?php echo $out->Vout ?>" class="output <?php if (isset($out['OutputAddresses']) && count($out['OutputAddresses']) > 0 && $out['OutputAddresses'][0]->Address == $sourceAddress): ?>is-source<?php endif; ?>">
|
||||||
|
<div class="labels">
|
||||||
|
<?php if($out->IsSupportClaim): ?><div class="support">SUPPORT</div><?php endif; ?>
|
||||||
|
<?php if($out->IsUpdateClaim): ?><div class="update">UPDATE</div><?php endif; ?>
|
||||||
|
<?php if($out->IsClaim): ?><div class="claim">CLAIM</div><?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (strlen(trim($out['Value'])) == 0): ?>
|
||||||
|
<div>Incomplete data</div>
|
||||||
|
<?php else:
|
||||||
|
$addr = $out['OutputAddresses'][0];
|
||||||
|
|
||||||
|
if (!isset($setAddressIds[$addr->Address])):
|
||||||
|
$setAddressIds[$addr->Address] = 1; ?>
|
||||||
|
<a id="<?php echo $addr->Address ?>"></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div><span class="value"><?php echo $this->Amount->format($out['Value']) ?> LBC</span> to</div>
|
||||||
|
<div class="address"><a href="/address/<?php echo $addr->Address ?>"><?php echo $addr->Address ?></a>
|
||||||
|
|
||||||
|
<?php if ($out->IsSpent): ?>(<a href="/tx/<?php echo $out->SpendInput->TransactionHash ?>#input-<?php echo $out->SpendInput->Id ?>">spent</a>)<?php else: ?>(unspent)<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (isset($addr->Tag) && strlen(trim($addr->Tag)) > 0): ?>
|
||||||
|
<div class="tag">
|
||||||
|
<?php if (strlen(trim($addr->TagUrl)) > 0): ?><a href="<?php echo $addr->TagUrl ?>" target="_blank" rel="nofollow"><?php echo $addr->Tag ?></a><?php else: echo $addr->Tag; endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
276
src/Template/Pages/home.ctp
Normal file
276
src/Template/Pages/home.ctp
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.10.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
use Cake\Cache\Cache;
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Core\Plugin;
|
||||||
|
use Cake\Datasource\ConnectionManager;
|
||||||
|
use Cake\Error\Debugger;
|
||||||
|
use Cake\Network\Exception\NotFoundException;
|
||||||
|
|
||||||
|
$this->layout = false;
|
||||||
|
|
||||||
|
if (!Configure::read('debug')):
|
||||||
|
throw new NotFoundException('Please replace src/Template/Pages/home.ctp with your own version.');
|
||||||
|
endif;
|
||||||
|
|
||||||
|
$cakeDescription = 'CakePHP: the rapid development PHP framework';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<?= $this->Html->charset() ?>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>
|
||||||
|
<?= $cakeDescription ?>
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<?= $this->Html->meta('icon') ?>
|
||||||
|
<?= $this->Html->css('base.css') ?>
|
||||||
|
<?= $this->Html->css('cake.css') ?>
|
||||||
|
<?= $this->Html->css('home.css') ?>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Raleway:500i|Roboto:300,400,700|Roboto+Mono" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="home">
|
||||||
|
|
||||||
|
<header class="row">
|
||||||
|
<div class="header-image"><?= $this->Html->image('cake.logo.svg') ?></div>
|
||||||
|
<div class="header-title">
|
||||||
|
<h1>Welcome to CakePHP <?= Configure::version() ?> Red Velvet. Build fast. Grow solid.</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns large-12">
|
||||||
|
<div class="ctp-warning alert text-center">
|
||||||
|
<p>Please be aware that this page will not be shown if you turn off debug mode unless you replace src/Template/Pages/home.ctp with your own version.</p>
|
||||||
|
</div>
|
||||||
|
<div id="url-rewriting-warning" class="alert url-rewriting">
|
||||||
|
<ul>
|
||||||
|
<li class="bullet problem">
|
||||||
|
URL rewriting is not properly configured on your server.<br />
|
||||||
|
1) <a target="_blank" href="http://book.cakephp.org/3.0/en/installation.html#url-rewriting">Help me configure it</a><br />
|
||||||
|
2) <a target="_blank" href="http://book.cakephp.org/3.0/en/development/configuration.html#general-configuration">I don't / can't use URL rewriting</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<?php Debugger::checkSecurityKeys(); ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns large-6">
|
||||||
|
<h4>Environment</h4>
|
||||||
|
<ul>
|
||||||
|
<?php if (version_compare(PHP_VERSION, '5.6.0', '>=')): ?>
|
||||||
|
<li class="bullet success">Your version of PHP is 5.6.0 or higher (detected <?= PHP_VERSION ?>).</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">Your version of PHP is too low. You need PHP 5.6.0 or higher to use CakePHP (detected <?= PHP_VERSION ?>).</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (extension_loaded('mbstring')): ?>
|
||||||
|
<li class="bullet success">Your version of PHP has the mbstring extension loaded.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">Your version of PHP does NOT have the mbstring extension loaded.</li>;
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (extension_loaded('openssl')): ?>
|
||||||
|
<li class="bullet success">Your version of PHP has the openssl extension loaded.</li>
|
||||||
|
<?php elseif (extension_loaded('mcrypt')): ?>
|
||||||
|
<li class="bullet success">Your version of PHP has the mcrypt extension loaded.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">Your version of PHP does NOT have the openssl or mcrypt extension loaded.</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (extension_loaded('intl')): ?>
|
||||||
|
<li class="bullet success">Your version of PHP has the intl extension loaded.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">Your version of PHP does NOT have the intl extension loaded.</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="columns large-6">
|
||||||
|
<h4>Filesystem</h4>
|
||||||
|
<ul>
|
||||||
|
<?php if (is_writable(TMP)): ?>
|
||||||
|
<li class="bullet success">Your tmp directory is writable.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">Your tmp directory is NOT writable.</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (is_writable(LOGS)): ?>
|
||||||
|
<li class="bullet success">Your logs directory is writable.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">Your logs directory is NOT writable.</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php $settings = Cache::config('_cake_core_'); ?>
|
||||||
|
<?php if (!empty($settings)): ?>
|
||||||
|
<li class="bullet success">The <em><?= $settings['className'] ?>Engine</em> is being used for core caching. To change the config edit config/app.php</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">Your cache is NOT working. Please check the settings in config/app.php</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns large-6">
|
||||||
|
<h4>Database</h4>
|
||||||
|
<?php
|
||||||
|
try {
|
||||||
|
$connection = ConnectionManager::get('default');
|
||||||
|
$connected = $connection->connect();
|
||||||
|
} catch (Exception $connectionError) {
|
||||||
|
$connected = false;
|
||||||
|
$errorMsg = $connectionError->getMessage();
|
||||||
|
if (method_exists($connectionError, 'getAttributes')):
|
||||||
|
$attributes = $connectionError->getAttributes();
|
||||||
|
if (isset($errorMsg['message'])):
|
||||||
|
$errorMsg .= '<br />' . $attributes['message'];
|
||||||
|
endif;
|
||||||
|
endif;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<ul>
|
||||||
|
<?php if ($connected): ?>
|
||||||
|
<li class="bullet success">CakePHP is able to connect to the database.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">CakePHP is NOT able to connect to the database.<br /><?= $errorMsg ?></li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="columns large-6">
|
||||||
|
<h4>DebugKit</h4>
|
||||||
|
<ul>
|
||||||
|
<?php if (Plugin::loaded('DebugKit')): ?>
|
||||||
|
<li class="bullet success">DebugKit is loaded.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="bullet problem">DebugKit is NOT loaded. You need to either install pdo_sqlite, or define the "debug_kit" connection name.</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns large-6">
|
||||||
|
<h3>Editing this Page</h3>
|
||||||
|
<ul>
|
||||||
|
<li class="bullet cutlery">To change the content of this page, edit: src/Template/Pages/home.ctp.</li>
|
||||||
|
<li class="bullet cutlery">You can also add some CSS styles for your pages at: webroot/css/.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="columns large-6">
|
||||||
|
<h3>Getting Started</h3>
|
||||||
|
<ul>
|
||||||
|
<li class="bullet book"><a target="_blank" href="http://book.cakephp.org/3.0/en/">CakePHP 3.0 Docs</a></li>
|
||||||
|
<li class="bullet book"><a target="_blank" href="http://book.cakephp.org/3.0/en/tutorials-and-examples/bookmarks/intro.html">The 15 min Bookmarker Tutorial</a></li>
|
||||||
|
<li class="bullet book"><a target="_blank" href="http://book.cakephp.org/3.0/en/tutorials-and-examples/blog/blog.html">The 15 min Blog Tutorial</a></li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns large-12 text-center">
|
||||||
|
<h3 class="more">More about Cake</h3>
|
||||||
|
<p>
|
||||||
|
CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Front Controller and MVC.<br />
|
||||||
|
Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns large-4">
|
||||||
|
<i class="icon support">P</i>
|
||||||
|
<h3>Help and Bug Reports</h3>
|
||||||
|
<ul>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="irc://irc.freenode.net/cakephp">irc.freenode.net #cakephp</a>
|
||||||
|
<ul><li>Live chat about CakePHP</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="https://github.com/cakephp/cakephp/issues">CakePHP Issues</a>
|
||||||
|
<ul><li>CakePHP issues and pull requests</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://discourse.cakephp.org/">CakePHP Forum</a>
|
||||||
|
<ul><li>CakePHP official discussion forum</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="https://groups.google.com/group/cake-php">CakePHP Google Group</a>
|
||||||
|
<ul><li>Community mailing list</li></ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="columns large-4">
|
||||||
|
<i class="icon docs">r</i>
|
||||||
|
<h3>Docs and Downloads</h3>
|
||||||
|
<ul>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://api.cakephp.org/3.0/">CakePHP API</a>
|
||||||
|
<ul><li>Quick Reference</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://book.cakephp.org/3.0/en/">CakePHP Documentation</a>
|
||||||
|
<ul><li>Your Rapid Development Cookbook</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://bakery.cakephp.org">The Bakery</a>
|
||||||
|
<ul><li>Everything CakePHP</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://plugins.cakephp.org">CakePHP plugins repo</a>
|
||||||
|
<ul><li>A comprehensive list of all CakePHP plugins created by the community</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="https://github.com/cakephp/">CakePHP Code</a>
|
||||||
|
<ul><li>For the Development of CakePHP Git repository, Downloads</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="https://github.com/FriendsOfCake/awesome-cakephp">CakePHP Awesome List</a>
|
||||||
|
<ul><li>A curated list of amazingly awesome CakePHP plugins, resources and shiny things.</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://www.cakephp.org">CakePHP</a>
|
||||||
|
<ul><li>The Rapid Development Framework</li></ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="columns large-4">
|
||||||
|
<i class="icon training">s</i>
|
||||||
|
<h3>Training and Certification</h3>
|
||||||
|
<ul>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://cakefoundation.org/">Cake Software Foundation</a>
|
||||||
|
<ul><li>Promoting development related to CakePHP</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://training.cakephp.org/">CakePHP Training</a>
|
||||||
|
<ul><li>Learn to use the CakePHP framework</li></ul>
|
||||||
|
</li>
|
||||||
|
<li class="bullet cutlery">
|
||||||
|
<a href="http://certification.cakephp.org/">CakePHP Certification</a>
|
||||||
|
<ul><li>Become a certified CakePHP developer</li></ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
49
src/View/AjaxView.php
Normal file
49
src/View/AjaxView.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.0.4
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\View;
|
||||||
|
|
||||||
|
use Cake\Event\EventManager;
|
||||||
|
use Cake\Network\Request;
|
||||||
|
use Cake\Network\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view class that is used for AJAX responses.
|
||||||
|
* Currently only switches the default layout and sets the response type -
|
||||||
|
* which just maps to text/html by default.
|
||||||
|
*/
|
||||||
|
class AjaxView extends AppView
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the layout file to render the view inside of. The name
|
||||||
|
* specified is the filename of the layout in /src/Template/Layout without
|
||||||
|
* the .ctp extension.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $layout = 'ajax';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization hook method.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
parent::initialize();
|
||||||
|
|
||||||
|
$this->response->type('ajax');
|
||||||
|
}
|
||||||
|
}
|
42
src/View/AppView.php
Normal file
42
src/View/AppView.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.0.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\View;
|
||||||
|
|
||||||
|
use Cake\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application View
|
||||||
|
*
|
||||||
|
* Your application’s default view class
|
||||||
|
*
|
||||||
|
* @link http://book.cakephp.org/3.0/en/views.html#the-app-view
|
||||||
|
*/
|
||||||
|
class AppView extends View
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization hook method.
|
||||||
|
*
|
||||||
|
* Use this method to add common initialization code like loading helpers.
|
||||||
|
*
|
||||||
|
* e.g. `$this->loadHelper('Html');`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
parent::initialize();
|
||||||
|
$this->loadHelper('Amount');
|
||||||
|
}
|
||||||
|
}
|
44
src/View/Helper/AmountHelper.php
Normal file
44
src/View/Helper/AmountHelper.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Helper;
|
||||||
|
|
||||||
|
use Cake\View\Helper;
|
||||||
|
|
||||||
|
class AmountHelper extends Helper {
|
||||||
|
public function format($value) {
|
||||||
|
$value = number_format($value, 8, '.', ',');
|
||||||
|
$dotIdx = strpos($value, '.');
|
||||||
|
if ($dotIdx !== false) {
|
||||||
|
$left = substr($value, 0, $dotIdx);
|
||||||
|
$right = substr($value, $dotIdx + 1);
|
||||||
|
|
||||||
|
$value = $left;
|
||||||
|
if ((int) $right > 0) {
|
||||||
|
$value .= '.' . rtrim($right, '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function formatCurrency($value) {
|
||||||
|
$dotIdx = strpos($value, '.');
|
||||||
|
if ($dotIdx !== false) {
|
||||||
|
$left = substr($value, 0, $dotIdx);
|
||||||
|
$right = substr($value, $dotIdx + 1);
|
||||||
|
|
||||||
|
$value = number_format($left, 0, '', ',');
|
||||||
|
if ((int) $right > 0) {
|
||||||
|
if (strlen($right) === 1) {
|
||||||
|
$value .= '.' . $right . '0';
|
||||||
|
} else {
|
||||||
|
$value .= '.' . substr($right, 0, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
46
tests/TestCase/ApplicationTest.php
Normal file
46
tests/TestCase/ApplicationTest.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 3.3.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\Test\TestCase;
|
||||||
|
|
||||||
|
use App\Application;
|
||||||
|
use Cake\Error\Middleware\ErrorHandlerMiddleware;
|
||||||
|
use Cake\Http\MiddlewareQueue;
|
||||||
|
use Cake\Routing\Middleware\AssetMiddleware;
|
||||||
|
use Cake\Routing\Middleware\RoutingMiddleware;
|
||||||
|
use Cake\TestSuite\IntegrationTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ApplicationTest class
|
||||||
|
*/
|
||||||
|
class ApplicationTest extends IntegrationTestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* testMiddleware
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testMiddleware()
|
||||||
|
{
|
||||||
|
$app = new Application(dirname(dirname(__DIR__)) . '/config');
|
||||||
|
$middleware = new MiddlewareQueue();
|
||||||
|
|
||||||
|
$middleware = $app->middleware($middleware);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(ErrorHandlerMiddleware::class, $middleware->get(0));
|
||||||
|
$this->assertInstanceOf(AssetMiddleware::class, $middleware->get(1));
|
||||||
|
$this->assertInstanceOf(RoutingMiddleware::class, $middleware->get(2));
|
||||||
|
}
|
||||||
|
}
|
97
tests/TestCase/Controller/PagesControllerTest.php
Normal file
97
tests/TestCase/Controller/PagesControllerTest.php
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 1.2.0
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
namespace App\Test\TestCase\Controller;
|
||||||
|
|
||||||
|
use App\Controller\PagesController;
|
||||||
|
use Cake\Core\App;
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
use Cake\Network\Request;
|
||||||
|
use Cake\Network\Response;
|
||||||
|
use Cake\TestSuite\IntegrationTestCase;
|
||||||
|
use Cake\View\Exception\MissingTemplateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PagesControllerTest class
|
||||||
|
*/
|
||||||
|
class PagesControllerTest extends IntegrationTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* testMultipleGet method
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testMultipleGet()
|
||||||
|
{
|
||||||
|
$this->get('/');
|
||||||
|
$this->assertResponseOk();
|
||||||
|
$this->get('/');
|
||||||
|
$this->assertResponseOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* testDisplay method
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testDisplay()
|
||||||
|
{
|
||||||
|
$this->get('/pages/home');
|
||||||
|
$this->assertResponseOk();
|
||||||
|
$this->assertResponseContains('CakePHP');
|
||||||
|
$this->assertResponseContains('<html>');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that missing template renders 404 page in production
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testMissingTemplate()
|
||||||
|
{
|
||||||
|
Configure::write('debug', false);
|
||||||
|
$this->get('/pages/not_existing');
|
||||||
|
|
||||||
|
$this->assertResponseError();
|
||||||
|
$this->assertResponseContains('Error');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that missing template in debug mode renders missing_template error page
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testMissingTemplateInDebug()
|
||||||
|
{
|
||||||
|
Configure::write('debug', true);
|
||||||
|
$this->get('/pages/not_existing');
|
||||||
|
|
||||||
|
$this->assertResponseFailure();
|
||||||
|
$this->assertResponseContains('Missing Template');
|
||||||
|
$this->assertResponseContains('Stacktrace');
|
||||||
|
$this->assertResponseContains('not_existing.ctp');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test directory traversal protection
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testDirectoryTraversalProtection()
|
||||||
|
{
|
||||||
|
$this->get('/pages/../Layout/ajax');
|
||||||
|
$this->assertResponseCode(403);
|
||||||
|
$this->assertResponseContains('Forbidden');
|
||||||
|
}
|
||||||
|
}
|
12
tests/bootstrap.php
Normal file
12
tests/bootstrap.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Test runner bootstrap.
|
||||||
|
*
|
||||||
|
* Add additional configuration/setup your application needs when running
|
||||||
|
* unit tests in this file.
|
||||||
|
*/
|
||||||
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
require dirname(__DIR__) . '/config/bootstrap.php';
|
||||||
|
|
||||||
|
$_SERVER['PHP_SELF'] = '/';
|
5
webroot/.htaccess
Normal file
5
webroot/.htaccess
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^ index.php [L]
|
||||||
|
</IfModule>
|
166
webroot/css/main.css
Normal file
166
webroot/css/main.css
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
* { box-sizing: border-box; font-family: 'jaf-facitweb', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale }
|
||||||
|
*:focus { outline: none }
|
||||||
|
.home-container { display: table; width: 100%; height: 100% }
|
||||||
|
.home-container-cell { display: table-cell; vertical-align: middle; min-height: 400px }
|
||||||
|
.main { display: block; width: 1200px; margin: 0 auto }
|
||||||
|
a:link, a:visited { color: #1e88e5; text-decoration: none }
|
||||||
|
a:hover { text-decoration: underline; color: #1976d2 }
|
||||||
|
|
||||||
|
.header { width: 1200px; margin: 0 auto 24px auto; padding: 12px 0 64px 0; border-bottom: 1px solid #ddd }
|
||||||
|
.header a:link, .header a:visited { color: #333; text-decoration: none }
|
||||||
|
.header .title { font-family: 'adelle-sans', sans-serif; font-weight: bold; font-size: 140%; float: left; position: relative; top: 4px }
|
||||||
|
.header .search { float: right }
|
||||||
|
.header .search .input-group { border: 2px solid #1e88e5; position: relative; width: 525px; height: 36px; border-radius: 8px }
|
||||||
|
.header .search .input-group input { border: none; border-radius: 8px; padding: 8px 133px 8px 8px; width: 100%; height: 32px; width: 521px }
|
||||||
|
.header .search .input-group .btn-inline-search { border: none; cursor: pointer; position: absolute; right: -4px; top: 0; height: 100%; background: #1e88e5; color: #fff; width: 125px;
|
||||||
|
border-radius: 0 6px 8px 0 }
|
||||||
|
.header .search .input-group .btn-inline-search:hover { background: #1976d2 }
|
||||||
|
|
||||||
|
.home-container-cell > .main > .title { font-family: 'adelle-sans', sans-serif; font-weight: bold; font-size: 280%; margin: 24px auto 24px auto; width: 600px; text-align: center; color: #333; cursor: default }
|
||||||
|
.home-container-cell .search-input { display: block; margin: 0 auto; padding: 8px; text-align: center; border: 3px solid #ddd; border-radius: 16px; width: 600px; font-size: 115%; font-weight: 300 }
|
||||||
|
.home-container-cell .ctls { width: 600px; text-align: center; margin: 24px auto; position: relative }
|
||||||
|
.home-container-cell .ctls .btn-search { font-size: 115%; display: inline-block; padding: 12px 48px; background: #1e88e5; color: #fff; border-radius: 8px; border: none; font-weight: 300; cursor: pointer }
|
||||||
|
.home-container-cell .ctls .btn-search:hover { background: #1976d2 }
|
||||||
|
.home-container-cell .ctls a { font-size: 115%; display: inline-block; font-weight: 300; position: absolute; right: 0; padding-top: 12px }
|
||||||
|
.home-container-cell .ctls a:hover { text-decoration: none; color: #1976d2 }
|
||||||
|
|
||||||
|
.home-container-cell .recent-blocks { width: 1000px; margin: 48px auto 0 auto; box-shadow: 0 6px 12px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px 36px 36px 36px; cursor: default }
|
||||||
|
.home-container-cell .recent-blocks h3 { font-weight: normal; margin: 0 0 12px 0; font-weight: 300 }
|
||||||
|
|
||||||
|
.table { width: 100%; cursor: default; border-collapse: collapse; font-size: 90% }
|
||||||
|
.table thead tr th { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; padding: 12px 4px }
|
||||||
|
.table tbody tr td { padding: 8px; border-bottom: 1px solid #eee; font-weight: 300; white-space: nowrap }
|
||||||
|
.table tbody tr td.nodata { text-align: center; font-style: italic; font-weight: 300 }
|
||||||
|
.table .last-cell { padding-left: 48px }
|
||||||
|
|
||||||
|
.left { text-align: left }
|
||||||
|
.center { text-align: center }
|
||||||
|
.right { text-align: right }
|
||||||
|
|
||||||
|
.w80 { width: 80px }
|
||||||
|
.w100 { width: 100px }
|
||||||
|
.w125 { width: 125px }
|
||||||
|
.w150 { width: 150px }
|
||||||
|
.w225 { width: 225px }
|
||||||
|
.w200, .w200 > div { width: 200px }
|
||||||
|
.w250, .w250 > div { width: 250px }
|
||||||
|
.w275, .w275 > div { width: 275px }
|
||||||
|
.w300, .w300 > div { width: 300px }
|
||||||
|
.w200 > div, .w250 > div, .w275 > div, .w300 > div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
||||||
|
|
||||||
|
footer { padding-top: 64px; font-size: 80%; cursor: default }
|
||||||
|
footer .content { width: 1200px; margin: 0 auto; border-top: 1px solid #ddd; padding: 24px 12px 48px 12px; line-height: 25px; font-weight: 300; position: relative }
|
||||||
|
footer .content .page-time { position: absolute; right: 12px; bottom: 0px; padding-bottom: 52px; font-size: 85%; color: #ccc }
|
||||||
|
|
||||||
|
.block-head { width: 1200px; margin: 0 auto 24px auto; cursor: default }
|
||||||
|
.block-head h3, h4 { font-weight: 300; margin: 0 }
|
||||||
|
.block-head h3 { font-size: 200%; margin-bottom: 3px }
|
||||||
|
.block-head h4 { font-size: 125% }
|
||||||
|
|
||||||
|
.block-info { width: 1200px; margin: 0 auto }
|
||||||
|
.block-info h3 { font-weight: normal; margin: 0 0 12px 0; font-weight: 300 }
|
||||||
|
|
||||||
|
.block-nav { width: 1200px; margin: 0 auto 24px auto }
|
||||||
|
.block-nav .btn { display: block; padding: 8px; width: 150px; text-align: center; border-radius: 3px; border: 1px solid #ddd; font-size: 80% }
|
||||||
|
.block-nav .btn:link, .block-nav .btn:visited { text-decoration: none; color: #333 }
|
||||||
|
.block-nav .btn:hover { background: #f1f1f1 }
|
||||||
|
|
||||||
|
.block-nav .btn-prev { float: left }
|
||||||
|
.block-nav .btn-next { float: right }
|
||||||
|
|
||||||
|
.block-summary { width: 460px; margin: 0; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 36px; cursor: default; float: left }
|
||||||
|
.block-summary .label { font-size: 80%; color: #1e88e5 }
|
||||||
|
.block-summary .value { font-weight: 300; word-break: break-word; word-wrap: break-word; font-size: 95% }
|
||||||
|
.block-summary .half-width { width: 50%; float: left }
|
||||||
|
.block-summary .spacer { height: 16px }
|
||||||
|
|
||||||
|
.block-transactions { width: 690px; margin: 0 0 0 50px; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 36px; cursor: default; float: left; overflow: auto; overflow-x: hidden }
|
||||||
|
|
||||||
|
.tx-head { width: 1200px; margin: 0 auto 24px auto; cursor: default }
|
||||||
|
.tx-head h3, h4 { font-weight: 300; margin: 0 }
|
||||||
|
.tx-head h3 { font-size: 200%; margin-bottom: 3px }
|
||||||
|
.tx-head h4 { font-size: 125% }
|
||||||
|
|
||||||
|
.realtime-head { width: 1200px; margin: 0 auto 36px auto; cursor: default }
|
||||||
|
.realtime-head h3 { font-weight: 300; margin: 0; font-size: 200% }
|
||||||
|
.realtime-main { width: 1200px; margin: 0 auto 0 auto }
|
||||||
|
.realtime-main h3 { font-weight: 300; margin: 0 0 12px 0 }
|
||||||
|
.realtime-main .realtime-blocks { width: 350px; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px; cursor: default; float: left }
|
||||||
|
.realtime-main .realtime-tx { width: 800px; margin-left: 50px; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px; cursor: default; float: left }
|
||||||
|
|
||||||
|
.stats { width: 1000px; margin: 0 auto 48px auto; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px; cursor: default }
|
||||||
|
.stats .box { padding: 24px 0; border-right: 1px solid #ccc; float: left; text-align: center; width: 25% }
|
||||||
|
.stats .box .title { color: #1e88e5; font-size: 90% }
|
||||||
|
.stats .box .value { font-size: 180%; font-weight: 300; margin-top: 8px }
|
||||||
|
.stats .box.last { border-color: transparent }
|
||||||
|
|
||||||
|
.tx-summary { width: 1200px; margin: 0 auto; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 16px 0; cursor: default }
|
||||||
|
.tx-summary .box { padding: 24px 0; border-right: 1px solid #ccc; float: left; text-align: center; width: 20% }
|
||||||
|
.tx-summary .box.p15 { width: 15% }
|
||||||
|
.tx-summary .box.p25 { width: 25% }
|
||||||
|
.tx-summary .box .title { color: #1e88e5; font-size: 90% }
|
||||||
|
.tx-summary .box .value { font-size: 180%; font-weight: 300; margin-top: 8px }
|
||||||
|
.tx-summary .box.last { border-color: transparent }
|
||||||
|
|
||||||
|
.tx-details { width: 1200px; margin: 48px auto 0 auto; cursor: default }
|
||||||
|
.tx-details h3 { font-weight: 300; margin: 0 0 8px 0 }
|
||||||
|
.tx-details-layout { display: table; width: 100%; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px; background: #f9f9f9 }
|
||||||
|
|
||||||
|
.tx-details-layout .divider { width: 20%; display: table-cell; text-align: center; vertical-align: top; padding-top: 40px }
|
||||||
|
.tx-details-layout .divider img { width: 72px }
|
||||||
|
.tx-details-layout .inputs, .tx-details-layout .outputs { width: 40%; display: table-cell; vertical-align: top }
|
||||||
|
.tx-details-layout .inputs .subtitle, .tx-details-layout .outputs .subtitle { margin-bottom: 16px; position: relative; line-height: 24px }
|
||||||
|
.tx-details-layout .outputs .subtitle .fee { position: absolute; display: block; right: 0; bottom: 0 }
|
||||||
|
.tx-details-layout .outputs .subtitle .fee .value { font-weight: 300; display: inline-block; margin-left: 16px }
|
||||||
|
.tx-details-layout .inputs .input, .tx-details-layout .outputs .output { border: 1px solid #ddd; padding: 16px; border-radius: 18px; font-size: 95%; font-weight: 300; margin-bottom: 16px; background: #fff; position: relative }
|
||||||
|
.tx-details-layout .tag { font-weight: normal; margin-top: 1px; font-size: 80%; color: #333 }
|
||||||
|
.tx-details-layout .tag a:link, .tx-details-layout .tag a:visited { color: #333 }
|
||||||
|
.tx-details-layout .tag a:hover { text-decoration: none }
|
||||||
|
|
||||||
|
.tx-details-layout .outputs .output .labels { position: absolute; right: 16px; top: 8px }
|
||||||
|
.tx-details-layout .outputs .output .labels > div { padding: 4px 12px; font-size: 70%; display: inline-block; margin-left: 4px }
|
||||||
|
.tx-details-layout .outputs .output .labels .support { background: #ffeb3b }
|
||||||
|
.tx-details-layout .outputs .output .labels .update { background: #ea80fc }
|
||||||
|
.tx-details-layout .outputs .output .labels .claim { background: #76ff03 }
|
||||||
|
.tx-details-layout .inputs .input .value, .tx-details-layout .outputs .output .value { font-weight: normal }
|
||||||
|
.tx-details-layout .inputs .input.is-source, .tx-details-layout .outputs .output.is-source { border-right-width: 18px; border-right-color: #1e88e5; background: #e3f2fd }
|
||||||
|
.tx-details-layout .inputs .input.highlighted, .tx-details-layout .outputs .output.highlighted { background: #f1f8e9 }
|
||||||
|
.tx-details-layout .outputs .output.is-source .labels { right: 8px }
|
||||||
|
|
||||||
|
.address-head { width: 1200px; margin: 0 auto 48px auto; cursor: default }
|
||||||
|
.address-head h3, h4 { font-weight: 300; margin: 0 }
|
||||||
|
.address-head h3 { font-size: 200%; margin-bottom: 3px }
|
||||||
|
.address-head h4 { font-size: 125%; display: inline-block }
|
||||||
|
.address-head .tag { display: inline-block; font-size: 80%; color: #1e88e5; margin-left: 8px }
|
||||||
|
.address-head .tag a:link, .address-head .tag a:visited { color: #1e88e5 }
|
||||||
|
.address-head .tag a:hover { text-decoration: none }
|
||||||
|
|
||||||
|
.address-head .tag-address-container { margin: 24px 0; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 24px; cursor: default; font-size: 80%; display: none }
|
||||||
|
.address-head .tag-address-container h4 { margin-bottom: 6px }
|
||||||
|
.address-head .tag-address-container .desc { line-height: 22px; font-weight: 300 }
|
||||||
|
.address-head .tag-address-container .form-group { margin-top: 12px }
|
||||||
|
.address-head .tag-address-container .form-group .error-message { margin-bottom: 6px; color: #ff0000; font-style: italic; font-size: 105%; height: 24px; line-height: 24px }
|
||||||
|
.address-head .tag-address-container .form-group .col { float: left; width: 300px; margin-right: 12px }
|
||||||
|
.address-head .tag-address-container .form-group .col input { display: block; border: 2px solid #ccc; padding: 6px; margin-bottom: 12px; border-radius: 4px; width: 300px; line-height: 24px }
|
||||||
|
.address-head .tag-address-container .form-group .col input:last-child { margin-bottom: 0 }
|
||||||
|
.address-head .tag-address-container .form-group .col textarea { display: block; border: 2px solid #ccc; width: 300px; padding: 6px; line-height: 24px; height: 92px; border-radius: 4px; resize: none; position: relative; top: -1px; font-size: 100% }
|
||||||
|
.address-head .tag-address-container .btn { display: inline-block; padding: 6px 36px; height: 39px; line-height: 24px; color: #fff; border-radius: 4px; font-weight: 300; cursor: pointer; border: none }
|
||||||
|
.address-head .tag-address-container .btn-tag { background: #1e88e5 }
|
||||||
|
.address-head .tag-address-container .btn-close { background: #ff0000; margin-left: 12px }
|
||||||
|
|
||||||
|
.address-subhead { width: 1200px; margin: 0 auto }
|
||||||
|
.address-qr { width: 170px; height: 170px; float: left }
|
||||||
|
.address-qr img { width: 170px; height: 170px; border: 1px solid rgba(0,0,0,.15); padding: 3px }
|
||||||
|
.address-summary { width: 982px; float: left; margin-left: 48px; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 25px 0 26px 0; cursor: default }
|
||||||
|
.address-summary .box { padding: 24px 0; border-right: 1px solid #ccc; float: left; text-align: center; width: 33% }
|
||||||
|
.address-summary .box .title { color: #1e88e5; font-size: 90% }
|
||||||
|
.address-summary .box .value { font-size: 180%; font-weight: 300; margin-top: 8px }
|
||||||
|
.address-summary .box.last { border-color: transparent }
|
||||||
|
|
||||||
|
.recent-transactions h3 { font-weight: 300; margin: 0; margin-bottom: 12px }
|
||||||
|
.recent-transactions { width: 1200px; margin: 48px auto 0 auto; box-shadow: 0 2px 6px rgba(0,0,0,.175); border: 1px solid rgba(0,0,0,.15); padding: 36px; cursor: default }
|
||||||
|
|
||||||
|
.tx-table .credit { color: #00e676 }
|
||||||
|
.tx-table .debit { color: #ff0000 }
|
||||||
|
|
||||||
|
.clear { clear: both }
|
BIN
webroot/img/right-arrow.png
Normal file
BIN
webroot/img/right-arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
37
webroot/index.php
Normal file
37
webroot/index.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Front Controller for handling every request
|
||||||
|
*
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* For full copyright and license information, please see the LICENSE.txt
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||||
|
* @link http://cakephp.org CakePHP(tm) Project
|
||||||
|
* @since 0.2.9
|
||||||
|
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
*/
|
||||||
|
// for built-in server
|
||||||
|
if (php_sapi_name() === 'cli-server') {
|
||||||
|
$_SERVER['PHP_SELF'] = '/' . basename(__FILE__);
|
||||||
|
|
||||||
|
$url = parse_url(urldecode($_SERVER['REQUEST_URI']));
|
||||||
|
$file = __DIR__ . $url['path'];
|
||||||
|
if (strpos($url['path'], '..') === false && strpos($url['path'], '.') !== false && is_file($file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
use App\Application;
|
||||||
|
use Cake\Http\Server;
|
||||||
|
|
||||||
|
// Bind your application to the server.
|
||||||
|
$server = new Server(new Application(dirname(__DIR__) . '/config'));
|
||||||
|
|
||||||
|
// Run the request/response through the application
|
||||||
|
// and emit the response.
|
||||||
|
$server->emit($server->run());
|
4
webroot/js/jquery.js
vendored
Normal file
4
webroot/js/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
webroot/js/moment.js
Normal file
7
webroot/js/moment.js
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue