mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-05-07 19:12:18 +09:00
commit
9330bb443a
611 changed files with 53504 additions and 10759 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
13
.travis.yml
13
.travis.yml
|
|
@ -1,6 +1,5 @@
|
|||
language: php
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
|
@ -11,14 +10,14 @@ before_script:
|
|||
- npm install grunt grunt-cli grunt-contrib-jshint grunt-contrib-csslint grunt-phplint --save-dev
|
||||
- mysql -u root -e "CREATE DATABASE rhymix"
|
||||
- mysql -u root -e "SET PASSWORD FOR 'travis'@'localhost' = PASSWORD('travis')"
|
||||
- if [[ $TRAVIS_PHP_VERSION != "hhvm" ]]; then php -S localhost:8000 & fi
|
||||
- if [[ $TRAVIS_PHP_VERSION == "5.3" ]]; then touch codecept.phar; fi
|
||||
- if [[ $TRAVIS_PHP_VERSION != "5.4" && $TRAVIS_PHP_VERSION != "hhvm" ]]; then php -S localhost:8000 & fi
|
||||
- if [[ $TRAVIS_PHP_VERSION == "5.4" ]]; then wget http://codeception.com/releases/2.0.16/codecept.phar; fi
|
||||
- if [[ ! -f codecept.phar ]]; then wget http://codeception.com/codecept.phar; fi
|
||||
- if [[ ! -f codecept.phar ]]; then wget http://codeception.com/releases/2.1.6/codecept.phar; fi
|
||||
script:
|
||||
- if [[ -f codecept.phar ]]; then php codecept.phar build; fi
|
||||
- if [[ $TRAVIS_PHP_VERSION == "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi
|
||||
- if [[ $TRAVIS_PHP_VERSION != "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis; fi
|
||||
- if [[ -s codecept.phar ]]; then php codecept.phar build; fi
|
||||
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION == "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi
|
||||
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION == "5.4" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi
|
||||
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION != "5.4" && $TRAVIS_PHP_VERSION != "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis; fi
|
||||
- grunt lint
|
||||
notifications:
|
||||
email: false
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ PHP, HTML, XML, CSS, JS 등 모든 텍스트 파일의 문자셋은 BOM이 없
|
|||
들여쓰기는 1개의 탭으로 합니다.
|
||||
단, 탭 대신 공백을 사용하는 파일에서는 일관성 유지를 위해 4칸의 공백을 사용할 수 있습니다.
|
||||
|
||||
들여쓴 줄들 사이의 빈 줄도 들여씁니다. (에디터에서 후행 공백을 제거하지 않도록 설정하십시오.)
|
||||
|
||||
PHP 코드만으로 이루어진 파일은 맨 끝에 `?>` 태그를 사용하지 않습니다.
|
||||
|
||||
### 공백 및 줄바꿈 규칙
|
||||
|
|
@ -155,5 +157,8 @@ Rhymix에서 정한 `error_reporting` 설정 하에서 어떤 에러도 발생
|
|||
문자열과 문자열, 정수와 정수를 비교할 때는 가능하면 `==` 대신 `===`을 사용합니다.
|
||||
실제 자료형이 다를 가능성이 있는 경우 `intval()`, `strval()` 등의 함수와 함께 사용합니다.
|
||||
|
||||
PHP 5.4 이상에서 지원하는 간단한 배열 문법(`[1, 2, 3]`)을 사용할 수 있으나,
|
||||
복잡한 구조의 배열을 선언할 때는 이 문법이 오히려 가독성을 해칠 수 있으니 주의하시기 바랍니다.
|
||||
|
||||
여기에서 규정하지 않은 내용은 [PSR-1](http://www.php-fig.org/psr/psr-1/)과
|
||||
[PSR-2](http://www.php-fig.org/psr/psr-2/)를 따릅니다.
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -32,18 +32,23 @@ Rhymix는 개발자와 사용자가 서로의 권리와 책임을 존중하는
|
|||
|
||||
### 설치 환경
|
||||
|
||||
- PHP 5.3.3 이상 (PHP 5.5.9 이상 권장, PHP 7 지원)
|
||||
- MySQL 4.1.13 이상 (MySQL 5.0.7 이상 또는 MariaDB 권장)
|
||||
- CUBRID 또는 MS SQL을 DB로 사용할 수도 있으나, 권장하지는 않습니다.
|
||||
Rhymix를 사용하려면 아래의 조건을 충족하는 웹호스팅이나 서버를 마련하셔야 합니다.
|
||||
|
||||
|
||||
- PHP 5.5.9 이상 (PHP 7 권장)
|
||||
- MySQL 5.0.7 이상 (MariaDB 권장)
|
||||
- 필수 PHP 모듈
|
||||
- curl
|
||||
- gd
|
||||
- iconv 또는 mbstring
|
||||
- json
|
||||
- mcrypt 또는 openssl
|
||||
- xml 및 simplexml
|
||||
- simplexml
|
||||
- php.ini에서 session.auto_start = Off로 설정되어 있어야 합니다.
|
||||
- 설치 폴더 또는 files 폴더에 쓰기 권한이 주어져야 합니다.
|
||||
- MySQL/MariaDB 외에도 아래의 DB를 사용할 수 있습니다.
|
||||
- CUBRID 9.0 이상
|
||||
- Microsoft SQL Server 2008 이상
|
||||
|
||||
### 개발 참여
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
/**
|
||||
* Manages Context such as request arguments/environment variables
|
||||
* It has dual method structure, easy-to use methods which can be called as self::methodname(),and methods called with static object.
|
||||
*
|
||||
* @author NAVER (developers@xpressengine.com)
|
||||
*/
|
||||
|
|
@ -394,11 +393,11 @@ class Context
|
|||
}
|
||||
if (strpos($current_url, 'xn--') !== false)
|
||||
{
|
||||
$current_url = self::decodeIdna($current_url);
|
||||
$current_url = Rhymix\Framework\URL::decodeIdna($current_url);
|
||||
}
|
||||
if (strpos($request_uri, 'xn--') !== false)
|
||||
{
|
||||
$request_uri = self::decodeIdna($request_uri);
|
||||
$request_uri = Rhymix\Framework\URL::decodeIdna($request_uri);
|
||||
}
|
||||
self::set('current_url', $current_url);
|
||||
self::set('request_uri', $request_uri);
|
||||
|
|
@ -449,6 +448,12 @@ class Context
|
|||
*/
|
||||
public static function close()
|
||||
{
|
||||
// Save debugging information.
|
||||
if (!DisplayHandler::$debug_printed)
|
||||
{
|
||||
DisplayHandler::getDebugInfo();
|
||||
}
|
||||
|
||||
// Check session status and close it if open.
|
||||
if (self::checkSessionStatus())
|
||||
{
|
||||
|
|
@ -574,8 +579,8 @@ class Context
|
|||
$db_info->sitelock_title = $config['lock']['title'];
|
||||
$db_info->sitelock_message = $config['lock']['message'];
|
||||
$db_info->sitelock_whitelist = count($config['lock']['allow']) ? $config['lock']['allow'] : array('127.0.0.1');
|
||||
$db_info->embed_white_iframe = $config['embedfilter']['iframe'];
|
||||
$db_info->embed_white_object = $config['embedfilter']['object'];
|
||||
$db_info->embed_white_iframe = $config['mediafilter']['iframe'] ?: $config['embedfilter']['iframe'];
|
||||
$db_info->embed_white_object = $config['mediafilter']['object'] ?: $config['embedfilter']['object'];
|
||||
$db_info->use_mobile_view = $config['use_mobile_view'] ? 'Y' : 'N';
|
||||
$db_info->use_prepared_statements = $config['use_prepared_statements'] ? 'Y' : 'N';
|
||||
$db_info->use_rewrite = $config['use_rewrite'] ? 'Y' : 'N';
|
||||
|
|
@ -686,7 +691,7 @@ class Context
|
|||
public function checkSSO()
|
||||
{
|
||||
// pass if it's not GET request or XE is not yet installed
|
||||
if(!config('use_sso') || isCrawler())
|
||||
if(!config('use_sso') || Rhymix\Framework\UA::isRobot())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -712,61 +717,66 @@ class Context
|
|||
$current_site = self::getRequestUri();
|
||||
|
||||
// Step 1: if the current site is not the default site, send SSO validation request to the default site
|
||||
if($default_url !== $current_site && !self::get('SSOID') && $_COOKIE['sso'] !== md5($current_site))
|
||||
if($default_url !== $current_site && !self::get('sso_response') && $_COOKIE['sso'] !== md5($current_site))
|
||||
{
|
||||
// Set sso cookie to prevent multiple simultaneous SSO validation requests
|
||||
setcookie('sso', md5($current_site), 0, '/');
|
||||
|
||||
// Redirect to the default site
|
||||
$redirect_url = sprintf('%s?return_url=%s', $default_url, urlencode(base64_encode($current_site)));
|
||||
$sso_request = Rhymix\Framework\Security::encrypt(Rhymix\Framework\URL::getCurrentURL());
|
||||
$redirect_url = $default_url . '?sso_request=' . urlencode($sso_request);
|
||||
header('Location:' . $redirect_url);
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: receive and process SSO validation request at the default site
|
||||
if($default_url === $current_site && self::get('return_url'))
|
||||
if($default_url === $current_site && self::get('sso_request'))
|
||||
{
|
||||
// Get the URL of the origin site
|
||||
$url = base64_decode(self::get('return_url'));
|
||||
$url_info = parse_url($url);
|
||||
$sso_request = Rhymix\Framework\Security::decrypt(self::get('sso_request'));
|
||||
if (!$sso_request || !preg_match('!^https?://!', $sso_request))
|
||||
{
|
||||
self::displayErrorPage('SSO Error', 'Invalid SSO Request', 400);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the origin site is a valid site in this XE installation (to prevent open redirect vuln)
|
||||
if(!getModel('module')->getSiteInfoByDomain(rtrim($url, '/'))->site_srl)
|
||||
{
|
||||
htmlHeader();
|
||||
echo self::getLang("msg_invalid_request");
|
||||
htmlFooter();
|
||||
return FALSE;
|
||||
self::displayErrorPage('SSO Error', 'Invalid SSO Request', 400);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Redirect back to the origin site
|
||||
$url_info['query'] .= ($url_info['query'] ? '&' : '') . 'SSOID=' . session_id();
|
||||
$redirect_url = sprintf('%s://%s%s%s%s', $url_info['scheme'], $url_info['host'], $url_info['port'] ? (':' . $url_info['port']) : '', $url_info['path'], ($url_info['query'] ? ('?' . $url_info['query']) : ''));
|
||||
header('Location:' . $redirect_url);
|
||||
return FALSE;
|
||||
$sso_response = Rhymix\Framework\Security::encrypt(session_id());
|
||||
header('Location: ' . Rhymix\Framework\URL::modifyURL($sso_request, array('sso_response' => $sso_response)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3: back at the origin site, set session ID to be the same as the default site
|
||||
if($default_url !== $current_site && self::get('SSOID'))
|
||||
if($default_url !== $current_site && self::get('sso_response'))
|
||||
{
|
||||
// Check that the session ID was given by the default site (to prevent session fixation CSRF)
|
||||
// Check SSO response
|
||||
$sso_response = Rhymix\Framework\Security::decrypt(self::get('sso_response'));
|
||||
if ($sso_response === false)
|
||||
{
|
||||
self::displayErrorPage('SSO Error', 'Invalid SSO Response', 400);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the response was given by the default site (to prevent session fixation CSRF)
|
||||
if(isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $default_url) !== 0)
|
||||
{
|
||||
htmlHeader();
|
||||
echo self::getLang("msg_invalid_request");
|
||||
htmlFooter();
|
||||
return FALSE;
|
||||
self::displayErrorPage('SSO Error', 'Invalid SSO Response', 400);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set session ID
|
||||
setcookie(session_name(), self::get('SSOID'));
|
||||
setcookie(session_name(), $sso_response);
|
||||
|
||||
// Finally, redirect to the originally requested URL
|
||||
$url_info = parse_url(self::getRequestUrl());
|
||||
$url_info['query'] = preg_replace('/(^|\b)SSOID=([^&?]+)/', '', $url_info['query']);
|
||||
$redirect_url = sprintf('%s://%s%s%s%s', $url_info['scheme'], $url_info['host'], $url_info['port'] ? (':' . $url_info['port']) : '', $url_info['path'], ($url_info['query'] ? ('?' . $url_info['query']) : ''));
|
||||
header('Location:' . $redirect_url);
|
||||
return FALSE;
|
||||
header('Location: ' . Rhymix\Framework\URL::getCurrentURL(array('sso_response' => null)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// If none of the conditions above apply, proceed normally
|
||||
|
|
@ -1058,7 +1068,11 @@ class Context
|
|||
*/
|
||||
public static function convertEncodingStr($str)
|
||||
{
|
||||
if(!$str) return null;
|
||||
if (!$str || utf8_check($str))
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
|
||||
$obj = new stdClass;
|
||||
$obj->str = $str;
|
||||
$obj = self::convertEncoding($obj);
|
||||
|
|
@ -1073,15 +1087,7 @@ class Context
|
|||
*/
|
||||
public static function encodeIdna($domain)
|
||||
{
|
||||
if(function_exists('idn_to_ascii'))
|
||||
{
|
||||
return idn_to_ascii($domain);
|
||||
}
|
||||
else
|
||||
{
|
||||
$encoder = new TrueBV\Punycode();
|
||||
return $encoder->encode($domain);
|
||||
}
|
||||
return Rhymix\Framework\URL::encodeIdna($domain);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1092,15 +1098,7 @@ class Context
|
|||
*/
|
||||
public static function decodeIdna($domain)
|
||||
{
|
||||
if(function_exists('idn_to_utf8'))
|
||||
{
|
||||
return idn_to_utf8($domain);
|
||||
}
|
||||
else
|
||||
{
|
||||
$decoder = new TrueBV\Punycode();
|
||||
return $decoder->decode($domain);
|
||||
}
|
||||
return Rhymix\Framework\URL::decodeIdna($domain);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1283,11 +1281,15 @@ class Context
|
|||
}
|
||||
|
||||
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
|
||||
if(Security::detectingXEE($xml))
|
||||
if(!Rhymix\Framework\Security::checkXEE($xml))
|
||||
{
|
||||
header("HTTP/1.0 400 Bad Request");
|
||||
exit;
|
||||
}
|
||||
if(function_exists('libxml_disable_entity_loader'))
|
||||
{
|
||||
libxml_disable_entity_loader(true);
|
||||
}
|
||||
|
||||
$oXml = new XmlParser();
|
||||
$xml_obj = $oXml->parse($xml);
|
||||
|
|
@ -1363,10 +1365,9 @@ class Context
|
|||
* @see Cast variables, such as _srl, page, and cpage, into interger
|
||||
* @param string $key Variable key
|
||||
* @param string $val Variable value
|
||||
* @param string $do_stripslashes Whether to strip slashes
|
||||
* @return mixed filtered value. Type are string or array
|
||||
*/
|
||||
public function _filterRequestVar($key, $val, $do_stripslashes = 1)
|
||||
public function _filterRequestVar($key, $val)
|
||||
{
|
||||
if(!($isArray = is_array($val)))
|
||||
{
|
||||
|
|
@ -1392,19 +1393,6 @@ class Context
|
|||
else
|
||||
{
|
||||
$result[$k] = $v;
|
||||
|
||||
if($do_stripslashes && version_compare(PHP_VERSION, '5.4.0', '<') && get_magic_quotes_gpc())
|
||||
{
|
||||
if (is_array($result[$k]))
|
||||
{
|
||||
array_walk_recursive($result[$k], function(&$val) { $val = stripslashes($val); });
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[$k] = stripslashes($result[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_array($result[$k]))
|
||||
{
|
||||
array_walk_recursive($result[$k], function(&$val) { $val = trim($val); });
|
||||
|
|
@ -1490,13 +1478,9 @@ class Context
|
|||
}
|
||||
|
||||
// Allow if the current user is in the list of allowed IPs.
|
||||
$allowed_list = config('lock.allow');
|
||||
foreach ($allowed_list as $allowed_ip)
|
||||
if (Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, config('lock.allow')))
|
||||
{
|
||||
if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $allowed_ip))
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Set headers and constants for backward compatibility.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ class DB
|
|||
*/
|
||||
protected static $priority_dbms = array(
|
||||
'mysqli' => 6,
|
||||
'mysql' => 4,
|
||||
'cubrid' => 2,
|
||||
'mssql' => 1
|
||||
);
|
||||
|
|
@ -271,9 +270,13 @@ class DB
|
|||
// after creating instance of class, check is supported
|
||||
foreach ($supported_list as $db_type)
|
||||
{
|
||||
if (strncasecmp($db_type, 'mysql', 5) === 0 && strtolower($db_type) !== 'mysqli')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$class_name = sprintf("DB%s%s", strtoupper(substr($db_type, 0, 1)), strtolower(substr($db_type, 1)));
|
||||
$class_file = sprintf(_XE_PATH_ . "classes/db/%s.class.php", $class_name);
|
||||
if(!file_exists($class_file) || stripos($class_file, '_innodb') !== false)
|
||||
if (!file_exists($class_file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,9 +81,14 @@ class DBCubrid extends DB
|
|||
define('__CUBRID_VERSION__', $cubrid_version);
|
||||
}
|
||||
|
||||
if(__CUBRID_VERSION__ >= '8.4.0')
|
||||
cubrid_set_autocommit($result, CUBRID_AUTOCOMMIT_TRUE);
|
||||
|
||||
if(version_compare(__CUBRID_VERSION__, '9.0', '<'))
|
||||
{
|
||||
$this->setError(-1, 'Rhymix requires CUBRID 9.0 or later. Current CUBRID version is ' . __CUBRID_VERSION__);
|
||||
return;
|
||||
}
|
||||
|
||||
cubrid_set_autocommit($result, CUBRID_AUTOCOMMIT_TRUE);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,16 @@ class DBMssql extends DB
|
|||
$this->setError(-1, 'database connect fail' . PHP_EOL . $errors);
|
||||
return;
|
||||
}
|
||||
|
||||
$server_info = sqlsrv_server_info($result);
|
||||
$server_version = $server_info['SQLServerVersion'];
|
||||
if ($server_version && version_compare($server_version, '10', '<'))
|
||||
{
|
||||
$this->setError(-1, 'Rhymix requires Microsoft SQL Server 2008 or later. Current version is ' . $server_version);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ class DBMysql extends DB
|
|||
$result = @mysql_connect($connection['host'], $connection['user'], $connection['pass']);
|
||||
if(!$result)
|
||||
{
|
||||
exit('Unable to connect to DB.');
|
||||
$this->setError(-1, 'Unable to connect to DB.');
|
||||
return;
|
||||
}
|
||||
|
||||
if(mysql_error())
|
||||
|
|
@ -76,10 +77,10 @@ class DBMysql extends DB
|
|||
return;
|
||||
}
|
||||
|
||||
// Error appears if the version is lower than 4.1.13
|
||||
if(version_compare(mysql_get_server_info($result), '4.1.13', '<'))
|
||||
// Error appears if the version is lower than 5.0.7
|
||||
if(version_compare(mysql_get_server_info($result), '5.0.7', '<'))
|
||||
{
|
||||
$this->setError(-1, 'Rhymix requires MySQL 4.1.13 or later. Current MySQL version is ' . mysql_get_server_info());
|
||||
$this->setError(-1, 'Rhymix requires MySQL 5.0.7 or later. Current MySQL version is ' . mysql_get_server_info());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +165,8 @@ class DBMysql extends DB
|
|||
{
|
||||
if(!$connection)
|
||||
{
|
||||
exit('Rhymix cannot handle DB connection.');
|
||||
$this->setError(-1, 'Unable to connect to DB.');
|
||||
return false;
|
||||
}
|
||||
// Run the query statement
|
||||
$result = @mysql_query($query, $connection);
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ class DBMysqli extends DBMysql
|
|||
{
|
||||
if ($connection === null)
|
||||
{
|
||||
debug_print_backtrace();
|
||||
exit;
|
||||
$this->setError(-1, 'Unable to connect to DB.');
|
||||
return false;
|
||||
}
|
||||
if($this->use_prepared_statements == 'Y')
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
class DisplayHandler extends Handler
|
||||
{
|
||||
public static $response_size = 0;
|
||||
public static $debug_printed = 0;
|
||||
var $content_size = 0; // /< The size of displaying contents
|
||||
var $gz_enabled = FALSE; // / <a flog variable whether to call contents after compressing by gzip
|
||||
var $handler = NULL;
|
||||
|
|
@ -136,16 +137,34 @@ class DisplayHandler extends Handler
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDebugInfo(&$output)
|
||||
public function getDebugInfo(&$output = null)
|
||||
{
|
||||
// Check if debugging information has already been printed.
|
||||
|
||||
if (self::$debug_printed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$debug_printed = 1;
|
||||
}
|
||||
|
||||
// Check if debugging is enabled for this request.
|
||||
if (!config('debug.enabled') || !Rhymix\Framework\Debug::isEnabledForCurrentUser())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not display debugging information if there is no output.
|
||||
$display_type = config('debug.display_type');
|
||||
if ($output === null && $display_type !== 'file')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Print debug information.
|
||||
switch ($display_type = config('debug.display_type'))
|
||||
switch ($display_type)
|
||||
{
|
||||
case 'panel':
|
||||
$data = Rhymix\Framework\Debug::getDebugData();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
class FileHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Changes path of target file, directory into absolute path
|
||||
*
|
||||
|
|
@ -17,12 +16,7 @@ class FileHandler
|
|||
*/
|
||||
public static function getRealPath($source)
|
||||
{
|
||||
if(strlen($source) >= 2 && substr_compare($source, './', 0, 2) === 0)
|
||||
{
|
||||
return _XE_PATH_ . substr($source, 2);
|
||||
}
|
||||
|
||||
return $source;
|
||||
return (strncmp($source, './', 2) === 0) ? (\RX_BASEDIR . substr($source, 2)) : $source;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -33,64 +27,11 @@ class FileHandler
|
|||
* @param string $source_dir Path of source directory
|
||||
* @param string $target_dir Path of target dir
|
||||
* @param string $filter Regex to filter files. If file matches this regex, the file is not copied.
|
||||
* @param string $type If set as 'force'. Even if the file exists in target, the file is copied.
|
||||
* @return void
|
||||
*/
|
||||
public static function copyDir($source_dir, $target_dir, $filter = null, $type = null)
|
||||
public static function copyDir($source_dir, $target_dir, $filter = null)
|
||||
{
|
||||
$source_dir = self::getRealPath($source_dir);
|
||||
$target_dir = self::getRealPath($target_dir);
|
||||
if(!is_dir($source_dir))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// generate when no target exists
|
||||
self::makeDir($target_dir);
|
||||
|
||||
if(substr($source_dir, -1) != DIRECTORY_SEPARATOR)
|
||||
{
|
||||
$source_dir .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
if(substr($target_dir, -1) != DIRECTORY_SEPARATOR)
|
||||
{
|
||||
$target_dir .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
$oDir = dir($source_dir);
|
||||
while($file = $oDir->read())
|
||||
{
|
||||
if($file{0} == '.')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if($filter && preg_match($filter, $file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(is_dir($source_dir . $file))
|
||||
{
|
||||
self::copyDir($source_dir . $file, $target_dir . $file, $type);
|
||||
}
|
||||
else
|
||||
{
|
||||
if($type == 'force')
|
||||
{
|
||||
@unlink($target_dir . $file);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!file_exists($target_dir . $file))
|
||||
{
|
||||
@copy($source_dir . $file, $target_dir . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$oDir->close();
|
||||
return Rhymix\Framework\Storage::copyDirectory(self::getRealPath($source_dir), self::getRealPath($target_dir), $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -104,18 +45,7 @@ class FileHandler
|
|||
public static function copyFile($source, $target, $force = 'Y')
|
||||
{
|
||||
setlocale(LC_CTYPE, 'en_US.UTF8', 'ko_KR.UTF8');
|
||||
$source = self::getRealPath($source);
|
||||
$target_dir = self::getRealPath(dirname($target));
|
||||
$target = basename($target);
|
||||
|
||||
self::makeDir($target_dir);
|
||||
|
||||
if($force == 'Y')
|
||||
{
|
||||
@unlink($target_dir . DIRECTORY_SEPARATOR . $target);
|
||||
}
|
||||
|
||||
@copy($source, $target_dir . DIRECTORY_SEPARATOR . $target);
|
||||
return Rhymix\Framework\Storage::copy(self::getRealPath($source), self::getRealPath($target));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,12 +56,7 @@ class FileHandler
|
|||
*/
|
||||
public static function readFile($filename)
|
||||
{
|
||||
if(($filename = self::exists($filename)) === FALSE || filesize($filename) < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return @file_get_contents($filename);
|
||||
return Rhymix\Framework\Storage::read(self::getRealPath($filename));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -144,22 +69,7 @@ class FileHandler
|
|||
*/
|
||||
public static function writeFile($filename, $buff, $mode = "w")
|
||||
{
|
||||
$filename = self::getRealPath($filename);
|
||||
$pathinfo = pathinfo($filename);
|
||||
self::makeDir($pathinfo['dirname']);
|
||||
|
||||
$flags = 0;
|
||||
if(strtolower($mode) == 'a')
|
||||
{
|
||||
$flags = FILE_APPEND;
|
||||
}
|
||||
|
||||
@file_put_contents($filename, $buff, $flags|LOCK_EX);
|
||||
@chmod($filename, 0644);
|
||||
if(function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
|
||||
{
|
||||
@opcache_invalidate($filename, true);
|
||||
}
|
||||
return Rhymix\Framework\Storage::write(self::getRealPath($filename), $buff, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -170,16 +80,7 @@ class FileHandler
|
|||
*/
|
||||
public static function removeFile($filename)
|
||||
{
|
||||
if(($filename = self::exists($filename)) === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$status = @unlink($filename);
|
||||
if(function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
|
||||
{
|
||||
@opcache_invalidate($filename, true);
|
||||
}
|
||||
return $status;
|
||||
return Rhymix\Framework\Storage::delete(self::getRealPath($filename));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -193,7 +94,7 @@ class FileHandler
|
|||
*/
|
||||
public static function rename($source, $target)
|
||||
{
|
||||
return @rename(self::getRealPath($source), self::getRealPath($target));
|
||||
return Rhymix\Framework\Storage::move(self::getRealPath($source), self::getRealPath($target));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -205,12 +106,7 @@ class FileHandler
|
|||
*/
|
||||
public static function moveFile($source, $target)
|
||||
{
|
||||
if(($source = self::exists($source)) !== FALSE)
|
||||
{
|
||||
self::removeFile($target);
|
||||
return self::rename($source, $target);
|
||||
}
|
||||
return FALSE;
|
||||
return Rhymix\Framework\Storage::move(self::getRealPath($source), self::getRealPath($target));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -224,7 +120,7 @@ class FileHandler
|
|||
*/
|
||||
public static function moveDir($source_dir, $target_dir)
|
||||
{
|
||||
self::rename($source_dir, $target_dir);
|
||||
return Rhymix\Framework\Storage::move(self::getRealPath($source_dir), self::getRealPath($target_dir));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -240,45 +136,31 @@ class FileHandler
|
|||
*/
|
||||
public static function readDir($path, $filter = '', $to_lower = FALSE, $concat_prefix = FALSE)
|
||||
{
|
||||
$path = self::getRealPath($path);
|
||||
$list = Rhymix\Framework\Storage::readDirectory(self::getRealPath($path), $concat_prefix, true, false);
|
||||
if (!$list)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$output = array();
|
||||
|
||||
if(substr($path, -1) != '/')
|
||||
foreach ($list as $filename)
|
||||
{
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
if(!is_dir($path))
|
||||
{
|
||||
return $output;
|
||||
}
|
||||
|
||||
$files = scandir($path);
|
||||
foreach($files as $file)
|
||||
{
|
||||
if($file{0} == '.' || ($filter && !preg_match($filter, $file)))
|
||||
$filename = str_replace(array('/\\', '//'), '/', $filename);
|
||||
$basename = $concat_prefix ? basename($filename) : $filename;
|
||||
if ($basename[0] === '.' || ($filter && !preg_match($filter, $basename)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if($to_lower)
|
||||
if ($to_lower)
|
||||
{
|
||||
$file = strtolower($file);
|
||||
$filename = strtolower($filename);
|
||||
}
|
||||
|
||||
if($filter)
|
||||
{
|
||||
$file = preg_replace($filter, '$1', $file);
|
||||
$filename = preg_replace($filter, '$1', $filename);
|
||||
}
|
||||
|
||||
if($concat_prefix)
|
||||
{
|
||||
$file = sprintf('%s%s', str_replace(_XE_PATH_, '', $path), $file);
|
||||
}
|
||||
|
||||
$output[] = str_replace(array('/\\', '//'), '/', $file);
|
||||
$output[] = $filename;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
|
@ -292,16 +174,12 @@ class FileHandler
|
|||
*/
|
||||
public static function makeDir($path_string)
|
||||
{
|
||||
if(self::exists($path_string) !== FALSE)
|
||||
if (!ini_get('safe_mode'))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(!ini_get('safe_mode'))
|
||||
{
|
||||
@mkdir($path_string, 0755, TRUE);
|
||||
@chmod($path_string, 0755);
|
||||
$path = self::getRealPath($path_string);
|
||||
return Rhymix\Framework\Storage::isDirectory($path) || Rhymix\Framework\Storage::createDirectory($path);
|
||||
}
|
||||
|
||||
// if safe_mode is on, use FTP
|
||||
else
|
||||
{
|
||||
|
|
@ -372,37 +250,7 @@ class FileHandler
|
|||
*/
|
||||
public static function removeDir($path)
|
||||
{
|
||||
if(($path = self::isDir($path)) === FALSE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(self::isDir($path))
|
||||
{
|
||||
$files = array_diff(scandir($path), array('..', '.'));
|
||||
|
||||
foreach($files as $file)
|
||||
{
|
||||
if(($target = self::getRealPath($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(is_dir($target))
|
||||
{
|
||||
self::removeDir($target);
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink($target);
|
||||
}
|
||||
}
|
||||
rmdir($path);
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink($path);
|
||||
}
|
||||
return Rhymix\Framework\Storage::deleteDirectory(self::getRealPath($path));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -413,27 +261,14 @@ class FileHandler
|
|||
*/
|
||||
public static function removeBlankDir($path)
|
||||
{
|
||||
if(($path = self::isDir($path)) === FALSE)
|
||||
$path = self::getRealPath($path);
|
||||
if (Rhymix\Framework\Storage::isEmptyDirectory($path))
|
||||
{
|
||||
return;
|
||||
return Rhymix\Framework\Storage::deleteDirectory($path);
|
||||
}
|
||||
|
||||
$files = array_diff(scandir($path), array('..', '.'));
|
||||
|
||||
if(count($files) < 1)
|
||||
else
|
||||
{
|
||||
rmdir($path);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($files as $file)
|
||||
{
|
||||
if(($target = self::isDir($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
self::removeBlankDir($target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -447,37 +282,7 @@ class FileHandler
|
|||
*/
|
||||
public static function removeFilesInDir($path)
|
||||
{
|
||||
if(($path = self::getRealPath($path)) === FALSE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_dir($path))
|
||||
{
|
||||
$files = array_diff(scandir($path), array('..', '.'));
|
||||
|
||||
foreach($files as $file)
|
||||
{
|
||||
if(($target = self::getRealPath($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(is_dir($target))
|
||||
{
|
||||
self::removeFilesInDir($target);
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink($target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(self::exists($path)) unlink($path);
|
||||
}
|
||||
|
||||
return Rhymix\Framework\Storage::deleteDirectory(self::getRealPath($path), false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -584,7 +389,14 @@ class FileHandler
|
|||
|
||||
if($response->success)
|
||||
{
|
||||
return $response->body;
|
||||
if (isset($request_config['filename']))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $response->body;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -611,13 +423,9 @@ class FileHandler
|
|||
*/
|
||||
public static function getRemoteFile($url, $target_filename, $body = null, $timeout = 3, $method = 'GET', $content_type = null, $headers = array(), $cookies = array(), $post_data = array(), $request_config = array())
|
||||
{
|
||||
if(!($body = self::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers,$cookies,$post_data,$request_config)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self::writeFile($target_filename, $body);
|
||||
return TRUE;
|
||||
$request_config['filename'] = $target_filename;
|
||||
$success = self::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers, $cookies, $post_data, $request_config);
|
||||
return $success ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -658,12 +466,17 @@ class FileHandler
|
|||
$channels = 6; //for png
|
||||
}
|
||||
$memoryNeeded = round(($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $channels / 8 + $K64 ) * $TWEAKFACTOR);
|
||||
$availableMemory = self::returnBytes(ini_get('memory_limit')) - memory_get_usage();
|
||||
$memoryLimit = self::returnBytes(ini_get('memory_limit'));
|
||||
if($memoryLimit < 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
$availableMemory = $memoryLimit - memory_get_usage();
|
||||
if($availableMemory < $memoryNeeded)
|
||||
{
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -882,19 +695,13 @@ class FileHandler
|
|||
*/
|
||||
public static function readIniFile($filename)
|
||||
{
|
||||
if(($filename = self::exists($filename)) === FALSE)
|
||||
if(!Rhymix\Framework\Storage::isReadable($filename))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
$arr = parse_ini_file($filename, TRUE);
|
||||
if(is_array($arr) && count($arr) > 0)
|
||||
{
|
||||
return $arr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return array();
|
||||
return false;
|
||||
}
|
||||
|
||||
$arr = parse_ini_file($filename, true);
|
||||
return is_array($arr) ? $arr : array();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -966,11 +773,9 @@ class FileHandler
|
|||
*/
|
||||
public static function openFile($filename, $mode)
|
||||
{
|
||||
$pathinfo = pathinfo($filename);
|
||||
self::makeDir($pathinfo['dirname']);
|
||||
|
||||
require_once("FileObject.class.php");
|
||||
return new FileObject($filename, $mode);
|
||||
$filename = self::getRealPath($filename);
|
||||
Rhymix\Framework\Storage::createDirectory(dirname($filename));
|
||||
return new FileObject($filename, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -981,7 +786,7 @@ class FileHandler
|
|||
*/
|
||||
public static function hasContent($filename)
|
||||
{
|
||||
return (is_readable($filename) && (filesize($filename) > 0));
|
||||
return Rhymix\Framework\Storage::getSize(self::getRealPath($filename)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -993,7 +798,7 @@ class FileHandler
|
|||
public static function exists($filename)
|
||||
{
|
||||
$filename = self::getRealPath($filename);
|
||||
return file_exists($filename) ? $filename : FALSE;
|
||||
return Rhymix\Framework\Storage::exists($filename) ? $filename : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1005,7 +810,7 @@ class FileHandler
|
|||
public static function isDir($path)
|
||||
{
|
||||
$path = self::getRealPath($path);
|
||||
return is_dir($path) ? $path : FALSE;
|
||||
return Rhymix\Framework\Storage::isDirectory($path) ? $path : false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1017,22 +822,7 @@ class FileHandler
|
|||
public static function isWritableDir($path)
|
||||
{
|
||||
$path = self::getRealPath($path);
|
||||
if(is_dir($path)==FALSE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$checkFile = $path . '/_CheckWritableDir';
|
||||
|
||||
$fp = fopen($checkFile, 'w');
|
||||
if(!is_resource($fp))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
fclose($fp);
|
||||
|
||||
self::removeFile($checkFile);
|
||||
return TRUE;
|
||||
return Rhymix\Framework\Storage::isDirectory($path) && Rhymix\Framework\Storage::isWritable($path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -460,81 +460,7 @@ class Mail
|
|||
*/
|
||||
function returnMIMEType($filename)
|
||||
{
|
||||
preg_match("|\.([a-z0-9]{2,4})$|i", $filename, $fileSuffix);
|
||||
switch(strtolower($fileSuffix[1]))
|
||||
{
|
||||
case "js" :
|
||||
return "application/x-javascript";
|
||||
case "json" :
|
||||
return "application/json";
|
||||
case "jpg" :
|
||||
case "jpeg" :
|
||||
case "jpe" :
|
||||
return "image/jpg";
|
||||
case "png" :
|
||||
case "gif" :
|
||||
case "bmp" :
|
||||
case "tiff" :
|
||||
return "image/" . strtolower($fileSuffix[1]);
|
||||
case "css" :
|
||||
return "text/css";
|
||||
case "xml" :
|
||||
return "application/xml";
|
||||
case "doc" :
|
||||
case "docx" :
|
||||
return "application/msword";
|
||||
case "xls" :
|
||||
case "xlt" :
|
||||
case "xlm" :
|
||||
case "xld" :
|
||||
case "xla" :
|
||||
case "xlc" :
|
||||
case "xlw" :
|
||||
case "xll" :
|
||||
return "application/vnd.ms-excel";
|
||||
case "ppt" :
|
||||
case "pps" :
|
||||
return "application/vnd.ms-powerpoint";
|
||||
case "rtf" :
|
||||
return "application/rtf";
|
||||
case "pdf" :
|
||||
return "application/pdf";
|
||||
case "html" :
|
||||
case "htm" :
|
||||
case "php" :
|
||||
return "text/html";
|
||||
case "txt" :
|
||||
return "text/plain";
|
||||
case "mpeg" :
|
||||
case "mpg" :
|
||||
case "mpe" :
|
||||
return "video/mpeg";
|
||||
case "mp3" :
|
||||
return "audio/mpeg3";
|
||||
case "wav" :
|
||||
return "audio/wav";
|
||||
case "aiff" :
|
||||
case "aif" :
|
||||
return "audio/aiff";
|
||||
case "avi" :
|
||||
return "video/msvideo";
|
||||
case "wmv" :
|
||||
return "video/x-ms-wmv";
|
||||
case "mov" :
|
||||
return "video/quicktime";
|
||||
case "zip" :
|
||||
return "application/zip";
|
||||
case "tar" :
|
||||
return "application/x-tar";
|
||||
case "swf" :
|
||||
return "application/x-shockwave-flash";
|
||||
default :
|
||||
if(function_exists("mime_content_type"))
|
||||
{
|
||||
$fileSuffix = mime_content_type($filename);
|
||||
}
|
||||
return "unknown/" . trim($fileSuffix[0], ".");
|
||||
}
|
||||
return Rhymix\Framework\MIME::getTypeByFilename($filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,9 +55,6 @@ class Mobile
|
|||
return $this->ismobile = false;
|
||||
}
|
||||
|
||||
$xe_web_path = Context::pathToUrl(_XE_PATH_);
|
||||
|
||||
// default setting. if there is cookie for a device, XE do not have to check if it is mobile or not and it will enhance performace of the server.
|
||||
$this->ismobile = FALSE;
|
||||
|
||||
$m = Context::get('m');
|
||||
|
|
@ -87,28 +84,14 @@ class Mobile
|
|||
}
|
||||
else
|
||||
{
|
||||
$this->ismobile = FALSE;
|
||||
setcookie("mobile", FALSE, 0, $xe_web_path);
|
||||
setcookie("user-agent", FALSE, 0, $xe_web_path);
|
||||
if(!self::isMobilePadCheckByAgent() && self::isMobileCheckByAgent())
|
||||
{
|
||||
$this->ismobile = TRUE;
|
||||
}
|
||||
setcookie("mobile", FALSE, 0, RX_BASEURL);
|
||||
setcookie("user-agent", FALSE, 0, RX_BASEURL);
|
||||
$this->ismobile = Rhymix\Framework\UA::isMobile() && !Rhymix\Framework\UA::isTablet();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(self::isMobilePadCheckByAgent())
|
||||
{
|
||||
$this->ismobile = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(self::isMobileCheckByAgent())
|
||||
{
|
||||
$this->ismobile = TRUE;
|
||||
}
|
||||
}
|
||||
$this->ismobile = Rhymix\Framework\UA::isMobile() && !Rhymix\Framework\UA::isTablet();
|
||||
}
|
||||
|
||||
if($this->ismobile !== NULL)
|
||||
|
|
@ -118,18 +101,18 @@ class Mobile
|
|||
if($_COOKIE['mobile'] != 'true')
|
||||
{
|
||||
$_COOKIE['mobile'] = 'true';
|
||||
setcookie("mobile", 'true', 0, $xe_web_path);
|
||||
setcookie("mobile", 'true', 0, RX_BASEURL);
|
||||
}
|
||||
}
|
||||
elseif(isset($_COOKIE['mobile']) && $_COOKIE['mobile'] != 'false')
|
||||
{
|
||||
$_COOKIE['mobile'] = 'false';
|
||||
setcookie("mobile", 'false', 0, $xe_web_path);
|
||||
setcookie("mobile", 'false', 0, RX_BASEURL);
|
||||
}
|
||||
|
||||
if(isset($_COOKIE['mobile']) && $_COOKIE['user-agent'] != md5($_SERVER['HTTP_USER_AGENT']))
|
||||
{
|
||||
setcookie("user-agent", md5($_SERVER['HTTP_USER_AGENT']), 0, $xe_web_path);
|
||||
setcookie("user-agent", md5($_SERVER['HTTP_USER_AGENT']), 0, RX_BASEURL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,31 +126,7 @@ class Mobile
|
|||
*/
|
||||
public static function isMobileCheckByAgent()
|
||||
{
|
||||
static $UACheck;
|
||||
if(isset($UACheck))
|
||||
{
|
||||
return $UACheck;
|
||||
}
|
||||
|
||||
$oMobile = Mobile::getInstance();
|
||||
$mobileAgent = array('iPod', 'iPhone', 'Android', 'BlackBerry', 'SymbianOS', 'Bada', 'Tizen', 'Kindle', 'Wii', 'SCH-', 'SPH-', 'CANU-', 'Windows Phone', 'Windows CE', 'POLARIS', 'Palm', 'Dorothy Browser', 'Mobile', 'Opera Mobi', 'Opera Mini', 'Minimo', 'AvantGo', 'NetFront', 'Nokia', 'LGPlayer', 'SonyEricsson', 'HTC');
|
||||
|
||||
if($oMobile->isMobilePadCheckByAgent())
|
||||
{
|
||||
$UACheck = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
foreach($mobileAgent as $agent)
|
||||
{
|
||||
if(stripos($_SERVER['HTTP_USER_AGENT'], $agent) !== FALSE)
|
||||
{
|
||||
$UACheck = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
$UACheck = FALSE;
|
||||
return FALSE;
|
||||
return Rhymix\Framework\UA::isMobile();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -177,45 +136,7 @@ class Mobile
|
|||
*/
|
||||
public static function isMobilePadCheckByAgent()
|
||||
{
|
||||
static $UACheck;
|
||||
if(isset($UACheck))
|
||||
{
|
||||
return $UACheck;
|
||||
}
|
||||
$padAgent = array('iPad', 'Android', 'webOS', 'hp-tablet', 'PlayBook');
|
||||
|
||||
// Android with 'Mobile' string is not a tablet-like device, and 'Andoroid' without 'Mobile' string is a tablet-like device.
|
||||
// $exceptionAgent[0] contains exception agents for all exceptions.
|
||||
$exceptionAgent = array(0 => array('Opera Mini', 'Opera Mobi'), 'Android' => 'Mobile');
|
||||
|
||||
foreach($padAgent as $agent)
|
||||
{
|
||||
if(strpos($_SERVER['HTTP_USER_AGENT'], $agent) !== FALSE)
|
||||
{
|
||||
if(!isset($exceptionAgent[$agent]))
|
||||
{
|
||||
$UACheck = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
elseif(strpos($_SERVER['HTTP_USER_AGENT'], $exceptionAgent[$agent]) === FALSE)
|
||||
{
|
||||
// If the agent is the Android, that can be either tablet and mobile phone.
|
||||
foreach($exceptionAgent[0] as $val)
|
||||
{
|
||||
if(strpos($_SERVER['HTTP_USER_AGENT'], $val) !== FALSE)
|
||||
{
|
||||
$UACheck = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
$UACheck = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$UACheck = FALSE;
|
||||
return FALSE;
|
||||
return Rhymix\Framework\UA::isTablet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -96,10 +96,13 @@ class ModuleHandler extends Handler
|
|||
{
|
||||
if(Context::get('_use_ssl') == 'optional' && Context::isExistsSSLAction($this->act) && !RX_SSL)
|
||||
{
|
||||
if(Context::get('_https_port')!=null) {
|
||||
header('location:https://' . $_SERVER['HTTP_HOST'] . ':' . Context::get('_https_port') . $_SERVER['REQUEST_URI']);
|
||||
} else {
|
||||
header('location:https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
|
||||
if(Context::get('_https_port') != null)
|
||||
{
|
||||
header('location: https://' . $_SERVER['HTTP_HOST'] . ':' . Context::get('_https_port') . $_SERVER['REQUEST_URI']);
|
||||
}
|
||||
else
|
||||
{
|
||||
header('location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -165,12 +168,21 @@ class ModuleHandler extends Handler
|
|||
// Get module's information based on document_srl, if it's specified
|
||||
if($this->document_srl)
|
||||
{
|
||||
|
||||
$module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl);
|
||||
|
||||
// If the document does not exist, remove document_srl
|
||||
if(!$module_info)
|
||||
{
|
||||
unset($this->document_srl);
|
||||
if(Context::getRequestMethod() == 'GET')
|
||||
{
|
||||
$this->error = 'The document does not exist';
|
||||
$this->httpStatusCode = '404';
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->document_srl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -178,19 +190,17 @@ class ModuleHandler extends Handler
|
|||
// if mids are not matching, set it as the document's mid
|
||||
if(!$this->mid || ($this->mid != $module_info->mid))
|
||||
{
|
||||
|
||||
if(Context::getRequestMethod() == 'GET')
|
||||
{
|
||||
$this->mid = $module_info->mid;
|
||||
header('location:' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $this->mid, 'document_srl', $this->document_srl));
|
||||
return FALSE;
|
||||
Context::setCacheControl(0);
|
||||
header('location: ' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $module_info->mid, 'document_srl', $this->document_srl), true, 301);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->mid = $module_info->mid;
|
||||
Context::set('mid', $this->mid);
|
||||
}
|
||||
|
||||
}
|
||||
// if requested module is different from one of the document, remove the module information retrieved based on the document number
|
||||
if($this->module && $module_info->module != $this->module)
|
||||
|
|
@ -198,7 +208,6 @@ class ModuleHandler extends Handler
|
|||
unset($module_info);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If module_info is not set yet, and there exists mid information, get module information based on the mid
|
||||
|
|
@ -211,9 +220,10 @@ class ModuleHandler extends Handler
|
|||
// redirect, if module_site_srl and site_srl are different
|
||||
if(!$this->module && !$module_info && $site_module_info->site_srl == 0 && $site_module_info->module_site_srl > 0)
|
||||
{
|
||||
Context::setCacheControl(0);
|
||||
$site_info = $oModuleModel->getSiteInfo($site_module_info->module_site_srl);
|
||||
header("location:" . getNotEncodedSiteUrl($site_info->domain, 'mid', $site_module_info->mid));
|
||||
return FALSE;
|
||||
header('location: ' . getNotEncodedSiteUrl($site_info->domain, 'mid', $site_module_info->mid), true, 301);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If module_info is not set still, and $module does not exist, find the default module
|
||||
|
|
@ -228,7 +238,7 @@ class ModuleHandler extends Handler
|
|||
}
|
||||
|
||||
// redirect, if site_srl of module_info is different from one of site's module_info
|
||||
if($module_info && $module_info->site_srl != $site_module_info->site_srl && !isCrawler())
|
||||
if($module_info && $module_info->site_srl != $site_module_info->site_srl && !Rhymix\Framework\UA::isRobot())
|
||||
{
|
||||
// If the module is of virtual site
|
||||
if($module_info->site_srl)
|
||||
|
|
@ -241,10 +251,20 @@ class ModuleHandler extends Handler
|
|||
{
|
||||
$redirect_url = getNotEncodedSiteUrl(Context::getDefaultUrl(), 'mid', Context::get('mid'), 'document_srl', Context::get('document_srl'), 'module_srl', Context::get('module_srl'), 'entry', Context::get('entry'));
|
||||
}
|
||||
header("Location: $redirect_url");
|
||||
return FALSE;
|
||||
|
||||
Context::setCacheControl(0);
|
||||
header("Location: $redirect_url", true, 301);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// redirect, if site start module
|
||||
if(isset($_GET['mid']) && $_GET['mid'] === $site_module_info->mid && !$this->act && !$this->module && !$this->document_srl)
|
||||
{
|
||||
Context::setCacheControl(0);
|
||||
header('location: ' . getNotEncodedSiteUrl($site_module_info->domain), true, 301);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If module info was set, retrieve variables from the module information
|
||||
if($module_info)
|
||||
{
|
||||
|
|
@ -290,6 +310,7 @@ class ModuleHandler extends Handler
|
|||
{
|
||||
$this->error = 'msg_module_is_not_exists';
|
||||
$this->httpStatusCode = '404';
|
||||
return true;
|
||||
}
|
||||
|
||||
// If mid exists, set mid into context
|
||||
|
|
@ -303,13 +324,13 @@ class ModuleHandler extends Handler
|
|||
if(!$output->toBool())
|
||||
{
|
||||
$this->error = $output->getMessage();
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set current module info into context
|
||||
Context::set('current_module_info', $this->module_info);
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -465,7 +486,6 @@ class ModuleHandler extends Handler
|
|||
// If there is no such action in the module object
|
||||
if(!isset($xml_info->action->{$this->act}) || !method_exists($oModule, $this->act))
|
||||
{
|
||||
|
||||
if(!Context::isInstalled())
|
||||
{
|
||||
self::_setInputErrorToContext();
|
||||
|
|
@ -760,10 +780,6 @@ class ModuleHandler extends Handler
|
|||
}
|
||||
self::_setInputValueToSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if($error != 0)
|
||||
{
|
||||
|
|
@ -884,7 +900,7 @@ class ModuleHandler extends Handler
|
|||
|
||||
if($_SESSION['XE_VALIDATOR_RETURN_URL'])
|
||||
{
|
||||
header('location:' . $_SESSION['XE_VALIDATOR_RETURN_URL']);
|
||||
header('location: ' . $_SESSION['XE_VALIDATOR_RETURN_URL']);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -939,13 +955,11 @@ class ModuleHandler extends Handler
|
|||
|
||||
if($layout_srl && !$oModule->getLayoutFile())
|
||||
{
|
||||
|
||||
// If layout_srl exists, get information of the layout, and set the location of layout_path/ layout_file
|
||||
$oLayoutModel = getModel('layout');
|
||||
$layout_info = $oLayoutModel->getLayout($layout_srl);
|
||||
if($layout_info)
|
||||
{
|
||||
|
||||
// Input extra_vars into $layout_info
|
||||
if($layout_info->extra_var_count)
|
||||
{
|
||||
|
|
@ -1283,6 +1297,8 @@ class ModuleHandler extends Handler
|
|||
$statusMessageList = array(
|
||||
'100' => 'Continue',
|
||||
'101' => 'Switching Protocols',
|
||||
'102' => 'Processing',
|
||||
'103' => 'Checkpoint',
|
||||
'200' => 'OK',
|
||||
'201' => 'Created',
|
||||
'202' => 'Accepted',
|
||||
|
|
@ -1290,13 +1306,18 @@ class ModuleHandler extends Handler
|
|||
'204' => 'No Content',
|
||||
'205' => 'Reset Content',
|
||||
'206' => 'Partial Content',
|
||||
'207' => 'Multi-Status',
|
||||
'208' => 'Already Reported',
|
||||
'226' => 'IM Used',
|
||||
'300' => 'Multiple Choices',
|
||||
'301' => 'Moved Permanently',
|
||||
'302' => 'Found',
|
||||
'303' => 'See Other',
|
||||
'304' => 'Not Modified',
|
||||
'305' => 'Use Proxy',
|
||||
'306' => 'Switch Proxy',
|
||||
'307' => 'Temporary Redirect',
|
||||
'308' => 'Permanent Redirect',
|
||||
'400' => 'Bad Request',
|
||||
'401' => 'Unauthorized',
|
||||
'402' => 'Payment Required',
|
||||
|
|
@ -1310,19 +1331,39 @@ class ModuleHandler extends Handler
|
|||
'410' => 'Gone',
|
||||
'411' => 'Length Required',
|
||||
'412' => 'Precondition Failed',
|
||||
'413' => 'Request Entity Too Large',
|
||||
'414' => 'Request-URI Too Long',
|
||||
'413' => 'Payload Too Large',
|
||||
'414' => 'URI Too Long',
|
||||
'415' => 'Unsupported Media Type',
|
||||
'416' => 'Requested Range Not Satisfiable',
|
||||
'416' => 'Range Not Satisfiable',
|
||||
'417' => 'Expectation Failed',
|
||||
'418' => 'I\'m a teapot',
|
||||
'420' => 'Enhance Your Calm',
|
||||
'421' => 'Misdirected Request',
|
||||
'422' => 'Unprocessable Entity',
|
||||
'423' => 'Locked',
|
||||
'424' => 'Failed Dependency',
|
||||
'425' => 'Unordered Collection',
|
||||
'426' => 'Upgrade Required',
|
||||
'428' => 'Precondition Required',
|
||||
'429' => 'Too Many Requests',
|
||||
'431' => 'Request Header Fields Too Large',
|
||||
'444' => 'No Response',
|
||||
'449' => 'Retry With',
|
||||
'451' => 'Unavailable For Legal Reasons',
|
||||
'500' => 'Internal Server Error',
|
||||
'501' => 'Not Implemented',
|
||||
'502' => 'Bad Gateway',
|
||||
'503' => 'Service Unavailable',
|
||||
'504' => 'Gateway Timeout',
|
||||
'505' => 'HTTP Version Not Supported',
|
||||
'506' => 'Variant Also Negotiates',
|
||||
'507' => 'Insufficient Storage',
|
||||
'508' => 'Loop Detected',
|
||||
'509' => 'Bandwidth Limit Exceeded',
|
||||
'510' => 'Not Extended',
|
||||
'511' => 'Network Authentication Required',
|
||||
);
|
||||
$statusMessage = $statusMessageList[$code];
|
||||
$statusMessage = $statusMessageList[strval($code)];
|
||||
if(!$statusMessage)
|
||||
{
|
||||
$statusMessage = 'OK';
|
||||
|
|
|
|||
|
|
@ -3,313 +3,78 @@
|
|||
|
||||
class EmbedFilter
|
||||
{
|
||||
|
||||
/**
|
||||
* allow script access list
|
||||
* Deprecated properties
|
||||
* @var array
|
||||
*/
|
||||
var $allowscriptaccessList = array();
|
||||
|
||||
/**
|
||||
* allow script access key
|
||||
* @var int
|
||||
*/
|
||||
var $allowscriptaccessKey = 0;
|
||||
var $whiteUrlList = array();
|
||||
var $whiteIframeUrlList = array();
|
||||
var $mimeTypeList = array();
|
||||
var $extList = array();
|
||||
var $parser = NULL;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->_makeWhiteDomainList();
|
||||
}
|
||||
public $whiteUrlList = array();
|
||||
public $whiteIframeUrlList = array();
|
||||
public $mimeTypeList = array();
|
||||
public $extList = array();
|
||||
|
||||
/**
|
||||
* Return EmbedFilter object
|
||||
* This method for singleton
|
||||
*
|
||||
* @return EmbedFilter
|
||||
*/
|
||||
function getInstance()
|
||||
{
|
||||
if(!isset($GLOBALS['__EMBEDFILTER_INSTANCE__']))
|
||||
{
|
||||
$GLOBALS['__EMBEDFILTER_INSTANCE__'] = new EmbedFilter();
|
||||
}
|
||||
return $GLOBALS['__EMBEDFILTER_INSTANCE__'];
|
||||
return new self();
|
||||
}
|
||||
|
||||
|
||||
public function getWhiteUrlList()
|
||||
{
|
||||
return $this->whiteUrlList;
|
||||
return Rhymix\Framework\Filters\MediaFilter::getObjectWhitelist();
|
||||
}
|
||||
|
||||
|
||||
public function getWhiteIframeUrlList()
|
||||
{
|
||||
return $this->whiteIframeUrlList;
|
||||
return Rhymix\Framework\Filters\MediaFilter::getIframeWhitelist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the content.
|
||||
* @return void
|
||||
*/
|
||||
function check(&$content)
|
||||
{
|
||||
$content = preg_replace_callback('/<(object|param|embed)[^>]*/is', array($this, '_checkAllowScriptAccess'), $content);
|
||||
$content = preg_replace_callback('/<object[^>]*>/is', array($this, '_addAllowScriptAccess'), $content);
|
||||
|
||||
$this->checkObjectTag($content);
|
||||
$this->checkEmbedTag($content);
|
||||
$this->checkParamTag($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check iframe tag in the content.
|
||||
* @return void
|
||||
*/
|
||||
function checkIframeTag(&$content)
|
||||
{
|
||||
// check in Purifier class
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check object tag in the content.
|
||||
* @return void
|
||||
*/
|
||||
function checkObjectTag(&$content)
|
||||
{
|
||||
$content = preg_replace_callback('/<\s*object\s*[^>]+(?:\/?>?)/is', function($m) {
|
||||
$html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]);
|
||||
foreach ($html->find('object') as $element)
|
||||
{
|
||||
if ($element->data && !$this->isWhiteDomain($element->data))
|
||||
{
|
||||
return escape($m[0], false);
|
||||
}
|
||||
if ($element->type && !$this->isWhiteMimetype($element->type))
|
||||
{
|
||||
return escape($m[0], false);
|
||||
}
|
||||
}
|
||||
return $m[0];
|
||||
}, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check embed tag in the content.
|
||||
* @return void
|
||||
*/
|
||||
function checkEmbedTag(&$content)
|
||||
{
|
||||
$content = preg_replace_callback('/<\s*embed\s*[^>]+(?:\/?>?)/is', function($m) {
|
||||
$html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]);
|
||||
foreach ($html->find('embed') as $element)
|
||||
{
|
||||
if ($element->src && !$this->isWhiteDomain($element->src))
|
||||
{
|
||||
return escape($m[0], false);
|
||||
}
|
||||
if ($element->type && !$this->isWhiteMimetype($element->type))
|
||||
{
|
||||
return escape($m[0], false);
|
||||
}
|
||||
}
|
||||
return $m[0];
|
||||
}, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check param tag in the content.
|
||||
* @return void
|
||||
*/
|
||||
function checkParamTag(&$content)
|
||||
{
|
||||
$content = preg_replace_callback('/<\s*param\s*[^>]+(?:\/?>?)/is', function($m) {
|
||||
$html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]);
|
||||
foreach ($html->find('param') as $element)
|
||||
{
|
||||
foreach (array('movie', 'src', 'href', 'url', 'source') as $attr)
|
||||
{
|
||||
if ($element->$attr && !$this->isWhiteDomain($element->$attr))
|
||||
{
|
||||
return escape($m[0], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $m[0];
|
||||
}, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check white domain in object data attribute or embed src attribute.
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function isWhiteDomain($urlAttribute)
|
||||
{
|
||||
if(is_array($this->whiteUrlList))
|
||||
{
|
||||
foreach($this->whiteUrlList AS $key => $value)
|
||||
{
|
||||
if(preg_match('@^https?://' . preg_quote($value, '@') . '@i', $urlAttribute))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
return Rhymix\Framework\Filters\MediaFilter::matchObjectWhitelist($urlAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check white domain in iframe src attribute.
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function isWhiteIframeDomain($urlAttribute)
|
||||
{
|
||||
if(is_array($this->whiteIframeUrlList))
|
||||
{
|
||||
foreach($this->whiteIframeUrlList AS $key => $value)
|
||||
{
|
||||
if(preg_match('@^https?://' . preg_quote($value, '@') . '@i', $urlAttribute))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
return Rhymix\Framework\Filters\MediaFilter::matchIframeWhitelist($urlAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check white mime type in object type attribute or embed type attribute.
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function isWhiteMimetype($mimeType)
|
||||
{
|
||||
if(isset($this->mimeTypeList[$mimeType]))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function isWhiteExt($ext)
|
||||
{
|
||||
if(isset($this->extList[$ext]))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
function _checkAllowScriptAccess($m)
|
||||
|
||||
function check(&$content)
|
||||
{
|
||||
if($m[1] == 'object')
|
||||
{
|
||||
$this->allowscriptaccessList[] = 1;
|
||||
}
|
||||
|
||||
if($m[1] == 'param')
|
||||
{
|
||||
if(stripos($m[0], 'allowscriptaccess'))
|
||||
{
|
||||
$m[0] = '<param name="allowscriptaccess" value="never"';
|
||||
if(substr($m[0], -1) == '/')
|
||||
{
|
||||
$m[0] .= '/';
|
||||
}
|
||||
$this->allowscriptaccessList[count($this->allowscriptaccessList) - 1]--;
|
||||
}
|
||||
}
|
||||
else if($m[1] == 'embed')
|
||||
{
|
||||
if(stripos($m[0], 'allowscriptaccess'))
|
||||
{
|
||||
$m[0] = preg_replace('/always|samedomain/i', 'never', $m[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$m[0] = preg_replace('/\<embed/i', '<embed allowscriptaccess="never"', $m[0]);
|
||||
}
|
||||
}
|
||||
return $m[0];
|
||||
// This functionality has been moved to the HTMLFilter class.
|
||||
}
|
||||
|
||||
function _addAllowScriptAccess($m)
|
||||
|
||||
function checkIframeTag(&$content)
|
||||
{
|
||||
if($this->allowscriptaccessList[$this->allowscriptaccessKey] == 1)
|
||||
{
|
||||
$m[0] = $m[0] . '<param name="allowscriptaccess" value="never"></param>';
|
||||
}
|
||||
$this->allowscriptaccessKey++;
|
||||
return $m[0];
|
||||
// This functionality has been moved to the HTMLFilter class.
|
||||
}
|
||||
|
||||
/**
|
||||
* Make white domain list cache file from xml config file.
|
||||
* @param $whitelist array
|
||||
* @return void
|
||||
*/
|
||||
function _makeWhiteDomainList($whitelist = NULL)
|
||||
|
||||
function checkObjectTag(&$content)
|
||||
{
|
||||
$whiteUrlDefaultList = (include RX_BASEDIR . 'common/defaults/whitelist.php');
|
||||
$this->extList = $whiteUrlDefaultList['extensions'];
|
||||
$this->mimeTypeList = $whiteUrlDefaultList['mime'];
|
||||
$this->whiteUrlList = array();
|
||||
$this->whiteIframeUrlList = array();
|
||||
|
||||
if($whitelist !== NULL)
|
||||
{
|
||||
if(!is_array($whitelist) || !isset($whitelist['object']) || !isset($whitelist['iframe']))
|
||||
{
|
||||
$whitelist = array(
|
||||
'object' => isset($whitelist->object) ? $whitelist->object : array(),
|
||||
'iframe' => isset($whitelist->iframe) ? $whitelist->iframe : array(),
|
||||
);
|
||||
}
|
||||
foreach ($whitelist['object'] as $prefix)
|
||||
{
|
||||
$this->whiteUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
|
||||
}
|
||||
foreach ($whitelist['iframe'] as $prefix)
|
||||
{
|
||||
$this->whiteIframeUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($whiteUrlDefaultList['object'] as $prefix)
|
||||
{
|
||||
$this->whiteUrlList[] = $prefix;
|
||||
}
|
||||
foreach ($whiteUrlDefaultList['iframe'] as $prefix)
|
||||
{
|
||||
$this->whiteIframeUrlList[] = $prefix;
|
||||
}
|
||||
if ($embedfilter_object = config('embedfilter.object'))
|
||||
{
|
||||
foreach ($embedfilter_object as $prefix)
|
||||
{
|
||||
$this->whiteUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
|
||||
}
|
||||
}
|
||||
if ($embedfilter_iframe = config('embedfilter.iframe'))
|
||||
{
|
||||
foreach ($embedfilter_iframe as $prefix)
|
||||
{
|
||||
$this->whiteIframeUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->whiteUrlList = array_unique($this->whiteUrlList);
|
||||
$this->whiteIframeUrlList = array_unique($this->whiteIframeUrlList);
|
||||
natcasesort($this->whiteUrlList);
|
||||
natcasesort($this->whiteIframeUrlList);
|
||||
// This functionality has been moved to the HTMLFilter class.
|
||||
}
|
||||
|
||||
function checkEmbedTag(&$content)
|
||||
{
|
||||
// This functionality has been moved to the HTMLFilter class.
|
||||
}
|
||||
|
||||
function checkParamTag(&$content)
|
||||
{
|
||||
// This functionality has been moved to the HTMLFilter class.
|
||||
}
|
||||
}
|
||||
/* End of file : EmbedFilter.class.php */
|
||||
|
|
|
|||
|
|
@ -6,26 +6,12 @@ class IpFilter
|
|||
public function filter($ip_list, $ip = NULL)
|
||||
{
|
||||
if(!$ip) $ip = $_SERVER['REMOTE_ADDR'];
|
||||
foreach($ip_list as $filter)
|
||||
{
|
||||
if(Rhymix\Framework\IpFilter::inRange($ip, $filter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Rhymix\Framework\Filters\IpFilter::inRanges($ip, $ip_list);
|
||||
}
|
||||
|
||||
public function validate($ip_list = array())
|
||||
{
|
||||
foreach($ip_list as $filter)
|
||||
{
|
||||
if(!Rhymix\Framework\IpFilter::validateRange($filter))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return Rhymix\Framework\Filters\IpFilter::validateRanges($ip_list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,458 +1,79 @@
|
|||
<?php
|
||||
/* Copyright (C) NAVER <http://www.navercorp.com> */
|
||||
|
||||
/**
|
||||
* This class can be used to hash passwords using various algorithms and check their validity.
|
||||
* It is fully compatible with previous defaults, while also supporting bcrypt and pbkdf2.
|
||||
*
|
||||
* @file Password.class.php
|
||||
* @author Kijin Sung (kijin@kijinsung.com)
|
||||
* @package /classes/security
|
||||
* @version 1.1
|
||||
*/
|
||||
class Password
|
||||
{
|
||||
/**
|
||||
* @brief Custom algorithms are stored here
|
||||
* @var array
|
||||
*/
|
||||
protected static $_custom = array();
|
||||
|
||||
/**
|
||||
* @brief Register a custom algorithm for password checking
|
||||
* @param string $name The name of the algorithm
|
||||
* @param string $regexp The regular expression to detect the algorithm
|
||||
* @param callable $callback The function to call to regenerate the hash
|
||||
* @return void
|
||||
*/
|
||||
public static function registerCustomAlgorithm($name, $regexp, $callback)
|
||||
{
|
||||
self::$_custom[$name] = array('regexp' => $regexp, 'callback' => $callback);
|
||||
Rhymix\Framework\Password::addAlgorithm($name, $regexp, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the list of hashing algorithms supported by this server
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedAlgorithms()
|
||||
{
|
||||
$retval = array();
|
||||
if(function_exists('hash_hmac') && in_array('sha256', hash_algos()))
|
||||
{
|
||||
$retval['pbkdf2'] = 'pbkdf2';
|
||||
}
|
||||
if(version_compare(PHP_VERSION, '5.3.7', '>=') && defined('CRYPT_BLOWFISH'))
|
||||
{
|
||||
$retval['bcrypt'] = 'bcrypt';
|
||||
}
|
||||
$retval['md5'] = 'md5';
|
||||
return $retval;
|
||||
return Rhymix\Framework\Password::getSupportedAlgorithms();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the best hashing algorithm supported by this server
|
||||
* @return string
|
||||
*/
|
||||
public function getBestAlgorithm()
|
||||
{
|
||||
$algos = $this->getSupportedAlgorithms();
|
||||
return key($algos);
|
||||
return Rhymix\Framework\Password::getBestSupportedAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the currently selected hashing algorithm
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentlySelectedAlgorithm()
|
||||
{
|
||||
if(function_exists('getModel'))
|
||||
{
|
||||
$config = getModel('member')->getMemberConfig();
|
||||
$algorithm = $config->password_hashing_algorithm;
|
||||
if(strval($algorithm) === '')
|
||||
{
|
||||
$algorithm = 'md5'; // Historical default for XE
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$algorithm = 'md5';
|
||||
}
|
||||
return $algorithm;
|
||||
return Rhymix\Framework\Password::getDefaultAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the currently configured work factor for bcrypt and other adjustable algorithms
|
||||
* @return int
|
||||
*/
|
||||
public function getWorkFactor()
|
||||
{
|
||||
if(function_exists('getModel'))
|
||||
{
|
||||
$config = getModel('member')->getMemberConfig();
|
||||
$work_factor = $config->password_hashing_work_factor;
|
||||
if(!$work_factor || $work_factor < 4 || $work_factor > 31)
|
||||
{
|
||||
$work_factor = 8; // Reasonable default
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$work_factor = 8;
|
||||
}
|
||||
return $work_factor;
|
||||
return Rhymix\Framework\Password::getWorkFactor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a hash using the specified algorithm
|
||||
* @param string $password The password
|
||||
* @param string $algorithm The algorithm (optional)
|
||||
* @return string
|
||||
*/
|
||||
public function createHash($password, $algorithm = null)
|
||||
{
|
||||
if($algorithm === null)
|
||||
{
|
||||
$algorithm = $this->getCurrentlySelectedAlgorithm();
|
||||
}
|
||||
if(!array_key_exists($algorithm, $this->getSupportedAlgorithms()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$password = trim($password);
|
||||
|
||||
switch($algorithm)
|
||||
{
|
||||
case 'md5':
|
||||
return md5($password);
|
||||
|
||||
case 'pbkdf2':
|
||||
$iterations = pow(2, $this->getWorkFactor() + 5);
|
||||
$salt = $this->createSecureSalt(12, 'alnum');
|
||||
$hash = base64_encode($this->pbkdf2($password, $salt, 'sha256', $iterations, 24));
|
||||
return 'sha256:'.sprintf('%07d', $iterations).':'.$salt.':'.$hash;
|
||||
|
||||
case 'bcrypt':
|
||||
return $this->bcrypt($password);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return Rhymix\Framework\Password::hashPassword($password, $algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a password matches a hash
|
||||
* @param string $password The password
|
||||
* @param string $hash The hash
|
||||
* @param string $algorithm The algorithm (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public function checkPassword($password, $hash, $algorithm = null)
|
||||
{
|
||||
if($algorithm === null)
|
||||
{
|
||||
$algorithm = $this->checkAlgorithm($hash);
|
||||
}
|
||||
|
||||
$password = trim($password);
|
||||
|
||||
switch($algorithm)
|
||||
{
|
||||
case 'md5':
|
||||
return md5($password) === $hash || md5(sha1(md5($password))) === $hash;
|
||||
|
||||
case 'mysql_old_password':
|
||||
return (class_exists('Context') && substr(Context::getDBType(), 0, 5) === 'mysql') ?
|
||||
DB::getInstance()->isValidOldPassword($password, $hash) : false;
|
||||
|
||||
case 'mysql_password':
|
||||
return $hash[0] === '*' && substr($hash, 1) === strtoupper(sha1(sha1($password, true)));
|
||||
|
||||
case 'pbkdf2':
|
||||
$hash = explode(':', $hash);
|
||||
$hash[3] = base64_decode($hash[3]);
|
||||
$hash_to_compare = $this->pbkdf2($password, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3]));
|
||||
return $this->strcmpConstantTime($hash_to_compare, $hash[3]);
|
||||
|
||||
case 'bcrypt':
|
||||
$hash_to_compare = $this->bcrypt($password, $hash);
|
||||
return $this->strcmpConstantTime($hash_to_compare, $hash);
|
||||
|
||||
default:
|
||||
if($algorithm && isset(self::$_custom[$algorithm]))
|
||||
{
|
||||
$hash_callback = self::$_custom[$algorithm]['callback'];
|
||||
$hash_to_compare = $hash_callback($password, $hash);
|
||||
return $this->strcmpConstantTime($hash_to_compare, $hash);
|
||||
}
|
||||
if(in_array($algorithm, hash_algos()))
|
||||
{
|
||||
return $this->strcmpConstantTime(hash($algorithm, $password), $hash);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return Rhymix\Framework\Password::checkPassword($password, $hash, $algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check the algorithm used to create a hash
|
||||
* @param string $hash The hash
|
||||
* @return string
|
||||
*/
|
||||
function checkAlgorithm($hash)
|
||||
{
|
||||
foreach(self::$_custom as $name => $definition)
|
||||
{
|
||||
if(preg_match($definition['regexp'], $hash))
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
|
||||
{
|
||||
return 'bcrypt';
|
||||
}
|
||||
elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches))
|
||||
{
|
||||
return 'pbkdf2';
|
||||
}
|
||||
elseif(strlen($hash) === 32 && ctype_xdigit($hash))
|
||||
{
|
||||
return 'md5';
|
||||
}
|
||||
elseif(strlen($hash) === 40 && ctype_xdigit($hash))
|
||||
{
|
||||
return 'sha1';
|
||||
}
|
||||
elseif(strlen($hash) === 64 && ctype_xdigit($hash))
|
||||
{
|
||||
return 'sha256';
|
||||
}
|
||||
elseif(strlen($hash) === 96 && ctype_xdigit($hash))
|
||||
{
|
||||
return 'sha384';
|
||||
}
|
||||
elseif(strlen($hash) === 128 && ctype_xdigit($hash))
|
||||
{
|
||||
return 'sha512';
|
||||
}
|
||||
elseif(strlen($hash) === 16 && ctype_xdigit($hash))
|
||||
{
|
||||
return 'mysql_old_password';
|
||||
}
|
||||
elseif(strlen($hash) === 41 && $hash[0] === '*')
|
||||
{
|
||||
return 'mysql_password';
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$algos = Rhymix\Framework\Password::checkAlgorithm($hash);
|
||||
return count($algos) ? $algos[0] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check the work factor of a hash
|
||||
* @param string $hash The hash
|
||||
* @return int
|
||||
*/
|
||||
function checkWorkFactor($hash)
|
||||
{
|
||||
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
|
||||
{
|
||||
return intval($matches[1], 10);
|
||||
}
|
||||
elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches))
|
||||
{
|
||||
return max(0, round(log($matches[1], 2)) - 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Rhymix\Framework\Password::checkWorkFactor($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a cryptographically secure random string to use as a salt
|
||||
* @param int $length The number of bytes to return
|
||||
* @param string $format hex or alnum
|
||||
* @return string
|
||||
*/
|
||||
public function createSecureSalt($length, $format = 'hex')
|
||||
{
|
||||
// Find out how many bytes of entropy we really need
|
||||
switch($format)
|
||||
{
|
||||
case 'hex':
|
||||
$entropy_required_bytes = ceil($length / 2);
|
||||
break;
|
||||
case 'alnum':
|
||||
case 'printable':
|
||||
$entropy_required_bytes = ceil($length * 3 / 4);
|
||||
break;
|
||||
default:
|
||||
$entropy_required_bytes = $length;
|
||||
}
|
||||
|
||||
// Cap entropy to 256 bits from any one source, because anything more is meaningless
|
||||
$entropy_capped_bytes = min(32, $entropy_required_bytes);
|
||||
|
||||
// Find and use the most secure way to generate a random string
|
||||
$entropy = false;
|
||||
$is_windows = (defined('PHP_OS') && strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
|
||||
if(function_exists('random_bytes')) // PHP 7
|
||||
{
|
||||
$entropy = random_bytes($entropy_capped_bytes);
|
||||
}
|
||||
elseif(function_exists('openssl_random_pseudo_bytes') && (!$is_windows || version_compare(PHP_VERSION, '5.4', '>=')))
|
||||
{
|
||||
$entropy = openssl_random_pseudo_bytes($entropy_capped_bytes);
|
||||
}
|
||||
elseif(function_exists('mcrypt_create_iv') && (!$is_windows || version_compare(PHP_VERSION, '5.3.7', '>=')))
|
||||
{
|
||||
$entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_DEV_URANDOM);
|
||||
}
|
||||
elseif(function_exists('mcrypt_create_iv') && $is_windows)
|
||||
{
|
||||
$entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_RAND);
|
||||
}
|
||||
elseif(!$is_windows && @is_readable('/dev/urandom'))
|
||||
{
|
||||
$fp = fopen('/dev/urandom', 'rb');
|
||||
if (function_exists('stream_set_read_buffer')) // This function does not exist in HHVM
|
||||
{
|
||||
stream_set_read_buffer($fp, 0); // Prevent reading several KB of unnecessary data from urandom
|
||||
}
|
||||
$entropy = fread($fp, $entropy_capped_bytes);
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
// Use built-in source of entropy if an error occurs while using other functions
|
||||
if($entropy === false || strlen($entropy) < $entropy_capped_bytes)
|
||||
{
|
||||
$entropy = '';
|
||||
for($i = 0; $i < $entropy_capped_bytes; $i += 2)
|
||||
{
|
||||
$entropy .= pack('S', rand(0, 65536) ^ mt_rand(0, 65535));
|
||||
}
|
||||
}
|
||||
|
||||
// Mixing (see RFC 4086 section 5)
|
||||
$output = '';
|
||||
for($i = 0; $i < $entropy_required_bytes; $i += 32)
|
||||
{
|
||||
$output .= hash('sha256', $entropy . $i . rand(), true);
|
||||
}
|
||||
|
||||
// Encode and return the random string
|
||||
switch($format)
|
||||
{
|
||||
case 'hex':
|
||||
return substr(bin2hex($output), 0, $length);
|
||||
case 'binary':
|
||||
return substr($output, 0, $length);
|
||||
case 'printable':
|
||||
$salt = '';
|
||||
for($i = 0; $i < $length; $i++)
|
||||
{
|
||||
$salt .= chr(33 + (crc32(sha1($i . $output)) % 94));
|
||||
}
|
||||
return $salt;
|
||||
case 'alnum':
|
||||
default:
|
||||
$salt = substr(base64_encode($output), 0, $length);
|
||||
$replacements = chr(rand(65, 90)) . chr(rand(97, 122)) . rand(0, 9);
|
||||
return strtr($salt, '+/=', $replacements);
|
||||
}
|
||||
return Rhymix\Framework\Security::getRandom($length, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a temporary password using the secure salt generator
|
||||
* @param int $length The number of bytes to return
|
||||
* @return string
|
||||
*/
|
||||
public function createTemporaryPassword($length = 16)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
$source = base64_encode($this->createSecureSalt(64, 'binary'));
|
||||
$source = strtr($source, 'iIoOjl10/', '@#$%&*-!?');
|
||||
$source_length = strlen($source);
|
||||
for($i = 0; $i < $source_length - $length; $i++)
|
||||
{
|
||||
$candidate = substr($source, $i, $length);
|
||||
if(preg_match('/[a-z]/', $candidate) && preg_match('/[A-Z]/', $candidate) &&
|
||||
preg_match('/[0-9]/', $candidate) && preg_match('/[^a-zA-Z0-9]/', $candidate))
|
||||
{
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Rhymix\Framework\Password::getRandomPassword($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate the PBKDF2 hash of a string using a salt
|
||||
* @param string $password The password
|
||||
* @param string $salt The salt
|
||||
* @param string $algorithm The algorithm (optional, default is sha256)
|
||||
* @param int $iterations Iteration count (optional, default is 8192)
|
||||
* @param int $length The length of the hash (optional, default is 32)
|
||||
* @return string
|
||||
*/
|
||||
public function pbkdf2($password, $salt, $algorithm = 'sha256', $iterations = 8192, $length = 24)
|
||||
{
|
||||
if(function_exists('hash_pbkdf2'))
|
||||
{
|
||||
return hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
$output = '';
|
||||
$block_count = ceil($length / strlen(hash($algorithm, '', true))); // key length divided by the length of one hash
|
||||
for($i = 1; $i <= $block_count; $i++)
|
||||
{
|
||||
$last = $salt . pack('N', $i); // $i encoded as 4 bytes, big endian
|
||||
$last = $xorsum = hash_hmac($algorithm, $last, $password, true); // first iteration
|
||||
for($j = 1; $j < $iterations; $j++) // The other $count - 1 iterations
|
||||
{
|
||||
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
|
||||
}
|
||||
$output .= $xorsum;
|
||||
}
|
||||
return substr($output, 0, $length);
|
||||
}
|
||||
$hash = Rhymix\Framework\Security::pbkdf2($password, $salt, $algorithm, $iterations, $length);
|
||||
$hash = explode(':', $hash);
|
||||
return base64_decode($hash[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate the bcrypt hash of a string using a salt
|
||||
* @param string $password The password
|
||||
* @param string $salt The salt (optional, auto-generated if empty)
|
||||
* @return string
|
||||
*/
|
||||
public function bcrypt($password, $salt = null)
|
||||
{
|
||||
if($salt === null)
|
||||
{
|
||||
$salt = '$2y$'.sprintf('%02d', $this->getWorkFactor()).'$'.$this->createSecureSalt(22, 'alnum');
|
||||
}
|
||||
return crypt($password, $salt);
|
||||
return Rhymix\Framework\Security::bcrypt($password, $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two strings in constant time
|
||||
* @param string $a The first string
|
||||
* @param string $b The second string
|
||||
* @return bool
|
||||
*/
|
||||
function strcmpConstantTime($a, $b)
|
||||
{
|
||||
$diff = strlen($a) ^ strlen($b);
|
||||
$maxlen = min(strlen($a), strlen($b));
|
||||
for($i = 0; $i < $maxlen; $i++)
|
||||
{
|
||||
$diff |= ord($a[$i]) ^ ord($b[$i]);
|
||||
}
|
||||
return $diff === 0;
|
||||
return Rhymix\Framework\Security::compareStrings($a, $b);
|
||||
}
|
||||
}
|
||||
/* End of file : Password.class.php */
|
||||
|
|
|
|||
|
|
@ -3,161 +3,14 @@
|
|||
|
||||
class Purifier
|
||||
{
|
||||
|
||||
private $_cacheDir;
|
||||
private $_htmlPurifier;
|
||||
private $_config;
|
||||
private $_def;
|
||||
|
||||
public function __construct()
|
||||
public static function getInstance()
|
||||
{
|
||||
$this->_checkCacheDir();
|
||||
$this->_setConfig();
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function getInstance()
|
||||
{
|
||||
if(!isset($GLOBALS['__PURIFIER_INSTANCE__']))
|
||||
{
|
||||
$GLOBALS['__PURIFIER_INSTANCE__'] = new Purifier();
|
||||
}
|
||||
return $GLOBALS['__PURIFIER_INSTANCE__'];
|
||||
}
|
||||
|
||||
private function _setConfig()
|
||||
{
|
||||
$this->_config = HTMLPurifier_Config::createDefault();
|
||||
$this->_config->set('HTML.TidyLevel', 'light');
|
||||
$this->_config->set('Output.FlashCompat', TRUE);
|
||||
$this->_config->set('HTML.SafeObject', TRUE);
|
||||
$this->_config->set('HTML.SafeEmbed', TRUE);
|
||||
$this->_config->set('HTML.SafeIframe', TRUE);
|
||||
$this->_config->set('URI.SafeIframeRegexp', $this->_getWhiteDomainRegexp());
|
||||
$this->_config->set('Cache.SerializerPath', $this->_cacheDir);
|
||||
$this->_config->set('Attr.AllowedFrameTargets', array('_blank'));
|
||||
//$allowdClasses = array('emoticon');
|
||||
//$this->_config->set('Attr.AllowedClasses', $allowdClasses);
|
||||
$this->_def = $this->_config->getHTMLDefinition(TRUE);
|
||||
}
|
||||
|
||||
private function _setDefinition(&$content)
|
||||
{
|
||||
// add attribute for edit component
|
||||
$editComponentAttrs = $this->_searchEditComponent($content);
|
||||
if(is_array($editComponentAttrs))
|
||||
{
|
||||
foreach($editComponentAttrs AS $k => $v)
|
||||
{
|
||||
$this->_def->addAttribute('img', $v, 'CDATA');
|
||||
$this->_def->addAttribute('div', $v, 'CDATA');
|
||||
}
|
||||
}
|
||||
|
||||
// add attribute for widget component
|
||||
$widgetAttrs = $this->_searchWidget($content);
|
||||
if(is_array($widgetAttrs))
|
||||
{
|
||||
foreach($widgetAttrs AS $k => $v)
|
||||
{
|
||||
$this->_def->addAttribute('img', $v, 'CDATA');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search attribute of edit component tag
|
||||
* @param string $content
|
||||
* @return array
|
||||
*/
|
||||
private function _searchEditComponent($content)
|
||||
{
|
||||
preg_match_all('!<(?:(div)|img)([^>]*)editor_component=([^>]*)>(?(1)(.*?)</div>)!is', $content, $m);
|
||||
|
||||
$attributeList = array();
|
||||
if(is_array($m[2]))
|
||||
{
|
||||
foreach($m[2] as $key => $value)
|
||||
{
|
||||
unset($script, $m2);
|
||||
$script = " {$m[2][$key]} editor_component={$m[3][$key]}";
|
||||
|
||||
if(preg_match_all('/([a-z0-9_-]+)="([^"]+)"/is', $script, $m2))
|
||||
{
|
||||
foreach($m2[1] as $value2)
|
||||
{
|
||||
//SECISSUE check style attr
|
||||
if($value2 == 'style')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$attributeList[] = $value2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($attributeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search edit component tag
|
||||
* @param string $content
|
||||
* @return array
|
||||
*/
|
||||
private function _searchWidget(&$content)
|
||||
{
|
||||
preg_match_all('!<(?:(div)|img)([^>]*)class="zbxe_widget_output"([^>]*)>(?(1)(.*?)</div>)!is', $content, $m);
|
||||
|
||||
$attributeList = array();
|
||||
if(is_array($m[3]))
|
||||
{
|
||||
$content = str_replace('<img class="zbxe_widget_output"', '<img src="" class="zbxe_widget_output"', $content);
|
||||
|
||||
foreach($m[3] as $key => $value)
|
||||
{
|
||||
if (preg_match_all('/([a-z0-9_-]+)="([^"]+)"/is', $m[3][$key], $m2))
|
||||
{
|
||||
foreach($m2[1] as $value2)
|
||||
{
|
||||
//SECISSUE check style attr
|
||||
if($value2 == 'style')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$attributeList[] = $value2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_unique($attributeList);
|
||||
}
|
||||
|
||||
private function _getWhiteDomainRegexp()
|
||||
{
|
||||
$oEmbedFilter = EmbedFilter::getInstance();
|
||||
$whiteIframeUrlList = $oEmbedFilter->getWhiteIframeUrlList();
|
||||
|
||||
$whiteDomains = array();
|
||||
foreach($whiteIframeUrlList as $domain)
|
||||
{
|
||||
$whiteDomains[] = preg_quote($domain, '%');
|
||||
}
|
||||
return '%^https?://(' . implode('|', $whiteDomains) . ')%';
|
||||
}
|
||||
|
||||
private function _checkCacheDir()
|
||||
{
|
||||
// check htmlpurifier cache directory
|
||||
$this->_cacheDir = _XE_PATH_ . 'files/cache/htmlpurifier';
|
||||
FileHandler::makeDir($this->_cacheDir);
|
||||
}
|
||||
|
||||
|
||||
public function purify(&$content)
|
||||
{
|
||||
$this->_setDefinition($content);
|
||||
$this->_htmlPurifier = new HTMLPurifier($this->_config);
|
||||
|
||||
$content = $this->_htmlPurifier->purify($content);
|
||||
$content = Rhymix\Framework\Filters\HTMLFilter::clean($content);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ class Security
|
|||
* Action target variable. If this value is null, the method will use Context variables
|
||||
* @var mixed
|
||||
*/
|
||||
var $_targetVar = NULL;
|
||||
public $_targetVar = NULL;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param mixed $var Target context
|
||||
* @return void
|
||||
*/
|
||||
function __construct($var = NULL)
|
||||
public function __construct($var = NULL)
|
||||
{
|
||||
$this->_targetVar = $var;
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ class Security
|
|||
* separate the owner(object or array) and the item(property or element) using a dot(.)
|
||||
* @return mixed
|
||||
*/
|
||||
function encodeHTML(/* , $varName1, $varName2, ... */)
|
||||
public function encodeHTML(/* , $varName1, $varName2, ... */)
|
||||
{
|
||||
$varNames = func_get_args();
|
||||
if(count($varNames) < 0)
|
||||
|
|
@ -109,7 +109,7 @@ class Security
|
|||
* @param array $name
|
||||
* @return mixed
|
||||
*/
|
||||
function _encodeHTML($var, $name = array())
|
||||
protected function _encodeHTML($var, $name = array())
|
||||
{
|
||||
if(is_string($var))
|
||||
{
|
||||
|
|
@ -183,46 +183,9 @@ class Security
|
|||
* @param string $xml
|
||||
* @return bool
|
||||
*/
|
||||
static function detectingXEE($xml)
|
||||
public static function detectingXEE($xml)
|
||||
{
|
||||
if(!$xml) return FALSE;
|
||||
|
||||
if(strpos($xml, '<!ENTITY') !== FALSE)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Strip XML declaration.
|
||||
$header = preg_replace('/<\?xml.*?\?'.'>/s', '', substr($xml, 0, 100), 1);
|
||||
$xml = trim(substr_replace($xml, $header, 0, 100));
|
||||
if($xml == '')
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Strip DTD.
|
||||
$header = preg_replace('/^<!DOCTYPE[^>]*+>/i', '', substr($xml, 0, 200), 1);
|
||||
$xml = trim(substr_replace($xml, $header, 0, 200));
|
||||
if($xml == '')
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Confirm the XML now starts with a valid root tag. A root tag can end in [> \t\r\n]
|
||||
$root_tag = substr($xml, 0, strcspn(substr($xml, 0, 20), "> \t\r\n"));
|
||||
|
||||
// Reject a second DTD.
|
||||
if(strtoupper($root_tag) == '<!DOCTYPE')
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(!in_array($root_tag, array('<methodCall', '<methodResponse', '<fault')))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return !Rhymix\Framework\Security::checkXEE($xml);
|
||||
}
|
||||
}
|
||||
/* End of file : Security.class.php */
|
||||
|
|
|
|||
|
|
@ -3,40 +3,9 @@
|
|||
|
||||
class UploadFileFilter
|
||||
{
|
||||
private static $_block_list = array ('exec', 'system', 'passthru', 'show_source', 'phpinfo', 'fopen', 'file_get_contents', 'file_put_contents', 'fwrite', 'proc_open', 'popen');
|
||||
|
||||
public function check($file)
|
||||
{
|
||||
// TODO: 기능개선후 enable
|
||||
|
||||
return TRUE; // disable
|
||||
if (! $file || ! FileHandler::exists($file)) return TRUE;
|
||||
return self::_check ( $file );
|
||||
}
|
||||
|
||||
private function _check($file)
|
||||
{
|
||||
if (! ($fp = fopen ( $file, 'r' ))) return FALSE;
|
||||
|
||||
$has_php_tag = FALSE;
|
||||
|
||||
while ( ! feof ( $fp ) )
|
||||
{
|
||||
$content = fread ( $fp, 8192 );
|
||||
if (FALSE === $has_php_tag) $has_php_tag = strpos ( $content, '<?' );
|
||||
foreach ( self::$_block_list as $v )
|
||||
{
|
||||
if (FALSE !== $has_php_tag && FALSE !== strpos ( $content, $v ))
|
||||
{
|
||||
fclose ( $fp );
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose ( $fp );
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/**
|
||||
* RX_VERSION is the version number of the Rhymix CMS.
|
||||
*/
|
||||
define('RX_VERSION', '1.8.17');
|
||||
define('RX_VERSION', '1.8.18');
|
||||
|
||||
/**
|
||||
* RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch.
|
||||
|
|
@ -53,8 +53,8 @@ else
|
|||
*/
|
||||
if (isset($_SERVER['HTTP_CF_CONNECTING_IP']))
|
||||
{
|
||||
include_once __DIR__ . '/framework/ipfilter.php';
|
||||
Rhymix\Framework\IpFilter::getCloudFlareRealIP();
|
||||
include_once __DIR__ . '/framework/filters/ipfilter.php';
|
||||
Rhymix\Framework\Filters\IpFilter::getCloudFlareRealIP();
|
||||
}
|
||||
if (isset($_SERVER['REMOTE_ADDR']) && preg_match('/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/', $_SERVER['REMOTE_ADDR'], $matches))
|
||||
{
|
||||
|
|
@ -124,7 +124,7 @@ define('__XE_VERSION_ALPHA__', false);
|
|||
define('__XE_VERSION_BETA__', false);
|
||||
define('__XE_VERSION_RC__', false);
|
||||
define('__XE_VERSION_STABLE__', true);
|
||||
define('__XE_MIN_PHP_VERSION__', '5.3.3');
|
||||
define('__XE_MIN_PHP_VERSION__', '5.5.9');
|
||||
define('__XE_RECOMMEND_PHP_VERSION__', '5.5.9');
|
||||
define('__ZBXE__', true);
|
||||
define('__ZBXE_VERSION__', RX_VERSION);
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ return array(
|
|||
'display_to' => 'admin',
|
||||
'allow' => array(),
|
||||
),
|
||||
'embedfilter' => array(
|
||||
'mediafilter' => array(
|
||||
'iframe' => array(),
|
||||
'object' => array(),
|
||||
),
|
||||
|
|
|
|||
2259
common/defaults/countries.php
Normal file
2259
common/defaults/countries.php
Normal file
File diff suppressed because it is too large
Load diff
3863
common/defaults/korea.ipv4.php
Normal file
3863
common/defaults/korea.ipv4.php
Normal file
File diff suppressed because it is too large
Load diff
503
common/defaults/korea.ipv6.php
Normal file
503
common/defaults/korea.ipv6.php
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Source: https://ip.kisa.or.kr/ip_cate_stat/stat_05_05.act
|
||||
* Last Updated: 2016-03-18
|
||||
*/
|
||||
return array (
|
||||
0 =>
|
||||
array (
|
||||
0 => '2001000000000000',
|
||||
1 => '2001000fffffffff',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
0 => '2001022000000000',
|
||||
1 => '20010220ffffffff',
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
0 => '2001023000000000',
|
||||
1 => '20010230ffffffff',
|
||||
),
|
||||
3 =>
|
||||
array (
|
||||
0 => '2001027000000000',
|
||||
1 => '20010270ffffffff',
|
||||
),
|
||||
4 =>
|
||||
array (
|
||||
0 => '2001028000000000',
|
||||
1 => '20010280ffffffff',
|
||||
),
|
||||
5 =>
|
||||
array (
|
||||
0 => '2001029000000000',
|
||||
1 => '20010290ffffffff',
|
||||
),
|
||||
6 =>
|
||||
array (
|
||||
0 => '200102b000000000',
|
||||
1 => '200102b0ffffffff',
|
||||
),
|
||||
7 =>
|
||||
array (
|
||||
0 => '200102b800000000',
|
||||
1 => '200102b8ffffffff',
|
||||
),
|
||||
8 =>
|
||||
array (
|
||||
0 => '200102d800000000',
|
||||
1 => '200102d8ffffffff',
|
||||
),
|
||||
9 =>
|
||||
array (
|
||||
0 => '2001032000000000',
|
||||
1 => '20010320ffffffff',
|
||||
),
|
||||
10 =>
|
||||
array (
|
||||
0 => '2001033000000000',
|
||||
1 => '20010330ffffffff',
|
||||
),
|
||||
11 =>
|
||||
array (
|
||||
0 => '2001037800000000',
|
||||
1 => '20010378ffffffff',
|
||||
),
|
||||
12 =>
|
||||
array (
|
||||
0 => '2001039000000000',
|
||||
1 => '20010390ffffffff',
|
||||
),
|
||||
13 =>
|
||||
array (
|
||||
0 => '200103a800000000',
|
||||
1 => '200103a8ffffffff',
|
||||
),
|
||||
14 =>
|
||||
array (
|
||||
0 => '200107fa00000002',
|
||||
1 => '200107fa00000002',
|
||||
),
|
||||
15 =>
|
||||
array (
|
||||
0 => '200107fa00080000',
|
||||
1 => '200107fa0008ffff',
|
||||
),
|
||||
16 =>
|
||||
array (
|
||||
0 => '20010c4800000000',
|
||||
1 => '20010c48ffffffff',
|
||||
),
|
||||
17 =>
|
||||
array (
|
||||
0 => '20010c9800000000',
|
||||
1 => '20010c98ffffffff',
|
||||
),
|
||||
18 =>
|
||||
array (
|
||||
0 => '20010cf000000000',
|
||||
1 => '20010cf0ffffffff',
|
||||
),
|
||||
19 =>
|
||||
array (
|
||||
0 => '20010d3800000000',
|
||||
1 => '20010d38ffffffff',
|
||||
),
|
||||
20 =>
|
||||
array (
|
||||
0 => '20010dc500000000',
|
||||
1 => '20010dc5ffffffff',
|
||||
),
|
||||
21 =>
|
||||
array (
|
||||
0 => '20010dcc00000000',
|
||||
1 => '20010dccffffffff',
|
||||
),
|
||||
22 =>
|
||||
array (
|
||||
0 => '20010ea000000000',
|
||||
1 => '20010ea0ffffffff',
|
||||
),
|
||||
23 =>
|
||||
array (
|
||||
0 => '20010ea800000000',
|
||||
1 => '20010ea8ffffffff',
|
||||
),
|
||||
24 =>
|
||||
array (
|
||||
0 => '20010eb800000000',
|
||||
1 => '20010eb8ffffffff',
|
||||
),
|
||||
25 =>
|
||||
array (
|
||||
0 => '20010ed000000000',
|
||||
1 => '20010ed0ffffffff',
|
||||
),
|
||||
26 =>
|
||||
array (
|
||||
0 => '20010ee800000000',
|
||||
1 => '20010ee8ffffffff',
|
||||
),
|
||||
27 =>
|
||||
array (
|
||||
0 => '20010ef000000000',
|
||||
1 => '20010ef0ffffffff',
|
||||
),
|
||||
28 =>
|
||||
array (
|
||||
0 => '20010ef800000000',
|
||||
1 => '20010ef8ffffffff',
|
||||
),
|
||||
29 =>
|
||||
array (
|
||||
0 => '20010f2800000000',
|
||||
1 => '20010f28ffffffff',
|
||||
),
|
||||
30 =>
|
||||
array (
|
||||
0 => '20010f4800000000',
|
||||
1 => '20010f48ffffffff',
|
||||
),
|
||||
31 =>
|
||||
array (
|
||||
0 => '24033e0000000000',
|
||||
1 => '24033e00ffffffff',
|
||||
),
|
||||
32 =>
|
||||
array (
|
||||
0 => '2001443000000000',
|
||||
1 => '20014430ffffffff',
|
||||
),
|
||||
33 =>
|
||||
array (
|
||||
0 => '2400000000000000',
|
||||
1 => '24000fffffffffff',
|
||||
),
|
||||
34 =>
|
||||
array (
|
||||
0 => '2400180000000000',
|
||||
1 => '24001800ffffffff',
|
||||
),
|
||||
35 =>
|
||||
array (
|
||||
0 => '2400330000000000',
|
||||
1 => '24003300ffffffff',
|
||||
),
|
||||
36 =>
|
||||
array (
|
||||
0 => '2400478000000000',
|
||||
1 => '24004780ffffffff',
|
||||
),
|
||||
37 =>
|
||||
array (
|
||||
0 => '2400498000000000',
|
||||
1 => '24004980ffffffff',
|
||||
),
|
||||
38 =>
|
||||
array (
|
||||
0 => '24009f8000000000',
|
||||
1 => '24009f80ffffffff',
|
||||
),
|
||||
39 =>
|
||||
array (
|
||||
0 => '2400a58000000000',
|
||||
1 => '2400a580ffffffff',
|
||||
),
|
||||
40 =>
|
||||
array (
|
||||
0 => '2400ab0000000000',
|
||||
1 => '2400ab00ffffffff',
|
||||
),
|
||||
41 =>
|
||||
array (
|
||||
0 => '2400cf0000000000',
|
||||
1 => '2400cf00ffffffff',
|
||||
),
|
||||
42 =>
|
||||
array (
|
||||
0 => '2400fd8000000000',
|
||||
1 => '2400fd80ffffffff',
|
||||
),
|
||||
43 =>
|
||||
array (
|
||||
0 => '2401270000000000',
|
||||
1 => '24012700ffffffff',
|
||||
),
|
||||
44 =>
|
||||
array (
|
||||
0 => '2401400000000000',
|
||||
1 => '24014000ffffffff',
|
||||
),
|
||||
45 =>
|
||||
array (
|
||||
0 => '2401a00000000000',
|
||||
1 => '2401a000ffffffff',
|
||||
),
|
||||
46 =>
|
||||
array (
|
||||
0 => '2401a80000000000',
|
||||
1 => '2401a800ffffffff',
|
||||
),
|
||||
47 =>
|
||||
array (
|
||||
0 => '2401c50000000000',
|
||||
1 => '2401c500ffffffff',
|
||||
),
|
||||
48 =>
|
||||
array (
|
||||
0 => '2402000000000000',
|
||||
1 => '240200ffffffffff',
|
||||
),
|
||||
49 =>
|
||||
array (
|
||||
0 => '24021a0000000000',
|
||||
1 => '24021a00ffffffff',
|
||||
),
|
||||
50 =>
|
||||
array (
|
||||
0 => '2402310000000000',
|
||||
1 => '24023100ffffffff',
|
||||
),
|
||||
51 =>
|
||||
array (
|
||||
0 => '2402580000000000',
|
||||
1 => '24025800ffffffff',
|
||||
),
|
||||
52 =>
|
||||
array (
|
||||
0 => '2402610000000000',
|
||||
1 => '24026100ffffffff',
|
||||
),
|
||||
53 =>
|
||||
array (
|
||||
0 => '2402700000000000',
|
||||
1 => '24027000ffffffff',
|
||||
),
|
||||
54 =>
|
||||
array (
|
||||
0 => '2402be0000000000',
|
||||
1 => '2402be00ffffffff',
|
||||
),
|
||||
55 =>
|
||||
array (
|
||||
0 => '2402de0000000000',
|
||||
1 => '2402de00ffffffff',
|
||||
),
|
||||
56 =>
|
||||
array (
|
||||
0 => '2402f40000000000',
|
||||
1 => '2402f400ffffffff',
|
||||
),
|
||||
57 =>
|
||||
array (
|
||||
0 => '2403370000000000',
|
||||
1 => '24033700ffffffff',
|
||||
),
|
||||
58 =>
|
||||
array (
|
||||
0 => '2403630000000000',
|
||||
1 => '24036300ffffffff',
|
||||
),
|
||||
59 =>
|
||||
array (
|
||||
0 => '2403650000000000',
|
||||
1 => '24036500ffffffff',
|
||||
),
|
||||
60 =>
|
||||
array (
|
||||
0 => '2404000000000000',
|
||||
1 => '2404000fffffffff',
|
||||
),
|
||||
61 =>
|
||||
array (
|
||||
0 => '2404080000000000',
|
||||
1 => '24040800ffffffff',
|
||||
),
|
||||
62 =>
|
||||
array (
|
||||
0 => '2404230000000000',
|
||||
1 => '24042300ffffffff',
|
||||
),
|
||||
63 =>
|
||||
array (
|
||||
0 => '2404460000000000',
|
||||
1 => '24044600ffffffff',
|
||||
),
|
||||
64 =>
|
||||
array (
|
||||
0 => '2405350000000000',
|
||||
1 => '24053500ffffffff',
|
||||
),
|
||||
65 =>
|
||||
array (
|
||||
0 => '2405430000000000',
|
||||
1 => '24054300ffffffff',
|
||||
),
|
||||
66 =>
|
||||
array (
|
||||
0 => '2405580000000000',
|
||||
1 => '24055800ffffffff',
|
||||
),
|
||||
67 =>
|
||||
array (
|
||||
0 => '20010e6000000000',
|
||||
1 => '20010e60ffffffff',
|
||||
),
|
||||
68 =>
|
||||
array (
|
||||
0 => '20010e7000000000',
|
||||
1 => '20010e70ffffffff',
|
||||
),
|
||||
69 =>
|
||||
array (
|
||||
0 => '20010e7800000000',
|
||||
1 => '20010e78ffffffff',
|
||||
),
|
||||
70 =>
|
||||
array (
|
||||
0 => '20010e9800000000',
|
||||
1 => '20010e98ffffffff',
|
||||
),
|
||||
71 =>
|
||||
array (
|
||||
0 => '24009e8000000000',
|
||||
1 => '24009e80ffffffff',
|
||||
),
|
||||
72 =>
|
||||
array (
|
||||
0 => '2400e18000000000',
|
||||
1 => '2400e180ffffffff',
|
||||
),
|
||||
73 =>
|
||||
array (
|
||||
0 => '2401e20000000000',
|
||||
1 => '2401e200ffffffff',
|
||||
),
|
||||
74 =>
|
||||
array (
|
||||
0 => '24053d0000000000',
|
||||
1 => '24053d00ffffffff',
|
||||
),
|
||||
75 =>
|
||||
array (
|
||||
0 => '24055f0000000000',
|
||||
1 => '24055f00ffffffff',
|
||||
),
|
||||
76 =>
|
||||
array (
|
||||
0 => '24057b0000000000',
|
||||
1 => '24057b00ffffffff',
|
||||
),
|
||||
77 =>
|
||||
array (
|
||||
0 => '2405860000000000',
|
||||
1 => '24058600ffffffff',
|
||||
),
|
||||
78 =>
|
||||
array (
|
||||
0 => '2405950000000000',
|
||||
1 => '24059500ffffffff',
|
||||
),
|
||||
79 =>
|
||||
array (
|
||||
0 => '2405c00000000000',
|
||||
1 => '2405c000ffffffff',
|
||||
),
|
||||
80 =>
|
||||
array (
|
||||
0 => '2406400000000000',
|
||||
1 => '24064000ffffffff',
|
||||
),
|
||||
81 =>
|
||||
array (
|
||||
0 => '2406590000000000',
|
||||
1 => '24065900ffffffff',
|
||||
),
|
||||
82 =>
|
||||
array (
|
||||
0 => '2406660000000000',
|
||||
1 => '24066600ffffffff',
|
||||
),
|
||||
83 =>
|
||||
array (
|
||||
0 => '2406680000000000',
|
||||
1 => '24066800ffffffff',
|
||||
),
|
||||
84 =>
|
||||
array (
|
||||
0 => '24066a0000000000',
|
||||
1 => '24066a00ffffffff',
|
||||
),
|
||||
85 =>
|
||||
array (
|
||||
0 => '2406ad0000000000',
|
||||
1 => '2406ad00ffffffff',
|
||||
),
|
||||
86 =>
|
||||
array (
|
||||
0 => '2406b00000000000',
|
||||
1 => '2406b000ffffffff',
|
||||
),
|
||||
87 =>
|
||||
array (
|
||||
0 => '2406d00000000000',
|
||||
1 => '2406d000ffffffff',
|
||||
),
|
||||
88 =>
|
||||
array (
|
||||
0 => '2406d70000000000',
|
||||
1 => '2406d700ffffffff',
|
||||
),
|
||||
89 =>
|
||||
array (
|
||||
0 => '24070b0000000000',
|
||||
1 => '24070b00ffffffff',
|
||||
),
|
||||
90 =>
|
||||
array (
|
||||
0 => '2407200000000000',
|
||||
1 => '24072000ffffffff',
|
||||
),
|
||||
91 =>
|
||||
array (
|
||||
0 => '2407350000000000',
|
||||
1 => '24073500ffffffff',
|
||||
),
|
||||
92 =>
|
||||
array (
|
||||
0 => '2407650000000000',
|
||||
1 => '24076500ffffffff',
|
||||
),
|
||||
93 =>
|
||||
array (
|
||||
0 => '2407670000000000',
|
||||
1 => '24076700ffffffff',
|
||||
),
|
||||
94 =>
|
||||
array (
|
||||
0 => '2407910000000000',
|
||||
1 => '24079100ffffffff',
|
||||
),
|
||||
95 =>
|
||||
array (
|
||||
0 => '2407b20000000000',
|
||||
1 => '2407b200ffffffff',
|
||||
),
|
||||
96 =>
|
||||
array (
|
||||
0 => '2407b80000000000',
|
||||
1 => '2407b800ffffffff',
|
||||
),
|
||||
97 =>
|
||||
array (
|
||||
0 => '2407c00000000000',
|
||||
1 => '2407c000ffffffff',
|
||||
),
|
||||
98 =>
|
||||
array (
|
||||
0 => '2407c70000000000',
|
||||
1 => '2407c700ffffffff',
|
||||
),
|
||||
);
|
||||
File diff suppressed because it is too large
Load diff
132
common/framework/calendar.php
Normal file
132
common/framework/calendar.php
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The calendar class.
|
||||
*/
|
||||
class Calendar
|
||||
{
|
||||
/**
|
||||
* This method returns the English name of a month, e.g. 9 = 'September'.
|
||||
*
|
||||
* @param int $month_number
|
||||
* @param bool $long_format (optional, default is true)
|
||||
* @return string
|
||||
*/
|
||||
public static function getMonthName($month_number, $long_format = true)
|
||||
{
|
||||
$month_number = intval($month_number, 10);
|
||||
if (!is_between($month_number, 1, 12))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return date($long_format ? 'F' : 'M', mktime(0, 0, 0, $month_number, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the day on which a month begins.
|
||||
*
|
||||
* 0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday.
|
||||
* If you do not specify a year, the current year is assumed.
|
||||
*
|
||||
* @param int $month_number
|
||||
* @param int $year (optional)
|
||||
* @return int
|
||||
*/
|
||||
public static function getMonthStartDayOfWeek($month_number, $year = null)
|
||||
{
|
||||
$month_number = intval($month_number, 10);
|
||||
if (!is_between($month_number, 1, 12))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int)date('w', mktime(0, 0, 0, $month_number, 1, $year ?: date('Y')));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the number of days in a month, e.g. February 2016 has 29 days.
|
||||
*
|
||||
* If you do not specify a year, the current year is assumed.
|
||||
* You must specify a year to get the number of days in February.
|
||||
*
|
||||
* @param int $month_number
|
||||
* @param int $year (optional)
|
||||
* @return int
|
||||
*/
|
||||
public static function getMonthDays($month_number, $year = null)
|
||||
{
|
||||
$month_number = intval($month_number, 10);
|
||||
if (!is_between($month_number, 1, 12))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int)date('t', mktime(0, 0, 0, $month_number, 1, $year ?: date('Y')));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a complete calendar for a month.
|
||||
*
|
||||
* The return value is an array with six members, each representing a week.
|
||||
* Each week is an array with seven members, each representing a day.
|
||||
* 6 weeks are returned. Empty cells are represented by nulls.
|
||||
*
|
||||
* If you do not specify a year, the current year is assumed.
|
||||
*
|
||||
* @param int $month_number
|
||||
* @param int $year (optional)
|
||||
* @param int $start_dow (optional)
|
||||
* @return array
|
||||
*/
|
||||
public static function getMonthCalendar($month_number, $year = null, $start_dow = 0)
|
||||
{
|
||||
$month_number = intval($month_number, 10);
|
||||
if (!is_between($month_number, 1, 12))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!is_between($start_dow, 0, 6))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!$year || !is_between($year, 1000, 9999))
|
||||
{
|
||||
$year = date('Y');
|
||||
}
|
||||
|
||||
$start = self::getMonthStartDayOfWeek($month_number, $year);
|
||||
$count = self::getMonthDays($month_number, $year);
|
||||
$initial_blank_cells = (7 + $start - $start_dow) % 7;
|
||||
$final_blank_cells = 42 - $count - $initial_blank_cells;
|
||||
$temp = array();
|
||||
|
||||
for ($i = 0; $i < $initial_blank_cells; $i++)
|
||||
{
|
||||
$temp[] = null;
|
||||
}
|
||||
for ($i = 0; $i < $count; $i++)
|
||||
{
|
||||
$temp[] = $i + 1;
|
||||
}
|
||||
for ($i = 0; $i < $final_blank_cells; $i++)
|
||||
{
|
||||
$temp[] = null;
|
||||
}
|
||||
|
||||
$return = array();
|
||||
for ($i = 0; $i < 6; $i++)
|
||||
{
|
||||
$week = array();
|
||||
for ($j = 0; $j < 7; $j++)
|
||||
{
|
||||
$week[] = array_shift($temp);
|
||||
}
|
||||
$return[] = $week;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,13 +28,13 @@ class Config
|
|||
*/
|
||||
public static function init()
|
||||
{
|
||||
if (file_exists(RX_BASEDIR . self::$config_filename))
|
||||
if (file_exists(\RX_BASEDIR . self::$config_filename))
|
||||
{
|
||||
self::$_config = (include RX_BASEDIR . self::$config_filename);
|
||||
self::$_config = (include \RX_BASEDIR . self::$config_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self::$_config = Compat\ConfigParser::convert())
|
||||
if (self::$_config = Parsers\ConfigParser::convert())
|
||||
{
|
||||
self::save();
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ class Config
|
|||
*/
|
||||
public static function getDefaults()
|
||||
{
|
||||
return (include RX_BASEDIR . self::$default_config_filename);
|
||||
return (include \RX_BASEDIR . self::$default_config_filename);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -135,7 +135,7 @@ class Config
|
|||
|
||||
// Save the main config file.
|
||||
$buff = '<?php' . "\n" . '// Rhymix System Configuration' . "\n" . 'return ' . self::serialize(self::$_config) . ';' . "\n";
|
||||
$result = \FileHandler::writeFile(RX_BASEDIR . self::$config_filename, $buff) ? true : false;
|
||||
$result = Storage::write(\RX_BASEDIR . self::$config_filename, $buff) ? true : false;
|
||||
//if (!$result) return false;
|
||||
|
||||
// Save XE-compatible config files.
|
||||
|
|
@ -144,9 +144,9 @@ class Config
|
|||
$db_info_without_ftp = clone $db_info;
|
||||
unset($db_info_without_ftp->ftp_info);
|
||||
$buff = '<?php' . "\n" . '$db_info = ' . self::serialize($db_info_without_ftp) . ';' . "\n";
|
||||
\FileHandler::writeFile(RX_BASEDIR . self::$old_db_config_filename, $buff);
|
||||
Storage::write(\RX_BASEDIR . self::$old_db_config_filename, $buff);
|
||||
$buff = '<?php' . "\n" . '$ftp_info = ' . self::serialize($ftp_info) . ';' . "\n";
|
||||
\FileHandler::writeFile(RX_BASEDIR . self::$old_ftp_config_filename, $buff);
|
||||
Storage::write(\RX_BASEDIR . self::$old_ftp_config_filename, $buff);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ class Debug
|
|||
self::$_entries[] = $entry;
|
||||
|
||||
// Add the entry to the error log.
|
||||
if (self::$write_to_error_log)
|
||||
if (self::$write_to_error_log && self::isEnabledForCurrentUser())
|
||||
{
|
||||
$log_entry = str_replace("\0", '', sprintf('Rhymix Debug: %s in %s on line %d',
|
||||
var_export($message, true), $entry->file, $entry->line));
|
||||
|
|
@ -173,8 +173,8 @@ class Debug
|
|||
|
||||
// Rewrite the error message with relative paths.
|
||||
$message = str_replace(array(
|
||||
' called in ' . RX_BASEDIR,
|
||||
' defined in ' . RX_BASEDIR,
|
||||
' called in ' . \RX_BASEDIR,
|
||||
' defined in ' . \RX_BASEDIR,
|
||||
), array(
|
||||
' called in ',
|
||||
' defined in ',
|
||||
|
|
@ -397,6 +397,12 @@ class Debug
|
|||
*/
|
||||
public static function displayErrorScreen($message)
|
||||
{
|
||||
// Do not display error screen in CLI.
|
||||
if (php_sapi_name() === 'cli')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable output buffering.
|
||||
while (ob_get_level())
|
||||
{
|
||||
|
|
@ -445,13 +451,17 @@ class Debug
|
|||
return $cache = true;
|
||||
|
||||
case 'ip':
|
||||
$allowed_ip = Config::get('debug.allow');
|
||||
foreach ($allowed_ip as $range)
|
||||
if (Filters\IpFilter::inRanges(\RX_CLIENT_IP, Config::get('debug.allow')))
|
||||
{
|
||||
if (IpFilter::inRange(RX_CLIENT_IP, $range))
|
||||
{
|
||||
return $cache = true;
|
||||
}
|
||||
return $cache = true;
|
||||
}
|
||||
if (\RX_CLIENT_IP === '127.0.0.1' || \RX_CLIENT_IP === '::1')
|
||||
{
|
||||
return $cache = true;
|
||||
}
|
||||
if (\RX_CLIENT_IP === $_SERVER['SERVER_ADDR'] || \RX_CLIENT_IP === $_SERVER['LOCAL_ADDR'])
|
||||
{
|
||||
return $cache = true;
|
||||
}
|
||||
return $cache = false;
|
||||
|
||||
|
|
@ -475,7 +485,7 @@ class Debug
|
|||
{
|
||||
// Collect debug information.
|
||||
$data = (object)array(
|
||||
'timestamp' => DateTime::formatTimestamp('Y-m-d H:i:s', RX_TIME),
|
||||
'timestamp' => DateTime::formatTimestamp('Y-m-d H:i:s', \RX_TIME),
|
||||
'url' => getCurrentPageUrl(),
|
||||
'request' => (object)array(
|
||||
'method' => $_SERVER['REQUEST_METHOD'] . ($_SERVER['REQUEST_METHOD'] !== \Context::getRequestMethod() ? (' (' . \Context::getRequestMethod() . ')') : ''),
|
||||
|
|
|
|||
88
common/framework/filters/filenamefilter.php
Normal file
88
common/framework/filters/filenamefilter.php
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Filters;
|
||||
|
||||
/**
|
||||
* The filename filter class.
|
||||
*/
|
||||
class FilenameFilter
|
||||
{
|
||||
/**
|
||||
* Remove illegal and dangerous characters from a filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public static function clean($filename)
|
||||
{
|
||||
// Replace dangerous characters with safe alternatives, maintaining meaning as much as possible.
|
||||
$illegal = array('\\', '/', '<', '>', '{', '}', ':', ';', '|', '"', '~', '`', '@', '#', '$', '%', '^', '&', '*', '?');
|
||||
$replace = array('', '', '(', ')', '(', ')', '_', ',', '_', '', '_', '\'', '_', '_', '_', '_', '_', '_', '', '');
|
||||
$filename = str_replace($illegal, $replace, $filename);
|
||||
|
||||
// Remove control characters.
|
||||
$filename = preg_replace('/([\\x00-\\x1f\\x7f\\xff]+)/u', '', $filename);
|
||||
|
||||
// Standardize whitespace characters.
|
||||
$filename = trim(preg_replace('/[\\pZ\\pC]+/u', ' ', $filename));
|
||||
|
||||
// Remove excess spaces and replacement characters.
|
||||
$filename = trim($filename, ' .-_');
|
||||
$filename = preg_replace('/__+/', '_', $filename);
|
||||
|
||||
// Change .php files to .phps to make them non-executable.
|
||||
if (strtolower(substr($filename, strlen($filename) - 4)) === '.php')
|
||||
{
|
||||
$filename = substr($filename, 0, strlen($filename) - 4) . '.phps';
|
||||
}
|
||||
|
||||
// Truncate filenames over 127 chars long, or extensions over 16 chars long.
|
||||
if (mb_strlen($filename, 'UTF-8') > 127)
|
||||
{
|
||||
$extension = strrchr($filename, '.');
|
||||
if (mb_strlen($extension, 'UTF-8') > 16) $extension = mb_substr($extension, 0, 16);
|
||||
$filename = mb_substr($filename, 0, 127 - mb_strlen($extension)) . $extension;
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean a path to remove ./, ../, trailing slashes, etc.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function cleanPath($path)
|
||||
{
|
||||
// Convert relative paths to absolute paths.
|
||||
if (!preg_match('@^(?:/|[a-z]:[\\\\/]|\\\\|https?:)@i', $path))
|
||||
{
|
||||
$path = \RX_BASEDIR . $path;
|
||||
}
|
||||
|
||||
// Convert backslashes to forward slashes.
|
||||
$path = str_replace('\\', '/', $path);
|
||||
|
||||
// Remove querystrings and URL fragments.
|
||||
if (($querystring = strpbrk($path, '?#')) !== false)
|
||||
{
|
||||
$path = substr($path, 0, -1 * strlen($querystring));
|
||||
}
|
||||
|
||||
// Remove single dots, three or more dots, and duplicate slashes.
|
||||
$path = preg_replace(array(
|
||||
'@(?<!^|^http:|^https:)/{2,}@',
|
||||
'@/(?:(?:\.|\.{3,})/)+@',
|
||||
), '/', $path);
|
||||
|
||||
// Remove double dots and the preceding directory.
|
||||
while (preg_match('@/(?!\.\.)[^/]+/\.\.(?:/|$)@', $path, $matches))
|
||||
{
|
||||
$path = str_replace($matches[0], '/', $path);
|
||||
}
|
||||
|
||||
// Trim trailing slashes.
|
||||
return rtrim($path, '/');
|
||||
}
|
||||
}
|
||||
501
common/framework/filters/htmlfilter.php
Normal file
501
common/framework/filters/htmlfilter.php
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Filters;
|
||||
|
||||
use Rhymix\Framework\Security;
|
||||
use Rhymix\Framework\Storage;
|
||||
|
||||
/**
|
||||
* The HTML filter class.
|
||||
*/
|
||||
class HTMLFilter
|
||||
{
|
||||
/**
|
||||
* HTMLPurifier instance is cached here.
|
||||
*/
|
||||
protected static $_htmlpurifier;
|
||||
|
||||
/**
|
||||
* Pre-processing and post-processing filters are stored here.
|
||||
*/
|
||||
protected static $_preproc = array();
|
||||
protected static $_postproc = array();
|
||||
|
||||
/**
|
||||
* Prepend a pre-processing filter.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function prependPreFilter($callback)
|
||||
{
|
||||
array_unshift(self::$_preproc, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a pre-processing filter.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function appendPreFilter($callback)
|
||||
{
|
||||
self::$_preproc[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a post-processing filter.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function prependPostFilter($callback)
|
||||
{
|
||||
array_unshift(self::$_postproc, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a post-processing filter.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function appendPostFilter($callback)
|
||||
{
|
||||
self::$_postproc[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter HTML content to block XSS attacks.
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
public static function clean($input)
|
||||
{
|
||||
foreach (self::$_preproc as $callback)
|
||||
{
|
||||
$input = $callback($input);
|
||||
}
|
||||
|
||||
$input = self::_preprocess($input);
|
||||
$output = self::getHTMLPurifier()->purify($input);
|
||||
$output = self::_postprocess($output);
|
||||
|
||||
foreach (self::$_postproc as $callback)
|
||||
{
|
||||
$output = $callback($output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of HTMLPurifier.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function getHTMLPurifier()
|
||||
{
|
||||
// Create an instance with reasonable defaults.
|
||||
if (self::$_htmlpurifier === null)
|
||||
{
|
||||
// Get the default configuration.
|
||||
$config = \HTMLPurifier_Config::createDefault();
|
||||
|
||||
// Customize the default configuration.
|
||||
$config->set('Attr.AllowedFrameTargets', array('_blank'));
|
||||
$config->set('Attr.DefaultImageAlt', '');
|
||||
$config->set('Attr.EnableID', false);
|
||||
$config->set('AutoFormat.AutoParagraph', false);
|
||||
$config->set('AutoFormat.DisplayLinkURI', false);
|
||||
$config->set('AutoFormat.Linkify', false);
|
||||
$config->set('Core.Encoding', 'UTF-8');
|
||||
$config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
|
||||
$config->set('HTML.FlashAllowFullScreen', true);
|
||||
$config->set('HTML.MaxImgLength', null);
|
||||
$config->set('CSS.MaxImgLength', null);
|
||||
$config->set('CSS.Proprietary', true);
|
||||
$config->set('Output.FlashCompat', true);
|
||||
$config->set('Output.Newline', "\n");
|
||||
$config->set('URI.MakeAbsolute', false);
|
||||
|
||||
// Allow embedding of external multimedia content.
|
||||
$config->set('HTML.SafeEmbed', true);
|
||||
$config->set('HTML.SafeIframe', true);
|
||||
$config->set('HTML.SafeObject', true);
|
||||
$config->set('URI.SafeIframeRegexp', MediaFilter::getIframeWhitelistRegex());
|
||||
|
||||
// Set the serializer path.
|
||||
$config->set('Cache.SerializerPath', \RX_BASEDIR . 'files/cache/htmlpurifier');
|
||||
Storage::createDirectory(\RX_BASEDIR . 'files/cache/htmlpurifier');
|
||||
|
||||
// Modify the HTML definition to support editor components and widgets.
|
||||
$def = $config->getHTMLDefinition(true);
|
||||
$def->addAttribute('img', 'editor_component', 'Text');
|
||||
$def->addAttribute('div', 'editor_component', 'Text');
|
||||
$def->addAttribute('img', 'rx_encoded_properties', 'Text');
|
||||
$def->addAttribute('div', 'rx_encoded_properties', 'Text');
|
||||
|
||||
// Support HTML5 and CSS3.
|
||||
self::_supportHTML5($config);
|
||||
self::_supportCSS3($config);
|
||||
|
||||
// Cache our instance of HTMLPurifier.
|
||||
self::$_htmlpurifier = new \HTMLPurifier($config);
|
||||
}
|
||||
|
||||
// Return the cached instance.
|
||||
return self::$_htmlpurifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch HTMLPurifier to support some HTML5 tags and attributes.
|
||||
*
|
||||
* These changes are based on https://github.com/xemlock/htmlpurifier-html5
|
||||
* but modified to support even more tags and attributes.
|
||||
*
|
||||
* @param object $config
|
||||
* @return void
|
||||
*/
|
||||
protected static function _supportHTML5($config)
|
||||
{
|
||||
// Get the HTML definition.
|
||||
$def = $config->getHTMLDefinition(true);
|
||||
|
||||
// Add various block-level tags.
|
||||
$def->addElement('header', 'Block', 'Flow', 'Common');
|
||||
$def->addElement('footer', 'Block', 'Flow', 'Common');
|
||||
$def->addElement('nav', 'Block', 'Flow', 'Common');
|
||||
$def->addElement('main', 'Block', 'Flow', 'Common');
|
||||
$def->addElement('section', 'Block', 'Flow', 'Common');
|
||||
$def->addElement('article', 'Block', 'Flow', 'Common');
|
||||
$def->addElement('aside', 'Block', 'Flow', 'Common');
|
||||
|
||||
// Add various inline tags.
|
||||
$def->addElement('s', 'Inline', 'Inline', 'Common');
|
||||
$def->addElement('sub', 'Inline', 'Inline', 'Common');
|
||||
$def->addElement('sup', 'Inline', 'Inline', 'Common');
|
||||
$def->addElement('mark', 'Inline', 'Inline', 'Common');
|
||||
$def->addElement('wbr', 'Inline', 'Empty', 'Core');
|
||||
|
||||
// Support figures.
|
||||
$def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common');
|
||||
$def->addElement('figcaption', 'Inline', 'Flow', 'Common');
|
||||
|
||||
// Support insertions and deletions.
|
||||
$def->addElement('ins', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text'));
|
||||
$def->addElement('del', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text'));
|
||||
|
||||
// Support the <time> tag.
|
||||
$time = $def->addElement('time', 'Inline', 'Inline', 'Common', array('datetime' => 'Text', 'pubdate' => 'Bool'));
|
||||
$time->excludes = array('time' => true);
|
||||
|
||||
// Suppport <audio> and <video> tags. DO NOT ALLOW AUTOPLAY.
|
||||
$def->addElement('audio', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array(
|
||||
'src' => 'URI',
|
||||
'type' => 'Text',
|
||||
'preload' => 'Enum#auto,metadata,none',
|
||||
'controls' => 'Bool',
|
||||
'muted' => 'Bool',
|
||||
'loop' => 'Bool',
|
||||
));
|
||||
$def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array(
|
||||
'src' => 'URI',
|
||||
'type' => 'Text',
|
||||
'width' => 'Length',
|
||||
'height' => 'Length',
|
||||
'poster' => 'URI',
|
||||
'preload' => 'Enum#auto,metadata,none',
|
||||
'controls' => 'Bool',
|
||||
'muted' => 'Bool',
|
||||
'loop' => 'Bool',
|
||||
));
|
||||
$def->addElement('source', 'Block', 'Empty', 'Common', array(
|
||||
'src' => 'URI',
|
||||
'media' => 'Text',
|
||||
'type' => 'Text',
|
||||
));
|
||||
$def->addElement('track', 'Block', 'Empty', 'Common', array(
|
||||
'src' => 'URI',
|
||||
'srclang' => 'Text',
|
||||
'label' => 'Text',
|
||||
'kind' => 'Enum#captions,chapters,descriptions,metadata,subtitles',
|
||||
'default' => 'Bool',
|
||||
));
|
||||
|
||||
// Support additional properties.
|
||||
$def->addAttribute('img', 'srcset', 'Text');
|
||||
$def->addAttribute('iframe', 'allowfullscreen', 'Bool');
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch HTMLPurifier to support more CSS2 and some CSS3 properties.
|
||||
*
|
||||
* These changes are based on:
|
||||
* - https://github.com/mattiaswelander/htmlpurifier
|
||||
*
|
||||
* @param object $config
|
||||
* @return void
|
||||
*/
|
||||
protected static function _supportCSS3($config)
|
||||
{
|
||||
// Initialize $info.
|
||||
$info = array();
|
||||
|
||||
// min-width, max-width, etc.
|
||||
$info['min-width'] = $info['max-width'] = $info['min-height'] =
|
||||
$info['max-height'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Length(0),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')),
|
||||
));
|
||||
|
||||
// border-radius, etc.
|
||||
$border_radius = $info['border-top-left-radius'] =
|
||||
$info['border-top-right-radius'] = $info['border-bottom-left-radius'] =
|
||||
$info['border-bottom-right-radius'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Length(0),
|
||||
new \HTMLPurifier_AttrDef_CSS_Percentage(true),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')),
|
||||
));
|
||||
$info['border-radius'] = new \HTMLPurifier_AttrDef_CSS_Multiple($border_radius);
|
||||
|
||||
// word-break word-wrap, etc.
|
||||
$info['word-break'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'normal', 'break-all', 'keep-all', 'initial', 'inherit',
|
||||
));
|
||||
$info['word-wrap'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'normal', 'break-word', 'initial', 'inherit',
|
||||
));
|
||||
$info['text-overflow'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_Enum(array('clip', 'ellipsis', 'initial', 'inherit')),
|
||||
));
|
||||
|
||||
// text-shadow
|
||||
$info['text-shadow'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new \HTMLPurifier_AttrDef_CSS_Color(),
|
||||
))),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')),
|
||||
));
|
||||
|
||||
// box-shadow and box-sizing
|
||||
$info['box-shadow'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new \HTMLPurifier_AttrDef_CSS_Percentage(),
|
||||
new \HTMLPurifier_AttrDef_CSS_Color(),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('none', 'inset', 'initial', 'inherit')),
|
||||
)));
|
||||
$info['box-sizing'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'content-box', 'border-box', 'initial', 'inherit',
|
||||
));
|
||||
|
||||
// outline
|
||||
$info['outline-color'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Color(),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('invert', 'initial', 'inherit')),
|
||||
));
|
||||
$info['outline-offset'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')),
|
||||
));
|
||||
$info['outline-style'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset', 'initial', 'inherit',
|
||||
));
|
||||
$info['outline-width'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('medium', 'thin', 'thick', 'initial', 'inherit')),
|
||||
));
|
||||
$info['outline'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
$info['outline-color'], $info['outline-style'], $info['outline-width'],
|
||||
new \HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')),
|
||||
)));
|
||||
|
||||
// flexbox
|
||||
$info['display'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'block', 'flex', '-webkit-flex', 'inline', 'inline-block', 'inline-flex', '-webkit-inline-flex', 'inline-table',
|
||||
'list-item', 'run-in', 'compact', 'marker', 'table', 'table-row-group', 'table-header-group', 'table-footer-group',
|
||||
'table-row', 'table-column-group', 'table-column', 'table-cell', 'table-caption',
|
||||
'none', 'initial', 'inherit',
|
||||
));
|
||||
$info['order'] = new \HTMLPurifier_AttrDef_CSS_Number();
|
||||
$info['align-content'] = $info['justify-content'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'stretch', 'center', 'flex-start', 'flex-end', 'space-between', 'space-around', 'initial', 'inherit',
|
||||
));
|
||||
$info['align-items'] = $info['align-self'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'stretch', 'center', 'flex-start', 'flex-end', 'baseline', 'initial', 'inherit',
|
||||
));
|
||||
$info['flex-basis'] = new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new \HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new \HTMLPurifier_AttrDef_CSS_Percentage(),
|
||||
new \HTMLPurifier_AttrDef_Enum(array('auto', 'initial', 'inherit')),
|
||||
));
|
||||
$info['flex-direction'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'row', 'row-reverse', 'column', 'column-reverse', 'initial', 'inherit',
|
||||
));
|
||||
$info['flex-wrap'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'nowrap', 'wrap', 'wrap-reverse', 'initial', 'inherit',
|
||||
));
|
||||
$info['flex-flow'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
$info['flex-direction'], $info['flex-wrap'],
|
||||
)));
|
||||
$info['flex-grow'] = new \HTMLPurifier_AttrDef_CSS_Number();
|
||||
$info['flex-shrink'] = new \HTMLPurifier_AttrDef_CSS_Number();
|
||||
$info['flex'] = new \HTMLPurifier_AttrDef_CSS_Multiple(new \HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
$info['flex-grow'], $info['flex-shrink'], $info['flex-basis'],
|
||||
new \HTMLPurifier_AttrDef_Enum(array('auto', 'none', 'initial', 'inherit')),
|
||||
)));
|
||||
|
||||
// misc
|
||||
$info['caption-side'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'top', 'bottom', 'initial', 'inherit',
|
||||
));
|
||||
$info['empty-cells'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'show', 'hide', 'initial', 'inherit',
|
||||
));
|
||||
$info['hanging-punctuation'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'none', 'first', 'last', 'allow-end', 'force-end', 'initial', 'inherit',
|
||||
));
|
||||
$info['overflow'] = $info['overflow-x'] = $info['overflow-y'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'visible', 'hidden', 'scroll', 'auto', 'initial', 'inherit',
|
||||
));
|
||||
$info['resize'] = new \HTMLPurifier_AttrDef_Enum(array(
|
||||
'none', 'both', 'horizontal', 'vertical', 'initial', 'inherit',
|
||||
));
|
||||
|
||||
// Wrap all new properties with a decorator that handles !important.
|
||||
$allow_important = $config->get('CSS.AllowImportant');
|
||||
$css_definition = $config->getCSSDefinition();
|
||||
foreach ($info as $key => $val)
|
||||
{
|
||||
$css_definition->info[$key] = new \HTMLPurifier_AttrDef_CSS_ImportantDecorator($val, $allow_important);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rhymix-specific preprocessing method.
|
||||
*
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
protected static function _preprocess($content)
|
||||
{
|
||||
// Encode widget and editor component properties so that they are not removed by HTMLPurifier.
|
||||
$content = self::_encodeWidgetsAndEditorComponents($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rhymix-specific postprocessing method.
|
||||
*
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
protected static function _postprocess($content)
|
||||
{
|
||||
// Define acts to allow and deny.
|
||||
$allow_acts = array('procFileDownload');
|
||||
$deny_acts = array('dispMemberLogout', 'dispLayoutPreview');
|
||||
|
||||
// Remove tags not supported in Rhymix. Some of these may also have been removed by HTMLPurifier.
|
||||
$content = preg_replace_callback('!</?(?:html|body|head|title|meta|base|link|script|style|applet)\b[^>]*>!i', function($matches) {
|
||||
return htmlspecialchars($matches[0], ENT_QUOTES, 'UTF-8');
|
||||
}, $content);
|
||||
|
||||
// Remove object and embed URLs that are not allowed.
|
||||
$whitelist = MediaFilter::getObjectWhitelistRegex();
|
||||
$content = preg_replace_callback('!<(object|embed|param|audio|video|source|track)([^>]+)>!i', function($matches) use($whitelist) {
|
||||
return preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($whitelist) {
|
||||
if (in_array($attr[1], array('data', 'src', 'href', 'url', 'movie', 'source')))
|
||||
{
|
||||
$url = trim(htmlspecialchars_decode($attr[2]));
|
||||
if (preg_match('!^(https?:)?//!i', $url) && !preg_match($whitelist, $url))
|
||||
{
|
||||
return $attr[1] . '=""';
|
||||
}
|
||||
}
|
||||
return $attr[0];
|
||||
}, $matches[0]);
|
||||
}, $content);
|
||||
|
||||
// Remove link URLs that may be CSRF attempts.
|
||||
$content = preg_replace_callback('!\b(src|href|data|value)="([^"]+)"!i', function($matches) use($allow_acts, $deny_acts) {
|
||||
$url = preg_replace('!\s+!', '', htmlspecialchars_decode(rawurldecode($matches[2])));
|
||||
if (preg_match('!\bact=((disp|proc)[^&]+)!i', $url, $urlmatches))
|
||||
{
|
||||
$act = $urlmatches[1];
|
||||
if (!in_array($act, $allow_acts) && (in_array($act, $deny_acts) || $urlmatches[2] === 'proc'))
|
||||
{
|
||||
return $matches[1] . '=""';
|
||||
}
|
||||
}
|
||||
return $matches[0];
|
||||
}, $content);
|
||||
|
||||
// Restore widget and editor component properties.
|
||||
$content = self::_decodeWidgetsAndEditorComponents($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode widgets and editor components before processing.
|
||||
*
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
protected static function _encodeWidgetsAndEditorComponents($content)
|
||||
{
|
||||
return preg_replace_callback('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', function($match) {
|
||||
$tag = strtolower($match[1]);
|
||||
$attrs = array();
|
||||
$html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($tag, &$attrs) {
|
||||
$attrkey = strtolower($attr[1]);
|
||||
if ($tag === 'img' && preg_match('/^(?:width|height|src|alt|ismap|usemap)$/', $attrkey))
|
||||
{
|
||||
return $attr[0];
|
||||
}
|
||||
if (preg_match('/^(?:on|data-|(?:accesskey|class|contextmenu|contenteditable|dir|draggable|dropzone|editor_component|hidden|id|lang|name|style|tabindex|title)$)/', $attrkey))
|
||||
{
|
||||
return $attr[0];
|
||||
}
|
||||
$attrs[$attrkey] = htmlspecialchars_decode($attr[2]);
|
||||
return '';
|
||||
}, $match[0]);
|
||||
if ($tag === 'img' && !preg_match('/\ssrc="/', $html))
|
||||
{
|
||||
$html = substr($html, 0, 4) . ' src=""' . substr($html, 4);
|
||||
}
|
||||
$encoded_properties = Security::encrypt(json_encode($attrs));
|
||||
return substr($html, 0, 4) . ' rx_encoded_properties="' . $encoded_properties . '"' . substr($html, 4);
|
||||
}, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode widgets and editor components after processing.
|
||||
*
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
protected static function _decodeWidgetsAndEditorComponents($content)
|
||||
{
|
||||
return preg_replace_callback('!<(div|img)([^>]*)(\srx_encoded_properties="([^"]+)")!i', function($match) {
|
||||
$attrs = array();
|
||||
$decoded_properties = Security::decrypt($match[4]);
|
||||
if (!$decoded_properties)
|
||||
{
|
||||
return str_replace($match[3], '', $match[0]);
|
||||
}
|
||||
$decoded_properties = json_decode($decoded_properties);
|
||||
if (!$decoded_properties)
|
||||
{
|
||||
return str_replace($match[3], '', $match[0]);
|
||||
}
|
||||
foreach ($decoded_properties as $key => $val)
|
||||
{
|
||||
$attrs[] = $key . '="' . htmlspecialchars($val) . '"';
|
||||
}
|
||||
return str_replace($match[3], ' ' . implode(' ', $attrs), $match[0]);
|
||||
}, $content);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
namespace Rhymix\Framework\Filters;
|
||||
|
||||
/**
|
||||
* The IP filter class.
|
||||
|
|
@ -66,6 +66,25 @@ class IpFilter
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given IP address belongs to a set of ranges.
|
||||
*
|
||||
* @param string $ip
|
||||
* @param array $ranges
|
||||
* @return bool
|
||||
*/
|
||||
public static function inRanges($ip, array $ranges)
|
||||
{
|
||||
foreach ($ranges as $range)
|
||||
{
|
||||
if (self::inRange($ip, $range))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a range definition is valid.
|
||||
*
|
||||
|
|
@ -91,6 +110,24 @@ class IpFilter
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a set of range definitions is valid.
|
||||
*
|
||||
* @param array $ranges
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateRanges(array $ranges)
|
||||
{
|
||||
foreach ($ranges as $range)
|
||||
{
|
||||
if (!self::validateRange($range))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get real IP from CloudFlare headers.
|
||||
*
|
||||
|
|
@ -103,7 +140,7 @@ class IpFilter
|
|||
return false;
|
||||
}
|
||||
|
||||
$cloudflare_ranges = (include RX_BASEDIR . 'common/defaults/cloudflare.php');
|
||||
$cloudflare_ranges = (include \RX_BASEDIR . 'common/defaults/cloudflare.php');
|
||||
foreach ($cloudflare_ranges as $cloudflare_range)
|
||||
{
|
||||
if (self::inRange($_SERVER['REMOTE_ADDR'], $cloudflare_range))
|
||||
254
common/framework/filters/mediafilter.php
Normal file
254
common/framework/filters/mediafilter.php
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Filters;
|
||||
|
||||
use Rhymix\Framework\Config;
|
||||
|
||||
/**
|
||||
* The media filter class.
|
||||
*/
|
||||
class MediaFilter
|
||||
{
|
||||
/**
|
||||
* Whitelists are cached here.
|
||||
*/
|
||||
protected static $_iframe_whitelist;
|
||||
protected static $_object_whitelist;
|
||||
|
||||
/**
|
||||
* Add a prefix to the iframe whitelist.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @parsm bool $permanently
|
||||
* @return void
|
||||
*/
|
||||
public static function addIframePrefix($prefix, $permanently = false)
|
||||
{
|
||||
if (!count(self::$_iframe_whitelist))
|
||||
{
|
||||
self::_loadWhitelists();
|
||||
}
|
||||
|
||||
$prefix = self::formatPrefix($prefix);
|
||||
if (!in_array($prefix, self::$_iframe_whitelist))
|
||||
{
|
||||
self::$_iframe_whitelist[] = $prefix;
|
||||
natcasesort(self::$_iframe_whitelist);
|
||||
|
||||
if ($permanently)
|
||||
{
|
||||
Config::set('mediafilter.iframe', self::$_iframe_whitelist);
|
||||
Config::save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a prefix to the object whitelist.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @parsm bool $permanently
|
||||
* @return void
|
||||
*/
|
||||
public static function addObjectPrefix($prefix, $permanently = false)
|
||||
{
|
||||
if (!count(self::$_object_whitelist))
|
||||
{
|
||||
self::_loadWhitelists();
|
||||
}
|
||||
|
||||
$prefix = self::formatPrefix($prefix);
|
||||
if (!in_array($prefix, self::$_object_whitelist))
|
||||
{
|
||||
self::$_object_whitelist[] = $prefix;
|
||||
natcasesort(self::$_object_whitelist);
|
||||
|
||||
if ($permanently)
|
||||
{
|
||||
Config::set('mediafilter.object', self::$_object_whitelist);
|
||||
Config::save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a prefix for standardization.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return string
|
||||
*/
|
||||
public static function formatPrefix($prefix)
|
||||
{
|
||||
$prefix = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix;
|
||||
if (strpos($prefix, '/') === false)
|
||||
{
|
||||
$prefix .= '/';
|
||||
}
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the iframe whitelist.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getIframeWhitelist()
|
||||
{
|
||||
if (!count(self::$_iframe_whitelist))
|
||||
{
|
||||
self::_loadWhitelists();
|
||||
}
|
||||
return self::$_iframe_whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the iframe whitelist as a regular expression.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getIframeWhitelistRegex()
|
||||
{
|
||||
if (!count(self::$_iframe_whitelist))
|
||||
{
|
||||
self::_loadWhitelists();
|
||||
}
|
||||
$result = array();
|
||||
foreach(self::$_iframe_whitelist as $domain)
|
||||
{
|
||||
$result[] = str_replace('\*\.', '[a-z0-9-]+\.', preg_quote($domain, '%'));
|
||||
}
|
||||
return '%^https?://(' . implode('|', $result) . ')%';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object whitelist.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getObjectWhitelist()
|
||||
{
|
||||
if (!count(self::$_object_whitelist))
|
||||
{
|
||||
self::_loadWhitelists();
|
||||
}
|
||||
return self::$_object_whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object whitelist as a regular expression.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getObjectWhitelistRegex()
|
||||
{
|
||||
if (!count(self::$_object_whitelist))
|
||||
{
|
||||
self::_loadWhitelists();
|
||||
}
|
||||
$result = array();
|
||||
foreach(self::$_object_whitelist as $domain)
|
||||
{
|
||||
$result[] = str_replace('\*\.', '[a-z0-9-]+\.', preg_quote($domain, '%'));
|
||||
}
|
||||
return '%^https?://(' . implode('|', $result) . ')%';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL matches the iframe whitelist.
|
||||
*
|
||||
* @param string $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function matchIframeWhitelist($url)
|
||||
{
|
||||
return preg_match(self::getIframeWhitelistRegex(), $url) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL matches the iframe whitelist.
|
||||
*
|
||||
* @param string $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function matchObjectWhitelist($url)
|
||||
{
|
||||
return preg_match(self::getObjectWhitelistRegex(), $url) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove embedded media from HTML content.
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $replacement
|
||||
* @return string
|
||||
*/
|
||||
public static function removeEmbeddedMedia($input, $replacement = '')
|
||||
{
|
||||
$input = preg_replace('!<object[^>]*>(.*?</object>)?!is', $replacement, $input);
|
||||
$input = preg_replace('!<embed[^>]*>(.*?</embed>)?!is', $replacement, $input);
|
||||
$input = preg_replace('!<img[^>]*editor_component="multimedia_link"[^>]*>(.*?</img>)?!is', $replacement, $input);
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load whitelists.
|
||||
*
|
||||
* @param array $custom_whitelist
|
||||
* @return void
|
||||
*/
|
||||
protected static function _loadWhitelists($custom_whitelist = array())
|
||||
{
|
||||
$default_whitelist = (include \RX_BASEDIR . 'common/defaults/whitelist.php');
|
||||
self::$_object_whitelist = array();
|
||||
self::$_iframe_whitelist = array();
|
||||
|
||||
if(count($custom_whitelist))
|
||||
{
|
||||
if(!is_array($custom_whitelist) || !isset($custom_whitelist['iframe']) || !isset($custom_whitelist['object']))
|
||||
{
|
||||
$whitelist = array(
|
||||
'iframe' => isset($whitelist->iframe) ? $whitelist->iframe : array(),
|
||||
'object' => isset($whitelist->object) ? $whitelist->object : array(),
|
||||
);
|
||||
}
|
||||
foreach ($custom_whitelist['iframe'] as $prefix)
|
||||
{
|
||||
self::$_iframe_whitelist[] = self::formatPrefix($prefix);
|
||||
}
|
||||
foreach ($custom_whitelist['object'] as $prefix)
|
||||
{
|
||||
self::$_object_whitelist[] = self::formatPrefix($prefix);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($default_whitelist['iframe'] as $prefix)
|
||||
{
|
||||
self::$_iframe_whitelist[] = $prefix;
|
||||
}
|
||||
foreach ($default_whitelist['object'] as $prefix)
|
||||
{
|
||||
self::$_object_whitelist[] = $prefix;
|
||||
}
|
||||
if ($iframe_whitelist = config('mediafilter.iframe') ?: config('embedfilter.iframe'))
|
||||
{
|
||||
foreach ($iframe_whitelist as $prefix)
|
||||
{
|
||||
self::$_iframe_whitelist[] = self::formatPrefix($prefix);
|
||||
}
|
||||
}
|
||||
if ($object_whitelist = config('mediafilter.object') ?: config('embedfilter.object'))
|
||||
{
|
||||
foreach ($object_whitelist as $prefix)
|
||||
{
|
||||
self::$_object_whitelist[] = self::formatPrefix($prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$_object_whitelist = array_unique(self::$_object_whitelist);
|
||||
self::$_iframe_whitelist = array_unique(self::$_iframe_whitelist);
|
||||
natcasesort(self::$_object_whitelist);
|
||||
natcasesort(self::$_iframe_whitelist);
|
||||
}
|
||||
}
|
||||
311
common/framework/formatter.php
Normal file
311
common/framework/formatter.php
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The formatter class.
|
||||
*/
|
||||
class Formatter
|
||||
{
|
||||
/**
|
||||
* Options for text to HTML conversion.
|
||||
*/
|
||||
const TEXT_NEWLINE_AS_P = 1;
|
||||
const TEXT_DOUBLE_NEWLINE_AS_P = 2;
|
||||
|
||||
/**
|
||||
* Options for Markdown to HTML conversion.
|
||||
*/
|
||||
const MD_NEWLINE_AS_BR = 16;
|
||||
const MD_ENABLE_EXTRA = 128;
|
||||
|
||||
/**
|
||||
* Convert plain text to HTML.
|
||||
*
|
||||
* @param string $text
|
||||
* @param int $options (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function text2html($text, $options = 0)
|
||||
{
|
||||
// This option uses <p> instead of <br> to separate lines.
|
||||
if ($options & self::TEXT_NEWLINE_AS_P)
|
||||
{
|
||||
$lines = array_map('trim', explode("\n", escape(trim($text))));
|
||||
$result = '';
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
$result .= "<p>$line</p>\n";
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
// This option uses <br> to separate lines and <p> to separate paragraphs.
|
||||
if ($options & self::TEXT_DOUBLE_NEWLINE_AS_P)
|
||||
{
|
||||
$lines = preg_replace('!(<br />)+\s*$!', '', nl2br(escape(trim($text))));
|
||||
$lines = preg_split('!(<br />\s*)+<br />!', $lines);
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
$result .= "<p>\n" . trim($line) . "\n</p>\n";
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
// The default is to use <br> always.
|
||||
return nl2br(escape(trim($text))) . "<br />\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTML to plain text.
|
||||
*
|
||||
* @param string $html
|
||||
* @return string
|
||||
*/
|
||||
public static function html2text($html)
|
||||
{
|
||||
// Add line breaks after <br> and <p> tags.
|
||||
$html = preg_replace('!<br[^>]*>\s*!i', "\n", $html);
|
||||
$html = preg_replace('!<p\b[^>]*>\s*!i', '', $html);
|
||||
$html = preg_replace('!</p[^>]*>\s*!i', "\n\n", $html);
|
||||
|
||||
// Encode links and images to preserve essential information.
|
||||
$html = preg_replace_callback('!<a\b[^>]*href="([^>"]+)"[^>]*>([^<]*)</a>!i', function($matches) {
|
||||
return trim($matches[2] . ' <' . $matches[1] . '>');
|
||||
}, $html);
|
||||
$html = preg_replace_callback('!<img\b[^>]*src="([^>"]+)"[^>]*>!i', function($matches) {
|
||||
$title = preg_match('!title="([^>"]+)"!i', $matches[0], $m) ? $m[1] : null;
|
||||
$title = $title ?: (preg_match('!alt="([^>"]+)"!i', $matches[0], $m) ? $m[1] : 'IMAGE');
|
||||
return trim('[' . $title . '] <' . $matches[1] . '>');
|
||||
}, $html);
|
||||
|
||||
// Strip all other HTML.
|
||||
$text = html_entity_decode(strip_tags($html));
|
||||
unset($html);
|
||||
|
||||
// Normalize whitespace and return.
|
||||
$text = str_replace("\r\n", "\n", $text);
|
||||
$text = preg_replace('/\n(?:\s*\n)+/', "\n\n", $text);
|
||||
return trim($text) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Markdown to HTML.
|
||||
*
|
||||
* @param string $markdown
|
||||
* @param int $options (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function markdown2html($markdown, $options = 0)
|
||||
{
|
||||
if ($options & self::MD_NEWLINE_AS_BR)
|
||||
{
|
||||
$markdown = preg_replace('/(?<!\n)\n(?![\n\*\#\-])/', " \n", $markdown);
|
||||
}
|
||||
|
||||
if ($options & self::MD_ENABLE_EXTRA)
|
||||
{
|
||||
$class_name = '\\Michelf\\MarkdownExtra';
|
||||
}
|
||||
else
|
||||
{
|
||||
$class_name = '\\Michelf\\Markdown';
|
||||
}
|
||||
|
||||
$html = $class_name::defaultTransform($markdown);
|
||||
return Filters\HTMLFilter::clean($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTML to Markdown.
|
||||
*
|
||||
* @param string $html
|
||||
* @return string
|
||||
*/
|
||||
public static function html2markdown($html)
|
||||
{
|
||||
$converter = new \League\HTMLToMarkdown\HtmlConverter();
|
||||
$converter->getConfig()->setOption('bold_style', '**');
|
||||
$converter->getConfig()->setOption('italic_style', '_');
|
||||
$converter->getConfig()->setOption('strip_tags', true);
|
||||
return trim($converter->convert($html)) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert BBCode to HTML.
|
||||
*
|
||||
* @param string $bbcode
|
||||
* @return string
|
||||
*/
|
||||
public static function bbcode($bbcode)
|
||||
{
|
||||
$parser = new \JBBCode\Parser;
|
||||
$parser->addCodeDefinitionSet(new \JBBCode\DefaultCodeDefinitionSet());
|
||||
|
||||
$builder = new \JBBCode\CodeDefinitionBuilder('quote', '<blockquote>{param}</blockquote>');
|
||||
$parser->addCodeDefinition($builder->build());
|
||||
$builder = new \JBBCode\CodeDefinitionBuilder('code', '<pre><code>{param}</code></pre>');
|
||||
$builder->setParseContent(false);
|
||||
$parser->addCodeDefinition($builder->build());
|
||||
|
||||
$parser->parse($bbcode);
|
||||
$html = $parser->getAsHtml();
|
||||
return Filters\HTMLFilter::clean($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply smart quotes and other stylistic enhancements to HTML.
|
||||
*
|
||||
* @param string $html
|
||||
* @return string
|
||||
*/
|
||||
public static function applySmartQuotes($html)
|
||||
{
|
||||
return \Michelf\SmartyPants::defaultTransform($html, 'qbBdDiew');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile LESS into CSS.
|
||||
*
|
||||
* @param string|array $source_filename
|
||||
* @param string $target_filename
|
||||
* @param array $variables (optional)
|
||||
* @parsm bool $minify (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function compileLESS($source_filename, $target_filename, $variables = array(), $minify = false)
|
||||
{
|
||||
// Get the cleaned and concatenated content.
|
||||
$content = self::_concatenate($source_filename, $target_filename);
|
||||
|
||||
// Compile!
|
||||
try
|
||||
{
|
||||
$less_compiler = new \lessc;
|
||||
$less_compiler->setFormatter($minify ? 'compressed' : 'lessjs');
|
||||
$less_compiler->setImportDir(array(dirname(is_array($source_filename) ? array_first($source_filename) : $source_filename)));
|
||||
if ($variables)
|
||||
{
|
||||
$less_compiler->setVariables($variables);
|
||||
}
|
||||
|
||||
$content = '@charset "UTF-8";' . "\n" . $less_compiler->compile($content) . "\n";
|
||||
$result = true;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$content = '/*' . "\n" . 'Error while compiling LESS:' . "\n" . $e->getMessage() . "\n" . '*/' . "\n";
|
||||
$result = false;
|
||||
}
|
||||
|
||||
// Save the result to the target file.
|
||||
Storage::write($target_filename, $content);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile SCSS into CSS.
|
||||
*
|
||||
* @param string|array $source_filename
|
||||
* @param string $target_filename
|
||||
* @param array $variables (optional)
|
||||
* @parsm bool $minify (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function compileSCSS($source_filename, $target_filename, $variables = array(), $minify = false)
|
||||
{
|
||||
// Get the cleaned and concatenated content.
|
||||
$content = self::_concatenate($source_filename, $target_filename);
|
||||
|
||||
// Compile!
|
||||
try
|
||||
{
|
||||
$scss_compiler = new \Leafo\ScssPhp\Compiler;
|
||||
$scss_compiler->setFormatter($minify ? '\Leafo\ScssPhp\Formatter\Crunched' : '\Leafo\ScssPhp\Formatter\Expanded');
|
||||
$scss_compiler->setImportPaths(array(dirname(is_array($source_filename) ? array_first($source_filename) : $source_filename)));
|
||||
if ($variables)
|
||||
{
|
||||
$scss_compiler->setVariables($variables);
|
||||
}
|
||||
|
||||
$content = '@charset "UTF-8";' . "\n" . $scss_compiler->compile($content) . "\n";
|
||||
$result = true;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$content = '/*' . "\n" . 'Error while compiling SCSS:' . "\n" . $e->getMessage() . "\n" . '*/' . "\n";
|
||||
$result = false;
|
||||
}
|
||||
|
||||
// Save the result to the target file.
|
||||
Storage::write($target_filename, $content);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minify CSS.
|
||||
*
|
||||
* @param string|array $source_filename
|
||||
* @param string $target_filename
|
||||
* @return bool
|
||||
*/
|
||||
public static function minifyCSS($source_filename, $target_filename)
|
||||
{
|
||||
$minifier = new \MatthiasMullie\Minify\CSS($source_filename);
|
||||
$content = $minifier->execute($target_filename);
|
||||
Storage::write($target_filename, $content);
|
||||
return strlen($content) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minify JS.
|
||||
*
|
||||
* @param string|array $source_filename
|
||||
* @param string $target_filename
|
||||
* @return bool
|
||||
*/
|
||||
public static function minifyJS($source_filename, $target_filename)
|
||||
{
|
||||
$minifier = new \MatthiasMullie\Minify\JS($source_filename);
|
||||
$content = $minifier->execute($target_filename);
|
||||
Storage::write($target_filename, $content);
|
||||
return strlen($content) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS concatenation subroutine for compileLESS() and compileSCSS().
|
||||
*
|
||||
* @param string|array $source_filename
|
||||
* @param string $target_filename
|
||||
* @return string
|
||||
*/
|
||||
protected static function _concatenate($source_filename, $target_filename)
|
||||
{
|
||||
$result = '';
|
||||
|
||||
if (!is_array($source_filename))
|
||||
{
|
||||
$source_filename = array($source_filename);
|
||||
}
|
||||
foreach ($source_filename as $filename)
|
||||
{
|
||||
$content = utf8_clean(file_get_contents($filename));
|
||||
$path_converter = new \MatthiasMullie\PathConverter\Converter($filename, $target_filename);
|
||||
$content = preg_replace_callback('/\burl\\(([^)]+)\\)/iU', function($matches) use ($path_converter) {
|
||||
$url = trim($matches[1], '\'"');
|
||||
if (!strlen($url) || $url[0] === '/')
|
||||
{
|
||||
return $matches[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 'url("' . escape_dqstr($path_converter->convert($url)) . '")';
|
||||
}
|
||||
}, $content);
|
||||
unset($path_converter);
|
||||
$result .= trim($content) . "\n\n";
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
78
common/framework/i18n.php
Normal file
78
common/framework/i18n.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The i18n (internationalization) class.
|
||||
*/
|
||||
class i18n
|
||||
{
|
||||
/**
|
||||
* Constants for sorting.
|
||||
*/
|
||||
const SORT_CODE_2 = 2;
|
||||
const SORT_CODE_3 = 3;
|
||||
const SORT_CODE_NUMERIC = 4;
|
||||
const SORT_CCTLD = 5;
|
||||
const SORT_NAME_ENGLISH = 6;
|
||||
const SORT_NAME_KOREAN = 7;
|
||||
const SORT_NAME_NATIVE = 8;
|
||||
|
||||
/**
|
||||
* Get the list of all countries.
|
||||
*
|
||||
* @param int $sort_by
|
||||
* @return array
|
||||
*/
|
||||
public static function listCountries($sort_by = self::SORT_NAME_ENGLISH)
|
||||
{
|
||||
$countries = (include \RX_BASEDIR . 'common/defaults/countries.php');
|
||||
$result = array();
|
||||
|
||||
foreach ($countries as $country)
|
||||
{
|
||||
$result[$country['iso_3166_1_alpha3']] = (object)$country;
|
||||
}
|
||||
|
||||
switch ($sort_by)
|
||||
{
|
||||
case self::SORT_CODE_2:
|
||||
uasort($result, function($a, $b) {
|
||||
return strcmp($a->iso_3166_1_alpha2, $b->iso_3166_1_alpha2);
|
||||
});
|
||||
break;
|
||||
case self::SORT_CODE_3:
|
||||
uasort($result, function($a, $b) {
|
||||
return strcmp($a->iso_3166_1_alpha3, $b->iso_3166_1_alpha3);
|
||||
});
|
||||
break;
|
||||
case self::SORT_CODE_NUMERIC:
|
||||
uasort($result, function($a, $b) {
|
||||
return strcmp($a->iso_3166_1_numeric, $b->iso_3166_1_numeric);
|
||||
});
|
||||
break;
|
||||
case self::SORT_CCTLD:
|
||||
uasort($result, function($a, $b) {
|
||||
return strcmp($a->cctld, $b->cctld);
|
||||
});
|
||||
break;
|
||||
case self::SORT_NAME_ENGLISH:
|
||||
uasort($result, function($a, $b) {
|
||||
return strcmp($a->name_english, $b->name_english);
|
||||
});
|
||||
break;
|
||||
case self::SORT_NAME_KOREAN:
|
||||
uasort($result, function($a, $b) {
|
||||
return strcmp($a->name_korean, $b->name_korean);
|
||||
});
|
||||
break;
|
||||
case self::SORT_NAME_NATIVE:
|
||||
uasort($result, function($a, $b) {
|
||||
return strcmp($a->name_native, $b->name_native);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
453
common/framework/korea.php
Normal file
453
common/framework/korea.php
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* Class for validating Korea-specific information.
|
||||
*/
|
||||
class Korea
|
||||
{
|
||||
/**
|
||||
* Format a phone number.
|
||||
*
|
||||
* @param string $num
|
||||
* @return string
|
||||
*/
|
||||
public static function formatPhoneNumber($num)
|
||||
{
|
||||
// Remove all non-numbers.
|
||||
$num = preg_replace('/[^0-9]/', '', $num);
|
||||
|
||||
// Remove the country code.
|
||||
if (strncmp($num, '82', 2) === 0)
|
||||
{
|
||||
$num = substr($num, 2);
|
||||
if (strncmp($num, '0', 1) !== 0)
|
||||
{
|
||||
$num = '0' . $num;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply different format based on the number of digits.
|
||||
switch (strlen($num))
|
||||
{
|
||||
case 8:
|
||||
return substr($num, 0, 4) . '-' . substr($num, 4);
|
||||
case 9:
|
||||
return substr($num, 0, 2) . '-' . substr($num, 2, 3) . '-' . substr($num, 5);
|
||||
case 10:
|
||||
if (substr($num, 0, 2) === '02')
|
||||
{
|
||||
return substr($num, 0, 2) . '-' . substr($num, 2, 4) . '-' . substr($num, 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
return substr($num, 0, 3) . '-' . substr($num, 3, 3) . '-' . substr($num, 6);
|
||||
}
|
||||
default:
|
||||
if (substr($num, 0, 4) === '0303' || substr($num, 0, 3) === '050')
|
||||
{
|
||||
return substr($num, 0, 4) . '-' . substr($num, 4, 3) . '-' . substr($num, 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
return substr($num, 0, 3) . '-' . substr($num, 3, 4) . '-' . substr($num, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a Korean phone number contains a valid area code and the correct number of digits.
|
||||
*
|
||||
* @param string $num
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidPhoneNumber($num)
|
||||
{
|
||||
$num = str_replace('-', '', self::formatPhoneNumber($num));
|
||||
if (preg_match('/^1[0-9]{7}$/', $num))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/^02[2-9][0-9]{6,7}$/', $num))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (preg_match('/^0[13-8][0-9][2-9][0-9]{6,7}$/', $num))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a Korean phone number is a mobile phone number.
|
||||
*
|
||||
* @param string $num
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidMobilePhoneNumber($num)
|
||||
{
|
||||
$num = str_replace('-', '', self::formatPhoneNumber($num));
|
||||
$len = strlen($num);
|
||||
return preg_match('/^01[016789][2-9][0-9]{6,7}$/', $num) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given string is a valid resident registration number (주민등록번호)
|
||||
* or foreigner registration number (외국인등록번호).
|
||||
*
|
||||
* This method only checks the format.
|
||||
* It does not check that the number is actually in use.
|
||||
*
|
||||
* @param string $code
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidJuminNumber($code)
|
||||
{
|
||||
// Return false if the format is obviously wrong.
|
||||
if (!preg_match('/^[0-9]{6}-?[0-9]{7}$/', $code))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove hyphen.
|
||||
$code = str_replace('-', '', $code);
|
||||
|
||||
// Return false if the date of birth is in the future.
|
||||
if (in_array((int)($code[6]), array(3, 4, 7, 8)) && intval(substr($code, 0, 6), 10) > date('ymd'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the checksum.
|
||||
$sum = 0;
|
||||
for ($i = 0; $i < 12; $i++)
|
||||
{
|
||||
$sum += $code[$i] * (($i % 8) + 2);
|
||||
}
|
||||
$checksum = (11 - ($sum % 11)) % 10;
|
||||
if (in_array((int)($code[6]), array(1, 2, 3, 4, 9, 0)))
|
||||
{
|
||||
return $checksum === (int)($code[12]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (substr($code, 7, 2) % 2 !== 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (($checksum + 2) % 10) === (int)($code[12]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given string is a valid corporation registration number (법인등록번호).
|
||||
*
|
||||
* This method only checks the format.
|
||||
* It does not check that the number is actually in use.
|
||||
*
|
||||
* @param string $code
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidCorporationNumber($code)
|
||||
{
|
||||
// Return false if the format is obviously wrong.
|
||||
if (!preg_match('/^[0-9]{6}-?[0-9]{7}$/', $code))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove hyphen.
|
||||
$code = str_replace('-', '', $code);
|
||||
|
||||
// Calculate the checksum.
|
||||
$sum = 0;
|
||||
for ($i = 0; $i < 12; $i++)
|
||||
{
|
||||
$sum += $code[$i] * (($i % 2) + 1);
|
||||
}
|
||||
$checksum = (10 - ($sum % 10)) % 10;
|
||||
|
||||
// Check the 7th and 13th digits.
|
||||
if ($code[6] !== '0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $checksum === (int)($code[12]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given string is a valid business registration number (사업자등록번호).
|
||||
*
|
||||
* This method only checks the format.
|
||||
* It does not check that the number is actually in use.
|
||||
*
|
||||
* @param string $code
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidBusinessNumber($code)
|
||||
{
|
||||
// Return false if the format is obviously wrong.
|
||||
if (!preg_match('/^[0-9]{3}-?[0-9]{2}-?[0-9]{5}$/', $code))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove hyphen.
|
||||
$code = str_replace('-', '', $code);
|
||||
|
||||
// Calculate the checksum.
|
||||
$sum = 0;
|
||||
$sum += $code[0] + ($code[1] * 3) + ($code[2] * 7);
|
||||
$sum += $code[3] + ($code[4] * 3) + ($code[5] * 7);
|
||||
$sum += $code[6] + ($code[7] * 3) + ($code[8] * 5);
|
||||
$sum += floor(($code[8] * 5) / 10);
|
||||
$checksum = (10 - ($sum % 10)) % 10;
|
||||
|
||||
// Check the last digit.
|
||||
return $checksum === (int)($code[9]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given IP address is Korean.
|
||||
*
|
||||
* This method may return incorrect results if the IP allocation databases
|
||||
* (korea.ipv4.php, korea.ipv6.php) are out of date.
|
||||
*
|
||||
* @param string $ip
|
||||
* @return bool
|
||||
*/
|
||||
public static function isKoreanIP($ip)
|
||||
{
|
||||
// Extract the IPv4 address from an "IPv4-mapped IPv6" address.
|
||||
if (preg_match('/::ffff:(?:0+:)?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/', $ip, $matches)) $ip = $matches[1];
|
||||
|
||||
// Return false if the IP address is not in the right format.
|
||||
if (!filter_var($ip, \FILTER_VALIDATE_IP)) return false;
|
||||
|
||||
// Check IPv4.
|
||||
if (filter_var($ip, \FILTER_VALIDATE_IP, array('flags' => \FILTER_FLAG_IPV4)))
|
||||
{
|
||||
// Convert to integer.
|
||||
$ipnum = sprintf('%u', ip2long($ip));
|
||||
|
||||
// Treat local addresses as Korean.
|
||||
if ($ipnum >= 167772160 && $ipnum <= 184549375) return true; // 10.0.0.0/8
|
||||
if ($ipnum >= 2130706432 && $ipnum <= 2147483647) return true; // 127.0.0.0/8
|
||||
if ($ipnum >= 3232235520 && $ipnum <= 3232301055) return true; // 192.168.0.0/16
|
||||
if ($ipnum >= 2886729728 && $ipnum <= 2887778303) return true; // 172.16.0.0/20
|
||||
|
||||
// Check the IPv4 allocation database.
|
||||
$ranges = (include \RX_BASEDIR . 'common/defaults/korea.ipv4.php');
|
||||
foreach ($ranges as $range)
|
||||
{
|
||||
if ($ipnum >= $range[0] && $ipnum <= $range[1]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check IPv6.
|
||||
elseif (function_exists('inet_pton'))
|
||||
{
|
||||
// Convert to hexadecimal format.
|
||||
$ipbin = strtolower(bin2hex(inet_pton($ip)));
|
||||
|
||||
// Treat local addresses as Korean.
|
||||
if ($ipbin == '00000000000000000000000000000001') return true; // ::1
|
||||
if (preg_match('/^f(?:[cd]|e80{13})/', $ipbin)) return true; // fc00::/8, fd00::/8, fe80::/64
|
||||
|
||||
// Check the IPv6 allocation database.
|
||||
$ranges = (include \RX_BASEDIR . 'common/defaults/korea.ipv6.php');
|
||||
foreach ($ranges as $range)
|
||||
{
|
||||
if (strncmp($ipbin, $range[0], 16) >= 0 && strncmp($ipbin, $range[1], 16) <= 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given email address is hosted by a Korean portal site.
|
||||
*
|
||||
* This can be used to tell which recipients may subscribe to the KISA RBL (kisarbl.or.kr).
|
||||
* If the domain is not found, this method returns false.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param bool $clear_cache (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function isKoreanEmailAddress($email_address, $clear_cache = false)
|
||||
{
|
||||
// Clear the cache if requested.
|
||||
if ($clear_cache)
|
||||
{
|
||||
self::$_domain_cache = array();
|
||||
}
|
||||
|
||||
// Get the domain from the email address.
|
||||
if ($pos = strpos($email_address, '@'))
|
||||
{
|
||||
$domain = substr($email_address, $pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
$domain = $email_address;
|
||||
}
|
||||
$domain = rtrim(strtolower($domain), '.');
|
||||
|
||||
// Return cached result if available.
|
||||
if (array_key_exists($domain, self::$_domain_cache))
|
||||
{
|
||||
return self::$_domain_cache[$domain];
|
||||
}
|
||||
|
||||
// Shortcut for known domains.
|
||||
if (in_array($domain, self::$known_korean))
|
||||
{
|
||||
return self::$_domain_cache[$domain] = true;
|
||||
}
|
||||
if (in_array($domain, self::$known_foreign))
|
||||
{
|
||||
return self::$_domain_cache[$domain] = false;
|
||||
}
|
||||
|
||||
// For unknown domains, check the MX record.
|
||||
$mx = self::_getDNSRecords($domain, \DNS_MX);
|
||||
|
||||
$i = 0;
|
||||
foreach ($mx as $mx)
|
||||
{
|
||||
$mx = rtrim($mx, '.');
|
||||
foreach (self::$known_korean as $portal)
|
||||
{
|
||||
if ($mx === $portal || ends_with('.' . $portal, $mx))
|
||||
{
|
||||
return self::$_domain_cache[$domain] = true;
|
||||
}
|
||||
}
|
||||
foreach (self::$known_foreign as $portal)
|
||||
{
|
||||
if ($mx === $portal || ends_with('.' . $portal, $mx))
|
||||
{
|
||||
return self::$_domain_cache[$domain] = false;
|
||||
}
|
||||
}
|
||||
foreach (self::_getDNSRecords($domain, \DNS_A) as $mx_ip)
|
||||
{
|
||||
return self::$_domain_cache[$domain] = self::isKoreanIP($mx_ip);
|
||||
}
|
||||
if (++$i > 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return self::$_domain_cache[$domain] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DNS records of a domain.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param int $type
|
||||
* @return array
|
||||
*/
|
||||
protected static function _getDNSRecords($domain, $type)
|
||||
{
|
||||
$records = dns_get_record($domain, $type);
|
||||
if (!$records)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($records as $record)
|
||||
{
|
||||
if (isset($record['pri']) && isset($record['target']))
|
||||
{
|
||||
$result[intval($record['pri'])] = $record['target'];
|
||||
}
|
||||
elseif (isset($record['target']))
|
||||
{
|
||||
$result[] = $record['target'];
|
||||
}
|
||||
elseif (isset($record['ip']) || isset($record['ipv6']))
|
||||
{
|
||||
$result[] = isset($record['ip']) ? $record['ip'] : $record['ipv6'];
|
||||
}
|
||||
elseif (isset($record['txt']))
|
||||
{
|
||||
$result[] = $record['txt'];
|
||||
}
|
||||
}
|
||||
|
||||
ksort($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent multiple lookups for the same domain.
|
||||
*/
|
||||
protected static $_domain_cache = array();
|
||||
|
||||
/**
|
||||
* Domains known to be Korean and subscribed to the KISA RBL.
|
||||
*/
|
||||
public static $known_korean = array(
|
||||
'hanmail.net',
|
||||
'hanmail2.net',
|
||||
'daum.net',
|
||||
'paran.com',
|
||||
'tistory.com',
|
||||
'naver.com',
|
||||
'navercorp.com',
|
||||
'nate.com',
|
||||
'cyworld.com',
|
||||
'dreamwiz.com',
|
||||
'korea.com',
|
||||
'dreamx.com',
|
||||
'chol.com',
|
||||
'chollian.net',
|
||||
'hanmir.com',
|
||||
'hitel.com',
|
||||
'freechal.com',
|
||||
'empas.com',
|
||||
'empal.com',
|
||||
'hanafos.com',
|
||||
);
|
||||
|
||||
/**
|
||||
* Domains known to be foreign.
|
||||
*/
|
||||
public static $known_foreign = array(
|
||||
'gmail.com',
|
||||
'googlemail.com',
|
||||
'google.com',
|
||||
'yahoo.com',
|
||||
'yahoo.co.kr',
|
||||
'hotmail.com',
|
||||
'hotmail.co.kr',
|
||||
'live.com',
|
||||
'outlook.com',
|
||||
'msn.com',
|
||||
'me.com',
|
||||
'mac.com',
|
||||
'icloud.com',
|
||||
'facebook.com',
|
||||
'aol.com',
|
||||
'gmx.com',
|
||||
'mail.com',
|
||||
'fastmail.com',
|
||||
'fastmail.fm',
|
||||
'runbox.com',
|
||||
'inbox.com',
|
||||
'lycos.com',
|
||||
'zoho.com',
|
||||
);
|
||||
}
|
||||
|
|
@ -75,19 +75,19 @@ class Lang
|
|||
|
||||
if ($name === 'common')
|
||||
{
|
||||
$this->loadDirectory(RX_BASEDIR . 'common/lang', 'common');
|
||||
$this->loadDirectory(\RX_BASEDIR . 'common/lang', 'common');
|
||||
}
|
||||
elseif (file_exists(RX_BASEDIR . "plugins/$name/lang"))
|
||||
elseif (file_exists(\RX_BASEDIR . "plugins/$name/lang"))
|
||||
{
|
||||
$this->loadDirectory(RX_BASEDIR . "plugins/$name/lang", $name);
|
||||
$this->loadDirectory(\RX_BASEDIR . "plugins/$name/lang", $name);
|
||||
}
|
||||
elseif (file_exists(RX_BASEDIR . "modules/$name/lang"))
|
||||
elseif (file_exists(\RX_BASEDIR . "modules/$name/lang"))
|
||||
{
|
||||
$this->loadDirectory(RX_BASEDIR . "modules/$name/lang", $name);
|
||||
$this->loadDirectory(\RX_BASEDIR . "modules/$name/lang", $name);
|
||||
}
|
||||
elseif (file_exists(RX_BASEDIR . "addons/$name/lang"))
|
||||
elseif (file_exists(\RX_BASEDIR . "addons/$name/lang"))
|
||||
{
|
||||
$this->loadDirectory(RX_BASEDIR . "addons/$name/lang", $name);
|
||||
$this->loadDirectory(\RX_BASEDIR . "addons/$name/lang", $name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,70 +108,52 @@ class Lang
|
|||
return true;
|
||||
}
|
||||
|
||||
// Load the language file.
|
||||
$lang = $this->getPluginLang($dir);
|
||||
// Initialize variables.
|
||||
$filename = null;
|
||||
$lang = new \stdClass;
|
||||
$result = true;
|
||||
|
||||
// Load the default language file.
|
||||
// Find a suitable language file in the given directory.
|
||||
if (file_exists($dir . '/' . $this->_language . '.php'))
|
||||
{
|
||||
$filename = $dir . '/' . $this->_language . '.php';
|
||||
}
|
||||
elseif (($hyphen = strpos($this->_language, '-')) !== false && file_exists($dir . '/' . substr($this->_language, 0, $hyphen) . '.php'))
|
||||
{
|
||||
$filename = $dir . '/' . substr($this->_language, 0, $hyphen) . '.php';
|
||||
}
|
||||
elseif (file_exists("$dir/lang.xml"))
|
||||
{
|
||||
$filename = Parsers\LangParser::compileXMLtoPHP("$dir/lang.xml", $this->_language === 'ja' ? 'jp' : $this->_language);
|
||||
}
|
||||
elseif (file_exists($dir . '/' . ($this->_language === 'ja' ? 'jp' : $this->_language) . '.lang.php'))
|
||||
{
|
||||
$filename = $dir . '/' . ($this->_language === 'ja' ? 'jp' : $this->_language) . '.lang.php';
|
||||
}
|
||||
|
||||
// Load the language file.
|
||||
if ($filename)
|
||||
{
|
||||
include $filename;
|
||||
array_unshift($this->_search_priority, $plugin_name);
|
||||
$result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
|
||||
// Mark this directory and plugin as loaded.
|
||||
$this->_loaded_directories[$dir] = true;
|
||||
$this->_loaded_plugins[$plugin_name] = $lang;
|
||||
|
||||
// Load the same directory in the default language, too.
|
||||
if ($this->_language !== 'en')
|
||||
{
|
||||
self::getInstance('en')->loadDirectory($dir, $plugin_name);
|
||||
}
|
||||
|
||||
if (!empty($lang))
|
||||
{
|
||||
$this->_loaded_directories[$dir] = true;
|
||||
$this->_loaded_plugins[$plugin_name] = $lang;
|
||||
array_unshift($this->_search_priority, $plugin_name);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_loaded_directories[$dir] = true;
|
||||
$this->_loaded_plugins[$plugin_name] = new \stdClass;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language file from plugin.
|
||||
*
|
||||
* @param string $dir
|
||||
* @param string $language
|
||||
* @return object
|
||||
*/
|
||||
public function getPluginLang($dir, $language = null)
|
||||
{
|
||||
if (!$language)
|
||||
{
|
||||
$language = $this->_language;
|
||||
}
|
||||
|
||||
if (file_exists($dir . '/' . $language . '.php'))
|
||||
{
|
||||
$filename = $dir . '/' . $language . '.php';
|
||||
}
|
||||
elseif (($hyphen = strpos($language, '-')) !== false && file_exists($dir . '/' . substr($language, 0, $hyphen) . '.php'))
|
||||
{
|
||||
$filename = $dir . '/' . substr($language, 0, $hyphen) . '.php';
|
||||
}
|
||||
elseif (file_exists("$dir/lang.xml"))
|
||||
{
|
||||
$filename = Compat\LangParser::compileXMLtoPHP("$dir/lang.xml", $language === 'ja' ? 'jp' : $language);
|
||||
}
|
||||
elseif (file_exists($dir . '/' . ($language === 'ja' ? 'jp' : $language) . '.lang.php'))
|
||||
{
|
||||
$filename = $dir . '/' . ($language === 'ja' ? 'jp' : $language) . '.lang.php';
|
||||
}
|
||||
|
||||
if (!$filename)
|
||||
{
|
||||
return new \stdClass;
|
||||
}
|
||||
|
||||
$lang = new \stdClass;
|
||||
include $filename;
|
||||
|
||||
return $lang;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -181,7 +163,7 @@ class Lang
|
|||
*/
|
||||
public static function getSupportedList()
|
||||
{
|
||||
return (include RX_BASEDIR . 'common/defaults/lang.php');
|
||||
return (include \RX_BASEDIR . 'common/defaults/lang.php');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -194,7 +176,19 @@ class Lang
|
|||
{
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
return $this->__call($key, $args);
|
||||
if (count($args) === 1 && is_array($args[0]))
|
||||
{
|
||||
$args = $args[0];
|
||||
}
|
||||
|
||||
// Get the translation.
|
||||
$translation = $this->__get($key);
|
||||
|
||||
// If there are no arguments, return the translation.
|
||||
if (!count($args)) return $translation;
|
||||
|
||||
// If there are arguments, interpolate them into the translation and return the result.
|
||||
return vsprintf($translation, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -209,6 +203,24 @@ class Lang
|
|||
$this->__set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback method for getting the default translation.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getFromDefaultLang($key)
|
||||
{
|
||||
if ($this->_language === 'en')
|
||||
{
|
||||
return $key;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::getInstance('en')->__get($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method for translations without arguments.
|
||||
*
|
||||
|
|
@ -217,84 +229,59 @@ class Lang
|
|||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
// Get default language
|
||||
if ($this->_language !== 'en')
|
||||
// Load a dot-separated key (prefixed by plugin name).
|
||||
if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key)) && count($keys) >= 2)
|
||||
{
|
||||
$lang_en = self::getInstance('en')->{$key};
|
||||
}
|
||||
|
||||
// Separate the plugin name from the key.
|
||||
if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key, 2)) && count($keys) === 2)
|
||||
{
|
||||
list($plugin_name, $lang_key) = $keys;
|
||||
// Attempt to load the plugin.
|
||||
$plugin_name = array_shift($keys);
|
||||
if (!isset($this->_loaded_plugins[$plugin_name]))
|
||||
{
|
||||
$this->loadPlugin($plugin_name);
|
||||
}
|
||||
|
||||
if (isset($this->_loaded_plugins[$plugin_name]->{$lang_key}))
|
||||
if (!isset($this->_loaded_plugins[$plugin_name]))
|
||||
{
|
||||
$lang = $this->_loaded_plugins[$plugin_name]->{$lang_key};
|
||||
if (is_array($lang) && is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE))
|
||||
{
|
||||
return $lang_en;
|
||||
}
|
||||
|
||||
return $lang;
|
||||
return $this->getFromDefaultLang($key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Search custom translations first.
|
||||
if (isset($this->_loaded_plugins['_custom_']->{$key}))
|
||||
|
||||
// Find the given key.
|
||||
$lang = $this->_loaded_plugins[$plugin_name];
|
||||
foreach ($keys as $subkey)
|
||||
{
|
||||
$lang = $this->_loaded_plugins['_custom_']->{$key};
|
||||
if (is_array($lang))
|
||||
if (is_object($lang) && isset($lang->{$subkey}))
|
||||
{
|
||||
if (is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE))
|
||||
{
|
||||
return new \ArrayObject($lang_en, 3);
|
||||
}
|
||||
|
||||
return new \ArrayObject($lang, 3);
|
||||
$lang = $lang->{$subkey};
|
||||
}
|
||||
elseif (is_array($lang) && isset($lang[$subkey]))
|
||||
{
|
||||
$lang = $lang[$subkey];
|
||||
}
|
||||
else
|
||||
{
|
||||
return $lang;
|
||||
}
|
||||
}
|
||||
|
||||
// Search other plugins.
|
||||
foreach ($this->_search_priority as $plugin_name)
|
||||
{
|
||||
if (isset($this->_loaded_plugins[$plugin_name]->{$key}))
|
||||
{
|
||||
$lang = $this->_loaded_plugins[$plugin_name]->{$key};
|
||||
if (is_array($lang))
|
||||
{
|
||||
if (is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE))
|
||||
{
|
||||
return new \ArrayObject($lang_en, 3);
|
||||
}
|
||||
|
||||
return new \ArrayObject($lang, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $lang;
|
||||
}
|
||||
return $this->getFromDefaultLang($key);
|
||||
}
|
||||
}
|
||||
return is_array($lang) ? new \ArrayObject($lang, 3) : $lang;
|
||||
}
|
||||
|
||||
// Search other language.
|
||||
if (isset($lang_en))
|
||||
// Search custom translations first.
|
||||
if (isset($this->_loaded_plugins['_custom_']->{$key}))
|
||||
{
|
||||
return $lang_en;
|
||||
$lang = $this->_loaded_plugins['_custom_']->{$key};
|
||||
return is_array($lang) ? new \ArrayObject($lang, 3) : $lang;
|
||||
}
|
||||
|
||||
// If no translation is found, return the key.
|
||||
return $key;
|
||||
// Search other plugins.
|
||||
foreach ($this->_search_priority as $plugin_name)
|
||||
{
|
||||
if (isset($this->_loaded_plugins[$plugin_name]->{$key}))
|
||||
{
|
||||
$lang = $this->_loaded_plugins[$plugin_name]->{$key};
|
||||
return is_array($lang) ? new \ArrayObject($lang, 3) : $lang;
|
||||
}
|
||||
}
|
||||
|
||||
// If no translation is found, return the default language.
|
||||
return $this->getFromDefaultLang($key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -306,6 +293,73 @@ class Lang
|
|||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
// Set a dot-separated key (prefixed by plugin name).
|
||||
if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key)) && count($keys) >= 2)
|
||||
{
|
||||
// Attempt to load the plugin.
|
||||
$plugin_name = array_shift($keys);
|
||||
if (!isset($this->_loaded_plugins[$plugin_name]))
|
||||
{
|
||||
$this->loadPlugin($plugin_name);
|
||||
}
|
||||
if (!isset($this->_loaded_plugins[$plugin_name]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the given key.
|
||||
$count = count($keys);
|
||||
$lang = $this->_loaded_plugins[$plugin_name];
|
||||
foreach ($keys as $i => $subkey)
|
||||
{
|
||||
if (is_object($lang) && isset($lang->{$subkey}))
|
||||
{
|
||||
if ($i === $count - 1)
|
||||
{
|
||||
$lang->{$subkey} = $value;
|
||||
break;
|
||||
}
|
||||
elseif (is_array($lang->{$subkey}))
|
||||
{
|
||||
$lang = &$lang->{$subkey};
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif (is_array($lang) && isset($lang[$subkey]))
|
||||
{
|
||||
if ($i === $count - 1)
|
||||
{
|
||||
$lang[$subkey] = $value;
|
||||
break;
|
||||
}
|
||||
elseif (is_array($lang[$subkey]))
|
||||
{
|
||||
$lang = &$lang[$subkey];
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_object($lang))
|
||||
{
|
||||
$lang->{$subkey} = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$lang[$subkey] = $value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set a regular key.
|
||||
$this->_loaded_plugins['_custom_']->{$key} = $value;
|
||||
}
|
||||
|
||||
|
|
@ -335,13 +389,7 @@ class Lang
|
|||
*/
|
||||
public function __unset($key)
|
||||
{
|
||||
foreach ($this->_loaded_plugins as $plugin_name => $translations)
|
||||
{
|
||||
if (isset($translations->{$key}))
|
||||
{
|
||||
unset($translations->{$key});
|
||||
}
|
||||
}
|
||||
$this->set($key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -353,16 +401,6 @@ class Lang
|
|||
*/
|
||||
public function __call($key, $args = array())
|
||||
{
|
||||
// Remove a colon from the beginning of the string.
|
||||
if ($key !== '' && $key[0] === ':') $key = substr($key, 1);
|
||||
|
||||
// Find the translation.
|
||||
$translation = $this->__get($key);
|
||||
|
||||
// If there are no arguments, return the translation.
|
||||
if (!count($args)) return $translation;
|
||||
|
||||
// If there are arguments, interpolate them into the translation and return the result.
|
||||
return vsprintf($translation, $args);
|
||||
return $this->get($key, $args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
154
common/framework/mime.php
Normal file
154
common/framework/mime.php
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The MIME class.
|
||||
*/
|
||||
class MIME
|
||||
{
|
||||
/**
|
||||
* Get the MIME type for the given extension.
|
||||
*
|
||||
* @param string $extension
|
||||
* @return string
|
||||
*/
|
||||
public static function getTypeByExtension($extension)
|
||||
{
|
||||
$extension = strtolower($extension);
|
||||
return array_key_exists($extension, self::$_types) ? self::$_types[$extension] : self::$_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MIME type for the given filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public static function getTypeByFilename($filename)
|
||||
{
|
||||
$extension = strrchr($filename, '.');
|
||||
if ($extension === false) return self::$_default;
|
||||
$extension = strtolower(substr($extension, 1));
|
||||
return array_key_exists($extension, self::$_types) ? self::$_types[$extension] : self::$_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most common extension for the given MIME type.
|
||||
*
|
||||
* @param string $type
|
||||
* @return string|false
|
||||
*/
|
||||
public static function getExtensionByType($type)
|
||||
{
|
||||
foreach (self::$_types as $extension => $mime)
|
||||
{
|
||||
if (!strncasecmp($type, $mime, strlen($type))) return $extension;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default MIME type for unknown extensions.
|
||||
*/
|
||||
protected static $_default = 'application/octet-stream';
|
||||
|
||||
/**
|
||||
* The list of known MIME types.
|
||||
*/
|
||||
protected static $_types = array(
|
||||
|
||||
// Text-based document formats.
|
||||
'html' => 'text/html',
|
||||
'htm' => 'text/html',
|
||||
'shtml' => 'text/html',
|
||||
'txt' => 'text/plain',
|
||||
'text' => 'text/plain',
|
||||
'log' => 'text/plain',
|
||||
'md' => 'text/markdown',
|
||||
'markdown' => 'text/markdown',
|
||||
'rtf' => 'text/rtf',
|
||||
'xml' => 'text/xml',
|
||||
'xsl' => 'text/xml',
|
||||
'css' => 'text/css',
|
||||
'csv' => 'text/csv',
|
||||
|
||||
// Binary document formats.
|
||||
'doc' => 'application/msword',
|
||||
'dot' => 'application/msword',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'odg' => 'application/vnd.oasis.opendocument.graphics',
|
||||
'odb' => 'application/vnd.oasis.opendocument.database',
|
||||
'pdf' => 'application/pdf',
|
||||
|
||||
// Images.
|
||||
'bmp' => 'image/bmp',
|
||||
'gif' => 'image/gif',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'jpe' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'svg' => 'image/svg+xml',
|
||||
'tiff' => 'image/tiff',
|
||||
'tif' => 'image/tiff',
|
||||
'ico' => 'image/vnd.microsoft.icon',
|
||||
|
||||
// Audio.
|
||||
'mid' => 'audio/midi',
|
||||
'midi' => 'audio/midi',
|
||||
'mpga' => 'audio/mpeg',
|
||||
'mp2' => 'audio/mpeg',
|
||||
'mp3' => 'audio/mpeg',
|
||||
'aif' => 'audio/x-aiff',
|
||||
'aiff' => 'audio/x-aiff',
|
||||
'ra' => 'audio/x-realaudio',
|
||||
'wav' => 'audio/x-wav',
|
||||
'ogg' => 'audio/ogg',
|
||||
|
||||
// Video.
|
||||
'avi' => 'video/x-msvideo',
|
||||
'flv' => 'video/x-flv',
|
||||
'mpeg' => 'video/mpeg',
|
||||
'mpg' => 'video/mpeg',
|
||||
'mpe' => 'video/mpeg',
|
||||
'mp4' => 'video/mpeg',
|
||||
'qt' => 'video/quicktime',
|
||||
'mov' => 'video/quicktime',
|
||||
'movie' => 'video/x-sgi-movie',
|
||||
'rv' => 'video/vnd.rn-realvideo',
|
||||
'dvi' => 'application/x-dvi',
|
||||
|
||||
// Other multimedia file formats.
|
||||
'psd' => 'application/x-photoshop',
|
||||
'swf' => 'application/x-shockwave-flash',
|
||||
'ai' => 'application/postscript',
|
||||
'eps' => 'application/postscript',
|
||||
'ps' => 'application/postscript',
|
||||
'mif' => 'application/vnd.mif',
|
||||
'xul' => 'application/vnd.mozilla.xul+xml',
|
||||
|
||||
// Source code formats.
|
||||
'phps' => 'application/x-httpd-php-source',
|
||||
'js' => 'application/x-javascript',
|
||||
|
||||
// Archives.
|
||||
'bz2' => 'application/x-bzip',
|
||||
'gz' => 'application/x-gzip',
|
||||
'tar' => 'application/x-tar',
|
||||
'tgz' => 'application/x-tar',
|
||||
'gtar' => 'application/x-gtar',
|
||||
'rar' => 'application/x-rar-compressed',
|
||||
'zip' => 'application/x-zip',
|
||||
|
||||
// RFC822 email message.
|
||||
'eml' => 'message/rfc822',
|
||||
);
|
||||
}
|
||||
153
common/framework/pagination.php
Normal file
153
common/framework/pagination.php
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The pagination class.
|
||||
*/
|
||||
class Pagination
|
||||
{
|
||||
/**
|
||||
* Count style constants.
|
||||
*/
|
||||
const COUNT_STYLE_NORMAL = 1;
|
||||
const COUNT_STYLE_CONTINUOUS = 2;
|
||||
|
||||
/**
|
||||
* Calculate the number of pages.
|
||||
*
|
||||
* @param int $total_items
|
||||
* @param int $items_per_page
|
||||
* @param int $minimum (optional)
|
||||
* @return int
|
||||
*/
|
||||
public static function countPages($total_items, $items_per_page, $minimum = 1)
|
||||
{
|
||||
if (!$items_per_page)
|
||||
{
|
||||
return (int)$minimum;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)max($minimum, ceil($total_items / $items_per_page));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HTML for pagination.
|
||||
*
|
||||
* @param string $base_url ($PAGE will be replaced with the page number)
|
||||
* @param int $current_page
|
||||
* @param int $total_pages
|
||||
* @param int $count (optional)
|
||||
*/
|
||||
public static function createLinks($base_url, $total_pages, $current_page, $count = 10, $count_style = self::COUNT_STYLE_NORMAL)
|
||||
{
|
||||
// Only integers are allowed here.
|
||||
$current_page = (int)$current_page;
|
||||
$total_pages = (int)$total_pages;
|
||||
$count = (int)$count;
|
||||
|
||||
// Determine the range of pages to show.
|
||||
if ($count_style === self::COUNT_STYLE_NORMAL)
|
||||
{
|
||||
$last_shown = ceil($current_page / $count) * $count;
|
||||
$first_shown = max(1, $last_shown - $count + 1);
|
||||
if ($last_shown > $total_pages)
|
||||
{
|
||||
$last_shown = $total_pages;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$first_shown = $current_page - floor(($count - 1) / 2);
|
||||
if ($first_shown < 1)
|
||||
{
|
||||
$first_shown = 1;
|
||||
}
|
||||
$last_shown = $first_shown + $count - 1;
|
||||
if ($last_shown > $total_pages)
|
||||
{
|
||||
$last_shown = $total_pages;
|
||||
$first_shown = max(1, $last_shown - $count + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Open the <div> tag.
|
||||
$return = array('<div class="pagination">');
|
||||
|
||||
// Compose the link to the first page.
|
||||
if ($first_shown > 1)
|
||||
{
|
||||
if (strpos($base_url, '$PAGE') !== false)
|
||||
{
|
||||
$target_url = str_replace('$PAGE', 1, $base_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
$target_url = $base_url . 1;
|
||||
}
|
||||
|
||||
$return[] = self::_composeLink($target_url, '<span class="arrow">«</span> <span class="page_number first_page">1</span>');
|
||||
$return[] = '<span class="ellipsis">...</span>';
|
||||
}
|
||||
|
||||
// Compose links for each page.
|
||||
for ($page = $first_shown; $page <= $last_shown; $page++)
|
||||
{
|
||||
if ($page == $current_page)
|
||||
{
|
||||
$opening_span = '<span class="page_number current_page">';
|
||||
}
|
||||
else
|
||||
{
|
||||
$opening_span = '<span class="page_number">';
|
||||
}
|
||||
|
||||
if (strpos($base_url, '$PAGE') !== false)
|
||||
{
|
||||
$target_url = str_replace('$PAGE', $page, $base_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
$target_url = $base_url . $page;
|
||||
}
|
||||
|
||||
$return[] = self::_composeLink($target_url, $opening_span . $page . '</span>');
|
||||
}
|
||||
|
||||
// Compose the link to the last page.
|
||||
if ($last_shown < $total_pages)
|
||||
{
|
||||
if (strpos($base_url, '$PAGE') !== false)
|
||||
{
|
||||
$target_url = str_replace('$PAGE', $total_pages, $base_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
$target_url = $base_url . $total_pages;
|
||||
}
|
||||
|
||||
$return[] = '<span class="ellipsis">...</span>';
|
||||
$return[] = self::_composeLink($target_url, '<span class="page_number last_page">' . $total_pages . '</span> <span class="arrow">»</span>');
|
||||
}
|
||||
|
||||
// Close the <div> tag.
|
||||
$return[] = '</div>';
|
||||
|
||||
// Return the completed HTML.
|
||||
return implode(' ', $return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link creation subroutine.
|
||||
*
|
||||
* @param string $target_url
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
protected static function _composeLink($target_url, $content)
|
||||
{
|
||||
return '<a href="' . escape($target_url) . '">' . $content . '</a>';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Compat;
|
||||
namespace Rhymix\Framework\Parsers;
|
||||
|
||||
use Rhymix\Framework\Config;
|
||||
use Rhymix\Framework\DateTime;
|
||||
use Rhymix\Framework\Security;
|
||||
|
||||
/**
|
||||
* Config parser class for XE compatibility.
|
||||
|
|
@ -18,9 +19,9 @@ class ConfigParser
|
|||
public static function convert()
|
||||
{
|
||||
// Load DB info file.
|
||||
if (file_exists(RX_BASEDIR . Config::$old_db_config_filename))
|
||||
if (file_exists(\RX_BASEDIR . Config::$old_db_config_filename))
|
||||
{
|
||||
include RX_BASEDIR . Config::$old_db_config_filename;
|
||||
include \RX_BASEDIR . Config::$old_db_config_filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -28,16 +29,16 @@ class ConfigParser
|
|||
}
|
||||
|
||||
// Load FTP info file.
|
||||
if (file_exists(RX_BASEDIR . Config::$old_ftp_config_filename))
|
||||
if (file_exists(\RX_BASEDIR . Config::$old_ftp_config_filename))
|
||||
{
|
||||
include RX_BASEDIR . Config::$old_ftp_config_filename;
|
||||
include \RX_BASEDIR . Config::$old_ftp_config_filename;
|
||||
}
|
||||
|
||||
// Load selected language file.
|
||||
if (file_exists(RX_BASEDIR . Config::$old_lang_config_filename))
|
||||
if (file_exists(\RX_BASEDIR . Config::$old_lang_config_filename))
|
||||
{
|
||||
$lang_selected = array();
|
||||
$lang_selected_raw = file_get_contents(RX_BASEDIR . Config::$old_lang_config_filename);
|
||||
$lang_selected_raw = file_get_contents(\RX_BASEDIR . Config::$old_lang_config_filename);
|
||||
$lang_selected_raw = array_map('trim', explode("\n", $lang_selected_raw));
|
||||
foreach ($lang_selected_raw as $lang_selected_item)
|
||||
{
|
||||
|
|
@ -58,7 +59,7 @@ class ConfigParser
|
|||
}
|
||||
|
||||
// Load defaults for the new configuration.
|
||||
$config = (include RX_BASEDIR . Config::$default_config_filename);
|
||||
$config = (include \RX_BASEDIR . Config::$default_config_filename);
|
||||
|
||||
// Convert database configuration.
|
||||
if (!isset($db_info->master_db))
|
||||
|
|
@ -151,9 +152,9 @@ class ConfigParser
|
|||
}
|
||||
|
||||
// Create new crypto keys.
|
||||
$config['crypto']['encryption_key'] = \Password::createSecureSalt(64, 'alnum');
|
||||
$config['crypto']['authentication_key'] = \Password::createSecureSalt(64, 'alnum');
|
||||
$config['crypto']['session_key'] = \Password::createSecureSalt(64, 'alnum');
|
||||
$config['crypto']['encryption_key'] = Security::getRandom(64, 'alnum');
|
||||
$config['crypto']['authentication_key'] = Security::getRandom(64, 'alnum');
|
||||
$config['crypto']['session_key'] = Security::getRandom(64, 'alnum');
|
||||
|
||||
// Convert language configuration.
|
||||
if (isset($db_info->lang_type))
|
||||
|
|
@ -183,7 +184,7 @@ class ConfigParser
|
|||
{
|
||||
$default_url = \Context::decodeIdna($default_url);
|
||||
}
|
||||
$config['url']['default'] = $default_url ?: (RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL;
|
||||
$config['url']['default'] = $default_url ?: (\RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL;
|
||||
$config['url']['http_port'] = $db_info->http_port ?: null;
|
||||
$config['url']['https_port'] = $db_info->https_port ?: null;
|
||||
$config['url']['ssl'] = $db_info->use_ssl ?: 'none';
|
||||
|
|
@ -216,14 +217,14 @@ class ConfigParser
|
|||
}
|
||||
$config['lock']['allow'] = array_values($db_info->sitelock_whitelist);
|
||||
|
||||
// Convert embed filter configuration.
|
||||
// Convert media filter configuration.
|
||||
if (is_array($db_info->embed_white_iframe))
|
||||
{
|
||||
$whitelist = array_unique(array_map(function($item) {
|
||||
return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item;
|
||||
}, $db_info->embed_white_iframe));
|
||||
natcasesort($whitelist);
|
||||
$config['embedfilter']['iframe'] = $whitelist;
|
||||
$config['mediafilter']['iframe'] = $whitelist;
|
||||
}
|
||||
if (is_array($db_info->embed_white_object))
|
||||
{
|
||||
|
|
@ -231,7 +232,7 @@ class ConfigParser
|
|||
return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item;
|
||||
}, $db_info->embed_white_object));
|
||||
natcasesort($whitelist);
|
||||
$config['embedfilter']['object'] = $whitelist;
|
||||
$config['mediafilter']['object'] = $whitelist;
|
||||
}
|
||||
|
||||
// Convert miscellaneous configuration.
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework\Compat;
|
||||
namespace Rhymix\Framework\Parsers;
|
||||
|
||||
use Rhymix\Framework\Lang;
|
||||
use Rhymix\Framework\Storage;
|
||||
|
||||
/**
|
||||
* Lang parser class for XE compatibility.
|
||||
|
|
@ -32,7 +33,7 @@ class LangParser
|
|||
foreach ($files as $filename)
|
||||
{
|
||||
$new_filename = preg_replace('/\.lang\.php$/', '.php', str_replace('jp.lang', 'ja.lang', $filename));
|
||||
\FileHandler::rename($filename, $new_filename);
|
||||
Storage::move($filename, $new_filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +50,7 @@ class LangParser
|
|||
// Check if the cache file already exists.
|
||||
if ($output_filename === null)
|
||||
{
|
||||
$output_filename = RX_BASEDIR . 'files/cache/lang/' . md5($filename) . '.' . $language . '.php';
|
||||
$output_filename = \RX_BASEDIR . 'files/cache/lang/' . md5($filename) . '.' . $language . '.php';
|
||||
if (file_exists($output_filename) && filemtime($output_filename) > filemtime($filename))
|
||||
{
|
||||
return $output_filename;
|
||||
|
|
@ -60,7 +61,7 @@ class LangParser
|
|||
$xml = @simplexml_load_file($filename);
|
||||
if ($xml === false)
|
||||
{
|
||||
\FileHandler::writeFile($output_filename, '');
|
||||
Storage::write($output_filename, '');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ class LangParser
|
|||
$buff .= '$lang->' . $key . ' = ' . var_export($value, true) . ";\n";
|
||||
}
|
||||
}
|
||||
\FileHandler::writeFile($output_filename, $buff);
|
||||
Storage::write($output_filename, $buff);
|
||||
return $output_filename;
|
||||
}
|
||||
|
||||
480
common/framework/password.php
Normal file
480
common/framework/password.php
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The password class.
|
||||
*/
|
||||
class Password
|
||||
{
|
||||
/**
|
||||
* Regular expressions to detect various hashing algorithms.
|
||||
*/
|
||||
protected static $_algorithm_callbacks = array();
|
||||
protected static $_algorithm_signatures = array(
|
||||
'bcrypt' => '/^\$2[a-z]\$[0-9]{2}\$/',
|
||||
'pbkdf2' => '/^[a-z0-9]+:[0-9]+:/',
|
||||
'md5' => '/^[0-9a-f]{32}$/',
|
||||
'md5,sha1,md5' => '/^[0-9a-f]{32}$/',
|
||||
'sha1' => '/^[0-9a-f]{40}$/',
|
||||
'sha256' => '/^[0-9a-f]{64}$/',
|
||||
'sha384' => '/^[0-9a-f]{96}$/',
|
||||
'sha512' => '/^[0-9a-f]{128}$/',
|
||||
'ripemd160' => '/^[0-9a-f]{40}$/',
|
||||
'whirlpool' => '/^[0-9a-f]{128}$/',
|
||||
'mssql_pwdencrypt' => '/^0x0100[0-9A-F]{48}$/',
|
||||
'mysql_old_password' => '/^[0-9a-f]{16}$/',
|
||||
'mysql_new_password' => '/^\*[0-9A-F]{40}$/',
|
||||
'portable' => '/^\$P\$/',
|
||||
'drupal' => '/^\$S\$/',
|
||||
'joomla' => '/^[0-9a-f]{32}:[0-9a-zA-Z\.\+\/\=]{32}$/',
|
||||
'kimsqrb' => '/\$[1-4]\$[0-9]{14}$/',
|
||||
'crypt' => '/^([0-9a-zA-Z\.\/]{13}$|_[0-9a-zA-Z\.\/]{19}$|\$[156]\$)/',
|
||||
);
|
||||
|
||||
/**
|
||||
* Add a custom algorithm.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $signature
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function addAlgorithm($name, $signature, $callback)
|
||||
{
|
||||
self::$_algorithm_signatures[$name] = $signature;
|
||||
self::$_algorithm_callbacks[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given sequence of algorithms is valid.
|
||||
*
|
||||
* @param array|string $algos
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidAlgorithm($algos)
|
||||
{
|
||||
$hash_algos = hash_algos();
|
||||
$algos = is_array($algos) ? $algos : explode(',', $algos);
|
||||
foreach ($algos as $algo)
|
||||
{
|
||||
if (array_key_exists($algo, self::$_algorithm_signatures))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (in_array($algo, $hash_algos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of hashing algorithms supported by this server.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSupportedAlgorithms()
|
||||
{
|
||||
$retval = array();
|
||||
if (defined('\CRYPT_BLOWFISH'))
|
||||
{
|
||||
$retval['bcrypt'] = 'bcrypt';
|
||||
}
|
||||
if (in_array('sha512', hash_algos()))
|
||||
{
|
||||
$retval['pbkdf2'] = 'pbkdf2';
|
||||
}
|
||||
$retval['sha512'] = 'sha512';
|
||||
$retval['sha256'] = 'sha256';
|
||||
$retval['sha1'] = 'sha1';
|
||||
$retval['md5'] = 'md5';
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the best hashing algorithm supported by this server.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getBestSupportedAlgorithm()
|
||||
{
|
||||
$algos = self::getSupportedAlgorithms();
|
||||
return key($algos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current default hashing algorithm.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDefaultAlgorithm()
|
||||
{
|
||||
if (function_exists('getModel'))
|
||||
{
|
||||
$config = getModel('member')->getMemberConfig();
|
||||
$algorithm = $config->password_hashing_algorithm;
|
||||
if (strval($algorithm) === '')
|
||||
{
|
||||
$algorithm = 'md5';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$algorithm = 'md5';
|
||||
}
|
||||
return $algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently configured work factor for bcrypt and other adjustable algorithms.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getWorkFactor()
|
||||
{
|
||||
if (function_exists('getModel'))
|
||||
{
|
||||
$config = getModel('member')->getMemberConfig();
|
||||
$work_factor = $config->password_hashing_work_factor;
|
||||
if (!$work_factor || $work_factor < 4 || $work_factor > 31)
|
||||
{
|
||||
$work_factor = 9;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$work_factor = 9;
|
||||
}
|
||||
|
||||
return $work_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a reasonably strong random password.
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
public static function getRandomPassword($length = 16)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
$source = base64_encode(Security::getRandom(64, 'binary'));
|
||||
$source = strtr($source, 'iIoOjl10/', '@#$%&*-!?');
|
||||
$source_length = strlen($source);
|
||||
for($i = 0; $i < $source_length - $length; $i++)
|
||||
{
|
||||
$candidate = substr($source, $i, $length);
|
||||
if(preg_match('/[a-z]/', $candidate) && preg_match('/[A-Z]/', $candidate) &&
|
||||
preg_match('/[0-9]/', $candidate) && preg_match('/[^a-zA-Z0-9]/', $candidate))
|
||||
{
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password.
|
||||
*
|
||||
* To use multiple algorithms in series, provide them as an array.
|
||||
* Salted algorithms such as bcrypt, pbkdf2, or portable must be used last.
|
||||
* On error, false will be returned.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string|array $algos (optional)
|
||||
* @param string $salt (optional)
|
||||
* @return string|false
|
||||
*/
|
||||
public static function hashPassword($password, $algos = null, $salt = null)
|
||||
{
|
||||
// If the algorithm is null, use the default algorithm.
|
||||
if ($algos === null)
|
||||
{
|
||||
$algos = self::getDefaultAlgorithm();
|
||||
}
|
||||
|
||||
// Initialize the chain of hashes.
|
||||
$algos = array_map('strtolower', array_map('trim', is_array($algos) ? $algos : explode(',', $algos)));
|
||||
$hashchain = preg_replace('/\\s+/', ' ', trim($password));
|
||||
|
||||
// Apply the given algorithms one by one.
|
||||
foreach ($algos as $algo)
|
||||
{
|
||||
switch ($algo)
|
||||
{
|
||||
// bcrypt (must be used last)
|
||||
case 'bcrypt':
|
||||
$hashchain = self::bcrypt($hashchain, $salt, self::getWorkFactor());
|
||||
if ($hashchain[0] === '*') return false;
|
||||
return $hashchain;
|
||||
|
||||
// PBKDF2 (must be used last)
|
||||
case 'pbkdf2':
|
||||
if ($salt === null)
|
||||
{
|
||||
$salt = Security::getRandom(12, 'alnum');
|
||||
$hash_algorithm = 'sha512';
|
||||
$iterations = intval(pow(2, self::getWorkFactor() + 5)) ?: 16384;
|
||||
$key_length = 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
$parts = explode(':', $salt);
|
||||
$salt = $parts[2];
|
||||
$hash_algorithm = $parts[0];
|
||||
$iterations = intval($parts[1], 10);
|
||||
$key_length = strlen(base64_decode($parts[3]));
|
||||
}
|
||||
return self::pbkdf2($hashchain, $salt, $hash_algorithm, $iterations, $key_length);
|
||||
|
||||
// phpass portable algorithm (must be used last)
|
||||
case 'portable':
|
||||
$phpass = new \Hautelook\Phpass\PasswordHash(self::getWorkFactor(), true);
|
||||
if ($salt === null)
|
||||
{
|
||||
$hashchain = $phpass->HashPassword($hashchain);
|
||||
return $hashchain;
|
||||
}
|
||||
else
|
||||
{
|
||||
$match = $phpass->CheckPassword($hashchain, $salt);
|
||||
return $match ? $salt : false;
|
||||
}
|
||||
|
||||
// Drupal's SHA-512 based algorithm (must be used last)
|
||||
case 'drupal':
|
||||
$hashchain = \VendorPass::drupal($password, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// Joomla's MD5 based algorithm (must be used last)
|
||||
case 'joomla':
|
||||
$hashchain = \VendorPass::joomla($password, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// KimsQ Rb algorithms (must be used last)
|
||||
case 'kimsqrb':
|
||||
$hashchain = \VendorPass::kimsqrb($password, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// crypt() function (must be used last)
|
||||
case 'crypt':
|
||||
if ($salt === null) $salt = Security::getRandom(2, 'alnum');
|
||||
$hashchain = crypt($hashchain, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// MS SQL's PWDENCRYPT() function (must be used last)
|
||||
case 'mssql_pwdencrypt':
|
||||
$hashchain = \VendorPass::mssql_pwdencrypt($hashchain, $salt);
|
||||
return $hashchain;
|
||||
|
||||
// MySQL's old PASSWORD() function.
|
||||
case 'mysql_old_password':
|
||||
$hashchain = \VendorPass::mysql_old_password($hashchain);
|
||||
break;
|
||||
|
||||
// MySQL's new PASSWORD() function.
|
||||
case 'mysql_new_password':
|
||||
$hashchain = \VendorPass::mysql_new_password($hashchain);
|
||||
break;
|
||||
|
||||
// A dummy algorithm that does nothing.
|
||||
case 'null':
|
||||
break;
|
||||
|
||||
// All other algorithms will be passed to hash() or treated as a function name.
|
||||
default:
|
||||
if (isset(self::$_algorithm_callbacks[$algo]))
|
||||
{
|
||||
$callback = self::$_algorithm_callbacks[$algo];
|
||||
$hashchain = $callback($hashchain, $salt);
|
||||
}
|
||||
elseif (in_array($algo, hash_algos()))
|
||||
{
|
||||
$hashchain = hash($algo, $hashchain);
|
||||
}
|
||||
elseif (function_exists($algo))
|
||||
{
|
||||
$hashchain = $algo($hashchain, $salt);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $hashchain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a password against a hash.
|
||||
*
|
||||
* This method returns true if the password is correct, and false otherwise.
|
||||
* If the algorithm is not specified, it will be guessed from the format of the hash.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $hash
|
||||
* @param array|string $algos
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkPassword($password, $hash, $algos = null)
|
||||
{
|
||||
if ($algos === null)
|
||||
{
|
||||
$algos = self::checkAlgorithm($hash);
|
||||
foreach ($algos as $algo)
|
||||
{
|
||||
if (Security::compareStrings($hash, self::hashPassword($password, $algo, $hash)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Security::compareStrings($hash, self::hashPassword($password, $algos, $hash));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess which algorithm(s) were used to generate the given hash.
|
||||
*
|
||||
* If there are multiple possibilities, all of them will be returned in an array.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return array
|
||||
*/
|
||||
public static function checkAlgorithm($hash)
|
||||
{
|
||||
$candidates = array();
|
||||
foreach (self::$_algorithm_signatures as $name => $signature)
|
||||
{
|
||||
if (preg_match($signature, $hash)) $candidates[] = $name;
|
||||
}
|
||||
return $candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the work factor of a hash.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return int
|
||||
*/
|
||||
public static function checkWorkFactor($hash)
|
||||
{
|
||||
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
|
||||
{
|
||||
return intval($matches[1], 10);
|
||||
}
|
||||
elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches))
|
||||
{
|
||||
return max(0, round(log($matches[1], 2)) - 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the bcrypt hash of a string.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $salt (optional)
|
||||
* @param int $work_factor (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function bcrypt($password, $salt = null, $work_factor = 10)
|
||||
{
|
||||
if ($salt === null)
|
||||
{
|
||||
$salt = '$2y$' . sprintf('%02d', $work_factor) . '$' . Security::getRandom(22, 'alnum');
|
||||
}
|
||||
|
||||
return crypt($password, $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the PBKDF2 hash of a string.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $salt (optional)
|
||||
* @param string $algorithm (optional)
|
||||
* @param int $iterations (optional)
|
||||
* @param int $length (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function pbkdf2($password, $salt = null, $algorithm = 'sha512', $iterations = 16384, $length = 24)
|
||||
{
|
||||
if ($salt === null)
|
||||
{
|
||||
$salt = Security::getRandom(12, 'alnum');
|
||||
}
|
||||
|
||||
if (function_exists('hash_pbkdf2'))
|
||||
{
|
||||
$hash = hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
$output = '';
|
||||
$block_count = ceil($length / strlen(hash($algorithm, '', true))); // key length divided by the length of one hash
|
||||
for ($i = 1; $i <= $block_count; $i++)
|
||||
{
|
||||
$last = $salt . pack('N', $i); // $i encoded as 4 bytes, big endian
|
||||
$last = $xorsum = hash_hmac($algorithm, $last, $password, true); // first iteration
|
||||
for ($j = 1; $j < $iterations; $j++) // The other $count - 1 iterations
|
||||
{
|
||||
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
|
||||
}
|
||||
$output .= $xorsum;
|
||||
}
|
||||
$hash = substr($output, 0, $length);
|
||||
}
|
||||
|
||||
return $algorithm . ':' . sprintf('%07d', $iterations) . ':' . $salt . ':' . base64_encode($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the amount of entropy that a password contains.
|
||||
*
|
||||
* @param string $password
|
||||
* @return int
|
||||
*/
|
||||
public static function countEntropyBits($password)
|
||||
{
|
||||
// An empty string has no entropy.
|
||||
|
||||
if ($password === '') return 0;
|
||||
|
||||
// Common character sets and the number of possible mutations.
|
||||
|
||||
static $entropy_per_char = array(
|
||||
'/^[0-9]+$/' => 10,
|
||||
'/^[a-z]+$/' => 26,
|
||||
'/^[A-Z]+$/' => 26,
|
||||
'/^[a-z0-9]+$/' => 36,
|
||||
'/^[A-Z0-9]+$/' => 36,
|
||||
'/^[a-zA-Z]+$/' => 52,
|
||||
'/^[a-zA-Z0-9]+$/' => 62,
|
||||
'/^[a-zA-Z0-9_-]+$/' => 64,
|
||||
'/^[\\x20-\\x7e]+$/' => 95,
|
||||
'/^[\\x00-\\x7f]+$/' => 128,
|
||||
);
|
||||
|
||||
foreach ($entropy_per_char as $regex => $entropy)
|
||||
{
|
||||
if (preg_match($regex, $password))
|
||||
{
|
||||
return log(pow($entropy, strlen($password)), 2);
|
||||
}
|
||||
}
|
||||
|
||||
return strlen($password) * 8;
|
||||
}
|
||||
}
|
||||
339
common/framework/security.php
Normal file
339
common/framework/security.php
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The security class.
|
||||
*/
|
||||
class Security
|
||||
{
|
||||
/**
|
||||
* Sanitize a variable.
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $type
|
||||
* @return string|false
|
||||
*/
|
||||
public static function sanitize($input, $type)
|
||||
{
|
||||
switch ($type)
|
||||
{
|
||||
// Escape HTML special characters.
|
||||
case 'escape':
|
||||
if (!utf8_check($input)) return false;
|
||||
return escape($input);
|
||||
|
||||
// Strip all HTML tags.
|
||||
case 'strip':
|
||||
if (!utf8_check($input)) return false;
|
||||
return escape(strip_tags($input));
|
||||
|
||||
// Clean up HTML content to prevent XSS attacks.
|
||||
case 'html':
|
||||
if (!utf8_check($input)) return false;
|
||||
return Filters\HTMLFilter::clean($input);
|
||||
|
||||
// Clean up the input to be used as a safe filename.
|
||||
case 'filename':
|
||||
if (!utf8_check($input)) return false;
|
||||
return Filters\FilenameFilter::clean($input);
|
||||
|
||||
// Unknown filters return false.
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a string using AES.
|
||||
*
|
||||
* @param string $plaintext
|
||||
* @param string $key (optional)
|
||||
* @param bool $force_compat (optional)
|
||||
* @return string|false
|
||||
*/
|
||||
public static function encrypt($plaintext, $key = null, $force_compat = false)
|
||||
{
|
||||
// Get the encryption key.
|
||||
$key = $key ?: config('crypto.encryption_key');
|
||||
$key = substr(hash('sha256', $key, true), 0, 16);
|
||||
|
||||
// Use defuse/php-encryption if possible.
|
||||
if (!$force_compat && function_exists('openssl_encrypt'))
|
||||
{
|
||||
try
|
||||
{
|
||||
return base64_encode(\Crypto::Encrypt($plaintext, $key));
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, use the CryptoCompat class.
|
||||
return base64_encode(\CryptoCompat::encrypt($plaintext, $key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a string using AES.
|
||||
*
|
||||
* @param string $plaintext
|
||||
* @param string $key (optional)
|
||||
* @param bool $force_compat (optional)
|
||||
* @return string|false
|
||||
*/
|
||||
public static function decrypt($ciphertext, $key = null, $force_compat = false)
|
||||
{
|
||||
// Get the encryption key.
|
||||
$key = $key ?: config('crypto.encryption_key');
|
||||
$key = substr(hash('sha256', $key, true), 0, 16);
|
||||
|
||||
// Check whether the ciphertext is valid.
|
||||
$ciphertext = @base64_decode($ciphertext);
|
||||
if (strlen($ciphertext) < 48)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use defuse/php-encryption if possible.
|
||||
if (!$force_compat && function_exists('openssl_decrypt'))
|
||||
{
|
||||
try
|
||||
{
|
||||
return \Crypto::Decrypt($ciphertext, $key);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, use the CryptoCompat class.
|
||||
return \CryptoCompat::decrypt($ciphertext, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a cryptographically secure random string.
|
||||
*
|
||||
* @param int $length
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
public function getRandom($length = 32, $format = 'alnum')
|
||||
{
|
||||
// Find out how many bytes of entropy we really need.
|
||||
switch($format)
|
||||
{
|
||||
case 'binary':
|
||||
$entropy_required_bytes = $length;
|
||||
break;
|
||||
case 'hex':
|
||||
$entropy_required_bytes = ceil($length / 2);
|
||||
break;
|
||||
case 'alnum':
|
||||
case 'printable':
|
||||
default:
|
||||
$entropy_required_bytes = ceil($length * 3 / 4);
|
||||
break;
|
||||
}
|
||||
|
||||
// Cap entropy to 256 bits from any one source, because anything more is meaningless.
|
||||
$entropy_capped_bytes = min(32, $entropy_required_bytes);
|
||||
|
||||
// Find and use the most secure way to generate a random string.
|
||||
$entropy = false;
|
||||
$is_windows = (defined('\PHP_OS') && strtoupper(substr(\PHP_OS, 0, 3)) === 'WIN');
|
||||
if(function_exists('random_bytes')) // PHP 7
|
||||
{
|
||||
$entropy = random_bytes($entropy_capped_bytes);
|
||||
}
|
||||
elseif(function_exists('openssl_random_pseudo_bytes'))
|
||||
{
|
||||
$entropy = openssl_random_pseudo_bytes($entropy_capped_bytes);
|
||||
}
|
||||
elseif(function_exists('mcrypt_create_iv') && !$is_windows)
|
||||
{
|
||||
$entropy = mcrypt_create_iv($entropy_capped_bytes, \MCRYPT_DEV_URANDOM);
|
||||
}
|
||||
elseif(function_exists('mcrypt_create_iv') && $is_windows)
|
||||
{
|
||||
$entropy = mcrypt_create_iv($entropy_capped_bytes, \MCRYPT_RAND);
|
||||
}
|
||||
elseif(!$is_windows && @is_readable('/dev/urandom'))
|
||||
{
|
||||
$fp = fopen('/dev/urandom', 'rb');
|
||||
if (function_exists('stream_set_read_buffer')) // This function does not exist in HHVM.
|
||||
{
|
||||
stream_set_read_buffer($fp, 0); // Prevent reading several KB of unnecessary data from urandom.
|
||||
}
|
||||
$entropy = fread($fp, $entropy_capped_bytes);
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
// Use built-in source of entropy if an error occurs while using other functions.
|
||||
if($entropy === false || strlen($entropy) < $entropy_capped_bytes)
|
||||
{
|
||||
$entropy = '';
|
||||
for($i = 0; $i < $entropy_capped_bytes; $i += 2)
|
||||
{
|
||||
$entropy .= pack('S', rand(0, 65536) ^ mt_rand(0, 65535));
|
||||
}
|
||||
}
|
||||
|
||||
// Mixing (see RFC 4086 section 5)
|
||||
$output = '';
|
||||
for($i = 0; $i < $entropy_required_bytes; $i += 32)
|
||||
{
|
||||
$output .= hash('sha256', $entropy . $i . rand(), true);
|
||||
}
|
||||
|
||||
// Encode and return the random string.
|
||||
switch($format)
|
||||
{
|
||||
case 'binary':
|
||||
return substr($output, 0, $length);
|
||||
case 'printable':
|
||||
$salt = '';
|
||||
for($i = 0; $i < $length; $i++)
|
||||
{
|
||||
$salt .= chr(33 + (crc32(sha1($i . $output)) % 94));
|
||||
}
|
||||
return $salt;
|
||||
case 'hex':
|
||||
return substr(bin2hex($output), 0, $length);
|
||||
case 'alnum':
|
||||
default:
|
||||
$salt = substr(base64_encode($output), 0, $length);
|
||||
$replacements = chr(rand(65, 90)) . chr(rand(97, 122)) . rand(0, 9);
|
||||
return strtr($salt, '+/=', $replacements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a cryptographically secure random number between $min and $max.
|
||||
*
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
* @return int
|
||||
*/
|
||||
public static function getRandomNumber($min = 0, $max = 0x7fffffff)
|
||||
{
|
||||
if (function_exists('random_int'))
|
||||
{
|
||||
return random_int($min, $max);
|
||||
}
|
||||
else
|
||||
{
|
||||
$bytes_required = min(4, ceil(log($max - $min, 2) / 8) + 1);
|
||||
$bytes = self::getRandom($bytes_required, 'binary');
|
||||
$offset = abs(hexdec(bin2hex($bytes)) % ($max - $min + 1));
|
||||
return intval($min + $offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random UUID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getRandomUUID()
|
||||
{
|
||||
$randpool = self::getRandom(16, 'binary');
|
||||
$randpool[6] = chr(ord($randpool[6]) & 0x0f | 0x40);
|
||||
$randpool[8] = chr(ord($randpool[8]) & 0x3f | 0x80);
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($randpool), 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two strings in constant time.
|
||||
*
|
||||
* @param string $a
|
||||
* @param string $b
|
||||
* @return bool
|
||||
*/
|
||||
public static function compareStrings($a, $b)
|
||||
{
|
||||
$diff = strlen($a) ^ strlen($b);
|
||||
$maxlen = min(strlen($a), strlen($b));
|
||||
for($i = 0; $i < $maxlen; $i++)
|
||||
{
|
||||
$diff |= ord($a[$i]) ^ ord($b[$i]);
|
||||
}
|
||||
return $diff === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request seems to be a CSRF attack.
|
||||
*
|
||||
* This method returns true if the request seems to be innocent,
|
||||
* and false if it seems to be a CSRF attack.
|
||||
*
|
||||
* @param string $referer (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkCSRF($referer = null)
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$referer)
|
||||
{
|
||||
$referer = strval($_SERVER['HTTP_REFERER']);
|
||||
if ($referer === '')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return URL::isInternalURL($referer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request seems to be an XEE attack.
|
||||
*
|
||||
* This method returns true if the request seems to be innocent,
|
||||
* and false if it seems to be an XEE attack.
|
||||
* This is the opposite of XE's Security::detectXEE() method.
|
||||
*
|
||||
* @param string $xml (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkXEE($xml = null)
|
||||
{
|
||||
// Stop if there is no XML content.
|
||||
if (!$xml)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reject entity tags.
|
||||
if (strpos($xml, '<!ENTITY') !== false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there is no content after the xml tag.
|
||||
$header = preg_replace('/<\?xml.*?\?'.'>/s', '', substr($xml, 0, 100), 1);
|
||||
if (($xml = trim(substr_replace($xml, $header, 0, 100))) === '')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there is no content after the DTD.
|
||||
$header = preg_replace('/^<!DOCTYPE[^>]*+>/i', '', substr($xml, 0, 200), 1);
|
||||
if (($xml = trim(substr_replace($xml, $header, 0, 200))) === '')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the root tag is valid.
|
||||
if (!preg_match('/^<(methodCall|methodResponse|fault)/', $xml))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
547
common/framework/storage.php
Normal file
547
common/framework/storage.php
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The storage class.
|
||||
*/
|
||||
class Storage
|
||||
{
|
||||
/**
|
||||
* Check if a path really exists.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function exists($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
clearstatcache(true, $path);
|
||||
return @file_exists($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path is a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isFile($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
return @self::exists($path) && @is_file($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path is an empty file.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEmptyFile($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
return @self::exists($path) && @is_file($path) && (@filesize($path) == 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given path is a directory.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDirectory($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
return @self::exists($path) && @is_dir($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path is an empty directory.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEmptyDirectory($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
if (!self::isDirectory($path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$iterator = new \FilesystemIterator($path, \FilesystemIterator::SKIP_DOTS);
|
||||
return (iterator_count($iterator)) === 0 ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path is a symbolic link.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSymlink($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
return @is_link($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path is a valid symbolic link.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidSymlink($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
return @is_link($path) && ($target = @readlink($path)) !== false && self::exists($target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path is readable.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isReadable($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
return @self::exists($path) && @is_readable($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given path is writable.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function isWritable($path)
|
||||
{
|
||||
$path = rtrim($path, '/\\');
|
||||
return @self::exists($path) && @is_writable($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a file.
|
||||
*
|
||||
* This method returns the size of a file, or false on error.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return int|false
|
||||
*/
|
||||
public static function getSize($filename)
|
||||
{
|
||||
$filename = rtrim($filename, '/\\');
|
||||
if (self::exists($filename) && @is_file($filename) && @is_readable($filename))
|
||||
{
|
||||
return @filesize($filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of a file.
|
||||
*
|
||||
* This method returns the content if it exists and is readable.
|
||||
* If $stream is true, it will return the content as a stream instead of
|
||||
* loading the entire content in memory. This may be useful for large files.
|
||||
* If the file cannot be opened, this method returns false.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param bool $stream (optional)
|
||||
* @return string|resource|false
|
||||
*/
|
||||
public static function read($filename, $stream = false)
|
||||
{
|
||||
$filename = rtrim($filename, '/\\');
|
||||
if (self::exists($filename) && @is_file($filename) && @is_readable($filename))
|
||||
{
|
||||
if ($stream)
|
||||
{
|
||||
return @fopen($filename, 'r');
|
||||
}
|
||||
else
|
||||
{
|
||||
return @file_get_contents($filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read PHP data from a file, formatted for easy retrieval.
|
||||
*
|
||||
* This method returns the data on success and false on failure.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return mixed
|
||||
*/
|
||||
public static function readPHPData($filename)
|
||||
{
|
||||
$filename = rtrim($filename, '/\\');
|
||||
if (@is_file($filename) && @is_readable($filename))
|
||||
{
|
||||
return @include $filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write $content to a file.
|
||||
*
|
||||
* If $content is a stream, this method will copy it to the target file
|
||||
* without loading the entire content in memory. This may be useful for large files.
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string|resource $content
|
||||
* @param string $mode (optional)
|
||||
* @param int $perms (optional)
|
||||
* @return string|false
|
||||
*/
|
||||
public static function write($filename, $content, $mode = 'w', $perms = null)
|
||||
{
|
||||
$filename = rtrim($filename, '/\\');
|
||||
$destination_dir = dirname($filename);
|
||||
if (!self::exists($destination_dir))
|
||||
{
|
||||
$mkdir_success = self::createDirectory($destination_dir);
|
||||
if (!$mkdir_success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fp = fopen($filename, $mode))
|
||||
{
|
||||
flock($fp, \LOCK_EX);
|
||||
if (is_resource($content))
|
||||
{
|
||||
$result = stream_copy_to_stream($content, $fp) ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = fwrite($fp, $content) ? true : false;
|
||||
}
|
||||
fflush($fp);
|
||||
flock($fp, \LOCK_UN);
|
||||
fclose($fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@chmod($filename, ($perms === null ? (0666 & ~umask()) : $perms));
|
||||
if (function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
|
||||
{
|
||||
@opcache_invalidate($filename, true);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write PHP data to a file, formatted for easy retrieval.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
* Resources and anonymous functions cannot be saved.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param mixed $data
|
||||
* @return string|false
|
||||
*/
|
||||
public static function writePHPData($filename, $data)
|
||||
{
|
||||
return self::write($filename, '<' . '?php return unserialize(' . var_export(serialize($data), true) . ');');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy $source to $destination.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
* If the destination permissions are not given, they will be copied from the source.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
* @param int $destination_perms
|
||||
* @return bool
|
||||
*/
|
||||
public static function copy($source, $destination, $destination_perms = null)
|
||||
{
|
||||
$source = rtrim($source, '/\\');
|
||||
$destination = rtrim($destination, '/\\');
|
||||
if (!self::exists($source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$destination_dir = dirname($destination);
|
||||
if (!self::exists($destination_dir) && !self::createDirectory($destination_dir))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
elseif (self::isDirectory($destination))
|
||||
{
|
||||
$destination = $destination . '/' . basename($source);
|
||||
}
|
||||
|
||||
$copy_success = @copy($source, $destination);
|
||||
if (!$copy_success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($destination_perms === null)
|
||||
{
|
||||
@chmod($destination, 0777 & @fileperms($source));
|
||||
}
|
||||
else
|
||||
{
|
||||
@chmod($destination, $destination_perms);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move $source to $destination.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
* @return bool
|
||||
*/
|
||||
public static function move($source, $destination)
|
||||
{
|
||||
$source = rtrim($source, '/\\');
|
||||
$destination = rtrim($destination, '/\\');
|
||||
if (!self::exists($source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$destination_dir = dirname($destination);
|
||||
if (!self::exists($destination_dir) && !self::createDirectory($destination_dir))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
elseif (self::isDirectory($destination))
|
||||
{
|
||||
$destination = $destination . '/' . basename($source);
|
||||
}
|
||||
|
||||
$result = @rename($source, $destination);
|
||||
if (function_exists('opcache_invalidate') && substr($source, -4) === '.php')
|
||||
{
|
||||
@opcache_invalidate($source, true);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file.
|
||||
*
|
||||
* This method returns true if the file exists and has been successfully
|
||||
* deleted, and false on any kind of failure.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete($filename)
|
||||
{
|
||||
$filename = rtrim($filename, '/\\');
|
||||
$result = @self::exists($filename) && @is_file($filename) && @unlink($filename);
|
||||
if (function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
|
||||
{
|
||||
@opcache_invalidate($filename, true);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a directory.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @return bool
|
||||
*/
|
||||
public static function createDirectory($dirname, $mode = null)
|
||||
{
|
||||
$dirname = rtrim($dirname, '/\\');
|
||||
if ($mode === null)
|
||||
{
|
||||
$mode = 0777 & ~umask();
|
||||
}
|
||||
return @mkdir($dirname, $mode, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the list of files in a directory.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param bool $full_path (optional)
|
||||
* @param bool $skip_dotfiles (optional)
|
||||
* @param bool $skip_subdirs (optional)
|
||||
* @return array|false
|
||||
*/
|
||||
public static function readDirectory($dirname, $full_path = true, $skip_dotfiles = true, $skip_subdirs = true)
|
||||
{
|
||||
$dirname = rtrim($dirname, '/\\');
|
||||
if (!self::isDirectory($dirname))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$iterator = new \FilesystemIterator($dirname, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS);
|
||||
}
|
||||
catch (\UnexpectedValueException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($iterator as $fileinfo)
|
||||
{
|
||||
if (!$skip_subdirs || !is_dir($fileinfo))
|
||||
{
|
||||
$basename = basename($fileinfo);
|
||||
if (!$skip_dotfiles || $basename[0] !== '.')
|
||||
{
|
||||
$result[] = $full_path ? $fileinfo : $basename;
|
||||
}
|
||||
}
|
||||
}
|
||||
sort($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a directory recursively.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
* @param string $exclude_regexp (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function copyDirectory($source, $destination, $exclude_regexp = null)
|
||||
{
|
||||
$source = rtrim($source, '/\\');
|
||||
$destination = rtrim($destination, '/\\');
|
||||
if (!self::isDirectory($source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!self::isDirectory($destination) && !self::createDirectory($destination))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$rdi_options = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS;
|
||||
$rii_options = \RecursiveIteratorIterator::CHILD_FIRST;
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, $rdi_options), $rii_options);
|
||||
|
||||
foreach ($iterator as $path)
|
||||
{
|
||||
$path_source = $path->getPathname();
|
||||
if (strpos($path_source, $source) !== 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ($exclude_regexp && preg_match($exclude_regexp, $path_source))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$path_destination = $destination . substr($path_source, strlen($source));
|
||||
if ($path->isDir())
|
||||
{
|
||||
$status = self::isDirectory($path_destination) || self::createDirectory($path_destination, $path->getPerms());
|
||||
if (!$status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$status = self::copy($path_source, $path_destination, $path->getPerms());
|
||||
if (!$status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a directory.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
* @return bool
|
||||
*/
|
||||
public static function moveDirectory($source, $destination)
|
||||
{
|
||||
return self::move($source, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a directory recursively.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param bool $delete_self (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteDirectory($dirname, $delete_self = true)
|
||||
{
|
||||
$dirname = rtrim($dirname, '/\\');
|
||||
if (!self::isDirectory($dirname))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$rdi_options = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS;
|
||||
$rii_options = \RecursiveIteratorIterator::CHILD_FIRST;
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dirname, $rdi_options), $rii_options);
|
||||
|
||||
foreach ($iterator as $path)
|
||||
{
|
||||
if ($path->isDir())
|
||||
{
|
||||
if (!@rmdir($path->getPathname()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!@unlink($path->getPathname()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($delete_self)
|
||||
{
|
||||
return @rmdir($dirname);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
109
common/framework/timer.php
Normal file
109
common/framework/timer.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The timer class.
|
||||
*/
|
||||
class Timer
|
||||
{
|
||||
/**
|
||||
* Timestamps are stored here.
|
||||
*/
|
||||
protected static $_timestamps = array();
|
||||
|
||||
/**
|
||||
* Start a timer.
|
||||
*
|
||||
* This method returns the current microtime.
|
||||
*
|
||||
* @param string $name (optional)
|
||||
* @return float
|
||||
*/
|
||||
public static function start($name = null)
|
||||
{
|
||||
$timestamp = microtime(true);
|
||||
|
||||
if ($name === null)
|
||||
{
|
||||
$name = 'anon-timer-' . $timestamp;
|
||||
}
|
||||
|
||||
self::$_timestamps[$name] = $timestamp;
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a timer and return the elapsed time.
|
||||
*
|
||||
* If the name is not given, the most recently started timer will be stopped.
|
||||
* If no timer has been started, this method returns false.
|
||||
*
|
||||
* @param string $name (optional)
|
||||
* @return float|false
|
||||
*/
|
||||
public static function stop($name = null)
|
||||
{
|
||||
$timestamp = microtime(true);
|
||||
$started_timestamp = 0;
|
||||
|
||||
if ($name === null)
|
||||
{
|
||||
if (count(self::$_timestamps))
|
||||
{
|
||||
$started_timestamp = array_pop(self::$_timestamps);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif (array_key_exists($name, self::$_timestamps))
|
||||
{
|
||||
$started_timestamp = self::$_timestamps[$name];
|
||||
unset(self::$_timestamps[$name]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $timestamp - $started_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a timer and return the elapsed time in a human-readable format.
|
||||
*
|
||||
* If the name is not given, the most recently started timer will be stopped.
|
||||
* If no timer has been started, this method returns false.
|
||||
*
|
||||
* @param string $name (optional)
|
||||
* @return string|false
|
||||
*/
|
||||
public static function stopFormat($name = null)
|
||||
{
|
||||
$result = self::stop($name);
|
||||
if ($result === false) return $result;
|
||||
return number_format($result * 1000, 1, '.', ',') . 'ms';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns how much time has elapsed since Rhymix startup.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function sinceStartup()
|
||||
{
|
||||
return microtime(true) - \RX_MICROTIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns how much time has elapsed since startup in a human-readable format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sinceStartupFormat()
|
||||
{
|
||||
return number_format((microtime(true) - \RX_MICROTIME) * 1000, 1, '.', ',') . 'ms';
|
||||
}
|
||||
}
|
||||
240
common/framework/ua.php
Normal file
240
common/framework/ua.php
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The user-agent class.
|
||||
*/
|
||||
class UA
|
||||
{
|
||||
/**
|
||||
* Cache to prevent multiple lookups.
|
||||
*/
|
||||
protected static $_mobile_cache = array();
|
||||
protected static $_tablet_cache = array();
|
||||
protected static $_robot_cache = array();
|
||||
|
||||
/**
|
||||
* Check whether the current visitor is using a mobile device.
|
||||
*
|
||||
* @param string $ua (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMobile($ua = null)
|
||||
{
|
||||
// Get the User-Agent header if the caller did not specify $ua.
|
||||
if ($ua === null)
|
||||
{
|
||||
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
|
||||
$using_header = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$using_header = false;
|
||||
}
|
||||
|
||||
// If the User-Agent header is missing, it's probably not a mobile browser.
|
||||
if (is_null($ua))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for headers that are only used in mobile browsers.
|
||||
if ($using_header && (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA'])))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look up the cache.
|
||||
if (isset(self::$_mobile_cache[$ua]))
|
||||
{
|
||||
return self::$_mobile_cache[$ua];
|
||||
}
|
||||
|
||||
// Look for the 'mobile' keyword and common mobile platform names.
|
||||
if (preg_match('/android|ip(hone|ad|od)|blackberry|nokia|palm|mobile/i', $ua))
|
||||
{
|
||||
return self::$_mobile_cache[$ua] = true;
|
||||
}
|
||||
|
||||
// Look for common non-mobile OS names.
|
||||
if (preg_match('/windows|linux|os [x9]|bsd/i', $ua))
|
||||
{
|
||||
return self::$_mobile_cache[$ua] = false;
|
||||
}
|
||||
|
||||
// Look for other platform, manufacturer, and device names that are known to be mobile.
|
||||
if (preg_match('/kindle|opera (mini|mobi)|polaris|netfront|fennec|motorola|symbianos|webos/i', $ua))
|
||||
{
|
||||
return self::$_mobile_cache[$ua] = true;
|
||||
}
|
||||
if (preg_match('/s[pgc]h-|lgtelecom|sonyericsson|alcatel|vodafone|maemo|minimo|bada/i', $ua))
|
||||
{
|
||||
return self::$_mobile_cache[$ua] = true;
|
||||
}
|
||||
|
||||
// If we're here, it's probably not a mobile device.
|
||||
return self::$_mobile_cache[$ua] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current visitor is using a tablet.
|
||||
*
|
||||
* @param string $ua (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTablet($ua = null)
|
||||
{
|
||||
// Get the User-Agent header if the caller did not specify $ua.
|
||||
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
|
||||
|
||||
// If the User-Agent header is missing, it's probably not a tablet.
|
||||
if (is_null($ua))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look up the cache.
|
||||
if (isset(self::$_tablet_cache[$ua]))
|
||||
{
|
||||
return self::$_tablet_cache[$ua];
|
||||
}
|
||||
|
||||
// Check if the user-agent is mobile.
|
||||
if (!self::isMobile($ua))
|
||||
{
|
||||
return self::$_tablet_cache[$ua] = false;
|
||||
}
|
||||
|
||||
// Check for Android tablets without the 'mobile' keyword.
|
||||
if (stripos($ua, 'android') !== false && stripos($ua, 'mobile') === false)
|
||||
{
|
||||
return self::$_tablet_cache[$ua] = true;
|
||||
}
|
||||
|
||||
// Check for common tablet identifiers.
|
||||
if (preg_match('/tablet|pad\b|tab\b|\bgt-\d+|kindle|nook|playbook|webos|xoom/i', $ua))
|
||||
{
|
||||
return self::$_tablet_cache[$ua] = true;
|
||||
}
|
||||
|
||||
// If we're here, it's probably not a tablet.
|
||||
return self::$_tablet_cache[$ua] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current visitor is a robot.
|
||||
*
|
||||
* @param string $ua (optional)
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRobot($ua = null)
|
||||
{
|
||||
// Get the User-Agent header if the caller did not specify $ua.
|
||||
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
|
||||
|
||||
// If the User-Agent header is missing, it's probably not a robot.
|
||||
if (is_null($ua))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look up the cache.
|
||||
if (isset(self::$_robot_cache[$ua]))
|
||||
{
|
||||
return self::$_robot_cache[$ua];
|
||||
}
|
||||
|
||||
// Look for common search engine names and the 'bot' keyword.
|
||||
if (preg_match('/bot|slurp|facebook(externalhit|scraper)|ia_archiver|ask jeeves|teoma|baidu|daumoa|lycos|pingdom/i', $ua))
|
||||
{
|
||||
return self::$_robot_cache[$ua] = true;
|
||||
}
|
||||
|
||||
// If we're here, it's probably not a robot.
|
||||
return self::$_robot_cache[$ua] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses the User-Agent string to guess what kind of browser it is.
|
||||
*
|
||||
* @param string $ua (optional)
|
||||
* @return object
|
||||
*/
|
||||
public static function getBrowserInfo($ua = null)
|
||||
{
|
||||
// Get the User-Agent header if the caller did not specify $ua.
|
||||
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
|
||||
|
||||
// Initialize the result.
|
||||
$result = (object)array(
|
||||
'browser' => null,
|
||||
'version' => null,
|
||||
'os' => null,
|
||||
'is_mobile' => null,
|
||||
'is_tablet' => null,
|
||||
);
|
||||
if (is_null($ua))
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Try to guess the OS.
|
||||
if (preg_match('#(Windows|Android|Linux|iOS|OS X|Macintosh)#i', $ua, $matches))
|
||||
{
|
||||
if ($matches[1] === 'Linux' && strpos($ua, 'Android') !== false)
|
||||
{
|
||||
$matches[1] = 'Android';
|
||||
}
|
||||
if ($matches[1] === 'Macintosh' && strpos($ua, 'OS X') !== false)
|
||||
{
|
||||
$matches[1] = 'OS X';
|
||||
}
|
||||
$result->os = $matches[1];
|
||||
}
|
||||
|
||||
// Fill in miscellaneous fields.
|
||||
$result->is_mobile = self::isMobile($ua);
|
||||
$result->is_tablet = self::isTablet($ua);
|
||||
|
||||
// Try to match some of the most common browsers.
|
||||
if (preg_match('#Android ([0-9]+\\.[0-9]+)#', $ua, $matches) && strpos($ua, 'Chrome') === false)
|
||||
{
|
||||
$result->browser = 'Android';
|
||||
$result->version = $matches[1];
|
||||
return $result;
|
||||
}
|
||||
if (preg_match('#Edge/([0-9]+\\.)#', $ua, $matches))
|
||||
{
|
||||
$result->browser = 'Edge';
|
||||
$result->version = $matches[1] . '0';
|
||||
return $result;
|
||||
}
|
||||
if (preg_match('#Trident/([0-9]+)\\.[0-9]+#', $ua, $matches))
|
||||
{
|
||||
$result->browser = 'IE';
|
||||
$result->version = ($matches[1] + 4) . '.0';
|
||||
return $result;
|
||||
}
|
||||
if (preg_match('#(MSIE|Chrome|Firefox|Safari)[ /:]([0-9]+\\.[0-9]+)#', $ua, $matches))
|
||||
{
|
||||
$result->browser = $matches[1] === 'MSIE' ? 'IE' : $matches[1];
|
||||
$result->version = $matches[2];
|
||||
return $result;
|
||||
}
|
||||
if (preg_match('#^Opera/.+(?:Opera |Version/)([0-9]+\\.[0-9]+)$#', $ua, $matches))
|
||||
{
|
||||
$result->browser = 'Opera';
|
||||
$result->version = $matches[1];
|
||||
return $result;
|
||||
}
|
||||
if (preg_match('#(?:Konqueror|KHTML)/([0-9]+\\.[0-9]+)$#', $ua, $matches))
|
||||
{
|
||||
$result->browser = 'Konqueror';
|
||||
$result->version = $matches[1];
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
220
common/framework/url.php
Normal file
220
common/framework/url.php
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
|
||||
namespace Rhymix\Framework;
|
||||
|
||||
/**
|
||||
* The URL class.
|
||||
*/
|
||||
class URL
|
||||
{
|
||||
/**
|
||||
* Get the current URL.
|
||||
*
|
||||
* If $changes are given, they will be appended to the current URL as a query string.
|
||||
* To delete an existing query string, set its value to null.
|
||||
*
|
||||
* @param array $changes
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentURL(array $changes = array())
|
||||
{
|
||||
$url = self::getCurrentDomainURL(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/');
|
||||
if (count($changes))
|
||||
{
|
||||
return self::modifyURL($url, $changes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::getCanonicalURL($url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current domain.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentDomainURL($path = '/')
|
||||
{
|
||||
$proto = \RX_SSL ? 'https://' : 'http://';
|
||||
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
|
||||
return $proto . $host . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a URL to its canonical format.
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function getCanonicalURL($url)
|
||||
{
|
||||
if (preg_match('#^\.?/([^/]|$)#', $url) || !preg_match('#^(https?:|/)#', $url))
|
||||
{
|
||||
$url = self::getCurrentDomainURL(\RX_BASEURL . ltrim($url, './'));
|
||||
}
|
||||
return preg_replace_callback('#^(https?:|)//([^/]+)#i', function($matches) {
|
||||
if ($matches[1] === '') $matches[1] = \RX_SSL ? 'https:' : 'http:';
|
||||
return $matches[1] . '//' . self::decodeIdna($matches[2]);
|
||||
}, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain from a URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @return string|false
|
||||
*/
|
||||
public static function getDomainFromURL($url)
|
||||
{
|
||||
$domain = @parse_url($url, \PHP_URL_HOST);
|
||||
if ($domain === false || $domain === null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::decodeIdna($domain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is internal to this site.
|
||||
*
|
||||
* @param string $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInternalURL($url)
|
||||
{
|
||||
$domain = self::getDomainFromURL($url);
|
||||
if ($domain === false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($domain === self::getDomainFromURL('http://' . $_SERVER['HTTP_HOST']))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($domain === self::getDomainFromURL(\Context::getDefaultUrl()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a URL.
|
||||
*
|
||||
* If $changes are given, they will be appended to the current URL as a query string.
|
||||
* To delete an existing query string, set its value to null.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $changes
|
||||
* @return string
|
||||
*/
|
||||
public static function modifyURL($url, array $changes = array())
|
||||
{
|
||||
$url = parse_url(self::getCanonicalURL($url));
|
||||
$prefix = sprintf('%s://%s%s%s', $url['scheme'], $url['host'], ($url['port'] ? (':' . $url['port']) : ''), $url['path']);
|
||||
parse_str($url['query'], $args);
|
||||
$changes = array_merge($args, $changes);
|
||||
$changes = array_filter($changes, function($val) { return $val !== null; });
|
||||
if (count($changes))
|
||||
{
|
||||
return $prefix . '?' . http_build_query($changes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a server-side path to a URL.
|
||||
*
|
||||
* This method returns false if the path cannot be converted to a URL,
|
||||
* e.g. if the path is outside of the document root.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string|false
|
||||
*/
|
||||
public static function fromServerPath($path)
|
||||
{
|
||||
$cleanpath = Filters\FilenameFilter::cleanPath($path);
|
||||
if (substr($path, -1) === '/')
|
||||
{
|
||||
$cleanpath .= '/';
|
||||
}
|
||||
$root = Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT']);
|
||||
if ($cleanpath === $root)
|
||||
{
|
||||
return self::getCurrentDomainURL('/');
|
||||
}
|
||||
if (starts_with($root . '/', $cleanpath))
|
||||
{
|
||||
return self::getCurrentDomainURL(substr($cleanpath, strlen($root)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a URL to a server-side path.
|
||||
*
|
||||
* This method returns false if the URL cannot be converted to a server-side path,
|
||||
* e.g. if the URL belongs to an external domain.
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function toServerPath($url)
|
||||
{
|
||||
$url = self::getCanonicalURL($url);
|
||||
if (!self::isInternalURL($url))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT'] . parse_url($url, \PHP_URL_PATH));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode UTF-8 domain into IDNA (punycode)
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string
|
||||
*/
|
||||
public static function encodeIdna($domain)
|
||||
{
|
||||
if (function_exists('idn_to_ascii'))
|
||||
{
|
||||
return idn_to_ascii($domain);
|
||||
}
|
||||
else
|
||||
{
|
||||
$encoder = new \TrueBV\Punycode();
|
||||
return $encoder->encode($domain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert IDNA (punycode) domain into UTF-8
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string
|
||||
*/
|
||||
public static function decodeIdna($domain)
|
||||
{
|
||||
if (function_exists('idn_to_utf8'))
|
||||
{
|
||||
return idn_to_utf8($domain);
|
||||
}
|
||||
else
|
||||
{
|
||||
$decoder = new \TrueBV\Punycode();
|
||||
return $decoder->decode($domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -133,6 +133,19 @@ function class_basename($class)
|
|||
return basename(str_replace('\\', '/', is_object($class) ? get_class($class) : $class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean a path to remove ./, ../, trailing slashes, etc.
|
||||
*
|
||||
* This function is an alias to Rhymix\Framework\Filters\FilenameFilter::cleanPath().
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function clean_path($path)
|
||||
{
|
||||
return Rhymix\Framework\Filters\FilenameFilter::cleanPath($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a shortcut to htmlspecialchars().
|
||||
*
|
||||
|
|
@ -238,7 +251,7 @@ function starts_with($needle, $haystack, $case_sensitive = true)
|
|||
}
|
||||
else
|
||||
{
|
||||
!strncasecmp($needle, $haystack, strlen($needle));
|
||||
return !strncasecmp($needle, $haystack, strlen($needle));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -337,6 +350,34 @@ function base64_decode_urlsafe($str)
|
|||
return @base64_decode(str_pad(strtr($str, '-_', '+/'), ceil(strlen($str) / 4) * 4, '=', STR_PAD_RIGHT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a server-side path to a URL.
|
||||
*
|
||||
* This function is an alias to Rhymix\Framework\URL::fromServerPath().
|
||||
* It returns false if the path cannot be converted.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string|false
|
||||
*/
|
||||
function path2url($path)
|
||||
{
|
||||
return Rhymix\Framework\URL::fromServerPath($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a URL to a server-side path.
|
||||
*
|
||||
* This function is an alias to Rhymix\Framework\URL::toServerPath().
|
||||
* It returns false if the URL cannot be converted.
|
||||
*
|
||||
* @param string $url
|
||||
* @return string|false
|
||||
*/
|
||||
function url2path($url)
|
||||
{
|
||||
return Rhymix\Framework\URL::toServerPath($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hexadecimal color codes to an array of R, G, B values.
|
||||
* This function can handle both 6-digit and 3-digit notations, optionally prefixed with '#'.
|
||||
|
|
@ -452,8 +493,11 @@ if (!function_exists('hex2bin'))
|
|||
*/
|
||||
function tobool($input)
|
||||
{
|
||||
if (preg_match('/^(1|[ty].*|on|oui|si|vrai|aye)$/i', $input)) return true;
|
||||
if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false;
|
||||
if (is_scalar($input))
|
||||
{
|
||||
if (preg_match('/^(1|[ty].*|on|ok.*oui|si|vrai|aye)$/i', $input)) return true;
|
||||
if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false;
|
||||
}
|
||||
return (bool)$input;
|
||||
}
|
||||
|
||||
|
|
@ -475,6 +519,27 @@ function utf8_check($str)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove BOM and invalid UTF-8 sequences from file content.
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
function utf8_clean($str)
|
||||
{
|
||||
if (strlen($str) >= 3 && substr($str, 0, 3) === "\xEF\xBB\xBF")
|
||||
{
|
||||
$str = substr($str, 3);
|
||||
}
|
||||
|
||||
if (!utf8_check($str))
|
||||
{
|
||||
$str = @iconv('UTF-8', 'UTF-8//IGNORE', $str);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode UTF-8 characters outside of the Basic Multilingual Plane in the &#xxxxxx format.
|
||||
* This allows emoticons and other characters to be stored in MySQL without utf8mb4 support.
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ jQuery(function($) {
|
|||
uri = uri.replace(re, toReplace);
|
||||
}
|
||||
|
||||
var bUseSSL = !!window.enforce_ssl;
|
||||
var bUseSSL = !!window.enforce_ssl || (location.protocol == 'https:');
|
||||
if (!bUseSSL && isArray(window.ssl_actions) && (act=uri.getQuery('act'))) {
|
||||
for (var i=0,c=ssl_actions.length; i < c; i++) {
|
||||
if (ssl_actions[i] === act) {
|
||||
|
|
@ -381,13 +381,11 @@ function sendMailTo(to) {
|
|||
* @brief url이동 (Rhymix 개선된 버전)
|
||||
*/
|
||||
function redirect(url) {
|
||||
if (url === window.location.href || url.indexOf(window.location.href.replace(/#.+$/, "") + "#") === 0)
|
||||
{
|
||||
if (url === window.location.href || url.indexOf(window.location.href.replace(/#.+$/, "") + "#") === 0 ||
|
||||
url === window.location.pathname || url.indexOf(window.location.pathname.replace(/#.+$/, "") + "#") === 0) {
|
||||
window.location.href = url;
|
||||
window.location.reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@
|
|||
|
||||
if(result.error == 0) {
|
||||
if(/\.(jpe?g|png|gif)$/i.test(result.source_filename)) {
|
||||
temp_code += '<img src="' + window.request_uri + result.download_url + '" alt="' + result.source_filename + '" editor_component="image_link" data-file-srl="' + result.file_srl + '" />';
|
||||
temp_code += '<img src="' + result.download_url + '" alt="' + result.source_filename + '" editor_component="image_link" data-file-srl="' + result.file_srl + '" />';
|
||||
temp_code += "\r\n<p><br></p>\r\n";
|
||||
}
|
||||
|
||||
|
|
@ -244,10 +244,10 @@
|
|||
if(!fileinfo) return;
|
||||
|
||||
if(/\.(jpe?g|png|gif)$/i.test(fileinfo.source_filename)) {
|
||||
temp_code += '<img src="' + window.request_uri + fileinfo.download_url + '" alt="' + fileinfo.source_filename + '" editor_component="image_link" data-file-srl="' + fileinfo.file_srl + '" />';
|
||||
temp_code += '<img src="' + fileinfo.download_url + '" alt="' + fileinfo.source_filename + '" editor_component="image_link" data-file-srl="' + fileinfo.file_srl + '" />';
|
||||
temp_code += "\r\n<p><br></p>\r\n";
|
||||
} else {
|
||||
temp_code += '<a href="' + window.request_uri + fileinfo.download_url + '" data-file-srl="' + fileinfo.file_srl + '">' + fileinfo.source_filename + "</a>\n";
|
||||
temp_code += '<a href="' + fileinfo.download_url + '" data-file-srl="' + fileinfo.file_srl + '">' + fileinfo.source_filename + "</a>\n";
|
||||
}
|
||||
|
||||
});
|
||||
|
|
@ -379,6 +379,3 @@
|
|||
return u;
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ jQuery(function($){
|
|||
$.getScript(request_uri + "./common/js/plugins/spectrum/i18n/jquery.spectrum-"+ xe.current_lang.replace("jp", "ja").toLowerCase() +".js", function() {
|
||||
var settings = {
|
||||
showInput: true,
|
||||
allowEmpty:true,
|
||||
showInitial: true,
|
||||
showPalette: true,
|
||||
showSelectionPalette: true,
|
||||
|
|
|
|||
37
common/js/plugins/ui/i18n/datepicker-af.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-af.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Afrikaans initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Renier Pretorius. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.af = {
|
||||
closeText: "Selekteer",
|
||||
prevText: "Vorige",
|
||||
nextText: "Volgende",
|
||||
currentText: "Vandag",
|
||||
monthNames: [ "Januarie","Februarie","Maart","April","Mei","Junie",
|
||||
"Julie","Augustus","September","Oktober","November","Desember" ],
|
||||
monthNamesShort: [ "Jan", "Feb", "Mrt", "Apr", "Mei", "Jun",
|
||||
"Jul", "Aug", "Sep", "Okt", "Nov", "Des" ],
|
||||
dayNames: [ "Sondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrydag", "Saterdag" ],
|
||||
dayNamesShort: [ "Son", "Maa", "Din", "Woe", "Don", "Vry", "Sat" ],
|
||||
dayNamesMin: [ "So","Ma","Di","Wo","Do","Vr","Sa" ],
|
||||
weekHeader: "Wk",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.af );
|
||||
|
||||
return datepicker.regional.af;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-ar-DZ.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-ar-DZ.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Algerian Arabic Translation for jQuery UI date picker plugin. (can be used for Tunisia)*/
|
||||
/* Mohamed Cherif BOUCHELAGHEM -- cherifbouchelaghem@yahoo.fr */
|
||||
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "ar-DZ" ] = {
|
||||
closeText: "إغلاق",
|
||||
prevText: "<السابق",
|
||||
nextText: "التالي>",
|
||||
currentText: "اليوم",
|
||||
monthNames: [ "جانفي", "فيفري", "مارس", "أفريل", "ماي", "جوان",
|
||||
"جويلية", "أوت", "سبتمبر","أكتوبر", "نوفمبر", "ديسمبر" ],
|
||||
monthNamesShort: [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ],
|
||||
dayNames: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
|
||||
dayNamesShort: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
|
||||
dayNamesMin: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
|
||||
weekHeader: "أسبوع",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 6,
|
||||
isRTL: true,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional[ "ar-DZ" ] );
|
||||
|
||||
return datepicker.regional[ "ar-DZ" ];
|
||||
|
||||
} ) );
|
||||
38
common/js/plugins/ui/i18n/datepicker-ar.js
Normal file
38
common/js/plugins/ui/i18n/datepicker-ar.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* Arabic Translation for jQuery UI date picker plugin. */
|
||||
/* Used in most of Arab countries, primarily in Bahrain, Kuwait, Oman, Qatar, Saudi Arabia and the United Arab Emirates, Egypt, Sudan and Yemen. */
|
||||
/* Written by Mohammed Alshehri -- m@dralshehri.com */
|
||||
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.ar = {
|
||||
closeText: "إغلاق",
|
||||
prevText: "<السابق",
|
||||
nextText: "التالي>",
|
||||
currentText: "اليوم",
|
||||
monthNames: [ "يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو",
|
||||
"يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر" ],
|
||||
monthNamesShort: [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ],
|
||||
dayNames: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ],
|
||||
dayNamesShort: [ "أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت" ],
|
||||
dayNamesMin: [ "ح", "ن", "ث", "ر", "خ", "ج", "س" ],
|
||||
weekHeader: "أسبوع",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 0,
|
||||
isRTL: true,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.ar );
|
||||
|
||||
return datepicker.regional.ar;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-az.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-az.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Azerbaijani (UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Jamil Najafov (necefov33@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.az = {
|
||||
closeText: "Bağla",
|
||||
prevText: "<Geri",
|
||||
nextText: "İrəli>",
|
||||
currentText: "Bugün",
|
||||
monthNames: [ "Yanvar","Fevral","Mart","Aprel","May","İyun",
|
||||
"İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr" ],
|
||||
monthNamesShort: [ "Yan","Fev","Mar","Apr","May","İyun",
|
||||
"İyul","Avq","Sen","Okt","Noy","Dek" ],
|
||||
dayNames: [ "Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə" ],
|
||||
dayNamesShort: [ "B","Be","Ça","Ç","Ca","C","Ş" ],
|
||||
dayNamesMin: [ "B","B","Ç","С","Ç","C","Ş" ],
|
||||
weekHeader: "Hf",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.az );
|
||||
|
||||
return datepicker.regional.az;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-be.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-be.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Belarusian initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Pavel Selitskas <p.selitskas@gmail.com> */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.be = {
|
||||
closeText: "Зачыніць",
|
||||
prevText: "←Папяр.",
|
||||
nextText: "Наст.→",
|
||||
currentText: "Сёньня",
|
||||
monthNames: [ "Студзень","Люты","Сакавік","Красавік","Травень","Чэрвень",
|
||||
"Ліпень","Жнівень","Верасень","Кастрычнік","Лістапад","Сьнежань" ],
|
||||
monthNamesShort: [ "Сту","Лют","Сак","Кра","Тра","Чэр",
|
||||
"Ліп","Жні","Вер","Кас","Ліс","Сьн" ],
|
||||
dayNames: [ "нядзеля","панядзелак","аўторак","серада","чацьвер","пятніца","субота" ],
|
||||
dayNamesShort: [ "ндз","пнд","аўт","срд","чцв","птн","сбт" ],
|
||||
dayNamesMin: [ "Нд","Пн","Аў","Ср","Чц","Пт","Сб" ],
|
||||
weekHeader: "Тд",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.be );
|
||||
|
||||
return datepicker.regional.be;
|
||||
|
||||
} ) );
|
||||
38
common/js/plugins/ui/i18n/datepicker-bg.js
Normal file
38
common/js/plugins/ui/i18n/datepicker-bg.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* Bulgarian initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Stoyan Kyosev (http://svest.org). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.bg = {
|
||||
closeText: "затвори",
|
||||
prevText: "<назад",
|
||||
nextText: "напред>",
|
||||
nextBigText: ">>",
|
||||
currentText: "днес",
|
||||
monthNames: [ "Януари","Февруари","Март","Април","Май","Юни",
|
||||
"Юли","Август","Септември","Октомври","Ноември","Декември" ],
|
||||
monthNamesShort: [ "Яну","Фев","Мар","Апр","Май","Юни",
|
||||
"Юли","Авг","Сеп","Окт","Нов","Дек" ],
|
||||
dayNames: [ "Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота" ],
|
||||
dayNamesShort: [ "Нед","Пон","Вто","Сря","Чет","Пет","Съб" ],
|
||||
dayNamesMin: [ "Не","По","Вт","Ср","Че","Пе","Съ" ],
|
||||
weekHeader: "Wk",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.bg );
|
||||
|
||||
return datepicker.regional.bg;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-bs.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-bs.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Bosnian i18n for the jQuery UI date picker plugin. */
|
||||
/* Written by Kenan Konjo. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.bs = {
|
||||
closeText: "Zatvori",
|
||||
prevText: "<",
|
||||
nextText: ">",
|
||||
currentText: "Danas",
|
||||
monthNames: [ "Januar","Februar","Mart","April","Maj","Juni",
|
||||
"Juli","August","Septembar","Oktobar","Novembar","Decembar" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun",
|
||||
"Jul","Aug","Sep","Okt","Nov","Dec" ],
|
||||
dayNames: [ "Nedelja","Ponedeljak","Utorak","Srijeda","Četvrtak","Petak","Subota" ],
|
||||
dayNamesShort: [ "Ned","Pon","Uto","Sri","Čet","Pet","Sub" ],
|
||||
dayNamesMin: [ "Ne","Po","Ut","Sr","Če","Pe","Su" ],
|
||||
weekHeader: "Wk",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.bs );
|
||||
|
||||
return datepicker.regional.bs;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-ca.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-ca.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Inicialització en català per a l'extensió 'UI date picker' per jQuery. */
|
||||
/* Writers: (joan.leon@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.ca = {
|
||||
closeText: "Tanca",
|
||||
prevText: "Anterior",
|
||||
nextText: "Següent",
|
||||
currentText: "Avui",
|
||||
monthNames: [ "gener","febrer","març","abril","maig","juny",
|
||||
"juliol","agost","setembre","octubre","novembre","desembre" ],
|
||||
monthNamesShort: [ "gen","feb","març","abr","maig","juny",
|
||||
"jul","ag","set","oct","nov","des" ],
|
||||
dayNames: [ "diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte" ],
|
||||
dayNamesShort: [ "dg","dl","dt","dc","dj","dv","ds" ],
|
||||
dayNamesMin: [ "dg","dl","dt","dc","dj","dv","ds" ],
|
||||
weekHeader: "Set",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.ca );
|
||||
|
||||
return datepicker.regional.ca;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-cs.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-cs.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Czech initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Tomas Muller (tomas@tomas-muller.net). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.cs = {
|
||||
closeText: "Zavřít",
|
||||
prevText: "<Dříve",
|
||||
nextText: "Později>",
|
||||
currentText: "Nyní",
|
||||
monthNames: [ "leden","únor","březen","duben","květen","červen",
|
||||
"červenec","srpen","září","říjen","listopad","prosinec" ],
|
||||
monthNamesShort: [ "led","úno","bře","dub","kvě","čer",
|
||||
"čvc","srp","zář","říj","lis","pro" ],
|
||||
dayNames: [ "neděle", "pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota" ],
|
||||
dayNamesShort: [ "ne", "po", "út", "st", "čt", "pá", "so" ],
|
||||
dayNamesMin: [ "ne","po","út","st","čt","pá","so" ],
|
||||
weekHeader: "Týd",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.cs );
|
||||
|
||||
return datepicker.regional.cs;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-cy-GB.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-cy-GB.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Welsh/UK initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by William Griffiths. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "cy-GB" ] = {
|
||||
closeText: "Done",
|
||||
prevText: "Prev",
|
||||
nextText: "Next",
|
||||
currentText: "Today",
|
||||
monthNames: [ "Ionawr","Chwefror","Mawrth","Ebrill","Mai","Mehefin",
|
||||
"Gorffennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr" ],
|
||||
monthNamesShort: [ "Ion", "Chw", "Maw", "Ebr", "Mai", "Meh",
|
||||
"Gor", "Aws", "Med", "Hyd", "Tac", "Rha" ],
|
||||
dayNames: [ "Dydd Sul", "Dydd Llun", "Dydd Mawrth", "Dydd Mercher", "Dydd Iau", "Dydd Gwener", "Dydd Sadwrn" ],
|
||||
dayNamesShort: [ "Sul", "Llu", "Maw", "Mer", "Iau", "Gwe", "Sad" ],
|
||||
dayNamesMin: [ "Su","Ll","Ma","Me","Ia","Gw","Sa" ],
|
||||
weekHeader: "Wy",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional[ "cy-GB" ] );
|
||||
|
||||
return datepicker.regional[ "cy-GB" ];
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-da.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-da.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Danish initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Jan Christensen ( deletestuff@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.da = {
|
||||
closeText: "Luk",
|
||||
prevText: "<Forrige",
|
||||
nextText: "Næste>",
|
||||
currentText: "Idag",
|
||||
monthNames: [ "Januar","Februar","Marts","April","Maj","Juni",
|
||||
"Juli","August","September","Oktober","November","December" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun",
|
||||
"Jul","Aug","Sep","Okt","Nov","Dec" ],
|
||||
dayNames: [ "Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag" ],
|
||||
dayNamesShort: [ "Søn","Man","Tir","Ons","Tor","Fre","Lør" ],
|
||||
dayNamesMin: [ "Sø","Ma","Ti","On","To","Fr","Lø" ],
|
||||
weekHeader: "Uge",
|
||||
dateFormat: "dd-mm-yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.da );
|
||||
|
||||
return datepicker.regional.da;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-de.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-de.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* German initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Milian Wolff (mail@milianw.de). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.de = {
|
||||
closeText: "Schließen",
|
||||
prevText: "<Zurück",
|
||||
nextText: "Vor>",
|
||||
currentText: "Heute",
|
||||
monthNames: [ "Januar","Februar","März","April","Mai","Juni",
|
||||
"Juli","August","September","Oktober","November","Dezember" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mär","Apr","Mai","Jun",
|
||||
"Jul","Aug","Sep","Okt","Nov","Dez" ],
|
||||
dayNames: [ "Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag" ],
|
||||
dayNamesShort: [ "So","Mo","Di","Mi","Do","Fr","Sa" ],
|
||||
dayNamesMin: [ "So","Mo","Di","Mi","Do","Fr","Sa" ],
|
||||
weekHeader: "KW",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.de );
|
||||
|
||||
return datepicker.regional.de;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-el.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-el.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Greek (el) initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Alex Cicovic (http://www.alexcicovic.com) */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.el = {
|
||||
closeText: "Κλείσιμο",
|
||||
prevText: "Προηγούμενος",
|
||||
nextText: "Επόμενος",
|
||||
currentText: "Σήμερα",
|
||||
monthNames: [ "Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος",
|
||||
"Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος" ],
|
||||
monthNamesShort: [ "Ιαν","Φεβ","Μαρ","Απρ","Μαι","Ιουν",
|
||||
"Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ" ],
|
||||
dayNames: [ "Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο" ],
|
||||
dayNamesShort: [ "Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ" ],
|
||||
dayNamesMin: [ "Κυ","Δε","Τρ","Τε","Πε","Πα","Σα" ],
|
||||
weekHeader: "Εβδ",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.el );
|
||||
|
||||
return datepicker.regional.el;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-en-AU.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-en-AU.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* English/Australia initialisation for the jQuery UI date picker plugin. */
|
||||
/* Based on the en-GB initialisation. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "en-AU" ] = {
|
||||
closeText: "Done",
|
||||
prevText: "Prev",
|
||||
nextText: "Next",
|
||||
currentText: "Today",
|
||||
monthNames: [ "January","February","March","April","May","June",
|
||||
"July","August","September","October","November","December" ],
|
||||
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
|
||||
dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
|
||||
dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
|
||||
dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ],
|
||||
weekHeader: "Wk",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional[ "en-AU" ] );
|
||||
|
||||
return datepicker.regional[ "en-AU" ];
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-en-GB.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-en-GB.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* English/UK initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Stuart. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "en-GB" ] = {
|
||||
closeText: "Done",
|
||||
prevText: "Prev",
|
||||
nextText: "Next",
|
||||
currentText: "Today",
|
||||
monthNames: [ "January","February","March","April","May","June",
|
||||
"July","August","September","October","November","December" ],
|
||||
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
|
||||
dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
|
||||
dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
|
||||
dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ],
|
||||
weekHeader: "Wk",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional[ "en-GB" ] );
|
||||
|
||||
return datepicker.regional[ "en-GB" ];
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-en-NZ.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-en-NZ.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* English/New Zealand initialisation for the jQuery UI date picker plugin. */
|
||||
/* Based on the en-GB initialisation. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "en-NZ" ] = {
|
||||
closeText: "Done",
|
||||
prevText: "Prev",
|
||||
nextText: "Next",
|
||||
currentText: "Today",
|
||||
monthNames: [ "January","February","March","April","May","June",
|
||||
"July","August","September","October","November","December" ],
|
||||
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
|
||||
dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
|
||||
dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
|
||||
dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ],
|
||||
weekHeader: "Wk",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional[ "en-NZ" ] );
|
||||
|
||||
return datepicker.regional[ "en-NZ" ];
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-eo.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-eo.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Esperanto initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Olivier M. (olivierweb@ifrance.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.eo = {
|
||||
closeText: "Fermi",
|
||||
prevText: "<Anta",
|
||||
nextText: "Sekv>",
|
||||
currentText: "Nuna",
|
||||
monthNames: [ "Januaro","Februaro","Marto","Aprilo","Majo","Junio",
|
||||
"Julio","Aŭgusto","Septembro","Oktobro","Novembro","Decembro" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun",
|
||||
"Jul","Aŭg","Sep","Okt","Nov","Dec" ],
|
||||
dayNames: [ "Dimanĉo","Lundo","Mardo","Merkredo","Ĵaŭdo","Vendredo","Sabato" ],
|
||||
dayNamesShort: [ "Dim","Lun","Mar","Mer","Ĵaŭ","Ven","Sab" ],
|
||||
dayNamesMin: [ "Di","Lu","Ma","Me","Ĵa","Ve","Sa" ],
|
||||
weekHeader: "Sb",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 0,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.eo );
|
||||
|
||||
return datepicker.regional.eo;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-es.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-es.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Inicialización en español para la extensión 'UI date picker' para jQuery. */
|
||||
/* Traducido por Vester (xvester@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.es = {
|
||||
closeText: "Cerrar",
|
||||
prevText: "<Ant",
|
||||
nextText: "Sig>",
|
||||
currentText: "Hoy",
|
||||
monthNames: [ "enero","febrero","marzo","abril","mayo","junio",
|
||||
"julio","agosto","septiembre","octubre","noviembre","diciembre" ],
|
||||
monthNamesShort: [ "ene","feb","mar","abr","may","jun",
|
||||
"jul","ago","sep","oct","nov","dic" ],
|
||||
dayNames: [ "domingo","lunes","martes","miércoles","jueves","viernes","sábado" ],
|
||||
dayNamesShort: [ "dom","lun","mar","mié","jue","vie","sáb" ],
|
||||
dayNamesMin: [ "D","L","M","X","J","V","S" ],
|
||||
weekHeader: "Sm",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.es );
|
||||
|
||||
return datepicker.regional.es;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-et.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-et.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Estonian initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Mart Sõmermaa (mrts.pydev at gmail com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.et = {
|
||||
closeText: "Sulge",
|
||||
prevText: "Eelnev",
|
||||
nextText: "Järgnev",
|
||||
currentText: "Täna",
|
||||
monthNames: [ "Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni",
|
||||
"Juuli","August","September","Oktoober","November","Detsember" ],
|
||||
monthNamesShort: [ "Jaan", "Veebr", "Märts", "Apr", "Mai", "Juuni",
|
||||
"Juuli", "Aug", "Sept", "Okt", "Nov", "Dets" ],
|
||||
dayNames: [ "Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev" ],
|
||||
dayNamesShort: [ "Pühap", "Esmasp", "Teisip", "Kolmap", "Neljap", "Reede", "Laup" ],
|
||||
dayNamesMin: [ "P","E","T","K","N","R","L" ],
|
||||
weekHeader: "näd",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.et );
|
||||
|
||||
return datepicker.regional.et;
|
||||
|
||||
} ) );
|
||||
36
common/js/plugins/ui/i18n/datepicker-eu.js
Normal file
36
common/js/plugins/ui/i18n/datepicker-eu.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* Karrikas-ek itzulia (karrikas@karrikas.com) */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.eu = {
|
||||
closeText: "Egina",
|
||||
prevText: "<Aur",
|
||||
nextText: "Hur>",
|
||||
currentText: "Gaur",
|
||||
monthNames: [ "urtarrila","otsaila","martxoa","apirila","maiatza","ekaina",
|
||||
"uztaila","abuztua","iraila","urria","azaroa","abendua" ],
|
||||
monthNamesShort: [ "urt.","ots.","mar.","api.","mai.","eka.",
|
||||
"uzt.","abu.","ira.","urr.","aza.","abe." ],
|
||||
dayNames: [ "igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata" ],
|
||||
dayNamesShort: [ "ig.","al.","ar.","az.","og.","ol.","lr." ],
|
||||
dayNamesMin: [ "ig","al","ar","az","og","ol","lr" ],
|
||||
weekHeader: "As",
|
||||
dateFormat: "yy-mm-dd",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.eu );
|
||||
|
||||
return datepicker.regional.eu;
|
||||
|
||||
} ) );
|
||||
73
common/js/plugins/ui/i18n/datepicker-fa.js
Normal file
73
common/js/plugins/ui/i18n/datepicker-fa.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* Persian (Farsi) Translation for the jQuery UI date picker plugin. */
|
||||
/* Javad Mowlanezhad -- jmowla@gmail.com */
|
||||
/* Jalali calendar should supported soon! (Its implemented but I have to test it) */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.fa = {
|
||||
closeText: "بستن",
|
||||
prevText: "<قبلی",
|
||||
nextText: "بعدی>",
|
||||
currentText: "امروز",
|
||||
monthNames: [
|
||||
"ژانویه",
|
||||
"فوریه",
|
||||
"مارس",
|
||||
"آوریل",
|
||||
"مه",
|
||||
"ژوئن",
|
||||
"ژوئیه",
|
||||
"اوت",
|
||||
"سپتامبر",
|
||||
"اکتبر",
|
||||
"نوامبر",
|
||||
"دسامبر"
|
||||
],
|
||||
monthNamesShort: [ "1","2","3","4","5","6","7","8","9","10","11","12" ],
|
||||
dayNames: [
|
||||
"يکشنبه",
|
||||
"دوشنبه",
|
||||
"سهشنبه",
|
||||
"چهارشنبه",
|
||||
"پنجشنبه",
|
||||
"جمعه",
|
||||
"شنبه"
|
||||
],
|
||||
dayNamesShort: [
|
||||
"ی",
|
||||
"د",
|
||||
"س",
|
||||
"چ",
|
||||
"پ",
|
||||
"ج",
|
||||
"ش"
|
||||
],
|
||||
dayNamesMin: [
|
||||
"ی",
|
||||
"د",
|
||||
"س",
|
||||
"چ",
|
||||
"پ",
|
||||
"ج",
|
||||
"ش"
|
||||
],
|
||||
weekHeader: "هف",
|
||||
dateFormat: "yy/mm/dd",
|
||||
firstDay: 6,
|
||||
isRTL: true,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.fa );
|
||||
|
||||
return datepicker.regional.fa;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-fi.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-fi.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Finnish initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Harri Kilpiö (harrikilpio@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.fi = {
|
||||
closeText: "Sulje",
|
||||
prevText: "«Edellinen",
|
||||
nextText: "Seuraava»",
|
||||
currentText: "Tänään",
|
||||
monthNames: [ "Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu",
|
||||
"Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu" ],
|
||||
monthNamesShort: [ "Tammi","Helmi","Maalis","Huhti","Touko","Kesä",
|
||||
"Heinä","Elo","Syys","Loka","Marras","Joulu" ],
|
||||
dayNamesShort: [ "Su","Ma","Ti","Ke","To","Pe","La" ],
|
||||
dayNames: [ "Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai" ],
|
||||
dayNamesMin: [ "Su","Ma","Ti","Ke","To","Pe","La" ],
|
||||
weekHeader: "Vk",
|
||||
dateFormat: "d.m.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.fi );
|
||||
|
||||
return datepicker.regional.fi;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-fo.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-fo.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Faroese initialisation for the jQuery UI date picker plugin */
|
||||
/* Written by Sverri Mohr Olsen, sverrimo@gmail.com */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.fo = {
|
||||
closeText: "Lat aftur",
|
||||
prevText: "<Fyrra",
|
||||
nextText: "Næsta>",
|
||||
currentText: "Í dag",
|
||||
monthNames: [ "Januar","Februar","Mars","Apríl","Mei","Juni",
|
||||
"Juli","August","September","Oktober","November","Desember" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mar","Apr","Mei","Jun",
|
||||
"Jul","Aug","Sep","Okt","Nov","Des" ],
|
||||
dayNames: [ "Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","Fríggjadagur","Leyardagur" ],
|
||||
dayNamesShort: [ "Sun","Mán","Týs","Mik","Hós","Frí","Ley" ],
|
||||
dayNamesMin: [ "Su","Má","Tý","Mi","Hó","Fr","Le" ],
|
||||
weekHeader: "Vk",
|
||||
dateFormat: "dd-mm-yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.fo );
|
||||
|
||||
return datepicker.regional.fo;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-fr-CA.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-fr-CA.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Canadian-French initialisation for the jQuery UI date picker plugin. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "fr-CA" ] = {
|
||||
closeText: "Fermer",
|
||||
prevText: "Précédent",
|
||||
nextText: "Suivant",
|
||||
currentText: "Aujourd'hui",
|
||||
monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin",
|
||||
"juillet", "août", "septembre", "octobre", "novembre", "décembre" ],
|
||||
monthNamesShort: [ "janv.", "févr.", "mars", "avril", "mai", "juin",
|
||||
"juil.", "août", "sept.", "oct.", "nov.", "déc." ],
|
||||
dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ],
|
||||
dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ],
|
||||
dayNamesMin: [ "D", "L", "M", "M", "J", "V", "S" ],
|
||||
weekHeader: "Sem.",
|
||||
dateFormat: "yy-mm-dd",
|
||||
firstDay: 0,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: ""
|
||||
};
|
||||
datepicker.setDefaults( datepicker.regional[ "fr-CA" ] );
|
||||
|
||||
return datepicker.regional[ "fr-CA" ];
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-fr-CH.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-fr-CH.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Swiss-French initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written Martin Voelkle (martin.voelkle@e-tc.ch). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "fr-CH" ] = {
|
||||
closeText: "Fermer",
|
||||
prevText: "<Préc",
|
||||
nextText: "Suiv>",
|
||||
currentText: "Courant",
|
||||
monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin",
|
||||
"juillet", "août", "septembre", "octobre", "novembre", "décembre" ],
|
||||
monthNamesShort: [ "janv.", "févr.", "mars", "avril", "mai", "juin",
|
||||
"juil.", "août", "sept.", "oct.", "nov.", "déc." ],
|
||||
dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ],
|
||||
dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ],
|
||||
dayNamesMin: [ "D", "L", "M", "M", "J", "V", "S" ],
|
||||
weekHeader: "Sm",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional[ "fr-CH" ] );
|
||||
|
||||
return datepicker.regional[ "fr-CH" ];
|
||||
|
||||
} ) );
|
||||
39
common/js/plugins/ui/i18n/datepicker-fr.js
Normal file
39
common/js/plugins/ui/i18n/datepicker-fr.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* French initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Keith Wood (kbwood{at}iinet.com.au),
|
||||
Stéphane Nahmani (sholby@sholby.net),
|
||||
Stéphane Raimbault <stephane.raimbault@gmail.com> */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.fr = {
|
||||
closeText: "Fermer",
|
||||
prevText: "Précédent",
|
||||
nextText: "Suivant",
|
||||
currentText: "Aujourd'hui",
|
||||
monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin",
|
||||
"juillet", "août", "septembre", "octobre", "novembre", "décembre" ],
|
||||
monthNamesShort: [ "janv.", "févr.", "mars", "avr.", "mai", "juin",
|
||||
"juil.", "août", "sept.", "oct.", "nov.", "déc." ],
|
||||
dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ],
|
||||
dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ],
|
||||
dayNamesMin: [ "D","L","M","M","J","V","S" ],
|
||||
weekHeader: "Sem.",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.fr );
|
||||
|
||||
return datepicker.regional.fr;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-gl.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-gl.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Galician localization for 'UI date picker' jQuery extension. */
|
||||
/* Translated by Jorge Barreiro <yortx.barry@gmail.com>. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.gl = {
|
||||
closeText: "Pechar",
|
||||
prevText: "<Ant",
|
||||
nextText: "Seg>",
|
||||
currentText: "Hoxe",
|
||||
monthNames: [ "Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño",
|
||||
"Xullo","Agosto","Setembro","Outubro","Novembro","Decembro" ],
|
||||
monthNamesShort: [ "Xan","Feb","Mar","Abr","Mai","Xuñ",
|
||||
"Xul","Ago","Set","Out","Nov","Dec" ],
|
||||
dayNames: [ "Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado" ],
|
||||
dayNamesShort: [ "Dom","Lun","Mar","Mér","Xov","Ven","Sáb" ],
|
||||
dayNamesMin: [ "Do","Lu","Ma","Mé","Xo","Ve","Sá" ],
|
||||
weekHeader: "Sm",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.gl );
|
||||
|
||||
return datepicker.regional.gl;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-he.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-he.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Hebrew initialisation for the UI Datepicker extension. */
|
||||
/* Written by Amir Hardon (ahardon at gmail dot com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.he = {
|
||||
closeText: "סגור",
|
||||
prevText: "<הקודם",
|
||||
nextText: "הבא>",
|
||||
currentText: "היום",
|
||||
monthNames: [ "ינואר","פברואר","מרץ","אפריל","מאי","יוני",
|
||||
"יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר" ],
|
||||
monthNamesShort: [ "ינו","פבר","מרץ","אפר","מאי","יוני",
|
||||
"יולי","אוג","ספט","אוק","נוב","דצמ" ],
|
||||
dayNames: [ "ראשון","שני","שלישי","רביעי","חמישי","שישי","שבת" ],
|
||||
dayNamesShort: [ "א'","ב'","ג'","ד'","ה'","ו'","שבת" ],
|
||||
dayNamesMin: [ "א'","ב'","ג'","ד'","ה'","ו'","שבת" ],
|
||||
weekHeader: "Wk",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 0,
|
||||
isRTL: true,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.he );
|
||||
|
||||
return datepicker.regional.he;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-hi.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-hi.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Hindi initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Michael Dawart. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.hi = {
|
||||
closeText: "बंद",
|
||||
prevText: "पिछला",
|
||||
nextText: "अगला",
|
||||
currentText: "आज",
|
||||
monthNames: [ "जनवरी ","फरवरी","मार्च","अप्रेल","मई","जून",
|
||||
"जूलाई","अगस्त ","सितम्बर","अक्टूबर","नवम्बर","दिसम्बर" ],
|
||||
monthNamesShort: [ "जन", "फर", "मार्च", "अप्रेल", "मई", "जून",
|
||||
"जूलाई", "अग", "सित", "अक्ट", "नव", "दि" ],
|
||||
dayNames: [ "रविवार", "सोमवार", "मंगलवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार" ],
|
||||
dayNamesShort: [ "रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि" ],
|
||||
dayNamesMin: [ "रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि" ],
|
||||
weekHeader: "हफ्ता",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.hi );
|
||||
|
||||
return datepicker.regional.hi;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-hr.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-hr.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Croatian i18n for the jQuery UI date picker plugin. */
|
||||
/* Written by Vjekoslav Nesek. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.hr = {
|
||||
closeText: "Zatvori",
|
||||
prevText: "<",
|
||||
nextText: ">",
|
||||
currentText: "Danas",
|
||||
monthNames: [ "Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj",
|
||||
"Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac" ],
|
||||
monthNamesShort: [ "Sij","Velj","Ožu","Tra","Svi","Lip",
|
||||
"Srp","Kol","Ruj","Lis","Stu","Pro" ],
|
||||
dayNames: [ "Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota" ],
|
||||
dayNamesShort: [ "Ned","Pon","Uto","Sri","Čet","Pet","Sub" ],
|
||||
dayNamesMin: [ "Ne","Po","Ut","Sr","Če","Pe","Su" ],
|
||||
weekHeader: "Tje",
|
||||
dateFormat: "dd.mm.yy.",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.hr );
|
||||
|
||||
return datepicker.regional.hr;
|
||||
|
||||
} ) );
|
||||
36
common/js/plugins/ui/i18n/datepicker-hu.js
Normal file
36
common/js/plugins/ui/i18n/datepicker-hu.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* Hungarian initialisation for the jQuery UI date picker plugin. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.hu = {
|
||||
closeText: "bezár",
|
||||
prevText: "vissza",
|
||||
nextText: "előre",
|
||||
currentText: "ma",
|
||||
monthNames: [ "Január", "Február", "Március", "Április", "Május", "Június",
|
||||
"Július", "Augusztus", "Szeptember", "Október", "November", "December" ],
|
||||
monthNamesShort: [ "Jan", "Feb", "Már", "Ápr", "Máj", "Jún",
|
||||
"Júl", "Aug", "Szep", "Okt", "Nov", "Dec" ],
|
||||
dayNames: [ "Vasárnap", "Hétfő", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat" ],
|
||||
dayNamesShort: [ "Vas", "Hét", "Ked", "Sze", "Csü", "Pén", "Szo" ],
|
||||
dayNamesMin: [ "V", "H", "K", "Sze", "Cs", "P", "Szo" ],
|
||||
weekHeader: "Hét",
|
||||
dateFormat: "yy.mm.dd.",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: true,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.hu );
|
||||
|
||||
return datepicker.regional.hu;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-hy.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-hy.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Armenian(UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Levon Zakaryan (levon.zakaryan@gmail.com)*/
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.hy = {
|
||||
closeText: "Փակել",
|
||||
prevText: "<Նախ.",
|
||||
nextText: "Հաջ.>",
|
||||
currentText: "Այսօր",
|
||||
monthNames: [ "Հունվար","Փետրվար","Մարտ","Ապրիլ","Մայիս","Հունիս",
|
||||
"Հուլիս","Օգոստոս","Սեպտեմբեր","Հոկտեմբեր","Նոյեմբեր","Դեկտեմբեր" ],
|
||||
monthNamesShort: [ "Հունվ","Փետր","Մարտ","Ապր","Մայիս","Հունիս",
|
||||
"Հուլ","Օգս","Սեպ","Հոկ","Նոյ","Դեկ" ],
|
||||
dayNames: [ "կիրակի","եկուշաբթի","երեքշաբթի","չորեքշաբթի","հինգշաբթի","ուրբաթ","շաբաթ" ],
|
||||
dayNamesShort: [ "կիր","երկ","երք","չրք","հնգ","ուրբ","շբթ" ],
|
||||
dayNamesMin: [ "կիր","երկ","երք","չրք","հնգ","ուրբ","շբթ" ],
|
||||
weekHeader: "ՇԲՏ",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.hy );
|
||||
|
||||
return datepicker.regional.hy;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-id.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-id.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Indonesian initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Deden Fathurahman (dedenf@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.id = {
|
||||
closeText: "Tutup",
|
||||
prevText: "<mundur",
|
||||
nextText: "maju>",
|
||||
currentText: "hari ini",
|
||||
monthNames: [ "Januari","Februari","Maret","April","Mei","Juni",
|
||||
"Juli","Agustus","September","Oktober","Nopember","Desember" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mar","Apr","Mei","Jun",
|
||||
"Jul","Agus","Sep","Okt","Nop","Des" ],
|
||||
dayNames: [ "Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu" ],
|
||||
dayNamesShort: [ "Min","Sen","Sel","Rab","kam","Jum","Sab" ],
|
||||
dayNamesMin: [ "Mg","Sn","Sl","Rb","Km","jm","Sb" ],
|
||||
weekHeader: "Mg",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 0,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.id );
|
||||
|
||||
return datepicker.regional.id;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-is.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-is.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Icelandic initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Haukur H. Thorsson (haukur@eskill.is). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.is = {
|
||||
closeText: "Loka",
|
||||
prevText: "< Fyrri",
|
||||
nextText: "Næsti >",
|
||||
currentText: "Í dag",
|
||||
monthNames: [ "Janúar","Febrúar","Mars","Apríl","Maí","Júní",
|
||||
"Júlí","Ágúst","September","Október","Nóvember","Desember" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mar","Apr","Maí","Jún",
|
||||
"Júl","Ágú","Sep","Okt","Nóv","Des" ],
|
||||
dayNames: [ "Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur" ],
|
||||
dayNamesShort: [ "Sun","Mán","Þri","Mið","Fim","Fös","Lau" ],
|
||||
dayNamesMin: [ "Su","Má","Þr","Mi","Fi","Fö","La" ],
|
||||
weekHeader: "Vika",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 0,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.is );
|
||||
|
||||
return datepicker.regional.is;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-it-CH.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-it-CH.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Italian initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Antonello Pasella (antonello.pasella@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional[ "it-CH" ] = {
|
||||
closeText: "Chiudi",
|
||||
prevText: "<Prec",
|
||||
nextText: "Succ>",
|
||||
currentText: "Oggi",
|
||||
monthNames: [ "Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno",
|
||||
"Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre" ],
|
||||
monthNamesShort: [ "Gen","Feb","Mar","Apr","Mag","Giu",
|
||||
"Lug","Ago","Set","Ott","Nov","Dic" ],
|
||||
dayNames: [ "Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato" ],
|
||||
dayNamesShort: [ "Dom","Lun","Mar","Mer","Gio","Ven","Sab" ],
|
||||
dayNamesMin: [ "Do","Lu","Ma","Me","Gi","Ve","Sa" ],
|
||||
weekHeader: "Sm",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional[ "it-CH" ] );
|
||||
|
||||
return datepicker.regional[ "it-CH" ];
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-it.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-it.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Italian initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Antonello Pasella (antonello.pasella@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.it = {
|
||||
closeText: "Chiudi",
|
||||
prevText: "<Prec",
|
||||
nextText: "Succ>",
|
||||
currentText: "Oggi",
|
||||
monthNames: [ "Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno",
|
||||
"Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre" ],
|
||||
monthNamesShort: [ "Gen","Feb","Mar","Apr","Mag","Giu",
|
||||
"Lug","Ago","Set","Ott","Nov","Dic" ],
|
||||
dayNames: [ "Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato" ],
|
||||
dayNamesShort: [ "Dom","Lun","Mar","Mer","Gio","Ven","Sab" ],
|
||||
dayNamesMin: [ "Do","Lu","Ma","Me","Gi","Ve","Sa" ],
|
||||
weekHeader: "Sm",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.it );
|
||||
|
||||
return datepicker.regional.it;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-ja.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-ja.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Japanese initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Kentaro SATO (kentaro@ranvis.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.ja = {
|
||||
closeText: "閉じる",
|
||||
prevText: "<前",
|
||||
nextText: "次>",
|
||||
currentText: "今日",
|
||||
monthNames: [ "1月","2月","3月","4月","5月","6月",
|
||||
"7月","8月","9月","10月","11月","12月" ],
|
||||
monthNamesShort: [ "1月","2月","3月","4月","5月","6月",
|
||||
"7月","8月","9月","10月","11月","12月" ],
|
||||
dayNames: [ "日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日" ],
|
||||
dayNamesShort: [ "日","月","火","水","木","金","土" ],
|
||||
dayNamesMin: [ "日","月","火","水","木","金","土" ],
|
||||
weekHeader: "週",
|
||||
dateFormat: "yy/mm/dd",
|
||||
firstDay: 0,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: true,
|
||||
yearSuffix: "年" };
|
||||
datepicker.setDefaults( datepicker.regional.ja );
|
||||
|
||||
return datepicker.regional.ja;
|
||||
|
||||
} ) );
|
||||
35
common/js/plugins/ui/i18n/datepicker-ka.js
Normal file
35
common/js/plugins/ui/i18n/datepicker-ka.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* Georgian (UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Lado Lomidze (lado.lomidze@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.ka = {
|
||||
closeText: "დახურვა",
|
||||
prevText: "< წინა",
|
||||
nextText: "შემდეგი >",
|
||||
currentText: "დღეს",
|
||||
monthNames: [ "იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი", "ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი" ],
|
||||
monthNamesShort: [ "იან","თებ","მარ","აპრ","მაი","ივნ", "ივლ","აგვ","სექ","ოქტ","ნოე","დეკ" ],
|
||||
dayNames: [ "კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი" ],
|
||||
dayNamesShort: [ "კვ","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ" ],
|
||||
dayNamesMin: [ "კვ","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ" ],
|
||||
weekHeader: "კვირა",
|
||||
dateFormat: "dd-mm-yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.ka );
|
||||
|
||||
return datepicker.regional.ka;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-kk.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-kk.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Kazakh (UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Dmitriy Karasyov (dmitriy.karasyov@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.kk = {
|
||||
closeText: "Жабу",
|
||||
prevText: "<Алдыңғы",
|
||||
nextText: "Келесі>",
|
||||
currentText: "Бүгін",
|
||||
monthNames: [ "Қаңтар","Ақпан","Наурыз","Сәуір","Мамыр","Маусым",
|
||||
"Шілде","Тамыз","Қыркүйек","Қазан","Қараша","Желтоқсан" ],
|
||||
monthNamesShort: [ "Қаң","Ақп","Нау","Сәу","Мам","Мау",
|
||||
"Шіл","Там","Қыр","Қаз","Қар","Жел" ],
|
||||
dayNames: [ "Жексенбі","Дүйсенбі","Сейсенбі","Сәрсенбі","Бейсенбі","Жұма","Сенбі" ],
|
||||
dayNamesShort: [ "жкс","дсн","ссн","срс","бсн","жма","снб" ],
|
||||
dayNamesMin: [ "Жк","Дс","Сс","Ср","Бс","Жм","Сн" ],
|
||||
weekHeader: "Не",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.kk );
|
||||
|
||||
return datepicker.regional.kk;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-km.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-km.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Khmer initialisation for the jQuery calendar extension. */
|
||||
/* Written by Chandara Om (chandara.teacher@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.km = {
|
||||
closeText: "ធ្វើរួច",
|
||||
prevText: "មុន",
|
||||
nextText: "បន្ទាប់",
|
||||
currentText: "ថ្ងៃនេះ",
|
||||
monthNames: [ "មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា",
|
||||
"កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ" ],
|
||||
monthNamesShort: [ "មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា",
|
||||
"កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ" ],
|
||||
dayNames: [ "អាទិត្យ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រហស្បតិ៍", "សុក្រ", "សៅរ៍" ],
|
||||
dayNamesShort: [ "អា", "ច", "អ", "ពុ", "ព្រហ", "សុ", "សៅ" ],
|
||||
dayNamesMin: [ "អា", "ច", "អ", "ពុ", "ព្រហ", "សុ", "សៅ" ],
|
||||
weekHeader: "សប្ដាហ៍",
|
||||
dateFormat: "dd-mm-yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.km );
|
||||
|
||||
return datepicker.regional.km;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-ko.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-ko.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Korean initialisation for the jQuery calendar extension. */
|
||||
/* Written by DaeKwon Kang (ncrash.dk@gmail.com), Edited by Genie and Myeongjin Lee. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.ko = {
|
||||
closeText: "닫기",
|
||||
prevText: "이전달",
|
||||
nextText: "다음달",
|
||||
currentText: "오늘",
|
||||
monthNames: [ "1월","2월","3월","4월","5월","6월",
|
||||
"7월","8월","9월","10월","11월","12월" ],
|
||||
monthNamesShort: [ "1월","2월","3월","4월","5월","6월",
|
||||
"7월","8월","9월","10월","11월","12월" ],
|
||||
dayNames: [ "일요일","월요일","화요일","수요일","목요일","금요일","토요일" ],
|
||||
dayNamesShort: [ "일","월","화","수","목","금","토" ],
|
||||
dayNamesMin: [ "일","월","화","수","목","금","토" ],
|
||||
weekHeader: "주",
|
||||
dateFormat: "yy. m. d.",
|
||||
firstDay: 0,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: true,
|
||||
yearSuffix: "년" };
|
||||
datepicker.setDefaults( datepicker.regional.ko );
|
||||
|
||||
return datepicker.regional.ko;
|
||||
|
||||
} ) );
|
||||
38
common/js/plugins/ui/i18n/datepicker-ky.js
Normal file
38
common/js/plugins/ui/i18n/datepicker-ky.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* Kyrgyz (UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Sergey Kartashov (ebishkek@yandex.ru). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.ky = {
|
||||
closeText: "Жабуу",
|
||||
prevText: "<Мур",
|
||||
nextText: "Кий>",
|
||||
currentText: "Бүгүн",
|
||||
monthNames: [ "Январь","Февраль","Март","Апрель","Май","Июнь",
|
||||
"Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь" ],
|
||||
monthNamesShort: [ "Янв","Фев","Мар","Апр","Май","Июн",
|
||||
"Июл","Авг","Сен","Окт","Ноя","Дек" ],
|
||||
dayNames: [ "жекшемби", "дүйшөмбү", "шейшемби", "шаршемби", "бейшемби", "жума", "ишемби" ],
|
||||
dayNamesShort: [ "жек", "дүй", "шей", "шар", "бей", "жум", "ише" ],
|
||||
dayNamesMin: [ "Жк","Дш","Шш","Шр","Бш","Жм","Иш" ],
|
||||
weekHeader: "Жум",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: ""
|
||||
};
|
||||
datepicker.setDefaults( datepicker.regional.ky );
|
||||
|
||||
return datepicker.regional.ky;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-lb.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-lb.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Luxembourgish initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Michel Weimerskirch <michel@weimerskirch.net> */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.lb = {
|
||||
closeText: "Fäerdeg",
|
||||
prevText: "Zréck",
|
||||
nextText: "Weider",
|
||||
currentText: "Haut",
|
||||
monthNames: [ "Januar","Februar","Mäerz","Abrëll","Mee","Juni",
|
||||
"Juli","August","September","Oktober","November","Dezember" ],
|
||||
monthNamesShort: [ "Jan", "Feb", "Mäe", "Abr", "Mee", "Jun",
|
||||
"Jul", "Aug", "Sep", "Okt", "Nov", "Dez" ],
|
||||
dayNames: [ "Sonndeg", "Méindeg", "Dënschdeg", "Mëttwoch", "Donneschdeg", "Freideg", "Samschdeg" ],
|
||||
dayNamesShort: [ "Son", "Méi", "Dën", "Mët", "Don", "Fre", "Sam" ],
|
||||
dayNamesMin: [ "So","Mé","Dë","Më","Do","Fr","Sa" ],
|
||||
weekHeader: "W",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.lb );
|
||||
|
||||
return datepicker.regional.lb;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-lt.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-lt.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Lithuanian (UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* @author Arturas Paleicikas <arturas@avalon.lt> */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.lt = {
|
||||
closeText: "Uždaryti",
|
||||
prevText: "<Atgal",
|
||||
nextText: "Pirmyn>",
|
||||
currentText: "Šiandien",
|
||||
monthNames: [ "Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis",
|
||||
"Liepa","Rugpjūtis","Rugsėjis","Spalis","Lapkritis","Gruodis" ],
|
||||
monthNamesShort: [ "Sau","Vas","Kov","Bal","Geg","Bir",
|
||||
"Lie","Rugp","Rugs","Spa","Lap","Gru" ],
|
||||
dayNames: [ "sekmadienis","pirmadienis","antradienis","trečiadienis","ketvirtadienis","penktadienis","šeštadienis" ],
|
||||
dayNamesShort: [ "sek","pir","ant","tre","ket","pen","šeš" ],
|
||||
dayNamesMin: [ "Se","Pr","An","Tr","Ke","Pe","Še" ],
|
||||
weekHeader: "SAV",
|
||||
dateFormat: "yy-mm-dd",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: true,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.lt );
|
||||
|
||||
return datepicker.regional.lt;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-lv.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-lv.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Latvian (UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* @author Arturas Paleicikas <arturas.paleicikas@metasite.net> */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.lv = {
|
||||
closeText: "Aizvērt",
|
||||
prevText: "Iepr.",
|
||||
nextText: "Nāk.",
|
||||
currentText: "Šodien",
|
||||
monthNames: [ "Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs",
|
||||
"Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris" ],
|
||||
monthNamesShort: [ "Jan","Feb","Mar","Apr","Mai","Jūn",
|
||||
"Jūl","Aug","Sep","Okt","Nov","Dec" ],
|
||||
dayNames: [ "svētdiena","pirmdiena","otrdiena","trešdiena","ceturtdiena","piektdiena","sestdiena" ],
|
||||
dayNamesShort: [ "svt","prm","otr","tre","ctr","pkt","sst" ],
|
||||
dayNamesMin: [ "Sv","Pr","Ot","Tr","Ct","Pk","Ss" ],
|
||||
weekHeader: "Ned.",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.lv );
|
||||
|
||||
return datepicker.regional.lv;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-mk.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-mk.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Macedonian i18n for the jQuery UI date picker plugin. */
|
||||
/* Written by Stojce Slavkovski. */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.mk = {
|
||||
closeText: "Затвори",
|
||||
prevText: "<",
|
||||
nextText: ">",
|
||||
currentText: "Денес",
|
||||
monthNames: [ "Јануари","Февруари","Март","Април","Мај","Јуни",
|
||||
"Јули","Август","Септември","Октомври","Ноември","Декември" ],
|
||||
monthNamesShort: [ "Јан","Фев","Мар","Апр","Мај","Јун",
|
||||
"Јул","Авг","Сеп","Окт","Ное","Дек" ],
|
||||
dayNames: [ "Недела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота" ],
|
||||
dayNamesShort: [ "Нед","Пон","Вто","Сре","Чет","Пет","Саб" ],
|
||||
dayNamesMin: [ "Не","По","Вт","Ср","Че","Пе","Са" ],
|
||||
weekHeader: "Сед",
|
||||
dateFormat: "dd.mm.yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.mk );
|
||||
|
||||
return datepicker.regional.mk;
|
||||
|
||||
} ) );
|
||||
37
common/js/plugins/ui/i18n/datepicker-ml.js
Normal file
37
common/js/plugins/ui/i18n/datepicker-ml.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Malayalam (UTF-8) initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Saji Nediyanchath (saji89@gmail.com). */
|
||||
( function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "../widgets/datepicker" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery.datepicker );
|
||||
}
|
||||
}( function( datepicker ) {
|
||||
|
||||
datepicker.regional.ml = {
|
||||
closeText: "ശരി",
|
||||
prevText: "മുന്നത്തെ",
|
||||
nextText: "അടുത്തത് ",
|
||||
currentText: "ഇന്ന്",
|
||||
monthNames: [ "ജനുവരി","ഫെബ്രുവരി","മാര്ച്ച്","ഏപ്രില്","മേയ്","ജൂണ്",
|
||||
"ജൂലൈ","ആഗസ്റ്റ്","സെപ്റ്റംബര്","ഒക്ടോബര്","നവംബര്","ഡിസംബര്" ],
|
||||
monthNamesShort: [ "ജനു", "ഫെബ്", "മാര്", "ഏപ്രി", "മേയ്", "ജൂണ്",
|
||||
"ജൂലാ", "ആഗ", "സെപ്", "ഒക്ടോ", "നവം", "ഡിസ" ],
|
||||
dayNames: [ "ഞായര്", "തിങ്കള്", "ചൊവ്വ", "ബുധന്", "വ്യാഴം", "വെള്ളി", "ശനി" ],
|
||||
dayNamesShort: [ "ഞായ", "തിങ്ക", "ചൊവ്വ", "ബുധ", "വ്യാഴം", "വെള്ളി", "ശനി" ],
|
||||
dayNamesMin: [ "ഞാ","തി","ചൊ","ബു","വ്യാ","വെ","ശ" ],
|
||||
weekHeader: "ആ",
|
||||
dateFormat: "dd/mm/yy",
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: "" };
|
||||
datepicker.setDefaults( datepicker.regional.ml );
|
||||
|
||||
return datepicker.regional.ml;
|
||||
|
||||
} ) );
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue