diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..d430f2cc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +### TorrentPier ### +.idea/ +data/avatars/**/ +data/old_files/ +data/torrent_files/ +internal_data/ajax_html/*.html +internal_data/atom/ +internal_data/cache/ +internal_data/captcha/**/ +internal_data/log/ +internal_data/sitemap/*.xml +internal_data/triggers/ +library/config.local.php + +### Archives ### +*.log +*.zip +*.rar +*.tar +*.gz +*.torrent + +### Windows ### +Thumbs.db +Desktop.ini +$RECYCLE.BIN/ +*.lnk + +### OSX ### +.DS_Store +.AppleDouble +.LSOverride +._* +.Spotlight-V100 +.Trashes \ No newline at end of file diff --git a/.htaccess b/.htaccess new file mode 100644 index 000000000..8298e9bdd --- /dev/null +++ b/.htaccess @@ -0,0 +1,18 @@ +## set default server charset +AddDefaultCharset UTF-8 + +## folder listing access control +Options All -Indexes + +## sitemap and atom rewrite +RewriteEngine On +RewriteRule ^sitemap.xml$ internal_data/sitemap/sitemap.xml [L] +RewriteRule ^/internal_data/atom/(.*) /atom$1 [L] + +## deny access to git folder +RedirectMatch 404 /\\.git(/|$) + +## deny access to system files + +deny from all + \ No newline at end of file diff --git a/contributors.txt b/CONTRIBUTORS.md similarity index 99% rename from contributors.txt rename to CONTRIBUTORS.md index c88224b24..7c8c5d101 100644 --- a/contributors.txt +++ b/CONTRIBUTORS.md @@ -36,6 +36,7 @@ nord51 Вася Alexander.S (http://torrent.dchub.ws/) sasha20072007 +gerhanovn ***************************** ** Прочая информация ** diff --git a/README.md b/README.md index 03597f610..af7f6a07f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ TorrentPier II - движок торрент-трекера, написанны Для установки вам необходимо выполнить несколько простых шагов: -1. Распаковываем на сервер содержимое папки **upload** +1. Распаковываем на сервер содержимое скачанной вами папки 2. Создаем базу данных, в которую при помощи phpmyadmin (или любого другого удобного инструмента) импортируем дамп, расположенный в папке **install/sql/mysql.sql** 3. Правим файл конфигурации **library/config.php**, загруженный на сервер: @@ -66,6 +66,10 @@ TorrentPier II - движок торрент-трекера, написанны Инструкция по сборке приведена на нашем форуме: https://torrentpier.me/threads/sborka-ocelot-pod-debian-7-1.26078/ Для работы анонсера требуется замена двух таблиц в базе данных - дамп в файле: **install/sql/ocelot.sql** +## Папка install + +В корне движка присутствует папка **install**, в которой находятся служебные файлы, необходимые для его установки (дамп базы, примеры конфигов) и обновления (дамперы, скрипты конвертации). Доступ к данной папке по-умолчанию закрыт, но если ее присутствие вам мешает - вы можете ее удалить. На файлы **README.md** и **CONTRIBUTORS.md** это также распространяется. + ## Полезные ссылки + Наш форум https://torrentpier.me/ diff --git a/upload/admin/admin_attach_cp.php b/admin/admin_attach_cp.php similarity index 100% rename from upload/admin/admin_attach_cp.php rename to admin/admin_attach_cp.php diff --git a/upload/admin/admin_attachments.php b/admin/admin_attachments.php similarity index 100% rename from upload/admin/admin_attachments.php rename to admin/admin_attachments.php diff --git a/upload/admin/admin_board.php b/admin/admin_board.php similarity index 100% rename from upload/admin/admin_board.php rename to admin/admin_board.php diff --git a/upload/admin/admin_bt_forum_cfg.php b/admin/admin_bt_forum_cfg.php similarity index 100% rename from upload/admin/admin_bt_forum_cfg.php rename to admin/admin_bt_forum_cfg.php diff --git a/upload/admin/admin_bt_tracker_cfg.php b/admin/admin_bt_tracker_cfg.php similarity index 100% rename from upload/admin/admin_bt_tracker_cfg.php rename to admin/admin_bt_tracker_cfg.php diff --git a/upload/admin/admin_cron.php b/admin/admin_cron.php similarity index 100% rename from upload/admin/admin_cron.php rename to admin/admin_cron.php diff --git a/upload/admin/admin_disallow.php b/admin/admin_disallow.php similarity index 100% rename from upload/admin/admin_disallow.php rename to admin/admin_disallow.php diff --git a/upload/admin/admin_extensions.php b/admin/admin_extensions.php similarity index 100% rename from upload/admin/admin_extensions.php rename to admin/admin_extensions.php diff --git a/upload/admin/admin_forum_prune.php b/admin/admin_forum_prune.php similarity index 100% rename from upload/admin/admin_forum_prune.php rename to admin/admin_forum_prune.php diff --git a/upload/admin/admin_forumauth.php b/admin/admin_forumauth.php similarity index 100% rename from upload/admin/admin_forumauth.php rename to admin/admin_forumauth.php diff --git a/upload/admin/admin_forumauth_list.php b/admin/admin_forumauth_list.php similarity index 100% rename from upload/admin/admin_forumauth_list.php rename to admin/admin_forumauth_list.php diff --git a/upload/admin/admin_forums.php b/admin/admin_forums.php similarity index 100% rename from upload/admin/admin_forums.php rename to admin/admin_forums.php diff --git a/upload/admin/admin_groups.php b/admin/admin_groups.php similarity index 100% rename from upload/admin/admin_groups.php rename to admin/admin_groups.php diff --git a/upload/admin/admin_log.php b/admin/admin_log.php similarity index 100% rename from upload/admin/admin_log.php rename to admin/admin_log.php diff --git a/upload/admin/admin_mass_email.php b/admin/admin_mass_email.php similarity index 100% rename from upload/admin/admin_mass_email.php rename to admin/admin_mass_email.php diff --git a/upload/admin/admin_phpinfo.php b/admin/admin_phpinfo.php similarity index 100% rename from upload/admin/admin_phpinfo.php rename to admin/admin_phpinfo.php diff --git a/upload/admin/admin_ranks.php b/admin/admin_ranks.php similarity index 100% rename from upload/admin/admin_ranks.php rename to admin/admin_ranks.php diff --git a/upload/admin/admin_rebuild_search.php b/admin/admin_rebuild_search.php similarity index 100% rename from upload/admin/admin_rebuild_search.php rename to admin/admin_rebuild_search.php diff --git a/upload/admin/admin_sitemap.php b/admin/admin_sitemap.php similarity index 100% rename from upload/admin/admin_sitemap.php rename to admin/admin_sitemap.php diff --git a/upload/admin/admin_smilies.php b/admin/admin_smilies.php similarity index 100% rename from upload/admin/admin_smilies.php rename to admin/admin_smilies.php diff --git a/upload/admin/admin_terms.php b/admin/admin_terms.php similarity index 100% rename from upload/admin/admin_terms.php rename to admin/admin_terms.php diff --git a/upload/admin/admin_ug_auth.php b/admin/admin_ug_auth.php similarity index 100% rename from upload/admin/admin_ug_auth.php rename to admin/admin_ug_auth.php diff --git a/upload/admin/admin_user_ban.php b/admin/admin_user_ban.php similarity index 100% rename from upload/admin/admin_user_ban.php rename to admin/admin_user_ban.php diff --git a/upload/admin/admin_user_search.php b/admin/admin_user_search.php similarity index 100% rename from upload/admin/admin_user_search.php rename to admin/admin_user_search.php diff --git a/upload/admin/admin_words.php b/admin/admin_words.php similarity index 100% rename from upload/admin/admin_words.php rename to admin/admin_words.php diff --git a/upload/admin/index.php b/admin/index.php similarity index 100% rename from upload/admin/index.php rename to admin/index.php diff --git a/upload/admin/pagestart.php b/admin/pagestart.php similarity index 100% rename from upload/admin/pagestart.php rename to admin/pagestart.php diff --git a/upload/admin/stats/tr_stats.php b/admin/stats/tr_stats.php similarity index 100% rename from upload/admin/stats/tr_stats.php rename to admin/stats/tr_stats.php diff --git a/upload/admin/stats/tracker.php b/admin/stats/tracker.php similarity index 100% rename from upload/admin/stats/tracker.php rename to admin/stats/tracker.php diff --git a/upload/ajax.php b/ajax.php similarity index 100% rename from upload/ajax.php rename to ajax.php diff --git a/upload/bt/announce.php b/bt/announce.php similarity index 99% rename from upload/bt/announce.php rename to bt/announce.php index 15bc122a4..70fc72331 100644 --- a/upload/bt/announce.php +++ b/bt/announce.php @@ -187,7 +187,7 @@ function msg_die ($msg) // Start announcer define('TR_ROOT', './'); -require(TR_ROOT .'includes/init_tr.php'); +require(TR_ROOT . 'includes/init_tr.php'); $seeder = ($left == 0) ? 1 : 0; $stopped = ($event === 'stopped'); diff --git a/upload/bt/includes/.htaccess b/bt/includes/.htaccess similarity index 100% rename from upload/bt/includes/.htaccess rename to bt/includes/.htaccess diff --git a/upload/bt/includes/init_tr.php b/bt/includes/init_tr.php similarity index 100% rename from upload/bt/includes/init_tr.php rename to bt/includes/init_tr.php diff --git a/upload/bt/index.php b/bt/index.php similarity index 100% rename from upload/bt/index.php rename to bt/index.php diff --git a/upload/bt/robots.txt b/bt/robots.txt similarity index 100% rename from upload/bt/robots.txt rename to bt/robots.txt diff --git a/upload/bt/scrape.php b/bt/scrape.php similarity index 96% rename from upload/bt/scrape.php rename to bt/scrape.php index 1821161ef..e09ebbb9b 100644 --- a/upload/bt/scrape.php +++ b/bt/scrape.php @@ -33,7 +33,7 @@ function msg_die ($msg) } define('TR_ROOT', './'); -require(TR_ROOT .'includes/init_tr.php'); +require(TR_ROOT . 'includes/init_tr.php'); $info_hash_sql = rtrim(DB()->escape($info_hash), ' '); diff --git a/upload/callseed.php b/callseed.php similarity index 100% rename from upload/callseed.php rename to callseed.php diff --git a/common.php b/common.php new file mode 100644 index 000000000..0bf328a70 --- /dev/null +++ b/common.php @@ -0,0 +1,507 @@ + true)); +$loader->register(); + +$server_protocol = ($bb_cfg['cookie_secure']) ? 'https://' : 'http://'; +$server_port = (in_array($bb_cfg['server_port'], array(80, 443))) ? '' : ':' . $bb_cfg['server_port']; +define('FORUM_PATH', $bb_cfg['script_path']); +define('FULL_URL', $server_protocol . $bb_cfg['server_name'] . $server_port . $bb_cfg['script_path']); +unset($server_protocol, $server_port); + +// Debug options +define('DBG_USER', (isset($_COOKIE[COOKIE_DBG]))); + +// Board/Tracker shared constants and functions +define('BB_BT_TORRENTS', 'bb_bt_torrents'); +define('BB_BT_TRACKER', 'bb_bt_tracker'); +define('BB_BT_TRACKER_SNAP', 'bb_bt_tracker_snap'); +define('BB_BT_USERS', 'bb_bt_users'); + +define('BT_AUTH_KEY_LENGTH', 10); + +define('PEER_HASH_PREFIX', 'peer_'); +define('PEERS_LIST_PREFIX', 'peers_list_'); +define('PEER_HASH_EXPIRE', round($bb_cfg['announce_interval'] * (0.85 * $tr_cfg['expire_factor']))); // sec +define('PEERS_LIST_EXPIRE', round($bb_cfg['announce_interval'] * 0.7)); // sec + +define('DL_STATUS_RELEASER', -1); +define('DL_STATUS_DOWN', 0); +define('DL_STATUS_COMPLETE', 1); +define('DL_STATUS_CANCEL', 3); +define('DL_STATUS_WILL', 4); + +define('TOR_TYPE_GOLD', 1); +define('TOR_TYPE_SILVER', 2); + +define('GUEST_UID', -1); +define('BOT_UID', -746); + +/** + * Database + */ +// Core DB class +require(CORE_DIR . 'dbs.php'); +$DBS = new DBS($bb_cfg); + +function DB ($db_alias = 'db1') +{ + global $DBS; + return $DBS->get_db_obj($db_alias); +} + +/** + * Cache + */ +// Main cache class +require(INC_DIR . 'cache/common.php'); +// Main datastore class +require(INC_DIR . 'datastore/common.php'); + +// Core CACHE class +require(CORE_DIR . 'caches.php'); +$CACHES = new CACHES($bb_cfg); + +function CACHE ($cache_name) +{ + global $CACHES; + return $CACHES->get_cache_obj($cache_name); +} + +// Common cache classes +require(INC_DIR . 'cache/memcache.php'); +require(INC_DIR . 'cache/sqlite.php'); +require(INC_DIR . 'cache/redis.php'); +require(INC_DIR . 'cache/apc.php'); +require(INC_DIR . 'cache/xcache.php'); +require(INC_DIR . 'cache/file.php'); + +/** +* Datastore +*/ +// Common datastore classes +require(INC_DIR . 'datastore/memcache.php'); +require(INC_DIR . 'datastore/sqlite.php'); +require(INC_DIR . 'datastore/redis.php'); +require(INC_DIR . 'datastore/apc.php'); +require(INC_DIR . 'datastore/xcache.php'); +require(INC_DIR . 'datastore/file.php'); + +// Initialize datastore +switch ($bb_cfg['datastore_type']) +{ + case 'memcache': + $datastore = new datastore_memcache($bb_cfg['cache']['memcache'], $bb_cfg['cache']['prefix']); + break; + + case 'sqlite': + $default_cfg = array( + 'db_file_path' => $bb_cfg['cache']['db_dir'] .'datastore.sqlite.db', + 'pconnect' => true, + 'con_required' => true, + ); + $datastore = new datastore_sqlite($default_cfg, $bb_cfg['cache']['prefix']); + break; + + case 'redis': + $datastore = new datastore_redis($bb_cfg['cache']['redis'], $bb_cfg['cache']['prefix']); + break; + + case 'apc': + $datastore = new datastore_apc($bb_cfg['cache']['prefix']); + break; + + case 'xcache': + $datastore = new datastore_xcache($bb_cfg['cache']['prefix']); + break; + + case 'filecache': + default: $datastore = new datastore_file($bb_cfg['cache']['db_dir'] . 'datastore/', $bb_cfg['cache']['prefix']); +} + +function sql_dbg_enabled () +{ + return (SQL_DEBUG && DBG_USER && !empty($_COOKIE['sql_log'])); +} + +function short_query ($sql, $esc_html = false) +{ + $max_len = 100; + $sql = str_compact($sql); + + if (!empty($_COOKIE['sql_log_full'])) + { + if (mb_strlen($sql, 'UTF-8') > $max_len) + { + $sql = mb_substr($sql, 0, 50) .' [...cut...] '. mb_substr($sql, -50); + } + } + + return ($esc_html) ? htmlCHR($sql, true) : $sql; +} + +// Functions +function utime () +{ + return array_sum(explode(' ', microtime())); +} + +function bb_log ($msg, $file_name) +{ + if (is_array($msg)) + { + $msg = join(LOG_LF, $msg); + } + $file_name .= (LOG_EXT) ? '.'. LOG_EXT : ''; + return file_write($msg, LOG_DIR . $file_name); +} + +function file_write ($str, $file, $max_size = LOG_MAX_SIZE, $lock = true, $replace_content = false) +{ + $bytes_written = false; + + if ($max_size && @filesize($file) >= $max_size) + { + $old_name = $file; $ext = ''; + if (preg_match('#^(.+)(\.[^\\/]+)$#', $file, $matches)) + { + $old_name = $matches[1]; $ext = $matches[2]; + } + $new_name = $old_name .'_[old]_'. date('Y-m-d_H-i-s_') . getmypid() . $ext; + clearstatcache(); + if (@file_exists($file) && @filesize($file) >= $max_size && !@file_exists($new_name)) + { + @rename($file, $new_name); + } + } + if (!$fp = @fopen($file, 'ab')) + { + if ($dir_created = bb_mkdir(dirname($file))) + { + $fp = @fopen($file, 'ab'); + } + } + if ($fp) + { + if ($lock) + { + @flock($fp, LOCK_EX); + } + if ($replace_content) + { + @ftruncate($fp, 0); + @fseek($fp, 0, SEEK_SET); + } + $bytes_written = @fwrite($fp, $str); + @fclose($fp); + } + + return $bytes_written; +} + +function bb_mkdir ($path, $mode = 0777) +{ + $old_um = umask(0); + $dir = mkdir_rec($path, $mode); + umask($old_um); + return $dir; +} + +function mkdir_rec ($path, $mode) +{ + if (is_dir($path)) + { + return ($path !== '.' && $path !== '..') ? is_writable($path) : false; + } + else + { + return (mkdir_rec(dirname($path), $mode)) ? @mkdir($path, $mode) : false; + } +} + +function verify_id ($id, $length) +{ + return (is_string($id) && preg_match('#^[a-zA-Z0-9]{'. $length .'}$#', $id)); +} + +function clean_filename ($fname) +{ + static $s = array('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' '); + return str_replace($s, '_', str_compact($fname)); +} + +function encode_ip ($ip) +{ + $d = explode('.', $ip); + return sprintf('%02x%02x%02x%02x', $d[0], $d[1], $d[2], $d[3]); +} + +function decode_ip ($ip) +{ + return long2ip("0x{$ip}"); +} + +function ip2int ($ip) +{ + return (float) sprintf('%u', ip2long($ip)); // для совместимости с 32 битными системами +} + +// long2ip( mask_ip_int(ip2int('1.2.3.4'), 24) ) = '1.2.3.255' +function mask_ip_int ($ip, $mask) +{ + $ip_int = is_numeric($ip) ? $ip : ip2int($ip); + $ip_masked = $ip_int | ((1 << (32 - $mask)) - 1); + return (float) sprintf('%u', $ip_masked); +} + +function bb_crc32 ($str) +{ + return (float) sprintf('%u', crc32($str)); +} + +function hexhex ($value) +{ + return dechex(hexdec($value)); +} + +function verify_ip ($ip) +{ + return preg_match('#^(\d{1,3}\.){3}\d{1,3}$#', $ip); +} + +function str_compact ($str) +{ + return preg_replace('#\s+#u', ' ', trim($str)); +} + +function make_rand_str ($len = 10) +{ + $str = ''; + while (strlen($str) < $len) + { + $str .= str_shuffle(preg_replace('#[^0-9a-zA-Z]#', '', crypt(uniqid(mt_rand(), true)))); + } + return substr($str, 0, $len); +} + +// bencode: based on OpenTracker +function bencode ($var) +{ + if (is_string($var)) + { + return strlen($var) .':'. $var; + } + else if (is_int($var)) + { + return 'i'. $var .'e'; + } + else if (is_float($var)) + { + return 'i'. sprintf('%.0f', $var) .'e'; + } + else if (is_array($var)) + { + if (count($var) == 0) + { + return 'de'; + } + else + { + $assoc = false; + + foreach ($var as $key => $val) + { + if (!is_int($key)) + { + $assoc = true; + break; + } + } + + if ($assoc) + { + ksort($var, SORT_REGULAR); + $ret = 'd'; + + foreach ($var as $key => $val) + { + $ret .= bencode($key) . bencode($val); + } + return $ret .'e'; + } + else + { + $ret = 'l'; + + foreach ($var as $val) + { + $ret .= bencode($val); + } + return $ret .'e'; + } + } + } + else + { + trigger_error('bencode error: wrong data type', E_USER_ERROR); + } +} + +function array_deep (&$var, $fn, $one_dimensional = false, $array_only = false) +{ + if (is_array($var)) + { + foreach ($var as $k => $v) + { + if (is_array($v)) + { + if ($one_dimensional) + { + unset($var[$k]); + } + else if ($array_only) + { + $var[$k] = $fn($v); + } + else + { + array_deep($var[$k], $fn); + } + } + else if (!$array_only) + { + $var[$k] = $fn($v); + } + } + } + else if (!$array_only) + { + $var = $fn($var); + } +} + +function hide_bb_path ($path) +{ + return ltrim(str_replace(BB_PATH, '', $path), '/\\'); +} + +function sys ($param) +{ + switch ($param) + { + case 'la': + return function_exists('sys_getloadavg') ? join(' ', sys_getloadavg()) : 0; + break; + case 'mem': + return function_exists('memory_get_usage') ? memory_get_usage() : 0; + break; + case 'mem_peak': + return function_exists('memory_get_peak_usage') ? memory_get_peak_usage() : 0; + break; + default: + trigger_error("invalid param: $param", E_USER_ERROR); + } +} + +function ver_compare ($version1, $operator, $version2) +{ + return version_compare($version1, $version2, $operator); +} + +function dbg_log ($str, $file) +{ + $dir = LOG_DIR . (defined('IN_TRACKER') ? 'dbg_tr/' : 'dbg_bb/') . date('m-d_H') .'/'; + return file_write($str, $dir . $file, false, false); +} + +function log_get ($file = '', $prepend_str = false) +{ + log_request($file, $prepend_str, false); +} + +function log_post ($file = '', $prepend_str = false) +{ + log_request($file, $prepend_str, true); +} + +function log_request ($file = '', $prepend_str = false, $add_post = true) +{ + global $user; + + $file = ($file) ? $file : 'req/'. date('m-d'); + $str = array(); + $str[] = date('m-d H:i:s'); + if ($prepend_str !== false) $str[] = $prepend_str; + if (!empty($user->data)) $str[] = $user->id ."\t". html_entity_decode($user->name); + $str[] = sprintf('%-15s', $_SERVER['REMOTE_ADDR']); + + if (isset($_SERVER['REQUEST_URI'])) { + $str[] = $_SERVER['REQUEST_URI']; + } + if (isset($_SERVER['HTTP_USER_AGENT'])) { + $str[] = $_SERVER['HTTP_USER_AGENT']; + } + if (isset($_SERVER['HTTP_REFERER'])) { + $str[] = $_SERVER['HTTP_REFERER']; + } + + if (!empty($_POST) && $add_post) $str[] = "post: ". str_compact(urldecode(http_build_query($_POST))); + $str = join("\t", $str) . "\n"; + bb_log($str, $file); +} + +// Board init +if (defined('IN_FORUM')) +{ + require(INC_DIR .'init_bb.php'); +} +// Tracker init +else if (defined('IN_TRACKER')) +{ + define('DUMMY_PEER', pack('Nn', ip2long($_SERVER['REMOTE_ADDR']), !empty($_GET['port']) ? intval($_GET['port']) : mt_rand(1000, 65000))); + + function dummy_exit ($interval = 1800) + { + $output = bencode(array( + 'interval' => (int) $interval, + 'min interval' => (int) $interval, + 'peers' => (string) DUMMY_PEER, + )); + + die($output); + } + + header('Content-Type: text/plain'); + header('Pragma: no-cache'); + + if (!defined('IN_ADMIN')) + { + // Exit if tracker is disabled via ON/OFF trigger + if (file_exists(BB_DISABLED)) + { + dummy_exit(mt_rand(60, 2400)); + } + } +} \ No newline at end of file diff --git a/upload/cron.php b/cron.php similarity index 100% rename from upload/cron.php rename to cron.php diff --git a/upload/crossdomain.xml b/crossdomain.xml similarity index 100% rename from upload/crossdomain.xml rename to crossdomain.xml diff --git a/upload/data/avatars/gallery/bot.gif b/data/avatars/gallery/bot.gif similarity index 100% rename from upload/data/avatars/gallery/bot.gif rename to data/avatars/gallery/bot.gif diff --git a/upload/data/avatars/gallery/noavatar.png b/data/avatars/gallery/noavatar.png similarity index 100% rename from upload/data/avatars/gallery/noavatar.png rename to data/avatars/gallery/noavatar.png diff --git a/upload/data/old_files/.htaccess b/data/old_files/.htaccess similarity index 100% rename from upload/data/old_files/.htaccess rename to data/old_files/.htaccess diff --git a/upload/data/torrent_files/.htaccess b/data/torrent_files/.htaccess similarity index 100% rename from upload/data/torrent_files/.htaccess rename to data/torrent_files/.htaccess diff --git a/upload/dl.php b/dl.php similarity index 97% rename from upload/dl.php rename to dl.php index 29f72bd3f..845e7353f 100644 --- a/upload/dl.php +++ b/dl.php @@ -207,7 +207,7 @@ else { global $template; - $redirect_url = !empty($_POST['redirect_url']) ? $_POST['redirect_url'] : @$_SERVER['HTTP_REFERER']; + $redirect_url = isset($_POST['redirect_url']) ? $_POST['redirect_url'] : (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/'); $message = '
'; $message .= $lang['CONFIRM_CODE']; $message .= '
'. CAPTCHA()->get_html() .'
'; diff --git a/upload/dl_list.php b/dl_list.php similarity index 87% rename from upload/dl_list.php rename to dl_list.php index 574c014a0..d4db8fd9c 100644 --- a/upload/dl_list.php +++ b/dl_list.php @@ -5,9 +5,9 @@ define('BB_SCRIPT', 'dl_list'); define('BB_ROOT', './'); require(BB_ROOT .'common.php'); -$forum_id = (@$_REQUEST[POST_FORUM_URL]) ? (int) $_REQUEST[POST_FORUM_URL] : 0; -$topic_id = (@$_REQUEST[POST_TOPIC_URL]) ? (int) $_REQUEST[POST_TOPIC_URL] : 0; -$mode = (@$_REQUEST['mode']) ? (string) $_REQUEST['mode'] : ''; +$forum_id = isset($_REQUEST[POST_FORUM_URL]) ? (int) $_REQUEST[POST_FORUM_URL] : 0; +$topic_id = isset($_REQUEST[POST_TOPIC_URL]) ? (int) $_REQUEST[POST_TOPIC_URL] : 0; +$mode = isset($_REQUEST['mode']) ? (string) $_REQUEST['mode'] : ''; $confirmed = isset($_POST['confirm']); // Get new DL-status @@ -40,9 +40,9 @@ if ($mode == 'set_dl_status' || $mode == 'set_topics_dl_status') } // Define redirect URL -$full_url = (@$_POST['full_url']) ? str_replace('&', '&', htmlspecialchars($_POST['full_url'])) : ''; +$full_url = isset($_POST['full_url']) ? str_replace('&', '&', htmlspecialchars($_POST['full_url'])) : ''; -if (@$_POST['redirect_type'] == 'search') +if (isset($_POST['redirect_type']) && $_POST['redirect_type'] == 'search') { $redirect_type = "search.php"; $redirect = ($full_url) ? $full_url : "$dl_key=1"; @@ -65,7 +65,7 @@ if (!$userdata['session_logged_in']) } // Check if user did not confirm -if (@$_POST['cancel']) +if (isset($_POST['cancel']) && $_POST['cancel']) { redirect("$redirect_type?$redirect"); } diff --git a/upload/favicon.ico b/favicon.ico similarity index 100% rename from upload/favicon.ico rename to favicon.ico diff --git a/upload/feed.php b/feed.php similarity index 92% rename from upload/feed.php rename to feed.php index 87b981957..454c6dd56 100644 --- a/upload/feed.php +++ b/feed.php @@ -7,9 +7,9 @@ require(BB_ROOT .'common.php'); $user->session_start(array('req_login' => true)); -$mode = (string) @$_REQUEST['mode']; -$type = (string) @$_POST['type']; -$id = (int) @$_POST['id']; +$mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : ''; +$type = isset($_POST['type']) ? $_POST['type'] : ''; +$id = isset($_POST['id']) ? $_POST['id'] : 0; $timecheck = TIMENOW - 600; if (!$mode) bb_simple_die($lang['ATOM_NO_MODE']); diff --git a/upload/group.php b/group.php similarity index 99% rename from upload/group.php rename to group.php index 07fc133d6..38778d453 100644 --- a/upload/group.php +++ b/group.php @@ -188,7 +188,7 @@ if (!$group_id) else bb_die($lang['NO_GROUPS_EXIST']); } } -else if (@$_POST['joingroup']) +else if (isset($_POST['joingroup']) && $_POST['joingroup']) { if ($group_info['group_type'] != GROUP_OPEN) { @@ -263,7 +263,7 @@ else if (!empty($_POST['add'])) { - if (!$row = get_userdata(@$_POST['username'], true)) + if (isset($_POST['username']) && !($row = get_userdata($_POST['username'], true))) { bb_die($lang['COULD_NOT_ADD_USER']); } diff --git a/upload/group_edit.php b/group_edit.php similarity index 88% rename from upload/group_edit.php rename to group_edit.php index 615131704..801e62f69 100644 --- a/upload/group_edit.php +++ b/group_edit.php @@ -78,6 +78,7 @@ if ($is_moderator) 'GROUP_DESCRIPTION' => htmlCHR($group_info['group_description']), 'GROUP_SIGNATURE' => htmlCHR($group_info['group_signature']), 'U_GROUP_URL' => GROUP_URL . $group_id, + 'RELEASE_GROUP' => ($group_info['release_group']) ? true : false, 'GROUP_TYPE' => $group_type, 'S_GROUP_OPEN_TYPE' => GROUP_OPEN, 'S_GROUP_CLOSED_TYPE' => GROUP_CLOSED, @@ -88,10 +89,8 @@ if ($is_moderator) 'S_HIDDEN_FIELDS' => $s_hidden_fields, 'S_GROUP_CONFIG_ACTION' => "group_edit.php?" . POST_GROUPS_URL . "=$group_id", - 'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], $bb_cfg['group_avatars']['max_width'], $bb_cfg['group_avatars']['max_height'], (round($bb_cfg['group_avatars']['max_size'] / 1024))), - 'AVATAR_URL_PATH' => ($group_info['avatar_ext_id']) ? get_avatar_path(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id']) : '', - - 'RELEASE_GROUP' => ($group_info['release_group']) ? true : false, + 'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], $bb_cfg['group_avatars']['max_width'], $bb_cfg['group_avatars']['max_height'], (round($bb_cfg['group_avatars']['max_size'] / 1024))), + 'AVATAR_IMG' => get_avatar(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id']), )); $template->set_filenames(array('body' => 'group_edit.tpl')); diff --git a/upload/index.php b/index.php similarity index 98% rename from upload/index.php rename to index.php index 28a5b4f50..7dc7b44a9 100644 --- a/upload/index.php +++ b/index.php @@ -373,15 +373,14 @@ if ($bb_cfg['birthday_check_day'] && $bb_cfg['birthday_enabled']) if ($stats['birthday_week_list']) { shuffle($stats['birthday_week_list']); - foreach($stats['birthday_week_list'] as $i => $week) + foreach ($stats['birthday_week_list'] as $i => $week) { - if($i >= 5) + if ($i >= 5) { $week_all = true; continue; } - - $week_list[] = profile_url($week) .' ('. birthday_age($week['user_birthday']) .')'; + $week_list[] = profile_url($week) .' ('. birthday_age($week['user_birthday']-1) .')'; } $week_all = ($week_all) ? ' ...' : ''; $week_list = sprintf($lang['BIRTHDAY_WEEK'], $bb_cfg['birthday_check_day'], join(', ', $week_list)) . $week_all; @@ -391,14 +390,13 @@ if ($bb_cfg['birthday_check_day'] && $bb_cfg['birthday_enabled']) if ($stats['birthday_today_list']) { shuffle($stats['birthday_today_list']); - foreach($stats['birthday_today_list'] as $i => $today) + foreach ($stats['birthday_today_list'] as $i => $today) { - if($i >= 5) + if ($i >= 5) { $today_all = true; continue; } - $today_list[] = profile_url($today) .' ('. birthday_age($today['user_birthday']) .')'; } $today_all = ($today_all) ? ' ...' : ''; diff --git a/upload/info.php b/info.php similarity index 95% rename from upload/info.php rename to info.php index 5a596c053..500596c8d 100644 --- a/upload/info.php +++ b/info.php @@ -46,7 +46,7 @@ $require = file_exists($html_dir . $info['src']) ? $html_dir . $info['src'] : $h - + '; + + if (null == $escapeStart && null == $escapeEnd) { + // inner wrap with comment end and start if !IE + if (str_replace(' ', '', $item->attributes['conditional']) === '!IE') { + $html = '' . $html . ''; + } + + return $html; + } + + /** + * Override append to enforce style creation + * + * @param mixed $value + * @throws Exception\InvalidArgumentException + * @return void + */ + public function append($value) + { + if (!$this->isValid($value)) { + throw new Exception\InvalidArgumentException( + 'Invalid value passed to append; please use appendStyle()' + ); + } + + return $this->getContainer()->append($value); + } + + /** + * Override offsetSet to enforce style creation + * + * @param string|int $index + * @param mixed $value + * @throws Exception\InvalidArgumentException + * @return void + */ + public function offsetSet($index, $value) + { + if (!$this->isValid($value)) { + throw new Exception\InvalidArgumentException( + 'Invalid value passed to offsetSet; please use offsetSetStyle()' + ); + } + + return $this->getContainer()->offsetSet($index, $value); + } + + /** + * Override prepend to enforce style creation + * + * @param mixed $value + * @throws Exception\InvalidArgumentException + * @return void + */ + public function prepend($value) + { + if (!$this->isValid($value)) { + throw new Exception\InvalidArgumentException( + 'Invalid value passed to prepend; please use prependStyle()' + ); + } + + return $this->getContainer()->prepend($value); + } + + /** + * Override set to enforce style creation + * + * @param mixed $value + * @throws Exception\InvalidArgumentException + * @return void + */ + public function set($value) + { + if (!$this->isValid($value)) { + throw new Exception\InvalidArgumentException('Invalid value passed to set; please use setStyle()'); + } + + return $this->getContainer()->set($value); + } +} diff --git a/library/Zend/View/Helper/HeadTitle.php b/library/Zend/View/Helper/HeadTitle.php new file mode 100755 index 000000000..2ea8d78dd --- /dev/null +++ b/library/Zend/View/Helper/HeadTitle.php @@ -0,0 +1,263 @@ +getDefaultAttachOrder()) + ? Placeholder\Container\AbstractContainer::APPEND + : $this->getDefaultAttachOrder(); + } + + $title = (string) $title; + if ($title !== '') { + if ($setType == Placeholder\Container\AbstractContainer::SET) { + $this->set($title); + } elseif ($setType == Placeholder\Container\AbstractContainer::PREPEND) { + $this->prepend($title); + } else { + $this->append($title); + } + } + + return $this; + } + + /** + * Render title (wrapped by title tag) + * + * @param string|null $indent + * @return string + */ + public function toString($indent = null) + { + $indent = (null !== $indent) + ? $this->getWhitespace($indent) + : $this->getIndent(); + + $output = $this->renderTitle(); + + return $indent . '' . $output . ''; + } + + /** + * Render title string + * + * @return string + */ + public function renderTitle() + { + $items = array(); + + if (null !== ($translator = $this->getTranslator())) { + foreach ($this as $item) { + $items[] = $translator->translate($item, $this->getTranslatorTextDomain()); + } + } else { + foreach ($this as $item) { + $items[] = $item; + } + } + + $separator = $this->getSeparator(); + $output = ''; + + $prefix = $this->getPrefix(); + if ($prefix) { + $output .= $prefix; + } + + $output .= implode($separator, $items); + + $postfix = $this->getPostfix(); + if ($postfix) { + $output .= $postfix; + } + + $output = ($this->autoEscape) ? $this->escape($output) : $output; + + return $output; + } + + /** + * Set a default order to add titles + * + * @param string $setType + * @throws Exception\DomainException + * @return HeadTitle + */ + public function setDefaultAttachOrder($setType) + { + if (!in_array($setType, array( + Placeholder\Container\AbstractContainer::APPEND, + Placeholder\Container\AbstractContainer::SET, + Placeholder\Container\AbstractContainer::PREPEND + ))) { + throw new Exception\DomainException( + "You must use a valid attach order: 'PREPEND', 'APPEND' or 'SET'" + ); + } + $this->defaultAttachOrder = $setType; + + return $this; + } + + /** + * Get the default attach order, if any. + * + * @return mixed + */ + public function getDefaultAttachOrder() + { + return $this->defaultAttachOrder; + } + + // Translator methods - Good candidate to refactor as a trait with PHP 5.4 + + /** + * Sets translator to use in helper + * + * @param Translator $translator [optional] translator. + * Default is null, which sets no translator. + * @param string $textDomain [optional] text domain + * Default is null, which skips setTranslatorTextDomain + * @return HeadTitle + */ + public function setTranslator(Translator $translator = null, $textDomain = null) + { + $this->translator = $translator; + if (null !== $textDomain) { + $this->setTranslatorTextDomain($textDomain); + } + return $this; + } + + /** + * Returns translator used in helper + * + * @return Translator|null + */ + public function getTranslator() + { + if (! $this->isTranslatorEnabled()) { + return null; + } + + return $this->translator; + } + + /** + * Checks if the helper has a translator + * + * @return bool + */ + public function hasTranslator() + { + return (bool) $this->getTranslator(); + } + + /** + * Sets whether translator is enabled and should be used + * + * @param bool $enabled [optional] whether translator should be used. + * Default is true. + * @return HeadTitle + */ + public function setTranslatorEnabled($enabled = true) + { + $this->translatorEnabled = (bool) $enabled; + return $this; + } + + /** + * Returns whether translator is enabled and should be used + * + * @return bool + */ + public function isTranslatorEnabled() + { + return $this->translatorEnabled; + } + + /** + * Set translation text domain + * + * @param string $textDomain + * @return HeadTitle + */ + public function setTranslatorTextDomain($textDomain = 'default') + { + $this->translatorTextDomain = $textDomain; + return $this; + } + + /** + * Return the translation text domain + * + * @return string + */ + public function getTranslatorTextDomain() + { + return $this->translatorTextDomain; + } +} diff --git a/library/Zend/View/Helper/HelperInterface.php b/library/Zend/View/Helper/HelperInterface.php new file mode 100755 index 000000000..2d58c01f9 --- /dev/null +++ b/library/Zend/View/Helper/HelperInterface.php @@ -0,0 +1,30 @@ + $data, 'quality' => 'high'), $params); + + $htmlObject = $this->getView()->plugin('htmlObject'); + return $htmlObject($data, self::TYPE, $attribs, $params, $content); + } +} diff --git a/library/Zend/View/Helper/HtmlList.php b/library/Zend/View/Helper/HtmlList.php new file mode 100755 index 000000000..29c79c73c --- /dev/null +++ b/library/Zend/View/Helper/HtmlList.php @@ -0,0 +1,58 @@ +getView()->plugin('escapeHtml'); + $item = $escaper($item); + } + $list .= '
  • ' . $item . '
  • ' . self::EOL; + } else { + $itemLength = 5 + strlen(self::EOL); + if ($itemLength < strlen($list)) { + $list = substr($list, 0, strlen($list) - $itemLength) + . $this($item, $ordered, $attribs, $escape) . '' . self::EOL; + } else { + $list .= '
  • ' . $this($item, $ordered, $attribs, $escape) . '
  • ' . self::EOL; + } + } + } + + if ($attribs) { + $attribs = $this->htmlAttribs($attribs); + } else { + $attribs = ''; + } + + $tag = ($ordered) ? 'ol' : 'ul'; + + return '<' . $tag . $attribs . '>' . self::EOL . $list . '' . self::EOL; + } +} diff --git a/library/Zend/View/Helper/HtmlObject.php b/library/Zend/View/Helper/HtmlObject.php new file mode 100755 index 000000000..17c2822f3 --- /dev/null +++ b/library/Zend/View/Helper/HtmlObject.php @@ -0,0 +1,63 @@ + $data, 'type' => $type), $attribs); + + // Params + $paramHtml = array(); + $closingBracket = $this->getClosingBracket(); + + foreach ($params as $param => $options) { + if (is_string($options)) { + $options = array('value' => $options); + } + + $options = array_merge(array('name' => $param), $options); + + $paramHtml[] = 'htmlAttribs($options) . $closingBracket; + } + + // Content + if (is_array($content)) { + $content = implode(self::EOL, $content); + } + + // Object header + $xhtml = 'htmlAttribs($attribs) . '>' . self::EOL + . implode(self::EOL, $paramHtml) . self::EOL + . ($content ? $content . self::EOL : '') + . ''; + + return $xhtml; + } +} diff --git a/library/Zend/View/Helper/HtmlPage.php b/library/Zend/View/Helper/HtmlPage.php new file mode 100755 index 000000000..ee170c1b0 --- /dev/null +++ b/library/Zend/View/Helper/HtmlPage.php @@ -0,0 +1,51 @@ + self::ATTRIB_CLASSID); + + /** + * Output a html object tag + * + * @param string $data The html url + * @param array $attribs Attribs for the object tag + * @param array $params Params for in the object tag + * @param string $content Alternative content + * @return string + */ + public function __invoke($data, array $attribs = array(), array $params = array(), $content = null) + { + // Attribs + $attribs = array_merge($this->attribs, $attribs); + + // Params + $params = array_merge(array('data' => $data), $params); + + $htmlObject = $this->getView()->plugin('htmlObject'); + return $htmlObject($data, self::TYPE, $attribs, $params, $content); + } +} diff --git a/library/Zend/View/Helper/HtmlQuicktime.php b/library/Zend/View/Helper/HtmlQuicktime.php new file mode 100755 index 000000000..629d6efa3 --- /dev/null +++ b/library/Zend/View/Helper/HtmlQuicktime.php @@ -0,0 +1,56 @@ + self::ATTRIB_CLASSID, 'codebase' => self::ATTRIB_CODEBASE); + + /** + * Output a quicktime movie object tag + * + * @param string $data The quicktime file + * @param array $attribs Attribs for the object tag + * @param array $params Params for in the object tag + * @param string $content Alternative content + * @return string + */ + public function __invoke($data, array $attribs = array(), array $params = array(), $content = null) + { + // Attrs + $attribs = array_merge($this->attribs, $attribs); + + // Params + $params = array_merge(array('src' => $data), $params); + + $htmlObject = $this->getView()->plugin('htmlObject'); + return $htmlObject($data, self::TYPE, $attribs, $params, $content); + } +} diff --git a/library/Zend/View/Helper/Identity.php b/library/Zend/View/Helper/Identity.php new file mode 100755 index 000000000..4fe145373 --- /dev/null +++ b/library/Zend/View/Helper/Identity.php @@ -0,0 +1,69 @@ +authenticationService instanceof AuthenticationService) { + throw new Exception\RuntimeException('No AuthenticationService instance provided'); + } + + if (!$this->authenticationService->hasIdentity()) { + return null; + } + + return $this->authenticationService->getIdentity(); + } + + /** + * Set AuthenticationService instance + * + * @param AuthenticationService $authenticationService + * @return Identity + */ + public function setAuthenticationService(AuthenticationService $authenticationService) + { + $this->authenticationService = $authenticationService; + return $this; + } + + /** + * Get AuthenticationService instance + * + * @return AuthenticationService + */ + public function getAuthenticationService() + { + return $this->authenticationService; + } +} diff --git a/library/Zend/View/Helper/InlineScript.php b/library/Zend/View/Helper/InlineScript.php new file mode 100755 index 000000000..57dace7d6 --- /dev/null +++ b/library/Zend/View/Helper/InlineScript.php @@ -0,0 +1,42 @@ +response instanceof Response) { + $headers = $this->response->getHeaders(); + $headers->addHeaderLine('Content-Type', 'application/json'); + } + + return $data; + } + + /** + * Set the response object + * + * @param Response $response + * @return Json + */ + public function setResponse(Response $response) + { + $this->response = $response; + return $this; + } +} diff --git a/library/Zend/View/Helper/Layout.php b/library/Zend/View/Helper/Layout.php new file mode 100755 index 000000000..65b630fb2 --- /dev/null +++ b/library/Zend/View/Helper/Layout.php @@ -0,0 +1,98 @@ +getRoot(); + } + + return $this->setTemplate($template); + } + + /** + * Get layout template + * + * @return string + */ + public function getLayout() + { + return $this->getRoot()->getTemplate(); + } + + /** + * Get the root view model + * + * @throws Exception\RuntimeException + * @return null|Model + */ + protected function getRoot() + { + $helper = $this->getViewModelHelper(); + + if (!$helper->hasRoot()) { + throw new Exception\RuntimeException(sprintf( + '%s: no view model currently registered as root in renderer', + __METHOD__ + )); + } + + return $helper->getRoot(); + } + + /** + * Set layout template + * + * @param string $template + * @return Layout + */ + public function setTemplate($template) + { + $this->getRoot()->setTemplate((string) $template); + return $this; + } + + /** + * Retrieve the view model helper + * + * @return ViewModel + */ + protected function getViewModelHelper() + { + if (null === $this->viewModelHelper) { + $this->viewModelHelper = $this->getView()->plugin('view_model'); + } + + return $this->viewModelHelper; + } +} diff --git a/library/Zend/View/Helper/Navigation.php b/library/Zend/View/Helper/Navigation.php new file mode 100755 index 000000000..015a5ec02 --- /dev/null +++ b/library/Zend/View/Helper/Navigation.php @@ -0,0 +1,346 @@ +setContainer($container); + } + + return $this; + } + + /** + * Magic overload: Proxy to other navigation helpers or the container + * + * Examples of usage from a view script or layout: + * + * // proxy to Menu helper and render container: + * echo $this->navigation()->menu(); + * + * // proxy to Breadcrumbs helper and set indentation: + * $this->navigation()->breadcrumbs()->setIndent(8); + * + * // proxy to container and find all pages with 'blog' route: + * $blogPages = $this->navigation()->findAllByRoute('blog'); + * + * + * @param string $method helper name or method name in container + * @param array $arguments [optional] arguments to pass + * @throws \Zend\View\Exception\ExceptionInterface if proxying to a helper, and the + * helper is not an instance of the + * interface specified in + * {@link findHelper()} + * @throws \Zend\Navigation\Exception\ExceptionInterface if method does not exist in container + * @return mixed returns what the proxied call returns + */ + public function __call($method, array $arguments = array()) + { + // check if call should proxy to another helper + $helper = $this->findHelper($method, false); + if ($helper) { + if ($helper instanceof ServiceLocatorAwareInterface && $this->getServiceLocator()) { + $helper->setServiceLocator($this->getServiceLocator()); + } + return call_user_func_array($helper, $arguments); + } + + // default behaviour: proxy call to container + return parent::__call($method, $arguments); + } + + /** + * Renders helper + * + * @param AbstractContainer $container + * @return string + * @throws Exception\RuntimeException + */ + public function render($container = null) + { + return $this->findHelper($this->getDefaultProxy())->render($container); + } + + /** + * Returns the helper matching $proxy + * + * The helper must implement the interface + * {@link Zend\View\Helper\Navigation\Helper}. + * + * @param string $proxy helper name + * @param bool $strict [optional] whether exceptions should be + * thrown if something goes + * wrong. Default is true. + * @throws Exception\RuntimeException if $strict is true and helper cannot be found + * @return \Zend\View\Helper\Navigation\HelperInterface helper instance + */ + public function findHelper($proxy, $strict = true) + { + $plugins = $this->getPluginManager(); + if (!$plugins->has($proxy)) { + if ($strict) { + throw new Exception\RuntimeException(sprintf( + 'Failed to find plugin for %s', + $proxy + )); + } + return false; + } + + $helper = $plugins->get($proxy); + $container = $this->getContainer(); + $hash = spl_object_hash($container) . spl_object_hash($helper); + + if (!isset($this->injected[$hash])) { + $helper->setContainer(); + $this->inject($helper); + $this->injected[$hash] = true; + } else { + if ($this->getInjectContainer()) { + $helper->setContainer($container); + } + } + + return $helper; + } + + /** + * Injects container, ACL, and translator to the given $helper if this + * helper is configured to do so + * + * @param NavigationHelper $helper helper instance + * @return void + */ + protected function inject(NavigationHelper $helper) + { + if ($this->getInjectContainer() && !$helper->hasContainer()) { + $helper->setContainer($this->getContainer()); + } + + if ($this->getInjectAcl()) { + if (!$helper->hasAcl()) { + $helper->setAcl($this->getAcl()); + } + if (!$helper->hasRole()) { + $helper->setRole($this->getRole()); + } + } + + if ($this->getInjectTranslator() && !$helper->hasTranslator()) { + $helper->setTranslator( + $this->getTranslator(), + $this->getTranslatorTextDomain() + ); + } + } + + /** + * Sets the default proxy to use in {@link render()} + * + * @param string $proxy default proxy + * @return Navigation + */ + public function setDefaultProxy($proxy) + { + $this->defaultProxy = (string) $proxy; + return $this; + } + + /** + * Returns the default proxy to use in {@link render()} + * + * @return string + */ + public function getDefaultProxy() + { + return $this->defaultProxy; + } + + /** + * Sets whether container should be injected when proxying + * + * @param bool $injectContainer + * @return Navigation + */ + public function setInjectContainer($injectContainer = true) + { + $this->injectContainer = (bool) $injectContainer; + return $this; + } + + /** + * Returns whether container should be injected when proxying + * + * @return bool + */ + public function getInjectContainer() + { + return $this->injectContainer; + } + + /** + * Sets whether ACL should be injected when proxying + * + * @param bool $injectAcl + * @return Navigation + */ + public function setInjectAcl($injectAcl = true) + { + $this->injectAcl = (bool) $injectAcl; + return $this; + } + + /** + * Returns whether ACL should be injected when proxying + * + * @return bool + */ + public function getInjectAcl() + { + return $this->injectAcl; + } + + /** + * Sets whether translator should be injected when proxying + * + * @param bool $injectTranslator + * @return Navigation + */ + public function setInjectTranslator($injectTranslator = true) + { + $this->injectTranslator = (bool) $injectTranslator; + return $this; + } + + /** + * Returns whether translator should be injected when proxying + * + * @return bool + */ + public function getInjectTranslator() + { + return $this->injectTranslator; + } + + /** + * Set manager for retrieving navigation helpers + * + * @param Navigation\PluginManager $plugins + * @return Navigation + */ + public function setPluginManager(Navigation\PluginManager $plugins) + { + $renderer = $this->getView(); + if ($renderer) { + $plugins->setRenderer($renderer); + } + $this->plugins = $plugins; + + return $this; + } + + /** + * Retrieve plugin loader for navigation helpers + * + * Lazy-loads an instance of Navigation\HelperLoader if none currently + * registered. + * + * @return Navigation\PluginManager + */ + public function getPluginManager() + { + if (null === $this->plugins) { + $this->setPluginManager(new Navigation\PluginManager()); + } + + return $this->plugins; + } + + /** + * Set the View object + * + * @param Renderer $view + * @return self + */ + public function setView(Renderer $view) + { + parent::setView($view); + if ($view && $this->plugins) { + $this->plugins->setRenderer($view); + } + return $this; + } +} diff --git a/library/Zend/View/Helper/Navigation/AbstractHelper.php b/library/Zend/View/Helper/Navigation/AbstractHelper.php new file mode 100755 index 000000000..1b997683f --- /dev/null +++ b/library/Zend/View/Helper/Navigation/AbstractHelper.php @@ -0,0 +1,945 @@ +getContainer(), $method), + $arguments); + } + + /** + * Magic overload: Proxy to {@link render()}. + * + * This method will trigger an E_USER_ERROR if rendering the helper causes + * an exception to be thrown. + * + * Implements {@link HelperInterface::__toString()}. + * + * @return string + */ + public function __toString() + { + try { + return $this->render(); + } catch (\Exception $e) { + $msg = get_class($e) . ': ' . $e->getMessage(); + trigger_error($msg, E_USER_ERROR); + return ''; + } + } + + /** + * Finds the deepest active page in the given container + * + * @param Navigation\AbstractContainer $container container to search + * @param int|null $minDepth [optional] minimum depth + * required for page to be + * valid. Default is to use + * {@link getMinDepth()}. A + * null value means no minimum + * depth required. + * @param int|null $maxDepth [optional] maximum depth + * a page can have to be + * valid. Default is to use + * {@link getMaxDepth()}. A + * null value means no maximum + * depth required. + * @return array an associative array with + * the values 'depth' and + * 'page', or an empty array + * if not found + */ + public function findActive($container, $minDepth = null, $maxDepth = -1) + { + $this->parseContainer($container); + if (!is_int($minDepth)) { + $minDepth = $this->getMinDepth(); + } + if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) { + $maxDepth = $this->getMaxDepth(); + } + + $found = null; + $foundDepth = -1; + $iterator = new RecursiveIteratorIterator( + $container, + RecursiveIteratorIterator::CHILD_FIRST + ); + + /** @var \Zend\Navigation\Page\AbstractPage $page */ + foreach ($iterator as $page) { + $currDepth = $iterator->getDepth(); + if ($currDepth < $minDepth || !$this->accept($page)) { + // page is not accepted + continue; + } + + if ($page->isActive(false) && $currDepth > $foundDepth) { + // found an active page at a deeper level than before + $found = $page; + $foundDepth = $currDepth; + } + } + + if (is_int($maxDepth) && $foundDepth > $maxDepth) { + while ($foundDepth > $maxDepth) { + if (--$foundDepth < $minDepth) { + $found = null; + break; + } + + $found = $found->getParent(); + if (!$found instanceof AbstractPage) { + $found = null; + break; + } + } + } + + if ($found) { + return array('page' => $found, 'depth' => $foundDepth); + } + + return array(); + } + + /** + * Verifies container and eventually fetches it from service locator if it is a string + * + * @param Navigation\AbstractContainer|string|null $container + * @throws Exception\InvalidArgumentException + */ + protected function parseContainer(&$container = null) + { + if (null === $container) { + return; + } + + if (is_string($container)) { + if (!$this->getServiceLocator()) { + throw new Exception\InvalidArgumentException(sprintf( + 'Attempted to set container with alias "%s" but no ServiceLocator was set', + $container + )); + } + + /** + * Load the navigation container from the root service locator + * + * The navigation container is probably located in Zend\ServiceManager\ServiceManager + * and not in the View\HelperPluginManager. If the set service locator is a + * HelperPluginManager, access the navigation container via the main service locator. + */ + $sl = $this->getServiceLocator(); + if ($sl instanceof View\HelperPluginManager) { + $sl = $sl->getServiceLocator(); + } + $container = $sl->get($container); + return; + } + + if (!$container instanceof Navigation\AbstractContainer) { + throw new Exception\InvalidArgumentException( + 'Container must be a string alias or an instance of ' . + 'Zend\Navigation\AbstractContainer' + ); + } + } + + // Iterator filter methods: + + /** + * Determines whether a page should be accepted when iterating + * + * Default listener may be 'overridden' by attaching listener to 'isAllowed' + * method. Listener must be 'short circuited' if overriding default ACL + * listener. + * + * Rules: + * - If a page is not visible it is not accepted, unless RenderInvisible has + * been set to true + * - If $useAcl is true (default is true): + * - Page is accepted if listener returns true, otherwise false + * - If page is accepted and $recursive is true, the page + * will not be accepted if it is the descendant of a non-accepted page + * + * @param AbstractPage $page page to check + * @param bool $recursive [optional] if true, page will not be + * accepted if it is the descendant of + * a page that is not accepted. Default + * is true + * + * @return bool Whether page should be accepted + */ + public function accept(AbstractPage $page, $recursive = true) + { + $accept = true; + + if (!$page->isVisible(false) && !$this->getRenderInvisible()) { + $accept = false; + } elseif ($this->getUseAcl()) { + $acl = $this->getAcl(); + $role = $this->getRole(); + $params = array('acl' => $acl, 'page' => $page, 'role' => $role); + $accept = $this->isAllowed($params); + } + + if ($accept && $recursive) { + $parent = $page->getParent(); + + if ($parent instanceof AbstractPage) { + $accept = $this->accept($parent, true); + } + } + + return $accept; + } + + /** + * Determines whether a page should be allowed given certain parameters + * + * @param array $params + * @return bool + */ + protected function isAllowed($params) + { + $results = $this->getEventManager()->trigger(__FUNCTION__, $this, $params); + return $results->last(); + } + + // Util methods: + + /** + * Retrieve whitespace representation of $indent + * + * @param int|string $indent + * @return string + */ + protected function getWhitespace($indent) + { + if (is_int($indent)) { + $indent = str_repeat(' ', $indent); + } + + return (string) $indent; + } + + /** + * Converts an associative array to a string of tag attributes. + * + * Overloads {@link View\Helper\AbstractHtmlElement::htmlAttribs()}. + * + * @param array $attribs an array where each key-value pair is converted + * to an attribute name and value + * @return string + */ + protected function htmlAttribs($attribs) + { + // filter out null values and empty string values + foreach ($attribs as $key => $value) { + if ($value === null || (is_string($value) && !strlen($value))) { + unset($attribs[$key]); + } + } + + return parent::htmlAttribs($attribs); + } + + /** + * Returns an HTML string containing an 'a' element for the given page + * + * @param AbstractPage $page page to generate HTML for + * @return string HTML string (Label) + */ + public function htmlify(AbstractPage $page) + { + $label = $this->translate($page->getLabel(), $page->getTextDomain()); + $title = $this->translate($page->getTitle(), $page->getTextDomain()); + + // get attribs for anchor element + $attribs = array( + 'id' => $page->getId(), + 'title' => $title, + 'class' => $page->getClass(), + 'href' => $page->getHref(), + 'target' => $page->getTarget() + ); + + /** @var \Zend\View\Helper\EscapeHtml $escaper */ + $escaper = $this->view->plugin('escapeHtml'); + $label = $escaper($label); + + return 'htmlAttribs($attribs) . '>' . $label . ''; + } + + /** + * Translate a message (for label, title, …) + * + * @param string $message ID of the message to translate + * @param string $textDomain Text domain (category name for the translations) + * @return string Translated message + */ + protected function translate($message, $textDomain = null) + { + if (is_string($message) && !empty($message)) { + if (null !== ($translator = $this->getTranslator())) { + if (null === $textDomain) { + $textDomain = $this->getTranslatorTextDomain(); + } + + return $translator->translate($message, $textDomain); + } + } + + return $message; + } + + /** + * Normalize an ID + * + * Overrides {@link View\Helper\AbstractHtmlElement::normalizeId()}. + * + * @param string $value + * @return string + */ + protected function normalizeId($value) + { + $prefix = get_class($this); + $prefix = strtolower(trim(substr($prefix, strrpos($prefix, '\\')), '\\')); + + return $prefix . '-' . $value; + } + + /** + * Sets ACL to use when iterating pages + * + * Implements {@link HelperInterface::setAcl()}. + * + * @param Acl\AclInterface $acl ACL object. + * @return AbstractHelper + */ + public function setAcl(Acl\AclInterface $acl = null) + { + $this->acl = $acl; + return $this; + } + + /** + * Returns ACL or null if it isn't set using {@link setAcl()} or + * {@link setDefaultAcl()} + * + * Implements {@link HelperInterface::getAcl()}. + * + * @return Acl\AclInterface|null ACL object or null + */ + public function getAcl() + { + if ($this->acl === null && static::$defaultAcl !== null) { + return static::$defaultAcl; + } + + return $this->acl; + } + + /** + * Checks if the helper has an ACL instance + * + * Implements {@link HelperInterface::hasAcl()}. + * + * @return bool + */ + public function hasAcl() + { + if ($this->acl instanceof Acl\Acl + || static::$defaultAcl instanceof Acl\Acl + ) { + return true; + } + + return false; + } + + /** + * Set the event manager. + * + * @param EventManagerInterface $events + * @return AbstractHelper + */ + public function setEventManager(EventManagerInterface $events) + { + $events->setIdentifiers(array( + __CLASS__, + get_called_class(), + )); + + $this->events = $events; + + $this->setDefaultListeners(); + + return $this; + } + + /** + * Get the event manager. + * + * @return EventManagerInterface + */ + public function getEventManager() + { + if (null === $this->events) { + $this->setEventManager(new EventManager()); + } + + return $this->events; + } + + /** + * Sets navigation container the helper operates on by default + * + * Implements {@link HelperInterface::setContainer()}. + * + * @param string|Navigation\AbstractContainer $container Default is null, meaning container will be reset. + * @return AbstractHelper + */ + public function setContainer($container = null) + { + $this->parseContainer($container); + $this->container = $container; + + return $this; + } + + /** + * Returns the navigation container helper operates on by default + * + * Implements {@link HelperInterface::getContainer()}. + * + * If no container is set, a new container will be instantiated and + * stored in the helper. + * + * @return Navigation\AbstractContainer navigation container + */ + public function getContainer() + { + if (null === $this->container) { + $this->container = new Navigation\Navigation(); + } + + return $this->container; + } + + /** + * Checks if the helper has a container + * + * Implements {@link HelperInterface::hasContainer()}. + * + * @return bool + */ + public function hasContainer() + { + return null !== $this->container; + } + + /** + * Set the indentation string for using in {@link render()}, optionally a + * number of spaces to indent with + * + * @param string|int $indent + * @return AbstractHelper + */ + public function setIndent($indent) + { + $this->indent = $this->getWhitespace($indent); + return $this; + } + + /** + * Returns indentation + * + * @return string + */ + public function getIndent() + { + return $this->indent; + } + + /** + * Sets the maximum depth a page can have to be included when rendering + * + * @param int $maxDepth Default is null, which sets no maximum depth. + * @return AbstractHelper + */ + public function setMaxDepth($maxDepth = null) + { + if (null === $maxDepth || is_int($maxDepth)) { + $this->maxDepth = $maxDepth; + } else { + $this->maxDepth = (int) $maxDepth; + } + + return $this; + } + + /** + * Returns maximum depth a page can have to be included when rendering + * + * @return int|null + */ + public function getMaxDepth() + { + return $this->maxDepth; + } + + /** + * Sets the minimum depth a page must have to be included when rendering + * + * @param int $minDepth Default is null, which sets no minimum depth. + * @return AbstractHelper + */ + public function setMinDepth($minDepth = null) + { + if (null === $minDepth || is_int($minDepth)) { + $this->minDepth = $minDepth; + } else { + $this->minDepth = (int) $minDepth; + } + + return $this; + } + + /** + * Returns minimum depth a page must have to be included when rendering + * + * @return int|null + */ + public function getMinDepth() + { + if (!is_int($this->minDepth) || $this->minDepth < 0) { + return 0; + } + + return $this->minDepth; + } + + /** + * Render invisible items? + * + * @param bool $renderInvisible + * @return AbstractHelper + */ + public function setRenderInvisible($renderInvisible = true) + { + $this->renderInvisible = (bool) $renderInvisible; + return $this; + } + + /** + * Return renderInvisible flag + * + * @return bool + */ + public function getRenderInvisible() + { + return $this->renderInvisible; + } + + /** + * Sets ACL role(s) to use when iterating pages + * + * Implements {@link HelperInterface::setRole()}. + * + * @param mixed $role [optional] role to set. Expects a string, an + * instance of type {@link Acl\Role\RoleInterface}, or null. Default + * is null, which will set no role. + * @return AbstractHelper + * @throws Exception\InvalidArgumentException + */ + public function setRole($role = null) + { + if (null === $role || is_string($role) || + $role instanceof Acl\Role\RoleInterface + ) { + $this->role = $role; + } else { + throw new Exception\InvalidArgumentException(sprintf( + '$role must be a string, null, or an instance of ' + . 'Zend\Permissions\Role\RoleInterface; %s given', + (is_object($role) ? get_class($role) : gettype($role)) + )); + } + + return $this; + } + + /** + * Returns ACL role to use when iterating pages, or null if it isn't set + * using {@link setRole()} or {@link setDefaultRole()} + * + * Implements {@link HelperInterface::getRole()}. + * + * @return string|Acl\Role\RoleInterface|null + */ + public function getRole() + { + if ($this->role === null && static::$defaultRole !== null) { + return static::$defaultRole; + } + + return $this->role; + } + + /** + * Checks if the helper has an ACL role + * + * Implements {@link HelperInterface::hasRole()}. + * + * @return bool + */ + public function hasRole() + { + if ($this->role instanceof Acl\Role\RoleInterface + || is_string($this->role) + || static::$defaultRole instanceof Acl\Role\RoleInterface + || is_string(static::$defaultRole) + ) { + return true; + } + + return false; + } + + /** + * Set the service locator. + * + * @param ServiceLocatorInterface $serviceLocator + * @return AbstractHelper + */ + public function setServiceLocator(ServiceLocatorInterface $serviceLocator) + { + $this->serviceLocator = $serviceLocator; + return $this; + } + + /** + * Get the service locator. + * + * @return ServiceLocatorInterface + */ + public function getServiceLocator() + { + return $this->serviceLocator; + } + + // Translator methods - Good candidate to refactor as a trait with PHP 5.4 + + /** + * Sets translator to use in helper + * + * @param Translator $translator [optional] translator. + * Default is null, which sets no translator. + * @param string $textDomain [optional] text domain + * Default is null, which skips setTranslatorTextDomain + * @return AbstractHelper + */ + public function setTranslator(Translator $translator = null, $textDomain = null) + { + $this->translator = $translator; + if (null !== $textDomain) { + $this->setTranslatorTextDomain($textDomain); + } + + return $this; + } + + /** + * Returns translator used in helper + * + * @return Translator|null + */ + public function getTranslator() + { + if (! $this->isTranslatorEnabled()) { + return null; + } + + return $this->translator; + } + + /** + * Checks if the helper has a translator + * + * @return bool + */ + public function hasTranslator() + { + return (bool) $this->getTranslator(); + } + + /** + * Sets whether translator is enabled and should be used + * + * @param bool $enabled + * @return AbstractHelper + */ + public function setTranslatorEnabled($enabled = true) + { + $this->translatorEnabled = (bool) $enabled; + return $this; + } + + /** + * Returns whether translator is enabled and should be used + * + * @return bool + */ + public function isTranslatorEnabled() + { + return $this->translatorEnabled; + } + + /** + * Set translation text domain + * + * @param string $textDomain + * @return AbstractHelper + */ + public function setTranslatorTextDomain($textDomain = 'default') + { + $this->translatorTextDomain = $textDomain; + return $this; + } + + /** + * Return the translation text domain + * + * @return string + */ + public function getTranslatorTextDomain() + { + return $this->translatorTextDomain; + } + + /** + * Sets whether ACL should be used + * + * Implements {@link HelperInterface::setUseAcl()}. + * + * @param bool $useAcl + * @return AbstractHelper + */ + public function setUseAcl($useAcl = true) + { + $this->useAcl = (bool) $useAcl; + return $this; + } + + /** + * Returns whether ACL should be used + * + * Implements {@link HelperInterface::getUseAcl()}. + * + * @return bool + */ + public function getUseAcl() + { + return $this->useAcl; + } + + // Static methods: + + /** + * Sets default ACL to use if another ACL is not explicitly set + * + * @param Acl\AclInterface $acl [optional] ACL object. Default is null, which + * sets no ACL object. + * @return void + */ + public static function setDefaultAcl(Acl\AclInterface $acl = null) + { + static::$defaultAcl = $acl; + } + + /** + * Sets default ACL role(s) to use when iterating pages if not explicitly + * set later with {@link setRole()} + * + * @param mixed $role [optional] role to set. Expects null, string, or an + * instance of {@link Acl\Role\RoleInterface}. Default is null, which + * sets no default role. + * @return void + * @throws Exception\InvalidArgumentException if role is invalid + */ + public static function setDefaultRole($role = null) + { + if (null === $role + || is_string($role) + || $role instanceof Acl\Role\RoleInterface + ) { + static::$defaultRole = $role; + } else { + throw new Exception\InvalidArgumentException(sprintf( + '$role must be null|string|Zend\Permissions\Role\RoleInterface; received "%s"', + (is_object($role) ? get_class($role) : gettype($role)) + )); + } + } + + /** + * Attaches default ACL listeners, if ACLs are in use + */ + protected function setDefaultListeners() + { + if (!$this->getUseAcl()) { + return; + } + + $this->getEventManager()->getSharedManager()->attach( + 'Zend\View\Helper\Navigation\AbstractHelper', + 'isAllowed', + array('Zend\View\Helper\Navigation\Listener\AclListener', 'accept') + ); + } +} diff --git a/library/Zend/View/Helper/Navigation/Breadcrumbs.php b/library/Zend/View/Helper/Navigation/Breadcrumbs.php new file mode 100755 index 000000000..5337ca8e0 --- /dev/null +++ b/library/Zend/View/Helper/Navigation/Breadcrumbs.php @@ -0,0 +1,292 @@ +setContainer($container); + } + + return $this; + } + + /** + * Renders helper + * + * Implements {@link HelperInterface::render()}. + * + * @param AbstractContainer $container [optional] container to render. Default is + * to render the container registered in the helper. + * @return string + */ + public function render($container = null) + { + $partial = $this->getPartial(); + if ($partial) { + return $this->renderPartial($container, $partial); + } + + return $this->renderStraight($container); + } + + /** + * Renders breadcrumbs by chaining 'a' elements with the separator + * registered in the helper + * + * @param AbstractContainer $container [optional] container to render. Default is + * to render the container registered in the helper. + * @return string + */ + public function renderStraight($container = null) + { + $this->parseContainer($container); + if (null === $container) { + $container = $this->getContainer(); + } + + // find deepest active + if (!$active = $this->findActive($container)) { + return ''; + } + + $active = $active['page']; + + // put the deepest active page last in breadcrumbs + if ($this->getLinkLast()) { + $html = $this->htmlify($active); + } else { + /** @var \Zend\View\Helper\EscapeHtml $escaper */ + $escaper = $this->view->plugin('escapeHtml'); + $html = $escaper( + $this->translate($active->getLabel(), $active->getTextDomain()) + ); + } + + // walk back to root + while ($parent = $active->getParent()) { + if ($parent instanceof AbstractPage) { + // prepend crumb to html + $html = $this->htmlify($parent) + . $this->getSeparator() + . $html; + } + + if ($parent === $container) { + // at the root of the given container + break; + } + + $active = $parent; + } + + return strlen($html) ? $this->getIndent() . $html : ''; + } + + /** + * Renders the given $container by invoking the partial view helper + * + * The container will simply be passed on as a model to the view script, + * so in the script it will be available in $this->container. + * + * @param AbstractContainer $container [optional] container to pass to view script. + * Default is to use the container registered + * in the helper. + * @param string|array $partial [optional] partial view script to use. + * Default is to use the partial registered + * in the helper. If an array is given, it + * is expected to contain two values; the + * partial view script to use, and the module + * where the script can be found. + * @throws Exception\RuntimeException if no partial provided + * @throws Exception\InvalidArgumentException if partial is invalid array + * @return string helper output + */ + public function renderPartial($container = null, $partial = null) + { + $this->parseContainer($container); + if (null === $container) { + $container = $this->getContainer(); + } + + if (null === $partial) { + $partial = $this->getPartial(); + } + + if (empty($partial)) { + throw new Exception\RuntimeException( + 'Unable to render menu: No partial view script provided' + ); + } + + // put breadcrumb pages in model + $model = array( + 'pages' => array(), + 'separator' => $this->getSeparator() + ); + $active = $this->findActive($container); + if ($active) { + $active = $active['page']; + $model['pages'][] = $active; + while ($parent = $active->getParent()) { + if ($parent instanceof AbstractPage) { + $model['pages'][] = $parent; + } else { + break; + } + + if ($parent === $container) { + // break if at the root of the given container + break; + } + + $active = $parent; + } + $model['pages'] = array_reverse($model['pages']); + } + + /** @var \Zend\View\Helper\Partial $partialHelper */ + $partialHelper = $this->view->plugin('partial'); + + if (is_array($partial)) { + if (count($partial) != 2) { + throw new Exception\InvalidArgumentException( + 'Unable to render menu: A view partial supplied as ' + . 'an array must contain two values: partial view ' + . 'script and module where script can be found' + ); + } + + return $partialHelper($partial[0], $model); + } + + return $partialHelper($partial, $model); + } + + /** + * Sets whether last page in breadcrumbs should be hyperlinked + * + * @param bool $linkLast whether last page should be hyperlinked + * @return Breadcrumbs + */ + public function setLinkLast($linkLast) + { + $this->linkLast = (bool) $linkLast; + return $this; + } + + /** + * Returns whether last page in breadcrumbs should be hyperlinked + * + * @return bool + */ + public function getLinkLast() + { + return $this->linkLast; + } + + /** + * Sets which partial view script to use for rendering menu + * + * @param string|array $partial partial view script or null. If an array is + * given, it is expected to contain two + * values; the partial view script to use, + * and the module where the script can be + * found. + * @return Breadcrumbs + */ + public function setPartial($partial) + { + if (null === $partial || is_string($partial) || is_array($partial)) { + $this->partial = $partial; + } + + return $this; + } + + /** + * Returns partial view script to use for rendering menu + * + * @return string|array|null + */ + public function getPartial() + { + return $this->partial; + } + + /** + * Sets breadcrumb separator + * + * @param string $separator separator string + * @return Breadcrumbs + */ + public function setSeparator($separator) + { + if (is_string($separator)) { + $this->separator = $separator; + } + + return $this; + } + + /** + * Returns breadcrumb separator + * + * @return string breadcrumb separator + */ + public function getSeparator() + { + return $this->separator; + } +} diff --git a/library/Zend/View/Helper/Navigation/HelperInterface.php b/library/Zend/View/Helper/Navigation/HelperInterface.php new file mode 100755 index 000000000..8b52c8c1d --- /dev/null +++ b/library/Zend/View/Helper/Navigation/HelperInterface.php @@ -0,0 +1,143 @@ + elements + */ +class Links extends AbstractHelper +{ + /** + * Constants used for specifying which link types to find and render + * + * @var int + */ + const RENDER_ALTERNATE = 0x0001; + const RENDER_STYLESHEET = 0x0002; + const RENDER_START = 0x0004; + const RENDER_NEXT = 0x0008; + const RENDER_PREV = 0x0010; + const RENDER_CONTENTS = 0x0020; + const RENDER_INDEX = 0x0040; + const RENDER_GLOSSARY = 0x0080; + const RENDER_COPYRIGHT = 0x0100; + const RENDER_CHAPTER = 0x0200; + const RENDER_SECTION = 0x0400; + const RENDER_SUBSECTION = 0x0800; + const RENDER_APPENDIX = 0x1000; + const RENDER_HELP = 0x2000; + const RENDER_BOOKMARK = 0x4000; + const RENDER_CUSTOM = 0x8000; + const RENDER_ALL = 0xffff; + + /** + * Maps render constants to W3C link types + * + * @var array + */ + protected static $RELATIONS = array( + self::RENDER_ALTERNATE => 'alternate', + self::RENDER_STYLESHEET => 'stylesheet', + self::RENDER_START => 'start', + self::RENDER_NEXT => 'next', + self::RENDER_PREV => 'prev', + self::RENDER_CONTENTS => 'contents', + self::RENDER_INDEX => 'index', + self::RENDER_GLOSSARY => 'glossary', + self::RENDER_COPYRIGHT => 'copyright', + self::RENDER_CHAPTER => 'chapter', + self::RENDER_SECTION => 'section', + self::RENDER_SUBSECTION => 'subsection', + self::RENDER_APPENDIX => 'appendix', + self::RENDER_HELP => 'help', + self::RENDER_BOOKMARK => 'bookmark', + ); + + /** + * The helper's render flag + * + * @see render() + * @see setRenderFlag() + * @var int + */ + protected $renderFlag = self::RENDER_ALL; + + /** + * Root container + * + * Used for preventing methods to traverse above the container given to + * the {@link render()} method. + * + * @see _findRoot() + * @var AbstractContainer + */ + protected $root; + + /** + * Helper entry point + * + * @param string|AbstractContainer $container container to operate on + * @return Links + */ + public function __invoke($container = null) + { + if (null !== $container) { + $this->setContainer($container); + } + + return $this; + } + + /** + * Magic overload: Proxy calls to {@link findRelation()} or container + * + * Examples of finder calls: + * + * // METHOD // SAME AS + * $h->findRelNext($page); // $h->findRelation($page, 'rel', 'next') + * $h->findRevSection($page); // $h->findRelation($page, 'rev', 'section'); + * $h->findRelFoo($page); // $h->findRelation($page, 'rel', 'foo'); + * + * + * @param string $method + * @param array $arguments + * @return mixed + * @throws Exception\ExceptionInterface + */ + public function __call($method, array $arguments = array()) + { + ErrorHandler::start(E_WARNING); + $result = preg_match('/find(Rel|Rev)(.+)/', $method, $match); + ErrorHandler::stop(); + if ($result) { + return $this->findRelation($arguments[0], + strtolower($match[1]), + strtolower($match[2])); + } + + return parent::__call($method, $arguments); + } + + /** + * Renders helper + * + * Implements {@link HelperInterface::render()}. + * + * @param AbstractContainer|string|null $container [optional] container to render. + * Default is to render the + * container registered in the + * helper. + * @return string + */ + public function render($container = null) + { + $this->parseContainer($container); + if (null === $container) { + $container = $this->getContainer(); + } + + $active = $this->findActive($container); + if ($active) { + $active = $active['page']; + } else { + // no active page + return ''; + } + + $output = ''; + $indent = $this->getIndent(); + $this->root = $container; + + $result = $this->findAllRelations($active, $this->getRenderFlag()); + foreach ($result as $attrib => $types) { + foreach ($types as $relation => $pages) { + foreach ($pages as $page) { + $r = $this->renderLink($page, $attrib, $relation); + if ($r) { + $output .= $indent . $r . self::EOL; + } + } + } + } + + $this->root = null; + + // return output (trim last newline by spec) + return strlen($output) ? rtrim($output, self::EOL) : ''; + } + + /** + * Renders the given $page as a link element, with $attrib = $relation + * + * @param AbstractPage $page the page to render the link for + * @param string $attrib the attribute to use for $type, + * either 'rel' or 'rev' + * @param string $relation relation type, muse be one of; + * alternate, appendix, bookmark, + * chapter, contents, copyright, + * glossary, help, home, index, next, + * prev, section, start, stylesheet, + * subsection + * @return string + * @throws Exception\DomainException + */ + public function renderLink(AbstractPage $page, $attrib, $relation) + { + if (!in_array($attrib, array('rel', 'rev'))) { + throw new Exception\DomainException(sprintf( + 'Invalid relation attribute "%s", must be "rel" or "rev"', + $attrib + )); + } + + if (!$href = $page->getHref()) { + return ''; + } + + // TODO: add more attribs + // http://www.w3.org/TR/html401/struct/links.html#h-12.2 + $attribs = array( + $attrib => $relation, + 'href' => $href, + 'title' => $page->getLabel() + ); + + return 'htmlAttribs($attribs) . + $this->getClosingBracket(); + } + + // Finder methods: + + /** + * Finds all relations (forward and reverse) for the given $page + * + * The form of the returned array: + * + * // $page denotes an instance of Zend\Navigation\Page\AbstractPage + * $returned = array( + * 'rel' => array( + * 'alternate' => array($page, $page, $page), + * 'start' => array($page), + * 'next' => array($page), + * 'prev' => array($page), + * 'canonical' => array($page) + * ), + * 'rev' => array( + * 'section' => array($page) + * ) + * ); + * + * + * @param AbstractPage $page page to find links for + * @param null|int + * @return array + */ + public function findAllRelations(AbstractPage $page, $flag = null) + { + if (!is_int($flag)) { + $flag = self::RENDER_ALL; + } + + $result = array('rel' => array(), 'rev' => array()); + $native = array_values(static::$RELATIONS); + + foreach (array_keys($result) as $rel) { + $meth = 'getDefined' . ucfirst($rel); + $types = array_merge($native, array_diff($page->$meth(), $native)); + + foreach ($types as $type) { + if (!$relFlag = array_search($type, static::$RELATIONS)) { + $relFlag = self::RENDER_CUSTOM; + } + if (!($flag & $relFlag)) { + continue; + } + + $found = $this->findRelation($page, $rel, $type); + if ($found) { + if (!is_array($found)) { + $found = array($found); + } + $result[$rel][$type] = $found; + } + } + } + + return $result; + } + + /** + * Finds relations of the given $rel=$type from $page + * + * This method will first look for relations in the page instance, then + * by searching the root container if nothing was found in the page. + * + * @param AbstractPage $page page to find relations for + * @param string $rel relation, "rel" or "rev" + * @param string $type link type, e.g. 'start', 'next' + * @return AbstractPage|array|null + * @throws Exception\DomainException if $rel is not "rel" or "rev" + */ + public function findRelation(AbstractPage $page, $rel, $type) + { + if (!in_array($rel, array('rel', 'rev'))) { + throw new Exception\DomainException(sprintf( + 'Invalid argument: $rel must be "rel" or "rev"; "%s" given', + $rel + )); + } + + if (!$result = $this->findFromProperty($page, $rel, $type)) { + $result = $this->findFromSearch($page, $rel, $type); + } + + return $result; + } + + /** + * Finds relations of given $type for $page by checking if the + * relation is specified as a property of $page + * + * @param AbstractPage $page page to find relations for + * @param string $rel relation, 'rel' or 'rev' + * @param string $type link type, e.g. 'start', 'next' + * @return AbstractPage|array|null + */ + protected function findFromProperty(AbstractPage $page, $rel, $type) + { + $method = 'get' . ucfirst($rel); + $result = $page->$method($type); + if ($result) { + $result = $this->convertToPages($result); + if ($result) { + if (!is_array($result)) { + $result = array($result); + } + + foreach ($result as $key => $page) { + if (!$this->accept($page)) { + unset($result[$key]); + } + } + + return count($result) == 1 ? $result[0] : $result; + } + } + + return null; + } + + /** + * Finds relations of given $rel=$type for $page by using the helper to + * search for the relation in the root container + * + * @param AbstractPage $page page to find relations for + * @param string $rel relation, 'rel' or 'rev' + * @param string $type link type, e.g. 'start', 'next', etc + * @return array|null + */ + protected function findFromSearch(AbstractPage $page, $rel, $type) + { + $found = null; + + $method = 'search' . ucfirst($rel) . ucfirst($type); + if (method_exists($this, $method)) { + $found = $this->$method($page); + } + + return $found; + } + + // Search methods: + + /** + * Searches the root container for the forward 'start' relation of the given + * $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to the first document in a collection of documents. This link type + * tells search engines which document is considered by the author to be the + * starting point of the collection. + * + * @param AbstractPage $page + * @return AbstractPage|null + */ + public function searchRelStart(AbstractPage $page) + { + $found = $this->findRoot($page); + if (!$found instanceof AbstractPage) { + $found->rewind(); + $found = $found->current(); + } + + if ($found === $page || !$this->accept($found)) { + $found = null; + } + + return $found; + } + + /** + * Searches the root container for the forward 'next' relation of the given + * $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to the next document in a linear sequence of documents. User + * agents may choose to preload the "next" document, to reduce the perceived + * load time. + * + * @param AbstractPage $page + * @return AbstractPage|null + */ + public function searchRelNext(AbstractPage $page) + { + $found = null; + $break = false; + $iterator = new RecursiveIteratorIterator($this->findRoot($page), + RecursiveIteratorIterator::SELF_FIRST); + foreach ($iterator as $intermediate) { + if ($intermediate === $page) { + // current page; break at next accepted page + $break = true; + continue; + } + + if ($break && $this->accept($intermediate)) { + $found = $intermediate; + break; + } + } + + return $found; + } + + /** + * Searches the root container for the forward 'prev' relation of the given + * $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to the previous document in an ordered series of documents. Some + * user agents also support the synonym "Previous". + * + * @param AbstractPage $page + * @return AbstractPage|null + */ + public function searchRelPrev(AbstractPage $page) + { + $found = null; + $prev = null; + $iterator = new RecursiveIteratorIterator( + $this->findRoot($page), + RecursiveIteratorIterator::SELF_FIRST); + foreach ($iterator as $intermediate) { + if (!$this->accept($intermediate)) { + continue; + } + if ($intermediate === $page) { + $found = $prev; + break; + } + + $prev = $intermediate; + } + + return $found; + } + + /** + * Searches the root container for forward 'chapter' relations of the given + * $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to a document serving as a chapter in a collection of documents. + * + * @param AbstractPage $page + * @return AbstractPage|array|null + */ + public function searchRelChapter(AbstractPage $page) + { + $found = array(); + + // find first level of pages + $root = $this->findRoot($page); + + // find start page(s) + $start = $this->findRelation($page, 'rel', 'start'); + if (!is_array($start)) { + $start = array($start); + } + + foreach ($root as $chapter) { + // exclude self and start page from chapters + if ($chapter !== $page && + !in_array($chapter, $start) && + $this->accept($chapter)) { + $found[] = $chapter; + } + } + + switch (count($found)) { + case 0: + return null; + case 1: + return $found[0]; + default: + return $found; + } + } + + /** + * Searches the root container for forward 'section' relations of the given + * $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to a document serving as a section in a collection of documents. + * + * @param AbstractPage $page + * @return AbstractPage|array|null + */ + public function searchRelSection(AbstractPage $page) + { + $found = array(); + + // check if given page has pages and is a chapter page + if ($page->hasPages() && $this->findRoot($page)->hasPage($page)) { + foreach ($page as $section) { + if ($this->accept($section)) { + $found[] = $section; + } + } + } + + switch (count($found)) { + case 0: + return null; + case 1: + return $found[0]; + default: + return $found; + } + } + + /** + * Searches the root container for forward 'subsection' relations of the + * given $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to a document serving as a subsection in a collection of + * documents. + * + * @param AbstractPage $page + * @return AbstractPage|array|null + */ + public function searchRelSubsection(AbstractPage $page) + { + $found = array(); + + if ($page->hasPages()) { + // given page has child pages, loop chapters + foreach ($this->findRoot($page) as $chapter) { + // is page a section? + if ($chapter->hasPage($page)) { + foreach ($page as $subsection) { + if ($this->accept($subsection)) { + $found[] = $subsection; + } + } + } + } + } + + switch (count($found)) { + case 0: + return null; + case 1: + return $found[0]; + default: + return $found; + } + } + + /** + * Searches the root container for the reverse 'section' relation of the + * given $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to a document serving as a section in a collection of documents. + * + * @param AbstractPage $page + * @return AbstractPage|null + */ + public function searchRevSection(AbstractPage $page) + { + $found = null; + $parent = $page->getParent(); + if ($parent) { + if ($parent instanceof AbstractPage && + $this->findRoot($page)->hasPage($parent)) { + $found = $parent; + } + } + + return $found; + } + + /** + * Searches the root container for the reverse 'section' relation of the + * given $page + * + * From {@link http://www.w3.org/TR/html4/types.html#type-links}: + * Refers to a document serving as a subsection in a collection of + * documents. + * + * @param AbstractPage $page + * @return AbstractPage|null + */ + public function searchRevSubsection(AbstractPage $page) + { + $found = null; + $parent = $page->getParent(); + if ($parent) { + if ($parent instanceof AbstractPage) { + $root = $this->findRoot($page); + foreach ($root as $chapter) { + if ($chapter->hasPage($parent)) { + $found = $parent; + break; + } + } + } + } + + return $found; + } + + // Util methods: + + /** + * Returns the root container of the given page + * + * When rendering a container, the render method still store the given + * container as the root container, and unset it when done rendering. This + * makes sure finder methods will not traverse above the container given + * to the render method. + * + * @param AbstractPage $page + * @return AbstractContainer + */ + protected function findRoot(AbstractPage $page) + { + if ($this->root) { + return $this->root; + } + + $root = $page; + + while ($parent = $page->getParent()) { + $root = $parent; + if ($parent instanceof AbstractPage) { + $page = $parent; + } else { + break; + } + } + + return $root; + } + + /** + * Converts a $mixed value to an array of pages + * + * @param mixed $mixed mixed value to get page(s) from + * @param bool $recursive whether $value should be looped + * if it is an array or a config + * @return AbstractPage|array|null + */ + protected function convertToPages($mixed, $recursive = true) + { + if ($mixed instanceof AbstractPage) { + // value is a page instance; return directly + return $mixed; + } elseif ($mixed instanceof AbstractContainer) { + // value is a container; return pages in it + $pages = array(); + foreach ($mixed as $page) { + $pages[] = $page; + } + return $pages; + } elseif ($mixed instanceof Traversable) { + $mixed = ArrayUtils::iteratorToArray($mixed); + } elseif (is_string($mixed)) { + // value is a string; make a URI page + return AbstractPage::factory(array( + 'type' => 'uri', + 'uri' => $mixed + )); + } + + if (is_array($mixed) && !empty($mixed)) { + if ($recursive && is_numeric(key($mixed))) { + // first key is numeric; assume several pages + $pages = array(); + foreach ($mixed as $value) { + $value = $this->convertToPages($value, false); + if ($value) { + $pages[] = $value; + } + } + return $pages; + } else { + // pass array to factory directly + try { + $page = AbstractPage::factory($mixed); + return $page; + } catch (\Exception $e) { + } + } + } + + // nothing found + return null; + } + + /** + * Sets the helper's render flag + * + * The helper uses the bitwise '&' operator against the hex values of the + * render constants. This means that the flag can is "bitwised" value of + * the render constants. Examples: + * + * // render all links except glossary + * $flag = Links:RENDER_ALL ^ Links:RENDER_GLOSSARY; + * $helper->setRenderFlag($flag); + * + * // render only chapters and sections + * $flag = Links:RENDER_CHAPTER | Links:RENDER_SECTION; + * $helper->setRenderFlag($flag); + * + * // render only relations that are not native W3C relations + * $helper->setRenderFlag(Links:RENDER_CUSTOM); + * + * // render all relations (default) + * $helper->setRenderFlag(Links:RENDER_ALL); + * + * + * Note that custom relations can also be rendered directly using the + * {@link renderLink()} method. + * + * @param int $renderFlag + * @return Links + */ + public function setRenderFlag($renderFlag) + { + $this->renderFlag = (int) $renderFlag; + + return $this; + } + + /** + * Returns the helper's render flag + * + * @return int + */ + public function getRenderFlag() + { + return $this->renderFlag; + } +} diff --git a/library/Zend/View/Helper/Navigation/Listener/AclListener.php b/library/Zend/View/Helper/Navigation/Listener/AclListener.php new file mode 100755 index 000000000..5c8656084 --- /dev/null +++ b/library/Zend/View/Helper/Navigation/Listener/AclListener.php @@ -0,0 +1,55 @@ +getParams(); + $acl = $params['acl']; + $page = $params['page']; + $role = $params['role']; + + if (!$acl) { + return $accepted; + } + + $resource = $page->getResource(); + $privilege = $page->getPrivilege(); + + if ($resource || $privilege) { + $accepted = $acl->hasResource($resource) + && $acl->isAllowed($role, $resource, $privilege); + } + + return $accepted; + } +} diff --git a/library/Zend/View/Helper/Navigation/Menu.php b/library/Zend/View/Helper/Navigation/Menu.php new file mode 100755 index 000000000..29d719923 --- /dev/null +++ b/library/Zend/View/Helper/Navigation/Menu.php @@ -0,0 +1,765 @@ + element + * + * @var bool + */ + protected $addClassToListItem = false; + + /** + * Whether labels should be escaped + * + * @var bool + */ + protected $escapeLabels = true; + + /** + * Whether only active branch should be rendered + * + * @var bool + */ + protected $onlyActiveBranch = false; + + /** + * Partial view script to use for rendering menu + * + * @var string|array + */ + protected $partial = null; + + /** + * Whether parents should be rendered when only rendering active branch + * + * @var bool + */ + protected $renderParents = true; + + /** + * CSS class to use for the ul element + * + * @var string + */ + protected $ulClass = 'navigation'; + + /** + * CSS class to use for the active li element + * + * @var string + */ + protected $liActiveClass = 'active'; + + /** + * View helper entry point: + * Retrieves helper and optionally sets container to operate on + * + * @param AbstractContainer $container [optional] container to operate on + * @return self + */ + public function __invoke($container = null) + { + if (null !== $container) { + $this->setContainer($container); + } + + return $this; + } + + /** + * Renders menu + * + * Implements {@link HelperInterface::render()}. + * + * If a partial view is registered in the helper, the menu will be rendered + * using the given partial script. If no partial is registered, the menu + * will be rendered as an 'ul' element by the helper's internal method. + * + * @see renderPartial() + * @see renderMenu() + * + * @param AbstractContainer $container [optional] container to render. Default is + * to render the container registered in the helper. + * @return string + */ + public function render($container = null) + { + $partial = $this->getPartial(); + if ($partial) { + return $this->renderPartial($container, $partial); + } + + return $this->renderMenu($container); + } + + /** + * Renders the deepest active menu within [$minDepth, $maxDepth], (called + * from {@link renderMenu()}) + * + * @param AbstractContainer $container container to render + * @param string $ulClass CSS class for first UL + * @param string $indent initial indentation + * @param int|null $minDepth minimum depth + * @param int|null $maxDepth maximum depth + * @param bool $escapeLabels Whether or not to escape the labels + * @param bool $addClassToListItem Whether or not page class applied to
  • element + * @param string $liActiveClass CSS class for active LI + * @return string + */ + protected function renderDeepestMenu( + AbstractContainer $container, + $ulClass, + $indent, + $minDepth, + $maxDepth, + $escapeLabels, + $addClassToListItem, + $liActiveClass + ) { + if (!$active = $this->findActive($container, $minDepth - 1, $maxDepth)) { + return ''; + } + + // special case if active page is one below minDepth + if ($active['depth'] < $minDepth) { + if (!$active['page']->hasPages(!$this->renderInvisible)) { + return ''; + } + } elseif (!$active['page']->hasPages(!$this->renderInvisible)) { + // found pages has no children; render siblings + $active['page'] = $active['page']->getParent(); + } elseif (is_int($maxDepth) && $active['depth'] +1 > $maxDepth) { + // children are below max depth; render siblings + $active['page'] = $active['page']->getParent(); + } + + /* @var $escaper \Zend\View\Helper\EscapeHtmlAttr */ + $escaper = $this->view->plugin('escapeHtmlAttr'); + $ulClass = $ulClass ? ' class="' . $escaper($ulClass) . '"' : ''; + $html = $indent . '' . PHP_EOL; + + foreach ($active['page'] as $subPage) { + if (!$this->accept($subPage)) { + continue; + } + + // render li tag and page + $liClasses = array(); + // Is page active? + if ($subPage->isActive(true)) { + $liClasses[] = $liActiveClass; + } + // Add CSS class from page to
  • + if ($addClassToListItem && $subPage->getClass()) { + $liClasses[] = $subPage->getClass(); + } + $liClass = empty($liClasses) ? '' : ' class="' . $escaper(implode(' ', $liClasses)) . '"'; + + $html .= $indent . ' ' . PHP_EOL; + $html .= $indent . ' ' . $this->htmlify($subPage, $escapeLabels, $addClassToListItem) . PHP_EOL; + $html .= $indent . '
  • ' . PHP_EOL; + } + + $html .= $indent . ''; + + return $html; + } + + /** + * Renders helper + * + * Renders a HTML 'ul' for the given $container. If $container is not given, + * the container registered in the helper will be used. + * + * Available $options: + * + * + * @param AbstractContainer $container [optional] container to create menu from. + * Default is to use the container retrieved + * from {@link getContainer()}. + * @param array $options [optional] options for controlling rendering + * @return string + */ + public function renderMenu($container = null, array $options = array()) + { + $this->parseContainer($container); + if (null === $container) { + $container = $this->getContainer(); + } + + + $options = $this->normalizeOptions($options); + + if ($options['onlyActiveBranch'] && !$options['renderParents']) { + $html = $this->renderDeepestMenu($container, + $options['ulClass'], + $options['indent'], + $options['minDepth'], + $options['maxDepth'], + $options['escapeLabels'], + $options['addClassToListItem'], + $options['liActiveClass'] + ); + } else { + $html = $this->renderNormalMenu($container, + $options['ulClass'], + $options['indent'], + $options['minDepth'], + $options['maxDepth'], + $options['onlyActiveBranch'], + $options['escapeLabels'], + $options['addClassToListItem'], + $options['liActiveClass'] + ); + } + + return $html; + } + + /** + * Renders a normal menu (called from {@link renderMenu()}) + * + * @param AbstractContainer $container container to render + * @param string $ulClass CSS class for first UL + * @param string $indent initial indentation + * @param int|null $minDepth minimum depth + * @param int|null $maxDepth maximum depth + * @param bool $onlyActive render only active branch? + * @param bool $escapeLabels Whether or not to escape the labels + * @param bool $addClassToListItem Whether or not page class applied to
  • element + * @param string $liActiveClass CSS class for active LI + * @return string + */ + protected function renderNormalMenu( + AbstractContainer $container, + $ulClass, + $indent, + $minDepth, + $maxDepth, + $onlyActive, + $escapeLabels, + $addClassToListItem, + $liActiveClass + ) { + $html = ''; + + // find deepest active + $found = $this->findActive($container, $minDepth, $maxDepth); + /* @var $escaper \Zend\View\Helper\EscapeHtmlAttr */ + $escaper = $this->view->plugin('escapeHtmlAttr'); + + if ($found) { + $foundPage = $found['page']; + $foundDepth = $found['depth']; + } else { + $foundPage = null; + } + + // create iterator + $iterator = new RecursiveIteratorIterator($container, + RecursiveIteratorIterator::SELF_FIRST); + if (is_int($maxDepth)) { + $iterator->setMaxDepth($maxDepth); + } + + // iterate container + $prevDepth = -1; + foreach ($iterator as $page) { + $depth = $iterator->getDepth(); + $isActive = $page->isActive(true); + if ($depth < $minDepth || !$this->accept($page)) { + // page is below minDepth or not accepted by acl/visibility + continue; + } elseif ($onlyActive && !$isActive) { + // page is not active itself, but might be in the active branch + $accept = false; + if ($foundPage) { + if ($foundPage->hasPage($page)) { + // accept if page is a direct child of the active page + $accept = true; + } elseif ($foundPage->getParent()->hasPage($page)) { + // page is a sibling of the active page... + if (!$foundPage->hasPages(!$this->renderInvisible) || + is_int($maxDepth) && $foundDepth + 1 > $maxDepth) { + // accept if active page has no children, or the + // children are too deep to be rendered + $accept = true; + } + } + } + + if (!$accept) { + continue; + } + } + + // make sure indentation is correct + $depth -= $minDepth; + $myIndent = $indent . str_repeat(' ', $depth); + + if ($depth > $prevDepth) { + // start new ul tag + if ($ulClass && $depth == 0) { + $ulClass = ' class="' . $escaper($ulClass) . '"'; + } else { + $ulClass = ''; + } + $html .= $myIndent . '' . PHP_EOL; + } elseif ($prevDepth > $depth) { + // close li/ul tags until we're at current depth + for ($i = $prevDepth; $i > $depth; $i--) { + $ind = $indent . str_repeat(' ', $i); + $html .= $ind . '
  • ' . PHP_EOL; + $html .= $ind . '' . PHP_EOL; + } + // close previous li tag + $html .= $myIndent . ' ' . PHP_EOL; + } else { + // close previous li tag + $html .= $myIndent . ' ' . PHP_EOL; + } + + // render li tag and page + $liClasses = array(); + // Is page active? + if ($isActive) { + $liClasses[] = $liActiveClass; + } + // Add CSS class from page to
  • + if ($addClassToListItem && $page->getClass()) { + $liClasses[] = $page->getClass(); + } + $liClass = empty($liClasses) ? '' : ' class="' . $escaper(implode(' ', $liClasses)) . '"'; + + $html .= $myIndent . ' ' . PHP_EOL + . $myIndent . ' ' . $this->htmlify($page, $escapeLabels, $addClassToListItem) . PHP_EOL; + + // store as previous depth for next iteration + $prevDepth = $depth; + } + + if ($html) { + // done iterating container; close open ul/li tags + for ($i = $prevDepth+1; $i > 0; $i--) { + $myIndent = $indent . str_repeat(' ', $i-1); + $html .= $myIndent . '
  • ' . PHP_EOL + . $myIndent . '' . PHP_EOL; + } + $html = rtrim($html, PHP_EOL); + } + + return $html; + } + + /** + * Renders the given $container by invoking the partial view helper + * + * The container will simply be passed on as a model to the view script + * as-is, and will be available in the partial script as 'container', e.g. + * echo 'Number of pages: ', count($this->container);. + * + * @param AbstractContainer $container [optional] container to pass to view + * script. Default is to use the container + * registered in the helper. + * @param string|array $partial [optional] partial view script to use. + * Default is to use the partial + * registered in the helper. If an array + * is given, it is expected to contain two + * values; the partial view script to use, + * and the module where the script can be + * found. + * @return string + * @throws Exception\RuntimeException if no partial provided + * @throws Exception\InvalidArgumentException if partial is invalid array + */ + public function renderPartial($container = null, $partial = null) + { + $this->parseContainer($container); + if (null === $container) { + $container = $this->getContainer(); + } + + if (null === $partial) { + $partial = $this->getPartial(); + } + + if (empty($partial)) { + throw new Exception\RuntimeException( + 'Unable to render menu: No partial view script provided' + ); + } + + $model = array( + 'container' => $container + ); + + /** @var \Zend\View\Helper\Partial $partialHelper */ + $partialHelper = $this->view->plugin('partial'); + + if (is_array($partial)) { + if (count($partial) != 2) { + throw new Exception\InvalidArgumentException( + 'Unable to render menu: A view partial supplied as ' + . 'an array must contain two values: partial view ' + . 'script and module where script can be found' + ); + } + + return $partialHelper($partial[0], $model); + } + + return $partialHelper($partial, $model); + } + + /** + * Renders the inner-most sub menu for the active page in the $container + * + * This is a convenience method which is equivalent to the following call: + * + * renderMenu($container, array( + * 'indent' => $indent, + * 'ulClass' => $ulClass, + * 'minDepth' => null, + * 'maxDepth' => null, + * 'onlyActiveBranch' => true, + * 'renderParents' => false, + * 'liActiveClass' => $liActiveClass + * )); + * + * + * @param AbstractContainer $container [optional] container to + * render. Default is to render + * the container registered in + * the helper. + * @param string $ulClass [optional] CSS class to + * use for UL element. Default + * is to use the value from + * {@link getUlClass()}. + * @param string|int $indent [optional] indentation as + * a string or number of + * spaces. Default is to use + * the value retrieved from + * {@link getIndent()}. + * @param string $liActiveClass [optional] CSS class to + * use for UL element. Default + * is to use the value from + * {@link getUlClass()}. + * @return string + */ + public function renderSubMenu( + AbstractContainer $container = null, + $ulClass = null, + $indent = null, + $liActiveClass = null + ) { + return $this->renderMenu($container, array( + 'indent' => $indent, + 'ulClass' => $ulClass, + 'minDepth' => null, + 'maxDepth' => null, + 'onlyActiveBranch' => true, + 'renderParents' => false, + 'escapeLabels' => true, + 'addClassToListItem' => false, + 'liActiveClass' => $liActiveClass + )); + } + + /** + * Returns an HTML string containing an 'a' element for the given page if + * the page's href is not empty, and a 'span' element if it is empty + * + * Overrides {@link AbstractHelper::htmlify()}. + * + * @param AbstractPage $page page to generate HTML for + * @param bool $escapeLabel Whether or not to escape the label + * @param bool $addClassToListItem Whether or not to add the page class to the list item + * @return string + */ + public function htmlify(AbstractPage $page, $escapeLabel = true, $addClassToListItem = false) + { + // get attribs for element + $attribs = array( + 'id' => $page->getId(), + 'title' => $this->translate($page->getTitle(), $page->getTextDomain()), + ); + + if ($addClassToListItem === false) { + $attribs['class'] = $page->getClass(); + } + + // does page have a href? + $href = $page->getHref(); + if ($href) { + $element = 'a'; + $attribs['href'] = $href; + $attribs['target'] = $page->getTarget(); + } else { + $element = 'span'; + } + + $html = '<' . $element . $this->htmlAttribs($attribs) . '>'; + $label = $this->translate($page->getLabel(), $page->getTextDomain()); + if ($escapeLabel === true) { + /** @var \Zend\View\Helper\EscapeHtml $escaper */ + $escaper = $this->view->plugin('escapeHtml'); + $html .= $escaper($label); + } else { + $html .= $label; + } + $html .= ''; + + return $html; + } + + /** + * Normalizes given render options + * + * @param array $options [optional] options to normalize + * @return array + */ + protected function normalizeOptions(array $options = array()) + { + if (isset($options['indent'])) { + $options['indent'] = $this->getWhitespace($options['indent']); + } else { + $options['indent'] = $this->getIndent(); + } + + if (isset($options['ulClass']) && $options['ulClass'] !== null) { + $options['ulClass'] = (string) $options['ulClass']; + } else { + $options['ulClass'] = $this->getUlClass(); + } + + if (array_key_exists('minDepth', $options)) { + if (null !== $options['minDepth']) { + $options['minDepth'] = (int) $options['minDepth']; + } + } else { + $options['minDepth'] = $this->getMinDepth(); + } + + if ($options['minDepth'] < 0 || $options['minDepth'] === null) { + $options['minDepth'] = 0; + } + + if (array_key_exists('maxDepth', $options)) { + if (null !== $options['maxDepth']) { + $options['maxDepth'] = (int) $options['maxDepth']; + } + } else { + $options['maxDepth'] = $this->getMaxDepth(); + } + + if (!isset($options['onlyActiveBranch'])) { + $options['onlyActiveBranch'] = $this->getOnlyActiveBranch(); + } + + if (!isset($options['escapeLabels'])) { + $options['escapeLabels'] = $this->escapeLabels; + } + + if (!isset($options['renderParents'])) { + $options['renderParents'] = $this->getRenderParents(); + } + + if (!isset($options['addClassToListItem'])) { + $options['addClassToListItem'] = $this->getAddClassToListItem(); + } + + if (isset($options['liActiveClass']) && $options['liActiveClass'] !== null) { + $options['liActiveClass'] = (string) $options['liActiveClass']; + } else { + $options['liActiveClass'] = $this->getLiActiveClass(); + } + + return $options; + } + + /** + * Sets a flag indicating whether labels should be escaped + * + * @param bool $flag [optional] escape labels + * @return self + */ + public function escapeLabels($flag = true) + { + $this->escapeLabels = (bool) $flag; + return $this; + } + + /** + * Enables/disables page class applied to
  • element + * + * @param bool $flag [optional] page class applied to
  • element + * Default is true. + * @return self fluent interface, returns self + */ + public function setAddClassToListItem($flag = true) + { + $this->addClassToListItem = (bool) $flag; + return $this; + } + + /** + * Returns flag indicating whether page class should be applied to
  • element + * + * By default, this value is false. + * + * @return bool whether parents should be rendered + */ + public function getAddClassToListItem() + { + return $this->addClassToListItem; + } + + /** + * Sets a flag indicating whether only active branch should be rendered + * + * @param bool $flag [optional] render only active branch. + * @return self + */ + public function setOnlyActiveBranch($flag = true) + { + $this->onlyActiveBranch = (bool) $flag; + return $this; + } + + /** + * Returns a flag indicating whether only active branch should be rendered + * + * By default, this value is false, meaning the entire menu will be + * be rendered. + * + * @return bool + */ + public function getOnlyActiveBranch() + { + return $this->onlyActiveBranch; + } + + /** + * Sets which partial view script to use for rendering menu + * + * @param string|array $partial partial view script or null. If an array is + * given, it is expected to contain two + * values; the partial view script to use, + * and the module where the script can be + * found. + * @return self + */ + public function setPartial($partial) + { + if (null === $partial || is_string($partial) || is_array($partial)) { + $this->partial = $partial; + } + + return $this; + } + + /** + * Returns partial view script to use for rendering menu + * + * @return string|array|null + */ + public function getPartial() + { + return $this->partial; + } + + /** + * Enables/disables rendering of parents when only rendering active branch + * + * See {@link setOnlyActiveBranch()} for more information. + * + * @param bool $flag [optional] render parents when rendering active branch. + * @return self + */ + public function setRenderParents($flag = true) + { + $this->renderParents = (bool) $flag; + return $this; + } + + /** + * Returns flag indicating whether parents should be rendered when rendering + * only the active branch + * + * By default, this value is true. + * + * @return bool + */ + public function getRenderParents() + { + return $this->renderParents; + } + + /** + * Sets CSS class to use for the first 'ul' element when rendering + * + * @param string $ulClass CSS class to set + * @return self + */ + public function setUlClass($ulClass) + { + if (is_string($ulClass)) { + $this->ulClass = $ulClass; + } + + return $this; + } + + /** + * Returns CSS class to use for the first 'ul' element when rendering + * + * @return string + */ + public function getUlClass() + { + return $this->ulClass; + } + + /** + * Sets CSS class to use for the active 'li' element when rendering + * + * @param string $liActiveClass CSS class to set + * @return self + */ + public function setLiActiveClass($liActiveClass) + { + if (is_string($liActiveClass)) { + $this->liActiveClass = $liActiveClass; + } + + return $this; + } + + /** + * Returns CSS class to use for the active 'li' element when rendering + * + * @return string + */ + public function getLiActiveClass() + { + return $this->liActiveClass; + } +} diff --git a/library/Zend/View/Helper/Navigation/PluginManager.php b/library/Zend/View/Helper/Navigation/PluginManager.php new file mode 100755 index 000000000..8faf86eb3 --- /dev/null +++ b/library/Zend/View/Helper/Navigation/PluginManager.php @@ -0,0 +1,58 @@ + 'Zend\View\Helper\Navigation\Breadcrumbs', + 'links' => 'Zend\View\Helper\Navigation\Links', + 'menu' => 'Zend\View\Helper\Navigation\Menu', + 'sitemap' => 'Zend\View\Helper\Navigation\Sitemap', + ); + + /** + * Validate the plugin + * + * Checks that the helper loaded is an instance of AbstractHelper. + * + * @param mixed $plugin + * @return void + * @throws Exception\InvalidArgumentException if invalid + */ + public function validatePlugin($plugin) + { + if ($plugin instanceof AbstractHelper) { + // we're okay + return; + } + + throw new Exception\InvalidArgumentException(sprintf( + 'Plugin of type %s is invalid; must implement %s\AbstractHelper', + (is_object($plugin) ? get_class($plugin) : gettype($plugin)), + __NAMESPACE__ + )); + } +} diff --git a/library/Zend/View/Helper/Navigation/Sitemap.php b/library/Zend/View/Helper/Navigation/Sitemap.php new file mode 100755 index 000000000..2ba2b4a4b --- /dev/null +++ b/library/Zend/View/Helper/Navigation/Sitemap.php @@ -0,0 +1,441 @@ + tag + * + * @var string + */ + const SITEMAP_NS = 'http://www.sitemaps.org/schemas/sitemap/0.9'; + + /** + * Schema URL + * + * @var string + */ + const SITEMAP_XSD = 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd'; + + /** + * Whether XML output should be formatted + * + * @var bool + */ + protected $formatOutput = false; + + /** + * Server url + * + * @var string + */ + protected $serverUrl; + + /** + * List of urls in the sitemap + * + * @var array + */ + protected $urls = array(); + + /** + * Whether sitemap should be validated using Zend\Validate\Sitemap\* + * + * @var bool + */ + protected $useSitemapValidators = true; + + /** + * Whether sitemap should be schema validated when generated + * + * @var bool + */ + protected $useSchemaValidation = false; + + /** + * Whether the XML declaration should be included in XML output + * + * @var bool + */ + protected $useXmlDeclaration = true; + + /** + * Helper entry point + * + * @param string|AbstractContainer $container container to operate on + * @return Sitemap + */ + public function __invoke($container = null) + { + if (null !== $container) { + $this->setContainer($container); + } + + return $this; + } + + /** + * Renders helper + * + * Implements {@link HelperInterface::render()}. + * + * @param AbstractContainer $container [optional] container to render. Default is + * to render the container registered in the helper. + * @return string + */ + public function render($container = null) + { + $dom = $this->getDomSitemap($container); + $xml = $this->getUseXmlDeclaration() ? + $dom->saveXML() : + $dom->saveXML($dom->documentElement); + + return rtrim($xml, PHP_EOL); + } + + /** + * Returns a DOMDocument containing the Sitemap XML for the given container + * + * @param AbstractContainer $container [optional] container to get + * breadcrumbs from, defaults + * to what is registered in the + * helper + * @return DOMDocument DOM representation of the + * container + * @throws Exception\RuntimeException if schema validation is on + * and the sitemap is invalid + * according to the sitemap + * schema, or if sitemap + * validators are used and the + * loc element fails validation + */ + public function getDomSitemap(AbstractContainer $container = null) + { + // Reset the urls + $this->urls = array(); + + if (null === $container) { + $container = $this->getContainer(); + } + + // check if we should validate using our own validators + if ($this->getUseSitemapValidators()) { + // create validators + $locValidator = new \Zend\Validator\Sitemap\Loc(); + $lastmodValidator = new \Zend\Validator\Sitemap\Lastmod(); + $changefreqValidator = new \Zend\Validator\Sitemap\Changefreq(); + $priorityValidator = new \Zend\Validator\Sitemap\Priority(); + } + + // create document + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = $this->getFormatOutput(); + + // ...and urlset (root) element + $urlSet = $dom->createElementNS(self::SITEMAP_NS, 'urlset'); + $dom->appendChild($urlSet); + + // create iterator + $iterator = new RecursiveIteratorIterator($container, + RecursiveIteratorIterator::SELF_FIRST); + + $maxDepth = $this->getMaxDepth(); + if (is_int($maxDepth)) { + $iterator->setMaxDepth($maxDepth); + } + $minDepth = $this->getMinDepth(); + if (!is_int($minDepth) || $minDepth < 0) { + $minDepth = 0; + } + + // iterate container + foreach ($iterator as $page) { + if ($iterator->getDepth() < $minDepth || !$this->accept($page)) { + // page should not be included + continue; + } + + // get absolute url from page + if (!$url = $this->url($page)) { + // skip page if it has no url (rare case) + // or already is in the sitemap + continue; + } + + // create url node for this page + $urlNode = $dom->createElementNS(self::SITEMAP_NS, 'url'); + $urlSet->appendChild($urlNode); + + if ($this->getUseSitemapValidators() + && !$locValidator->isValid($url) + ) { + throw new Exception\RuntimeException(sprintf( + 'Encountered an invalid URL for Sitemap XML: "%s"', + $url + )); + } + + // put url in 'loc' element + $urlNode->appendChild($dom->createElementNS(self::SITEMAP_NS, + 'loc', $url)); + + // add 'lastmod' element if a valid lastmod is set in page + if (isset($page->lastmod)) { + $lastmod = strtotime((string) $page->lastmod); + + // prevent 1970-01-01... + if ($lastmod !== false) { + $lastmod = date('c', $lastmod); + } + + if (!$this->getUseSitemapValidators() || + $lastmodValidator->isValid($lastmod)) { + $urlNode->appendChild( + $dom->createElementNS(self::SITEMAP_NS, 'lastmod', + $lastmod) + ); + } + } + + // add 'changefreq' element if a valid changefreq is set in page + if (isset($page->changefreq)) { + $changefreq = $page->changefreq; + if (!$this->getUseSitemapValidators() || + $changefreqValidator->isValid($changefreq)) { + $urlNode->appendChild( + $dom->createElementNS(self::SITEMAP_NS, 'changefreq', + $changefreq) + ); + } + } + + // add 'priority' element if a valid priority is set in page + if (isset($page->priority)) { + $priority = $page->priority; + if (!$this->getUseSitemapValidators() || + $priorityValidator->isValid($priority)) { + $urlNode->appendChild( + $dom->createElementNS(self::SITEMAP_NS, 'priority', $priority) + ); + } + } + } + + // validate using schema if specified + if ($this->getUseSchemaValidation()) { + ErrorHandler::start(); + $test = $dom->schemaValidate(self::SITEMAP_XSD); + $error = ErrorHandler::stop(); + if (!$test) { + throw new Exception\RuntimeException(sprintf( + 'Sitemap is invalid according to XML Schema at "%s"', + self::SITEMAP_XSD + ), 0, $error); + } + } + + return $dom; + } + + /** + * Returns an escaped absolute URL for the given page + * + * @param AbstractPage $page + * @return string + */ + public function url(AbstractPage $page) + { + $href = $page->getHref(); + + if (!isset($href{0})) { + // no href + return ''; + } elseif ($href{0} == '/') { + // href is relative to root; use serverUrl helper + $url = $this->getServerUrl() . $href; + } elseif (preg_match('/^[a-z]+:/im', (string) $href)) { + // scheme is given in href; assume absolute URL already + $url = (string) $href; + } else { + // href is relative to current document; use url helpers + $basePathHelper = $this->getView()->plugin('basepath'); + $curDoc = $basePathHelper(); + $curDoc = ('/' == $curDoc) ? '' : trim($curDoc, '/'); + $url = rtrim($this->getServerUrl(), '/') . '/' + . $curDoc + . (empty($curDoc) ? '' : '/') . $href; + } + + if (! in_array($url, $this->urls)) { + $this->urls[] = $url; + return $this->xmlEscape($url); + } + + return null; + } + + /** + * Escapes string for XML usage + * + * @param string $string + * @return string + */ + protected function xmlEscape($string) + { + $escaper = $this->view->plugin('escapeHtml'); + return $escaper($string); + } + + /** + * Sets whether XML output should be formatted + * + * @param bool $formatOutput + * @return Sitemap + */ + public function setFormatOutput($formatOutput = true) + { + $this->formatOutput = (bool) $formatOutput; + return $this; + } + + /** + * Returns whether XML output should be formatted + * + * @return bool + */ + public function getFormatOutput() + { + return $this->formatOutput; + } + + /** + * Sets server url (scheme and host-related stuff without request URI) + * + * E.g. http://www.example.com + * + * @param string $serverUrl + * @return Sitemap + * @throws Exception\InvalidArgumentException + */ + public function setServerUrl($serverUrl) + { + $uri = Uri\UriFactory::factory($serverUrl); + $uri->setFragment(''); + $uri->setPath(''); + $uri->setQuery(''); + + if ($uri->isValid()) { + $this->serverUrl = $uri->toString(); + } else { + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid server URL: "%s"', + $serverUrl + )); + } + + return $this; + } + + /** + * Returns server URL + * + * @return string + */ + public function getServerUrl() + { + if (!isset($this->serverUrl)) { + $serverUrlHelper = $this->getView()->plugin('serverUrl'); + $this->serverUrl = $serverUrlHelper(); + } + + return $this->serverUrl; + } + + /** + * Sets whether sitemap should be validated using Zend\Validate\Sitemap_* + * + * @param bool $useSitemapValidators + * @return Sitemap + */ + public function setUseSitemapValidators($useSitemapValidators) + { + $this->useSitemapValidators = (bool) $useSitemapValidators; + return $this; + } + + /** + * Returns whether sitemap should be validated using Zend\Validate\Sitemap_* + * + * @return bool + */ + public function getUseSitemapValidators() + { + return $this->useSitemapValidators; + } + + /** + * Sets whether sitemap should be schema validated when generated + * + * @param bool $schemaValidation + * @return Sitemap + */ + public function setUseSchemaValidation($schemaValidation) + { + $this->useSchemaValidation = (bool) $schemaValidation; + return $this; + } + + /** + * Returns true if sitemap should be schema validated when generated + * + * @return bool + */ + public function getUseSchemaValidation() + { + return $this->useSchemaValidation; + } + + /** + * Sets whether the XML declaration should be used in output + * + * @param bool $useXmlDecl + * @return Sitemap + */ + public function setUseXmlDeclaration($useXmlDecl) + { + $this->useXmlDeclaration = (bool) $useXmlDecl; + return $this; + } + + /** + * Returns whether the XML declaration should be used in output + * + * @return bool + */ + public function getUseXmlDeclaration() + { + return $this->useXmlDeclaration; + } +} diff --git a/library/Zend/View/Helper/PaginationControl.php b/library/Zend/View/Helper/PaginationControl.php new file mode 100755 index 000000000..9963fdd35 --- /dev/null +++ b/library/Zend/View/Helper/PaginationControl.php @@ -0,0 +1,131 @@ +paginator is set and, + * if so, uses that. Also, if no scrolling style or partial are specified, + * the defaults will be used (if set). + * + * @param Paginator\Paginator $paginator (Optional) + * @param string $scrollingStyle (Optional) Scrolling style + * @param string $partial (Optional) View partial + * @param array|string $params (Optional) params to pass to the partial + * @throws Exception\RuntimeException if no paginator or no view partial provided + * @throws Exception\InvalidArgumentException if partial is invalid array + * @return string + */ + public function __invoke(Paginator\Paginator $paginator = null, $scrollingStyle = null, $partial = null, $params = null) + { + if ($paginator === null) { + if (isset($this->view->paginator) and $this->view->paginator !== null and $this->view->paginator instanceof Paginator\Paginator) { + $paginator = $this->view->paginator; + } else { + throw new Exception\RuntimeException('No paginator instance provided or incorrect type'); + } + } + + if ($partial === null) { + if (static::$defaultViewPartial === null) { + throw new Exception\RuntimeException('No view partial provided and no default set'); + } + + $partial = static::$defaultViewPartial; + } + + if ($scrollingStyle === null) { + $scrollingStyle = static::$defaultScrollingStyle; + } + + $pages = get_object_vars($paginator->getPages($scrollingStyle)); + + if ($params !== null) { + $pages = array_merge($pages, (array) $params); + } + + if (is_array($partial)) { + if (count($partial) != 2) { + throw new Exception\InvalidArgumentException( + 'A view partial supplied as an array must contain two values: the filename and its module' + ); + } + + if ($partial[1] !== null) { + $partialHelper = $this->view->plugin('partial'); + return $partialHelper($partial[0], $pages); + } + + $partial = $partial[0]; + } + + $partialHelper = $this->view->plugin('partial'); + return $partialHelper($partial, $pages); + } + + /** + * Sets the default Scrolling Style + * + * @param string $style string 'all' | 'elastic' | 'sliding' | 'jumping' + */ + public static function setDefaultScrollingStyle($style) + { + static::$defaultScrollingStyle = $style; + } + + /** + * Gets the default scrolling style + * + * @return string + */ + public static function getDefaultScrollingStyle() + { + return static::$defaultScrollingStyle; + } + + /** + * Sets the default view partial. + * + * @param string|array $partial View partial + */ + public static function setDefaultViewPartial($partial) + { + static::$defaultViewPartial = $partial; + } + + /** + * Gets the default view partial + * + * @return string|array + */ + public static function getDefaultViewPartial() + { + return static::$defaultViewPartial; + } +} diff --git a/library/Zend/View/Helper/Partial.php b/library/Zend/View/Helper/Partial.php new file mode 100755 index 000000000..444f161b9 --- /dev/null +++ b/library/Zend/View/Helper/Partial.php @@ -0,0 +1,94 @@ +getView()->render($name); + } + + if (is_scalar($values)) { + $values = array(); + } elseif ($values instanceof ModelInterface) { + $values = $values->getVariables(); + } elseif (is_object($values)) { + if (null !== ($objectKey = $this->getObjectKey())) { + $values = array($objectKey => $values); + } elseif (method_exists($values, 'toArray')) { + $values = $values->toArray(); + } else { + $values = get_object_vars($values); + } + } + + return $this->getView()->render($name, $values); + } + + /** + * Set object key + * + * @param string $key + * @return Partial + */ + public function setObjectKey($key) + { + if (null === $key) { + $this->objectKey = null; + return $this; + } + + $this->objectKey = (string) $key; + + return $this; + } + + /** + * Retrieve object key + * + * The objectKey is the variable to which an object in the iterator will be + * assigned. + * + * @return null|string + */ + public function getObjectKey() + { + return $this->objectKey; + } +} diff --git a/library/Zend/View/Helper/PartialLoop.php b/library/Zend/View/Helper/PartialLoop.php new file mode 100755 index 000000000..f66297e67 --- /dev/null +++ b/library/Zend/View/Helper/PartialLoop.php @@ -0,0 +1,77 @@ +toArray(); + } else { + throw new Exception\InvalidArgumentException('PartialLoop helper requires iterable data'); + } + } + + // reset the counter if it's called again + $this->partialCounter = 0; + $content = ''; + + foreach ($values as $item) { + $this->partialCounter++; + $content .= parent::__invoke($name, $item); + } + + return $content; + } + + /** + * Get the partial counter + * + * @return int + */ + public function getPartialCounter() + { + return $this->partialCounter; + } +} diff --git a/library/Zend/View/Helper/Placeholder.php b/library/Zend/View/Helper/Placeholder.php new file mode 100755 index 000000000..297c17a55 --- /dev/null +++ b/library/Zend/View/Helper/Placeholder.php @@ -0,0 +1,98 @@ +getContainer($name); + } + + /** + * createContainer + * + * @param string $key + * @param array $value + * @return Container\AbstractContainer + */ + public function createContainer($key, array $value = array()) + { + $key = (string) $key; + + $this->items[$key] = new $this->containerClass($value); + return $this->items[$key]; + } + + /** + * Retrieve a placeholder container + * + * @param string $key + * @return Container\AbstractContainer + */ + public function getContainer($key) + { + $key = (string) $key; + if (isset($this->items[$key])) { + return $this->items[$key]; + } + + $container = $this->createContainer($key); + + return $container; + } + + /** + * Does a particular container exist? + * + * @param string $key + * @return bool + */ + public function containerExists($key) + { + $key = (string) $key; + $return = array_key_exists($key, $this->items); + return $return; + } +} diff --git a/library/Zend/View/Helper/Placeholder/Container.php b/library/Zend/View/Helper/Placeholder/Container.php new file mode 100755 index 000000000..35220176c --- /dev/null +++ b/library/Zend/View/Helper/Placeholder/Container.php @@ -0,0 +1,17 @@ +toString(); + } + + /** + * Render the placeholder + * + * @param null|int|string $indent + * @return string + */ + public function toString($indent = null) + { + $indent = ($indent !== null) + ? $this->getWhitespace($indent) + : $this->getIndent(); + + $items = $this->getArrayCopy(); + $return = $indent + . $this->getPrefix() + . implode($this->getSeparator(), $items) + . $this->getPostfix(); + $return = preg_replace("/(\r\n?|\n)/", '$1' . $indent, $return); + + return $return; + } + + /** + * Start capturing content to push into placeholder + * + * @param string $type How to capture content into placeholder; append, prepend, or set + * @param mixed $key Key to which to capture content + * @throws Exception\RuntimeException if nested captures detected + * @return void + */ + public function captureStart($type = AbstractContainer::APPEND, $key = null) + { + if ($this->captureLock) { + throw new Exception\RuntimeException( + 'Cannot nest placeholder captures for the same placeholder' + ); + } + + $this->captureLock = true; + $this->captureType = $type; + if ((null !== $key) && is_scalar($key)) { + $this->captureKey = (string) $key; + } + ob_start(); + } + + /** + * End content capture + * + * @return void + */ + public function captureEnd() + { + $data = ob_get_clean(); + $key = null; + $this->captureLock = false; + if (null !== $this->captureKey) { + $key = $this->captureKey; + } + switch ($this->captureType) { + case self::SET: + if (null !== $key) { + $this[$key] = $data; + } else { + $this->exchangeArray(array($data)); + } + break; + case self::PREPEND: + if (null !== $key) { + $array = array($key => $data); + $values = $this->getArrayCopy(); + $final = $array + $values; + $this->exchangeArray($final); + } else { + $this->prepend($data); + } + break; + case self::APPEND: + default: + if (null !== $key) { + if (empty($this[$key])) { + $this[$key] = $data; + } else { + $this[$key] .= $data; + } + } else { + $this[$this->nextIndex()] = $data; + } + break; + } + } + + /** + * Get keys + * + * @return array + */ + public function getKeys() + { + $array = $this->getArrayCopy(); + + return array_keys($array); + } + + /** + * Retrieve container value + * + * If single element registered, returns that element; otherwise, + * serializes to array. + * + * @return mixed + */ + public function getValue() + { + if (1 == count($this)) { + $keys = $this->getKeys(); + $key = array_shift($keys); + return $this[$key]; + } + + return $this->getArrayCopy(); + } + + /** + * Retrieve whitespace representation of $indent + * + * @param int|string $indent + * @return string + */ + public function getWhitespace($indent) + { + if (is_int($indent)) { + $indent = str_repeat(' ', $indent); + } + + return (string) $indent; + } + + /** + * Set a single value + * + * @param mixed $value + * @return void + */ + public function set($value) + { + $this->exchangeArray(array($value)); + + return $this; + } + + /** + * Prepend a value to the top of the container + * + * @param mixed $value + * @return self + */ + public function prepend($value) + { + $values = $this->getArrayCopy(); + array_unshift($values, $value); + $this->exchangeArray($values); + + return $this; + } + + /** + * Append a value to the end of the container + * + * @param mixed $value + * @return self + */ + public function append($value) + { + parent::append($value); + return $this; + } + + /** + * Next Index as defined by the PHP manual + * + * @return int + */ + public function nextIndex() + { + $keys = $this->getKeys(); + if (0 == count($keys)) { + return 0; + } + + return $nextIndex = max($keys) + 1; + } + + /** + * Set the indentation string for __toString() serialization, + * optionally, if a number is passed, it will be the number of spaces + * + * @param string|int $indent + * @return self + */ + public function setIndent($indent) + { + $this->indent = $this->getWhitespace($indent); + return $this; + } + + /** + * Retrieve indentation + * + * @return string + */ + public function getIndent() + { + return $this->indent; + } + + /** + * Set postfix for __toString() serialization + * + * @param string $postfix + * @return self + */ + public function setPostfix($postfix) + { + $this->postfix = (string) $postfix; + return $this; + } + + /** + * Retrieve postfix + * + * @return string + */ + public function getPostfix() + { + return $this->postfix; + } + + /** + * Set prefix for __toString() serialization + * + * @param string $prefix + * @return self + */ + public function setPrefix($prefix) + { + $this->prefix = (string) $prefix; + return $this; + } + + /** + * Retrieve prefix + * + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * Set separator for __toString() serialization + * + * Used to implode elements in container + * + * @param string $separator + * @return self + */ + public function setSeparator($separator) + { + $this->separator = (string) $separator; + return $this; + } + + /** + * Retrieve separator + * + * @return string + */ + public function getSeparator() + { + return $this->separator; + } +} diff --git a/library/Zend/View/Helper/Placeholder/Container/AbstractStandalone.php b/library/Zend/View/Helper/Placeholder/Container/AbstractStandalone.php new file mode 100755 index 000000000..619ba7073 --- /dev/null +++ b/library/Zend/View/Helper/Placeholder/Container/AbstractStandalone.php @@ -0,0 +1,376 @@ +setContainer($this->getContainer()); + } + + /** + * Overload + * + * Proxy to container methods + * + * @param string $method + * @param array $args + * @throws Exception\BadMethodCallException + * @return mixed + */ + public function __call($method, $args) + { + $container = $this->getContainer(); + if (method_exists($container, $method)) { + $return = call_user_func_array(array($container, $method), $args); + if ($return === $container) { + // If the container is returned, we really want the current object + return $this; + } + return $return; + } + + throw new Exception\BadMethodCallException('Method "' . $method . '" does not exist'); + } + + /** + * Overloading: set property value + * + * @param string $key + * @param mixed $value + * @return void + */ + public function __set($key, $value) + { + $container = $this->getContainer(); + $container[$key] = $value; + } + + /** + * Overloading: retrieve property + * + * @param string $key + * @return mixed + */ + public function __get($key) + { + $container = $this->getContainer(); + if (isset($container[$key])) { + return $container[$key]; + } + + return null; + } + + /** + * Overloading: check if property is set + * + * @param string $key + * @return bool + */ + public function __isset($key) + { + $container = $this->getContainer(); + return isset($container[$key]); + } + + /** + * Overloading: unset property + * + * @param string $key + * @return void + */ + public function __unset($key) + { + $container = $this->getContainer(); + if (isset($container[$key])) { + unset($container[$key]); + } + } + + /** + * Cast to string representation + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * String representation + * + * @return string + */ + public function toString() + { + return $this->getContainer()->toString(); + } + + /** + * Escape a string + * + * @param string $string + * @return string + */ + protected function escape($string) + { + if ($this->getView() instanceof RendererInterface + && method_exists($this->getView(), 'getEncoding') + ) { + $escaper = $this->getView()->plugin('escapeHtml'); + return $escaper((string) $string); + } + + return $this->getEscaper()->escapeHtml((string) $string); + } + + /** + * Set whether or not auto escaping should be used + * + * @param bool $autoEscape whether or not to auto escape output + * @return AbstractStandalone + */ + public function setAutoEscape($autoEscape = true) + { + $this->autoEscape = ($autoEscape) ? true : false; + return $this; + } + + /** + * Return whether autoEscaping is enabled or disabled + * + * return bool + */ + public function getAutoEscape() + { + return $this->autoEscape; + } + + /** + * Set container on which to operate + * + * @param AbstractContainer $container + * @return AbstractStandalone + */ + public function setContainer(AbstractContainer $container) + { + $this->container = $container; + return $this; + } + + /** + * Retrieve placeholder container + * + * @return AbstractContainer + */ + public function getContainer() + { + if (!$this->container instanceof AbstractContainer) { + $this->container = new $this->containerClass(); + } + return $this->container; + } + + /** + * Delete a container + * + * @return bool + */ + public function deleteContainer() + { + if (null != $this->container) { + $this->container = null; + return true; + } + + return false; + } + + /** + * Set the container class to use + * + * @param string $name + * @throws Exception\InvalidArgumentException + * @throws Exception\DomainException + * @return \Zend\View\Helper\Placeholder\Container\AbstractStandalone + */ + public function setContainerClass($name) + { + if (!class_exists($name)) { + throw new Exception\DomainException( + sprintf( + '%s expects a valid container class name; received "%s", which did not resolve', + __METHOD__, + $name + ) + ); + } + + if (!in_array('Zend\View\Helper\Placeholder\Container\AbstractContainer', class_parents($name))) { + throw new Exception\InvalidArgumentException('Invalid Container class specified'); + } + + $this->containerClass = $name; + return $this; + } + + /** + * Retrieve the container class + * + * @return string + */ + public function getContainerClass() + { + return $this->containerClass; + } + + /** + * Set Escaper instance + * + * @param Escaper $escaper + * @return AbstractStandalone + */ + public function setEscaper(Escaper $escaper) + { + $encoding = $escaper->getEncoding(); + $this->escapers[$encoding] = $escaper; + + return $this; + } + + /** + * Get Escaper instance + * + * Lazy-loads one if none available + * + * @param string|null $enc Encoding to use + * @return mixed + */ + public function getEscaper($enc = 'UTF-8') + { + $enc = strtolower($enc); + if (!isset($this->escapers[$enc])) { + $this->setEscaper(new Escaper($enc)); + } + + return $this->escapers[$enc]; + } + + /** + * Countable + * + * @return int + */ + public function count() + { + $container = $this->getContainer(); + return count($container); + } + + /** + * ArrayAccess: offsetExists + * + * @param string|int $offset + * @return bool + */ + public function offsetExists($offset) + { + return $this->getContainer()->offsetExists($offset); + } + + /** + * ArrayAccess: offsetGet + * + * @param string|int $offset + * @return mixed + */ + public function offsetGet($offset) + { + return $this->getContainer()->offsetGet($offset); + } + + /** + * ArrayAccess: offsetSet + * + * @param string|int $offset + * @param mixed $value + * @return void + */ + public function offsetSet($offset, $value) + { + return $this->getContainer()->offsetSet($offset, $value); + } + + /** + * ArrayAccess: offsetUnset + * + * @param string|int $offset + * @return void + */ + public function offsetUnset($offset) + { + return $this->getContainer()->offsetUnset($offset); + } + + /** + * IteratorAggregate: get Iterator + * + * @return \Iterator + */ + public function getIterator() + { + return $this->getContainer()->getIterator(); + } +} diff --git a/library/Zend/View/Helper/Placeholder/Registry.php b/library/Zend/View/Helper/Placeholder/Registry.php new file mode 100755 index 000000000..f86b4e1ad --- /dev/null +++ b/library/Zend/View/Helper/Placeholder/Registry.php @@ -0,0 +1,185 @@ +items[$key] = $container; + + return $this; + } + + /** + * Retrieve a placeholder container + * + * @param string $key + * @return Container\AbstractContainer + */ + public function getContainer($key) + { + $key = (string) $key; + if (isset($this->items[$key])) { + return $this->items[$key]; + } + + $container = $this->createContainer($key); + + return $container; + } + + /** + * Does a particular container exist? + * + * @param string $key + * @return bool + */ + public function containerExists($key) + { + $key = (string) $key; + + return array_key_exists($key, $this->items); + } + + /** + * createContainer + * + * @param string $key + * @param array $value + * @return Container\AbstractContainer + */ + public function createContainer($key, array $value = array()) + { + $key = (string) $key; + + $this->items[$key] = new $this->containerClass($value); + + return $this->items[$key]; + } + + /** + * Delete a container + * + * @param string $key + * @return bool + */ + public function deleteContainer($key) + { + $key = (string) $key; + if (isset($this->items[$key])) { + unset($this->items[$key]); + return true; + } + + return false; + } + + /** + * Set the container class to use + * + * @param string $name + * @throws Exception\InvalidArgumentException + * @throws Exception\DomainException + * @return Registry + */ + public function setContainerClass($name) + { + if (!class_exists($name)) { + throw new Exception\DomainException( + sprintf( + '%s expects a valid registry class name; received "%s", which did not resolve', + __METHOD__, + $name + ) + ); + } + + if (!in_array('Zend\View\Helper\Placeholder\Container\AbstractContainer', class_parents($name))) { + throw new Exception\InvalidArgumentException('Invalid Container class specified'); + } + + $this->containerClass = $name; + + return $this; + } + + /** + * Retrieve the container class + * + * @return string + */ + public function getContainerClass() + { + return $this->containerClass; + } +} diff --git a/library/Zend/View/Helper/RenderChildModel.php b/library/Zend/View/Helper/RenderChildModel.php new file mode 100755 index 000000000..d59edfe05 --- /dev/null +++ b/library/Zend/View/Helper/RenderChildModel.php @@ -0,0 +1,133 @@ +render($child); + } + + /** + * Render a model + * + * If a matching child model is found, it is rendered. If not, an empty + * string is returned. + * + * @param string $child + * @return string + */ + public function render($child) + { + $model = $this->findChild($child); + if (!$model) { + return ''; + } + + $current = $this->current; + $view = $this->getView(); + $return = $view->render($model); + $helper = $this->getViewModelHelper(); + $helper->setCurrent($current); + + return $return; + } + + /** + * Find the named child model + * + * Iterates through the current view model, looking for a child model that + * has a captureTo value matching the requested $child. If found, that child + * model is returned; otherwise, a boolean false is returned. + * + * @param string $child + * @return false|Model + */ + protected function findChild($child) + { + $this->current = $model = $this->getCurrent(); + foreach ($model->getChildren() as $childModel) { + if ($childModel->captureTo() == $child) { + return $childModel; + } + } + + return false; + } + + /** + * Get the current view model + * + * @throws Exception\RuntimeException + * @return null|Model + */ + protected function getCurrent() + { + $helper = $this->getViewModelHelper(); + if (!$helper->hasCurrent()) { + throw new Exception\RuntimeException(sprintf( + '%s: no view model currently registered in renderer; cannot query for children', + __METHOD__ + )); + } + + return $helper->getCurrent(); + } + + /** + * Retrieve the view model helper + * + * @return ViewModel + */ + protected function getViewModelHelper() + { + if ($this->viewModelHelper) { + return $this->viewModelHelper; + } + + if (method_exists($this->getView(), 'plugin')) { + $this->viewModelHelper = $this->view->plugin('view_model'); + } + + return $this->viewModelHelper; + } +} diff --git a/library/Zend/View/Helper/RenderToPlaceholder.php b/library/Zend/View/Helper/RenderToPlaceholder.php new file mode 100755 index 000000000..3707995b3 --- /dev/null +++ b/library/Zend/View/Helper/RenderToPlaceholder.php @@ -0,0 +1,35 @@ +view->plugin('placeholder'); + $placeholderHelper($placeholder)->captureStart(); + echo $this->view->render($script); + $placeholderHelper($placeholder)->captureEnd(); + } +} diff --git a/library/Zend/View/Helper/ServerUrl.php b/library/Zend/View/Helper/ServerUrl.php new file mode 100755 index 000000000..ae846a9ba --- /dev/null +++ b/library/Zend/View/Helper/ServerUrl.php @@ -0,0 +1,330 @@ +getScheme() . '://' . $this->getHost() . $path; + } + + /** + * Detect the host based on headers + * + * @return void + */ + protected function detectHost() + { + if ($this->setHostFromProxy()) { + return; + } + + if (isset($_SERVER['HTTP_HOST']) && !empty($_SERVER['HTTP_HOST'])) { + // Detect if the port is set in SERVER_PORT and included in HTTP_HOST + if (isset($_SERVER['SERVER_PORT'])) { + $portStr = ':' . $_SERVER['SERVER_PORT']; + if (substr($_SERVER['HTTP_HOST'], 0-strlen($portStr), strlen($portStr)) == $portStr) { + $this->setHost(substr($_SERVER['HTTP_HOST'], 0, 0-strlen($portStr))); + return; + } + } + + $this->setHost($_SERVER['HTTP_HOST']); + + return; + } + + if (!isset($_SERVER['SERVER_NAME']) || !isset($_SERVER['SERVER_PORT'])) { + return; + } + + $name = $_SERVER['SERVER_NAME']; + $this->setHost($name); + } + + /** + * Detect the port + * + * @return null + */ + protected function detectPort() + { + if ($this->setPortFromProxy()) { + return; + } + + if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT']) { + $this->setPort($_SERVER['SERVER_PORT']); + return; + } + } + + /** + * Detect the scheme + * + * @return null + */ + protected function detectScheme() + { + if ($this->setSchemeFromProxy()) { + return; + } + + switch (true) { + case (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true)): + case (isset($_SERVER['HTTP_SCHEME']) && ($_SERVER['HTTP_SCHEME'] == 'https')): + case (443 === $this->getPort()): + $scheme = 'https'; + break; + default: + $scheme = 'http'; + break; + } + + $this->setScheme($scheme); + } + + /** + * Detect if a proxy is in use, and, if so, set the host based on it + * + * @return bool + */ + protected function setHostFromProxy() + { + if (!$this->useProxy) { + return false; + } + + if (!isset($_SERVER['HTTP_X_FORWARDED_HOST']) || empty($_SERVER['HTTP_X_FORWARDED_HOST'])) { + return false; + } + + $host = $_SERVER['HTTP_X_FORWARDED_HOST']; + if (strpos($host, ',') !== false) { + $hosts = explode(',', $host); + $host = trim(array_pop($hosts)); + } + if (empty($host)) { + return false; + } + $this->setHost($host); + + return true; + } + + /** + * Set port based on detected proxy headers + * + * @return bool + */ + protected function setPortFromProxy() + { + if (!$this->useProxy) { + return false; + } + + if (!isset($_SERVER['HTTP_X_FORWARDED_PORT']) || empty($_SERVER['HTTP_X_FORWARDED_PORT'])) { + return false; + } + + $port = $_SERVER['HTTP_X_FORWARDED_PORT']; + $this->setPort($port); + + return true; + } + + /** + * Set the current scheme based on detected proxy headers + * + * @return bool + */ + protected function setSchemeFromProxy() + { + if (!$this->useProxy) { + return false; + } + + if (isset($_SERVER['SSL_HTTPS'])) { + $sslHttps = strtolower($_SERVER['SSL_HTTPS']); + if (in_array($sslHttps, array('on', 1))) { + $this->setScheme('https'); + return true; + } + } + + if (!isset($_SERVER['HTTP_X_FORWARDED_PROTO']) || empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) { + return false; + } + + $scheme = trim(strtolower($_SERVER['HTTP_X_FORWARDED_PROTO'])); + if (empty($scheme)) { + return false; + } + + $this->setScheme($scheme); + + return true; + } + + /** + * Sets host + * + * @param string $host + * @return ServerUrl + */ + public function setHost($host) + { + $port = $this->getPort(); + $scheme = $this->getScheme(); + + if (($scheme == 'http' && (null === $port || $port == 80)) + || ($scheme == 'https' && (null === $port || $port == 443)) + ) { + $this->host = $host; + return $this; + } + + $this->host = $host . ':' . $port; + + return $this; + } + + /** + * Returns host + * + * @return string + */ + public function getHost() + { + if (null === $this->host) { + $this->detectHost(); + } + + return $this->host; + } + + /** + * Set server port + * + * @param int $port + * @return ServerUrl + */ + public function setPort($port) + { + $this->port = (int) $port; + + return $this; + } + + /** + * Retrieve the server port + * + * @return int|null + */ + public function getPort() + { + if (null === $this->port) { + $this->detectPort(); + } + + return $this->port; + } + + /** + * Sets scheme (typically http or https) + * + * @param string $scheme + * @return ServerUrl + */ + public function setScheme($scheme) + { + $this->scheme = $scheme; + + return $this; + } + + /** + * Returns scheme (typically http or https) + * + * @return string + */ + public function getScheme() + { + if (null === $this->scheme) { + $this->detectScheme(); + } + + return $this->scheme; + } + + /** + * Set flag indicating whether or not to query proxy servers + * + * @param bool $useProxy + * @return ServerUrl + */ + public function setUseProxy($useProxy = false) + { + $this->useProxy = (bool) $useProxy; + + return $this; + } +} diff --git a/library/Zend/View/Helper/Service/FlashMessengerFactory.php b/library/Zend/View/Helper/Service/FlashMessengerFactory.php new file mode 100755 index 000000000..fbaf53e8b --- /dev/null +++ b/library/Zend/View/Helper/Service/FlashMessengerFactory.php @@ -0,0 +1,47 @@ +getServiceLocator(); + $helper = new FlashMessenger(); + $controllerPluginManager = $serviceLocator->get('ControllerPluginManager'); + $flashMessenger = $controllerPluginManager->get('flashmessenger'); + $helper->setPluginFlashMessenger($flashMessenger); + $config = $serviceLocator->get('Config'); + if (isset($config['view_helper_config']['flashmessenger'])) { + $configHelper = $config['view_helper_config']['flashmessenger']; + if (isset($configHelper['message_open_format'])) { + $helper->setMessageOpenFormat($configHelper['message_open_format']); + } + if (isset($configHelper['message_separator_string'])) { + $helper->setMessageSeparatorString($configHelper['message_separator_string']); + } + if (isset($configHelper['message_close_string'])) { + $helper->setMessageCloseString($configHelper['message_close_string']); + } + } + + return $helper; + } +} diff --git a/library/Zend/View/Helper/Service/IdentityFactory.php b/library/Zend/View/Helper/Service/IdentityFactory.php new file mode 100755 index 000000000..a065a1d9d --- /dev/null +++ b/library/Zend/View/Helper/Service/IdentityFactory.php @@ -0,0 +1,32 @@ +getServiceLocator(); + $helper = new Identity(); + if ($services->has('Zend\Authentication\AuthenticationService')) { + $helper->setAuthenticationService($services->get('Zend\Authentication\AuthenticationService')); + } + return $helper; + } +} diff --git a/library/Zend/View/Helper/Url.php b/library/Zend/View/Helper/Url.php new file mode 100755 index 000000000..9b2af7e97 --- /dev/null +++ b/library/Zend/View/Helper/Url.php @@ -0,0 +1,126 @@ +router) { + throw new Exception\RuntimeException('No RouteStackInterface instance provided'); + } + + if (3 == func_num_args() && is_bool($options)) { + $reuseMatchedParams = $options; + $options = array(); + } + + if ($name === null) { + if ($this->routeMatch === null) { + throw new Exception\RuntimeException('No RouteMatch instance provided'); + } + + $name = $this->routeMatch->getMatchedRouteName(); + + if ($name === null) { + throw new Exception\RuntimeException('RouteMatch does not contain a matched route name'); + } + } + + if (!is_array($params)) { + if (!$params instanceof Traversable) { + throw new Exception\InvalidArgumentException( + 'Params is expected to be an array or a Traversable object' + ); + } + $params = iterator_to_array($params); + } + + if ($reuseMatchedParams && $this->routeMatch !== null) { + $routeMatchParams = $this->routeMatch->getParams(); + + if (isset($routeMatchParams[ModuleRouteListener::ORIGINAL_CONTROLLER])) { + $routeMatchParams['controller'] = $routeMatchParams[ModuleRouteListener::ORIGINAL_CONTROLLER]; + unset($routeMatchParams[ModuleRouteListener::ORIGINAL_CONTROLLER]); + } + + if (isset($routeMatchParams[ModuleRouteListener::MODULE_NAMESPACE])) { + unset($routeMatchParams[ModuleRouteListener::MODULE_NAMESPACE]); + } + + $params = array_merge($routeMatchParams, $params); + } + + $options['name'] = $name; + + return $this->router->assemble($params, $options); + } + + /** + * Set the router to use for assembling. + * + * @param RouteStackInterface $router + * @return Url + */ + public function setRouter(RouteStackInterface $router) + { + $this->router = $router; + return $this; + } + + /** + * Set route match returned by the router. + * + * @param RouteMatch $routeMatch + * @return Url + */ + public function setRouteMatch(RouteMatch $routeMatch) + { + $this->routeMatch = $routeMatch; + return $this; + } +} diff --git a/library/Zend/View/Helper/ViewModel.php b/library/Zend/View/Helper/ViewModel.php new file mode 100755 index 000000000..49b130649 --- /dev/null +++ b/library/Zend/View/Helper/ViewModel.php @@ -0,0 +1,92 @@ +current = $model; + return $this; + } + + /** + * Get the current view model + * + * @return null|Model + */ + public function getCurrent() + { + return $this->current; + } + + /** + * Is a current view model composed? + * + * @return bool + */ + public function hasCurrent() + { + return ($this->current instanceof Model); + } + + /** + * Set the root view model + * + * @param Model $model + * @return ViewModel + */ + public function setRoot(Model $model) + { + $this->root = $model; + return $this; + } + + /** + * Get the root view model + * + * @return null|Model + */ + public function getRoot() + { + return $this->root; + } + + /** + * Is a root view model composed? + * + * @return bool + */ + public function hasRoot() + { + return ($this->root instanceof Model); + } +} diff --git a/library/Zend/View/HelperPluginManager.php b/library/Zend/View/HelperPluginManager.php new file mode 100755 index 000000000..d2ddc6904 --- /dev/null +++ b/library/Zend/View/HelperPluginManager.php @@ -0,0 +1,194 @@ + 'Zend\View\Helper\Service\FlashMessengerFactory', + 'identity' => 'Zend\View\Helper\Service\IdentityFactory', + ); + + /** + * Default set of helpers + * + * @var array + */ + protected $invokableClasses = array( + // basepath, doctype, and url are set up as factories in the ViewHelperManagerFactory. + // basepath and url are not very useful without their factories, however the doctype + // helper works fine as an invokable. The factory for doctype simply checks for the + // config value from the merged config. + 'basepath' => 'Zend\View\Helper\BasePath', + 'cycle' => 'Zend\View\Helper\Cycle', + 'declarevars' => 'Zend\View\Helper\DeclareVars', + 'doctype' => 'Zend\View\Helper\Doctype', // overridden by a factory in ViewHelperManagerFactory + 'escapehtml' => 'Zend\View\Helper\EscapeHtml', + 'escapehtmlattr' => 'Zend\View\Helper\EscapeHtmlAttr', + 'escapejs' => 'Zend\View\Helper\EscapeJs', + 'escapecss' => 'Zend\View\Helper\EscapeCss', + 'escapeurl' => 'Zend\View\Helper\EscapeUrl', + 'gravatar' => 'Zend\View\Helper\Gravatar', + 'headlink' => 'Zend\View\Helper\HeadLink', + 'headmeta' => 'Zend\View\Helper\HeadMeta', + 'headscript' => 'Zend\View\Helper\HeadScript', + 'headstyle' => 'Zend\View\Helper\HeadStyle', + 'headtitle' => 'Zend\View\Helper\HeadTitle', + 'htmlflash' => 'Zend\View\Helper\HtmlFlash', + 'htmllist' => 'Zend\View\Helper\HtmlList', + 'htmlobject' => 'Zend\View\Helper\HtmlObject', + 'htmlpage' => 'Zend\View\Helper\HtmlPage', + 'htmlquicktime' => 'Zend\View\Helper\HtmlQuicktime', + 'inlinescript' => 'Zend\View\Helper\InlineScript', + 'json' => 'Zend\View\Helper\Json', + 'layout' => 'Zend\View\Helper\Layout', + 'paginationcontrol' => 'Zend\View\Helper\PaginationControl', + 'partialloop' => 'Zend\View\Helper\PartialLoop', + 'partial' => 'Zend\View\Helper\Partial', + 'placeholder' => 'Zend\View\Helper\Placeholder', + 'renderchildmodel' => 'Zend\View\Helper\RenderChildModel', + 'rendertoplaceholder' => 'Zend\View\Helper\RenderToPlaceholder', + 'serverurl' => 'Zend\View\Helper\ServerUrl', + 'url' => 'Zend\View\Helper\Url', + 'viewmodel' => 'Zend\View\Helper\ViewModel', + ); + + /** + * @var Renderer\RendererInterface + */ + protected $renderer; + + /** + * Constructor + * + * After invoking parent constructor, add an initializer to inject the + * attached renderer and translator, if any, to the currently requested helper. + * + * @param null|ConfigInterface $configuration + */ + public function __construct(ConfigInterface $configuration = null) + { + parent::__construct($configuration); + + $this->addInitializer(array($this, 'injectRenderer')) + ->addInitializer(array($this, 'injectTranslator')); + } + + /** + * Set renderer + * + * @param Renderer\RendererInterface $renderer + * @return HelperPluginManager + */ + public function setRenderer(Renderer\RendererInterface $renderer) + { + $this->renderer = $renderer; + + return $this; + } + + /** + * Retrieve renderer instance + * + * @return null|Renderer\RendererInterface + */ + public function getRenderer() + { + return $this->renderer; + } + + /** + * Inject a helper instance with the registered renderer + * + * @param Helper\HelperInterface $helper + * @return void + */ + public function injectRenderer($helper) + { + $renderer = $this->getRenderer(); + if (null === $renderer) { + return; + } + $helper->setView($renderer); + } + + /** + * Inject a helper instance with the registered translator + * + * @param Helper\HelperInterface $helper + * @return void + */ + public function injectTranslator($helper) + { + if (!$helper instanceof TranslatorAwareInterface) { + return; + } + + $locator = $this->getServiceLocator(); + + if (!$locator) { + return; + } + + if ($locator->has('MvcTranslator')) { + $helper->setTranslator($locator->get('MvcTranslator')); + return; + } + + if ($locator->has('Zend\I18n\Translator\TranslatorInterface')) { + $helper->setTranslator($locator->get('Zend\I18n\Translator\TranslatorInterface')); + return; + } + + if ($locator->has('Translator')) { + $helper->setTranslator($locator->get('Translator')); + return; + } + } + + /** + * Validate the plugin + * + * Checks that the helper loaded is an instance of Helper\HelperInterface. + * + * @param mixed $plugin + * @return void + * @throws Exception\InvalidHelperException if invalid + */ + public function validatePlugin($plugin) + { + if ($plugin instanceof Helper\HelperInterface) { + // we're okay + return; + } + + throw new Exception\InvalidHelperException(sprintf( + 'Plugin of type %s is invalid; must implement %s\Helper\HelperInterface', + (is_object($plugin) ? get_class($plugin) : gettype($plugin)), + __NAMESPACE__ + )); + } +} diff --git a/library/Zend/View/Model/ClearableModelInterface.php b/library/Zend/View/Model/ClearableModelInterface.php new file mode 100755 index 000000000..2edfe719e --- /dev/null +++ b/library/Zend/View/Model/ClearableModelInterface.php @@ -0,0 +1,23 @@ +options['errorLevel'] = $errorLevel; + } + + /** + * @return int + */ + public function getErrorLevel() + { + if (array_key_exists('errorLevel', $this->options)) { + return $this->options['errorLevel']; + } + } + + /** + * Set result text. + * + * @param string $text + * @return \Zend\View\Model\ConsoleModel + */ + public function setResult($text) + { + $this->setVariable(self::RESULT, $text); + return $this; + } + + /** + * Get result text. + * + * @return mixed + */ + public function getResult() + { + return $this->getVariable(self::RESULT); + } +} diff --git a/library/Zend/View/Model/FeedModel.php b/library/Zend/View/Model/FeedModel.php new file mode 100755 index 000000000..fc8351cd0 --- /dev/null +++ b/library/Zend/View/Model/FeedModel.php @@ -0,0 +1,89 @@ +feed instanceof Feed) { + return $this->feed; + } + + if (!$this->type) { + $options = $this->getOptions(); + if (isset($options['feed_type'])) { + $this->type = $options['feed_type']; + } + } + + $variables = $this->getVariables(); + $feed = FeedFactory::factory($variables); + $this->setFeed($feed); + + return $this->feed; + } + + /** + * Set the feed object + * + * @param Feed $feed + * @return FeedModel + */ + public function setFeed(Feed $feed) + { + $this->feed = $feed; + return $this; + } + + /** + * Get the feed type + * + * @return false|string + */ + public function getFeedType() + { + if ($this->type) { + return $this->type; + } + + $options = $this->getOptions(); + if (isset($options['feed_type'])) { + $this->type = $options['feed_type']; + } + return $this->type; + } +} diff --git a/library/Zend/View/Model/JsonModel.php b/library/Zend/View/Model/JsonModel.php new file mode 100755 index 000000000..191d3f5ec --- /dev/null +++ b/library/Zend/View/Model/JsonModel.php @@ -0,0 +1,69 @@ +jsonpCallback = $callback; + return $this; + } + + /** + * Serialize to JSON + * + * @return string + */ + public function serialize() + { + $variables = $this->getVariables(); + if ($variables instanceof Traversable) { + $variables = ArrayUtils::iteratorToArray($variables); + } + + if (null !== $this->jsonpCallback) { + return $this->jsonpCallback.'('.Json::encode($variables).');'; + } + return Json::encode($variables); + } +} diff --git a/library/Zend/View/Model/ModelInterface.php b/library/Zend/View/Model/ModelInterface.php new file mode 100755 index 000000000..810540d70 --- /dev/null +++ b/library/Zend/View/Model/ModelInterface.php @@ -0,0 +1,167 @@ +setVariables($variables, true); + + if (null !== $options) { + $this->setOptions($options); + } + } + + /** + * Property overloading: set variable value + * + * @param string $name + * @param mixed $value + * @return void + */ + public function __set($name, $value) + { + $this->setVariable($name, $value); + } + + /** + * Property overloading: get variable value + * + * @param string $name + * @return mixed + */ + public function __get($name) + { + if (!$this->__isset($name)) { + return null; + } + + $variables = $this->getVariables(); + return $variables[$name]; + } + + /** + * Property overloading: do we have the requested variable value? + * + * @param string $name + * @return bool + */ + public function __isset($name) + { + $variables = $this->getVariables(); + return isset($variables[$name]); + } + + /** + * Property overloading: unset the requested variable + * + * @param string $name + * @return void + */ + public function __unset($name) + { + if (!$this->__isset($name)) { + return null; + } + + unset($this->variables[$name]); + } + + /** + * Set a single option + * + * @param string $name + * @param mixed $value + * @return ViewModel + */ + public function setOption($name, $value) + { + $this->options[(string) $name] = $value; + return $this; + } + + /** + * Get a single option + * + * @param string $name The option to get. + * @param mixed|null $default (optional) A default value if the option is not yet set. + * @return mixed + */ + public function getOption($name, $default = null) + { + $name = (string) $name; + return array_key_exists($name, $this->options) ? $this->options[$name] : $default; + } + + /** + * Set renderer options/hints en masse + * + * @param array|Traversable $options + * @throws \Zend\View\Exception\InvalidArgumentException + * @return ViewModel + */ + public function setOptions($options) + { + // Assumption is that lowest common denominator for renderer configuration + // is an array + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } + + if (!is_array($options)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects an array, or Traversable argument; received "%s"', + __METHOD__, + (is_object($options) ? get_class($options) : gettype($options)) + )); + } + + $this->options = $options; + return $this; + } + + /** + * Get renderer options/hints + * + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Clear any existing renderer options/hints + * + * @return ViewModel + */ + public function clearOptions() + { + $this->options = array(); + return $this; + } + + /** + * Get a single view variable + * + * @param string $name + * @param mixed|null $default (optional) default value if the variable is not present. + * @return mixed + */ + public function getVariable($name, $default = null) + { + $name = (string) $name; + if (array_key_exists($name, $this->variables)) { + return $this->variables[$name]; + } + + return $default; + } + + /** + * Set view variable + * + * @param string $name + * @param mixed $value + * @return ViewModel + */ + public function setVariable($name, $value) + { + $this->variables[(string) $name] = $value; + return $this; + } + + /** + * Set view variables en masse + * + * Can be an array or a Traversable + ArrayAccess object. + * + * @param array|ArrayAccess|Traversable $variables + * @param bool $overwrite Whether or not to overwrite the internal container with $variables + * @throws Exception\InvalidArgumentException + * @return ViewModel + */ + public function setVariables($variables, $overwrite = false) + { + if (!is_array($variables) && !$variables instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects an array, or Traversable argument; received "%s"', + __METHOD__, + (is_object($variables) ? get_class($variables) : gettype($variables)) + )); + } + + if ($overwrite) { + if (is_object($variables) && !$variables instanceof ArrayAccess) { + $variables = ArrayUtils::iteratorToArray($variables); + } + + $this->variables = $variables; + return $this; + } + + foreach ($variables as $key => $value) { + $this->setVariable($key, $value); + } + + return $this; + } + + /** + * Get view variables + * + * @return array|ArrayAccess|Traversable + */ + public function getVariables() + { + return $this->variables; + } + + /** + * Clear all variables + * + * Resets the internal variable container to an empty container. + * + * @return ViewModel + */ + public function clearVariables() + { + $this->variables = new ViewVariables(); + return $this; + } + + /** + * Set the template to be used by this model + * + * @param string $template + * @return ViewModel + */ + public function setTemplate($template) + { + $this->template = (string) $template; + return $this; + } + + /** + * Get the template to be used by this model + * + * @return string + */ + public function getTemplate() + { + return $this->template; + } + + /** + * Add a child model + * + * @param ModelInterface $child + * @param null|string $captureTo Optional; if specified, the "capture to" value to set on the child + * @param null|bool $append Optional; if specified, append to child with the same capture + * @return ViewModel + */ + public function addChild(ModelInterface $child, $captureTo = null, $append = null) + { + $this->children[] = $child; + if (null !== $captureTo) { + $child->setCaptureTo($captureTo); + } + if (null !== $append) { + $child->setAppend($append); + } + + return $this; + } + + /** + * Return all children. + * + * Return specifies an array, but may be any iterable object. + * + * @return array + */ + public function getChildren() + { + return $this->children; + } + + /** + * Does the model have any children? + * + * @return bool + */ + public function hasChildren() + { + return (0 < count($this->children)); + } + + /** + * Clears out all child models + * + * @return ViewModel + */ + public function clearChildren() + { + $this->children = array(); + return $this; + } + + /** + * Returns an array of Viewmodels with captureTo value $capture + * + * @param string $capture + * @param bool $recursive search recursive through children, default true + * @return array + */ + public function getChildrenByCaptureTo($capture, $recursive = true) + { + $children = array(); + + foreach ($this->children as $child) { + if ($recursive === true) { + $children += $child->getChildrenByCaptureTo($capture); + } + + if ($child->captureTo() === $capture) { + $children[] = $child; + } + } + + return $children; + } + + /** + * Set the name of the variable to capture this model to, if it is a child model + * + * @param string $capture + * @return ViewModel + */ + public function setCaptureTo($capture) + { + $this->captureTo = (string) $capture; + return $this; + } + + /** + * Get the name of the variable to which to capture this model + * + * @return string + */ + public function captureTo() + { + return $this->captureTo; + } + + /** + * Set flag indicating whether or not this is considered a terminal or standalone model + * + * @param bool $terminate + * @return ViewModel + */ + public function setTerminal($terminate) + { + $this->terminate = (bool) $terminate; + return $this; + } + + /** + * Is this considered a terminal or standalone model? + * + * @return bool + */ + public function terminate() + { + return $this->terminate; + } + + /** + * Set flag indicating whether or not append to child with the same capture + * + * @param bool $append + * @return ViewModel + */ + public function setAppend($append) + { + $this->append = (bool) $append; + return $this; + } + + /** + * Is this append to child with the same capture? + * + * @return bool + */ + public function isAppend() + { + return $this->append; + } + + /** + * Return count of children + * + * @return int + */ + public function count() + { + return count($this->children); + } + + /** + * Get iterator of children + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->children); + } +} diff --git a/library/Zend/View/README.md b/library/Zend/View/README.md new file mode 100755 index 000000000..ec19b2c01 --- /dev/null +++ b/library/Zend/View/README.md @@ -0,0 +1,15 @@ +View Component from ZF2 +======================= + +This is the View component for ZF2. + +- File issues at https://github.com/zendframework/zf2/issues +- Create pull requests against https://github.com/zendframework/zf2 +- Documentation is at http://framework.zend.com/docs + +LICENSE +------- + +The files in this archive are released under the [Zend Framework +license](http://framework.zend.com/license), which is a 3-clause BSD license. + diff --git a/library/Zend/View/Renderer/ConsoleRenderer.php b/library/Zend/View/Renderer/ConsoleRenderer.php new file mode 100755 index 000000000..5f0cf10b2 --- /dev/null +++ b/library/Zend/View/Renderer/ConsoleRenderer.php @@ -0,0 +1,149 @@ +init(); + } + + public function setResolver(ResolverInterface $resolver) + { + return $this; + } + + /** + * Return the template engine object + * + * Returns the object instance, as it is its own template engine + * + * @return PhpRenderer + */ + public function getEngine() + { + return $this; + } + + /** + * Allow custom object initialization when extending ConsoleRenderer + * + * Triggered by {@link __construct() the constructor} as its final action. + * + * @return void + */ + public function init() + { + } + + /** + * Set filter chain + * + * @param FilterChain $filters + * @return ConsoleRenderer + */ + public function setFilterChain(FilterChain $filters) + { + $this->__filterChain = $filters; + return $this; + } + + /** + * Retrieve filter chain for post-filtering script content + * + * @return FilterChain + */ + public function getFilterChain() + { + if (null === $this->__filterChain) { + $this->setFilterChain(new FilterChain()); + } + return $this->__filterChain; + } + + /** + * Recursively processes all ViewModels and returns output. + * + * @param string|ModelInterface $model A ViewModel instance. + * @param null|array|\Traversable $values Values to use when rendering. If none + * provided, uses those in the composed + * variables container. + * @return string Console output. + */ + public function render($model, $values = null) + { + if (!$model instanceof ModelInterface) { + return ''; + } + + $result = ''; + $options = $model->getOptions(); + foreach ($options as $setting => $value) { + $method = 'set' . $setting; + if (method_exists($this, $method)) { + $this->$method($value); + } + unset($method, $setting, $value); + } + unset($options); + + $values = $model->getVariables(); + + if (isset($values['result'])) { + // filter and append the result + $result .= $this->getFilterChain()->filter($values['result']); + } + + if ($model->hasChildren()) { + // recursively render all children + foreach ($model->getChildren() as $child) { + $result .= $this->render($child, $values); + } + } + + return $result; + } + + /** + * @see Zend\View\Renderer\TreeRendererInterface + * @return bool + */ + public function canRenderTrees() + { + return true; + } +} diff --git a/library/Zend/View/Renderer/FeedRenderer.php b/library/Zend/View/Renderer/FeedRenderer.php new file mode 100755 index 000000000..b91964683 --- /dev/null +++ b/library/Zend/View/Renderer/FeedRenderer.php @@ -0,0 +1,138 @@ +resolver = $resolver; + } + + /** + * Renders values as JSON + * + * @todo Determine what use case exists for accepting only $nameOrModel + * @param string|Model $nameOrModel The script/resource process, or a view model + * @param null|array|\ArrayAccess $values Values to use during rendering + * @throws Exception\InvalidArgumentException + * @return string The script output. + */ + public function render($nameOrModel, $values = null) + { + if ($nameOrModel instanceof Model) { + // Use case 1: View Model provided + // Non-FeedModel: cast to FeedModel + if (!$nameOrModel instanceof FeedModel) { + $vars = $nameOrModel->getVariables(); + $options = $nameOrModel->getOptions(); + $type = $this->getFeedType(); + if (isset($options['feed_type'])) { + $type = $options['feed_type']; + } else { + $this->setFeedType($type); + } + $nameOrModel = new FeedModel($vars, array('feed_type' => $type)); + } + } elseif (is_string($nameOrModel)) { + // Use case 2: string $nameOrModel + array|Traversable|Feed $values + $nameOrModel = new FeedModel($values, (array) $nameOrModel); + } else { + // Use case 3: failure + throw new Exception\InvalidArgumentException(sprintf( + '%s expects a ViewModel or a string feed type as the first argument; received "%s"', + __METHOD__, + (is_object($nameOrModel) ? get_class($nameOrModel) : gettype($nameOrModel)) + )); + } + + // Get feed and type + $feed = $nameOrModel->getFeed(); + $type = $nameOrModel->getFeedType(); + if (!$type) { + $type = $this->getFeedType(); + } else { + $this->setFeedType($type); + } + + // Render feed + return $feed->export($type); + } + + /** + * Set feed type ('rss' or 'atom') + * + * @param string $feedType + * @throws Exception\InvalidArgumentException + * @return FeedRenderer + */ + public function setFeedType($feedType) + { + $feedType = strtolower($feedType); + if (!in_array($feedType, array('rss', 'atom'))) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects a string of either "rss" or "atom"', + __METHOD__ + )); + } + + $this->feedType = $feedType; + return $this; + } + + /** + * Get feed type + * + * @return string + */ + public function getFeedType() + { + return $this->feedType; + } +} diff --git a/library/Zend/View/Renderer/JsonRenderer.php b/library/Zend/View/Renderer/JsonRenderer.php new file mode 100755 index 000000000..9666261db --- /dev/null +++ b/library/Zend/View/Renderer/JsonRenderer.php @@ -0,0 +1,243 @@ +resolver = $resolver; + } + + /** + * Set flag indicating whether or not to merge unnamed children + * + * @param bool $mergeUnnamedChildren + * @return JsonRenderer + */ + public function setMergeUnnamedChildren($mergeUnnamedChildren) + { + $this->mergeUnnamedChildren = (bool) $mergeUnnamedChildren; + return $this; + } + + /** + * Set the JSONP callback function name + * + * @param string $callback + * @return JsonRenderer + */ + public function setJsonpCallback($callback) + { + $callback = (string) $callback; + if (!empty($callback)) { + $this->jsonpCallback = $callback; + } + return $this; + } + + /** + * Returns whether or not the jsonpCallback has been set + * + * @return bool + */ + public function hasJsonpCallback() + { + return (null !== $this->jsonpCallback); + } + + /** + * Should we merge unnamed children? + * + * @return bool + */ + public function mergeUnnamedChildren() + { + return $this->mergeUnnamedChildren; + } + + /** + * Renders values as JSON + * + * @todo Determine what use case exists for accepting both $nameOrModel and $values + * @param string|Model $nameOrModel The script/resource process, or a view model + * @param null|array|\ArrayAccess $values Values to use during rendering + * @throws Exception\DomainException + * @return string The script output. + */ + public function render($nameOrModel, $values = null) + { + // use case 1: View Models + // Serialize variables in view model + if ($nameOrModel instanceof Model) { + if ($nameOrModel instanceof JsonModel) { + $children = $this->recurseModel($nameOrModel, false); + $this->injectChildren($nameOrModel, $children); + $values = $nameOrModel->serialize(); + } else { + $values = $this->recurseModel($nameOrModel); + $values = Json::encode($values); + } + + if ($this->hasJsonpCallback()) { + $values = $this->jsonpCallback . '(' . $values . ');'; + } + return $values; + } + + // use case 2: $nameOrModel is populated, $values is not + // Serialize $nameOrModel + if (null === $values) { + if (!is_object($nameOrModel) || $nameOrModel instanceof JsonSerializable) { + $return = Json::encode($nameOrModel); + } elseif ($nameOrModel instanceof Traversable) { + $nameOrModel = ArrayUtils::iteratorToArray($nameOrModel); + $return = Json::encode($nameOrModel); + } else { + $return = Json::encode(get_object_vars($nameOrModel)); + } + + if ($this->hasJsonpCallback()) { + $return = $this->jsonpCallback . '(' . $return . ');'; + } + return $return; + } + + // use case 3: Both $nameOrModel and $values are populated + throw new Exception\DomainException(sprintf( + '%s: Do not know how to handle operation when both $nameOrModel and $values are populated', + __METHOD__ + )); + } + + /** + * Can this renderer render trees of view models? + * + * Yes. + * + * @return true + */ + public function canRenderTrees() + { + return true; + } + + /** + * Retrieve values from a model and recurse its children to build a data structure + * + * @param Model $model + * @param bool $mergeWithVariables Whether or not to merge children with + * the variables of the $model + * @return array + */ + protected function recurseModel(Model $model, $mergeWithVariables = true) + { + $values = array(); + if ($mergeWithVariables) { + $values = $model->getVariables(); + } + + if ($values instanceof Traversable) { + $values = ArrayUtils::iteratorToArray($values); + } + + if (!$model->hasChildren()) { + return $values; + } + + $mergeChildren = $this->mergeUnnamedChildren(); + foreach ($model as $child) { + $captureTo = $child->captureTo(); + if (!$captureTo && !$mergeChildren) { + // We don't want to do anything with this child + continue; + } + + $childValues = $this->recurseModel($child); + if ($captureTo) { + // Capturing to a specific key + // TODO please complete if append is true. must change old + // value to array and append to array? + $values[$captureTo] = $childValues; + } elseif ($mergeChildren) { + // Merging values with parent + $values = array_replace_recursive($values, $childValues); + } + } + return $values; + } + + /** + * Inject discovered child model values into parent model + * + * @todo detect collisions and decide whether to append and/or aggregate? + * @param Model $model + * @param array $children + */ + protected function injectChildren(Model $model, array $children) + { + foreach ($children as $child => $value) { + // TODO detect collisions and decide whether to append and/or aggregate? + $model->setVariable($child, $value); + } + } +} diff --git a/library/Zend/View/Renderer/PhpRenderer.php b/library/Zend/View/Renderer/PhpRenderer.php new file mode 100755 index 000000000..87baf3d98 --- /dev/null +++ b/library/Zend/View/Renderer/PhpRenderer.php @@ -0,0 +1,574 @@ +init(); + } + + /** + * Return the template engine object + * + * Returns the object instance, as it is its own template engine + * + * @return PhpRenderer + */ + public function getEngine() + { + return $this; + } + + /** + * Allow custom object initialization when extending PhpRenderer + * + * Triggered by {@link __construct() the constructor} as its final action. + * + * @return void + */ + public function init() + { + } + + /** + * Set script resolver + * + * @param Resolver $resolver + * @return PhpRenderer + * @throws Exception\InvalidArgumentException + */ + public function setResolver(Resolver $resolver) + { + $this->__templateResolver = $resolver; + return $this; + } + + /** + * Retrieve template name or template resolver + * + * @param null|string $name + * @return string|Resolver + */ + public function resolver($name = null) + { + if (null === $this->__templateResolver) { + $this->setResolver(new TemplatePathStack()); + } + + if (null !== $name) { + return $this->__templateResolver->resolve($name, $this); + } + + return $this->__templateResolver; + } + + /** + * Set variable storage + * + * Expects either an array, or an object implementing ArrayAccess. + * + * @param array|ArrayAccess $variables + * @return PhpRenderer + * @throws Exception\InvalidArgumentException + */ + public function setVars($variables) + { + if (!is_array($variables) && !$variables instanceof ArrayAccess) { + throw new Exception\InvalidArgumentException(sprintf( + 'Expected array or ArrayAccess object; received "%s"', + (is_object($variables) ? get_class($variables) : gettype($variables)) + )); + } + + // Enforce a Variables container + if (!$variables instanceof Variables) { + $variablesAsArray = array(); + foreach ($variables as $key => $value) { + $variablesAsArray[$key] = $value; + } + $variables = new Variables($variablesAsArray); + } + + $this->__vars = $variables; + return $this; + } + + /** + * Get a single variable, or all variables + * + * @param mixed $key + * @return mixed + */ + public function vars($key = null) + { + if (null === $this->__vars) { + $this->setVars(new Variables()); + } + + if (null === $key) { + return $this->__vars; + } + return $this->__vars[$key]; + } + + /** + * Get a single variable + * + * @param mixed $key + * @return mixed + */ + public function get($key) + { + if (null === $this->__vars) { + $this->setVars(new Variables()); + } + + return $this->__vars[$key]; + } + + /** + * Overloading: proxy to Variables container + * + * @param string $name + * @return mixed + */ + public function __get($name) + { + $vars = $this->vars(); + return $vars[$name]; + } + + /** + * Overloading: proxy to Variables container + * + * @param string $name + * @param mixed $value + * @return void + */ + public function __set($name, $value) + { + $vars = $this->vars(); + $vars[$name] = $value; + } + + /** + * Overloading: proxy to Variables container + * + * @param string $name + * @return bool + */ + public function __isset($name) + { + $vars = $this->vars(); + return isset($vars[$name]); + } + + /** + * Overloading: proxy to Variables container + * + * @param string $name + * @return void + */ + public function __unset($name) + { + $vars = $this->vars(); + if (!isset($vars[$name])) { + return; + } + unset($vars[$name]); + } + + /** + * Set helper plugin manager instance + * + * @param string|HelperPluginManager $helpers + * @return PhpRenderer + * @throws Exception\InvalidArgumentException + */ + public function setHelperPluginManager($helpers) + { + if (is_string($helpers)) { + if (!class_exists($helpers)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid helper helpers class provided (%s)', + $helpers + )); + } + $helpers = new $helpers(); + } + if (!$helpers instanceof HelperPluginManager) { + throw new Exception\InvalidArgumentException(sprintf( + 'Helper helpers must extend Zend\View\HelperPluginManager; got type "%s" instead', + (is_object($helpers) ? get_class($helpers) : gettype($helpers)) + )); + } + $helpers->setRenderer($this); + $this->__helpers = $helpers; + + return $this; + } + + /** + * Get helper plugin manager instance + * + * @return HelperPluginManager + */ + public function getHelperPluginManager() + { + if (null === $this->__helpers) { + $this->setHelperPluginManager(new HelperPluginManager()); + } + return $this->__helpers; + } + + /** + * Get plugin instance + * + * @param string $name Name of plugin to return + * @param null|array $options Options to pass to plugin constructor (if not already instantiated) + * @return AbstractHelper + */ + public function plugin($name, array $options = null) + { + return $this->getHelperPluginManager()->get($name, $options); + } + + /** + * Overloading: proxy to helpers + * + * Proxies to the attached plugin manager to retrieve, return, and potentially + * execute helpers. + * + * * If the helper does not define __invoke, it will be returned + * * If the helper does define __invoke, it will be called as a functor + * + * @param string $method + * @param array $argv + * @return mixed + */ + public function __call($method, $argv) + { + if (!isset($this->__pluginCache[$method])) { + $this->__pluginCache[$method] = $this->plugin($method); + } + if (is_callable($this->__pluginCache[$method])) { + return call_user_func_array($this->__pluginCache[$method], $argv); + } + return $this->__pluginCache[$method]; + } + + /** + * Set filter chain + * + * @param FilterChain $filters + * @return PhpRenderer + */ + public function setFilterChain(FilterChain $filters) + { + $this->__filterChain = $filters; + return $this; + } + + /** + * Retrieve filter chain for post-filtering script content + * + * @return FilterChain + */ + public function getFilterChain() + { + if (null === $this->__filterChain) { + $this->setFilterChain(new FilterChain()); + } + return $this->__filterChain; + } + + /** + * Processes a view script and returns the output. + * + * @param string|Model $nameOrModel Either the template to use, or a + * ViewModel. The ViewModel must have the + * template as an option in order to be + * valid. + * @param null|array|Traversable $values Values to use when rendering. If none + * provided, uses those in the composed + * variables container. + * @return string The script output. + * @throws Exception\DomainException if a ViewModel is passed, but does not + * contain a template option. + * @throws Exception\InvalidArgumentException if the values passed are not + * an array or ArrayAccess object + * @throws Exception\RuntimeException if the template cannot be rendered + */ + public function render($nameOrModel, $values = null) + { + if ($nameOrModel instanceof Model) { + $model = $nameOrModel; + $nameOrModel = $model->getTemplate(); + if (empty($nameOrModel)) { + throw new Exception\DomainException(sprintf( + '%s: received View Model argument, but template is empty', + __METHOD__ + )); + } + $options = $model->getOptions(); + foreach ($options as $setting => $value) { + $method = 'set' . $setting; + if (method_exists($this, $method)) { + $this->$method($value); + } + unset($method, $setting, $value); + } + unset($options); + + // Give view model awareness via ViewModel helper + $helper = $this->plugin('view_model'); + $helper->setCurrent($model); + + $values = $model->getVariables(); + unset($model); + } + + // find the script file name using the parent private method + $this->addTemplate($nameOrModel); + unset($nameOrModel); // remove $name from local scope + + $this->__varsCache[] = $this->vars(); + + if (null !== $values) { + $this->setVars($values); + } + unset($values); + + // extract all assigned vars (pre-escaped), but not 'this'. + // assigns to a double-underscored variable, to prevent naming collisions + $__vars = $this->vars()->getArrayCopy(); + if (array_key_exists('this', $__vars)) { + unset($__vars['this']); + } + extract($__vars); + unset($__vars); // remove $__vars from local scope + + while ($this->__template = array_pop($this->__templates)) { + $this->__file = $this->resolver($this->__template); + if (!$this->__file) { + throw new Exception\RuntimeException(sprintf( + '%s: Unable to render template "%s"; resolver could not resolve to a file', + __METHOD__, + $this->__template + )); + } + try { + ob_start(); + $includeReturn = include $this->__file; + $this->__content = ob_get_clean(); + } catch (\Exception $ex) { + ob_end_clean(); + throw $ex; + } + if ($includeReturn === false && empty($this->__content)) { + throw new Exception\UnexpectedValueException(sprintf( + '%s: Unable to render template "%s"; file include failed', + __METHOD__, + $this->__file + )); + } + } + + $this->setVars(array_pop($this->__varsCache)); + + return $this->getFilterChain()->filter($this->__content); // filter output + } + + /** + * Set flag indicating whether or not we should render trees of view models + * + * If set to true, the View instance will not attempt to render children + * separately, but instead pass the root view model directly to the PhpRenderer. + * It is then up to the developer to render the children from within the + * view script. + * + * @param bool $renderTrees + * @return PhpRenderer + */ + public function setCanRenderTrees($renderTrees) + { + $this->__renderTrees = (bool) $renderTrees; + return $this; + } + + /** + * Can we render trees, or are we configured to do so? + * + * @return bool + */ + public function canRenderTrees() + { + return $this->__renderTrees; + } + + /** + * Add a template to the stack + * + * @param string $template + * @return PhpRenderer + */ + public function addTemplate($template) + { + $this->__templates[] = $template; + return $this; + } + + /** + * Make sure View variables are cloned when the view is cloned. + * + * @return PhpRenderer + */ + public function __clone() + { + $this->__vars = clone $this->vars(); + } +} diff --git a/library/Zend/View/Renderer/RendererInterface.php b/library/Zend/View/Renderer/RendererInterface.php new file mode 100755 index 000000000..d3dfa77f7 --- /dev/null +++ b/library/Zend/View/Renderer/RendererInterface.php @@ -0,0 +1,47 @@ +queue = new PriorityQueue(); + } + + /** + * Return count of attached resolvers + * + * @return int + */ + public function count() + { + return $this->queue->count(); + } + + /** + * IteratorAggregate: return internal iterator + * + * @return PriorityQueue + */ + public function getIterator() + { + return $this->queue; + } + + /** + * Attach a resolver + * + * @param Resolver $resolver + * @param int $priority + * @return AggregateResolver + */ + public function attach(Resolver $resolver, $priority = 1) + { + $this->queue->insert($resolver, $priority); + return $this; + } + + /** + * Resolve a template/pattern name to a resource the renderer can consume + * + * @param string $name + * @param null|Renderer $renderer + * @return false|string + */ + public function resolve($name, Renderer $renderer = null) + { + $this->lastLookupFailure = false; + $this->lastSuccessfulResolver = null; + + if (0 === count($this->queue)) { + $this->lastLookupFailure = static::FAILURE_NO_RESOLVERS; + return false; + } + + foreach ($this->queue as $resolver) { + $resource = $resolver->resolve($name, $renderer); + if (!$resource) { + // No resource found; try next resolver + continue; + } + + // Resource found; return it + $this->lastSuccessfulResolver = $resolver; + return $resource; + } + + $this->lastLookupFailure = static::FAILURE_NOT_FOUND; + return false; + } + + /** + * Return the last successful resolver, if any + * + * @return Resolver + */ + public function getLastSuccessfulResolver() + { + return $this->lastSuccessfulResolver; + } + + /** + * Get last lookup failure + * + * @return false|string + */ + public function getLastLookupFailure() + { + return $this->lastLookupFailure; + } +} diff --git a/library/Zend/View/Resolver/ResolverInterface.php b/library/Zend/View/Resolver/ResolverInterface.php new file mode 100755 index 000000000..8ffe130c6 --- /dev/null +++ b/library/Zend/View/Resolver/ResolverInterface.php @@ -0,0 +1,24 @@ +setMap($map); + } + + /** + * IteratorAggregate: return internal iterator + * + * @return Traversable + */ + public function getIterator() + { + return new ArrayIterator($this->map); + } + + /** + * Set (overwrite) template map + * + * Maps should be arrays or Traversable objects with name => path pairs + * + * @param array|Traversable $map + * @throws Exception\InvalidArgumentException + * @return TemplateMapResolver + */ + public function setMap($map) + { + if (!is_array($map) && !$map instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects an array or Traversable, received "%s"', + __METHOD__, + (is_object($map) ? get_class($map) : gettype($map)) + )); + } + + if ($map instanceof Traversable) { + $map = ArrayUtils::iteratorToArray($map); + } + + $this->map = $map; + return $this; + } + + /** + * Add an entry to the map + * + * @param string|array|Traversable $nameOrMap + * @param null|string $path + * @throws Exception\InvalidArgumentException + * @return TemplateMapResolver + */ + public function add($nameOrMap, $path = null) + { + if (is_array($nameOrMap) || $nameOrMap instanceof Traversable) { + $this->merge($nameOrMap); + return $this; + } + + if (!is_string($nameOrMap)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects a string, array, or Traversable for the first argument; received "%s"', + __METHOD__, + (is_object($nameOrMap) ? get_class($nameOrMap) : gettype($nameOrMap)) + )); + } + + if (empty($path)) { + if (isset($this->map[$nameOrMap])) { + unset($this->map[$nameOrMap]); + } + return $this; + } + + $this->map[$nameOrMap] = $path; + return $this; + } + + /** + * Merge internal map with provided map + * + * @param array|Traversable $map + * @throws Exception\InvalidArgumentException + * @return TemplateMapResolver + */ + public function merge($map) + { + if (!is_array($map) && !$map instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + '%s: expects an array or Traversable, received "%s"', + __METHOD__, + (is_object($map) ? get_class($map) : gettype($map)) + )); + } + + if ($map instanceof Traversable) { + $map = ArrayUtils::iteratorToArray($map); + } + + $this->map = array_replace_recursive($this->map, $map); + return $this; + } + + /** + * Does the resolver contain an entry for the given name? + * + * @param string $name + * @return bool + */ + public function has($name) + { + return array_key_exists($name, $this->map); + } + + /** + * Retrieve a template path by name + * + * @param string $name + * @return false|string + * @throws Exception\DomainException if no entry exists + */ + public function get($name) + { + if (!$this->has($name)) { + return false; + } + return $this->map[$name]; + } + + /** + * Retrieve the template map + * + * @return array + */ + public function getMap() + { + return $this->map; + } + + /** + * Resolve a template/pattern name to a resource the renderer can consume + * + * @param string $name + * @param null|Renderer $renderer + * @return string + */ + public function resolve($name, Renderer $renderer = null) + { + return $this->get($name); + } +} diff --git a/library/Zend/View/Resolver/TemplatePathStack.php b/library/Zend/View/Resolver/TemplatePathStack.php new file mode 100755 index 000000000..359be18f3 --- /dev/null +++ b/library/Zend/View/Resolver/TemplatePathStack.php @@ -0,0 +1,338 @@ +useViewStream = (bool) ini_get('short_open_tag'); + if ($this->useViewStream) { + if (!in_array('zend.view', stream_get_wrappers())) { + stream_wrapper_register('zend.view', 'Zend\View\Stream'); + } + } + + $this->paths = new SplStack; + if (null !== $options) { + $this->setOptions($options); + } + } + + /** + * Configure object + * + * @param array|Traversable $options + * @return void + * @throws Exception\InvalidArgumentException + */ + public function setOptions($options) + { + if (!is_array($options) && !$options instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + 'Expected array or Traversable object; received "%s"', + (is_object($options) ? get_class($options) : gettype($options)) + )); + } + + foreach ($options as $key => $value) { + switch (strtolower($key)) { + case 'lfi_protection': + $this->setLfiProtection($value); + break; + case 'script_paths': + $this->addPaths($value); + break; + case 'use_stream_wrapper': + $this->setUseStreamWrapper($value); + break; + case 'default_suffix': + $this->setDefaultSuffix($value); + break; + default: + break; + } + } + } + + /** + * Set default file suffix + * + * @param string $defaultSuffix + * @return TemplatePathStack + */ + public function setDefaultSuffix($defaultSuffix) + { + $this->defaultSuffix = (string) $defaultSuffix; + $this->defaultSuffix = ltrim($this->defaultSuffix, '.'); + return $this; + } + + /** + * Get default file suffix + * + * @return string + */ + public function getDefaultSuffix() + { + return $this->defaultSuffix; + } + + /** + * Add many paths to the stack at once + * + * @param array $paths + * @return TemplatePathStack + */ + public function addPaths(array $paths) + { + foreach ($paths as $path) { + $this->addPath($path); + } + return $this; + } + + /** + * Rest the path stack to the paths provided + * + * @param SplStack|array $paths + * @return TemplatePathStack + * @throws Exception\InvalidArgumentException + */ + public function setPaths($paths) + { + if ($paths instanceof SplStack) { + $this->paths = $paths; + } elseif (is_array($paths)) { + $this->clearPaths(); + $this->addPaths($paths); + } else { + throw new Exception\InvalidArgumentException( + "Invalid argument provided for \$paths, expecting either an array or SplStack object" + ); + } + + return $this; + } + + /** + * Normalize a path for insertion in the stack + * + * @param string $path + * @return string + */ + public static function normalizePath($path) + { + $path = rtrim($path, '/'); + $path = rtrim($path, '\\'); + $path .= DIRECTORY_SEPARATOR; + return $path; + } + + /** + * Add a single path to the stack + * + * @param string $path + * @return TemplatePathStack + * @throws Exception\InvalidArgumentException + */ + public function addPath($path) + { + if (!is_string($path)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid path provided; must be a string, received %s', + gettype($path) + )); + } + $this->paths[] = static::normalizePath($path); + return $this; + } + + /** + * Clear all paths + * + * @return void + */ + public function clearPaths() + { + $this->paths = new SplStack; + } + + /** + * Returns stack of paths + * + * @return SplStack + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Set LFI protection flag + * + * @param bool $flag + * @return TemplatePathStack + */ + public function setLfiProtection($flag) + { + $this->lfiProtectionOn = (bool) $flag; + return $this; + } + + /** + * Return status of LFI protection flag + * + * @return bool + */ + public function isLfiProtectionOn() + { + return $this->lfiProtectionOn; + } + + /** + * Set flag indicating if stream wrapper should be used if short_open_tag is off + * + * @param bool $flag + * @return TemplatePathStack + */ + public function setUseStreamWrapper($flag) + { + $this->useStreamWrapper = (bool) $flag; + return $this; + } + + /** + * Should the stream wrapper be used if short_open_tag is off? + * + * Returns true if the use_stream_wrapper flag is set, and if short_open_tag + * is disabled. + * + * @return bool + */ + public function useStreamWrapper() + { + return ($this->useViewStream && $this->useStreamWrapper); + } + + /** + * Retrieve the filesystem path to a view script + * + * @param string $name + * @param null|Renderer $renderer + * @return string + * @throws Exception\DomainException + */ + public function resolve($name, Renderer $renderer = null) + { + $this->lastLookupFailure = false; + + if ($this->isLfiProtectionOn() && preg_match('#\.\.[\\\/]#', $name)) { + throw new Exception\DomainException( + 'Requested scripts may not include parent directory traversal ("../", "..\\" notation)' + ); + } + + if (!count($this->paths)) { + $this->lastLookupFailure = static::FAILURE_NO_PATHS; + return false; + } + + // Ensure we have the expected file extension + $defaultSuffix = $this->getDefaultSuffix(); + if (pathinfo($name, PATHINFO_EXTENSION) == '') { + $name .= '.' . $defaultSuffix; + } + + foreach ($this->paths as $path) { + $file = new SplFileInfo($path . $name); + if ($file->isReadable()) { + // Found! Return it. + if (($filePath = $file->getRealPath()) === false && substr($path, 0, 7) === 'phar://') { + // Do not try to expand phar paths (realpath + phars == fail) + $filePath = $path . $name; + if (!file_exists($filePath)) { + break; + } + } + if ($this->useStreamWrapper()) { + // If using a stream wrapper, prepend the spec to the path + $filePath = 'zend.view://' . $filePath; + } + return $filePath; + } + } + + $this->lastLookupFailure = static::FAILURE_NOT_FOUND; + return false; + } + + /** + * Get the last lookup failure message, if any + * + * @return false|string + */ + public function getLastLookupFailure() + { + return $this->lastLookupFailure; + } +} diff --git a/library/Zend/View/Strategy/FeedStrategy.php b/library/Zend/View/Strategy/FeedStrategy.php new file mode 100755 index 000000000..40140da5a --- /dev/null +++ b/library/Zend/View/Strategy/FeedStrategy.php @@ -0,0 +1,111 @@ +renderer = $renderer; + } + + /** + * {@inheritDoc} + */ + public function attach(EventManagerInterface $events, $priority = 1) + { + $this->listeners[] = $events->attach(ViewEvent::EVENT_RENDERER, array($this, 'selectRenderer'), $priority); + $this->listeners[] = $events->attach(ViewEvent::EVENT_RESPONSE, array($this, 'injectResponse'), $priority); + } + + /** + * Detect if we should use the FeedRenderer based on model type and/or + * Accept header + * + * @param ViewEvent $e + * @return null|FeedRenderer + */ + public function selectRenderer(ViewEvent $e) + { + $model = $e->getModel(); + + if (!$model instanceof Model\FeedModel) { + // no FeedModel present; do nothing + return; + } + + // FeedModel found + return $this->renderer; + } + + /** + * Inject the response with the feed payload and appropriate Content-Type header + * + * @param ViewEvent $e + * @return void + */ + public function injectResponse(ViewEvent $e) + { + $renderer = $e->getRenderer(); + if ($renderer !== $this->renderer) { + // Discovered renderer is not ours; do nothing + return; + } + + $result = $e->getResult(); + if (!is_string($result) && !$result instanceof Feed) { + // We don't have a string, and thus, no feed + return; + } + + // If the result is a feed, export it + if ($result instanceof Feed) { + $result = $result->export($renderer->getFeedType()); + } + + // Get the content-type header based on feed type + $feedType = $renderer->getFeedType(); + $feedType = ('rss' == $feedType) + ? 'application/rss+xml' + : 'application/atom+xml'; + + $model = $e->getModel(); + $charset = ''; + + if ($model instanceof Model\FeedModel) { + $feed = $model->getFeed(); + + $charset = '; charset=' . $feed->getEncoding() . ';'; + } + + // Populate response + $response = $e->getResponse(); + $response->setContent($result); + $headers = $response->getHeaders(); + $headers->addHeaderLine('content-type', $feedType . $charset); + } +} diff --git a/library/Zend/View/Strategy/JsonStrategy.php b/library/Zend/View/Strategy/JsonStrategy.php new file mode 100755 index 000000000..7cf4b91f0 --- /dev/null +++ b/library/Zend/View/Strategy/JsonStrategy.php @@ -0,0 +1,141 @@ +renderer = $renderer; + } + + /** + * {@inheritDoc} + */ + public function attach(EventManagerInterface $events, $priority = 1) + { + $this->listeners[] = $events->attach(ViewEvent::EVENT_RENDERER, array($this, 'selectRenderer'), $priority); + $this->listeners[] = $events->attach(ViewEvent::EVENT_RESPONSE, array($this, 'injectResponse'), $priority); + } + + /** + * Set the content-type character set + * + * @param string $charset + * @return JsonStrategy + */ + public function setCharset($charset) + { + $this->charset = (string) $charset; + return $this; + } + + /** + * Retrieve the current character set + * + * @return string + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Detect if we should use the JsonRenderer based on model type and/or + * Accept header + * + * @param ViewEvent $e + * @return null|JsonRenderer + */ + public function selectRenderer(ViewEvent $e) + { + $model = $e->getModel(); + + if (!$model instanceof Model\JsonModel) { + // no JsonModel; do nothing + return; + } + + // JsonModel found + return $this->renderer; + } + + /** + * Inject the response with the JSON payload and appropriate Content-Type header + * + * @param ViewEvent $e + * @return void + */ + public function injectResponse(ViewEvent $e) + { + $renderer = $e->getRenderer(); + if ($renderer !== $this->renderer) { + // Discovered renderer is not ours; do nothing + return; + } + + $result = $e->getResult(); + if (!is_string($result)) { + // We don't have a string, and thus, no JSON + return; + } + + // Populate response + $response = $e->getResponse(); + $response->setContent($result); + $headers = $response->getHeaders(); + + if ($this->renderer->hasJsonpCallback()) { + $contentType = 'application/javascript'; + } else { + $contentType = 'application/json'; + } + + $contentType .= '; charset=' . $this->charset; + $headers->addHeaderLine('content-type', $contentType); + + if (in_array(strtoupper($this->charset), $this->multibyteCharsets)) { + $headers->addHeaderLine('content-transfer-encoding', 'BINARY'); + } + } +} diff --git a/library/Zend/View/Strategy/PhpRendererStrategy.php b/library/Zend/View/Strategy/PhpRendererStrategy.php new file mode 100755 index 000000000..e8e04ceb3 --- /dev/null +++ b/library/Zend/View/Strategy/PhpRendererStrategy.php @@ -0,0 +1,127 @@ +renderer = $renderer; + } + + /** + * Retrieve the composed renderer + * + * @return PhpRenderer + */ + public function getRenderer() + { + return $this->renderer; + } + + /** + * Set list of possible content placeholders + * + * @param array $contentPlaceholders + * @return PhpRendererStrategy + */ + public function setContentPlaceholders(array $contentPlaceholders) + { + $this->contentPlaceholders = $contentPlaceholders; + return $this; + } + + /** + * Get list of possible content placeholders + * + * @return array + */ + public function getContentPlaceholders() + { + return $this->contentPlaceholders; + } + + /** + * {@inheritDoc} + */ + public function attach(EventManagerInterface $events, $priority = 1) + { + $this->listeners[] = $events->attach(ViewEvent::EVENT_RENDERER, array($this, 'selectRenderer'), $priority); + $this->listeners[] = $events->attach(ViewEvent::EVENT_RESPONSE, array($this, 'injectResponse'), $priority); + } + + /** + * Select the PhpRenderer; typically, this will be registered last or at + * low priority. + * + * @param ViewEvent $e + * @return PhpRenderer + */ + public function selectRenderer(ViewEvent $e) + { + return $this->renderer; + } + + /** + * Populate the response object from the View + * + * Populates the content of the response object from the view rendering + * results. + * + * @param ViewEvent $e + * @return void + */ + public function injectResponse(ViewEvent $e) + { + $renderer = $e->getRenderer(); + if ($renderer !== $this->renderer) { + return; + } + + $result = $e->getResult(); + $response = $e->getResponse(); + + // Set content + // If content is empty, check common placeholders to determine if they are + // populated, and set the content from them. + if (empty($result)) { + $placeholders = $renderer->plugin('placeholder'); + foreach ($this->contentPlaceholders as $placeholder) { + if ($placeholders->containerExists($placeholder)) { + $result = (string) $placeholders->getContainer($placeholder); + break; + } + } + } + $response->setContent($result); + } +} diff --git a/library/Zend/View/Stream.php b/library/Zend/View/Stream.php new file mode 100755 index 000000000..42bb7a962 --- /dev/null +++ b/library/Zend/View/Stream.php @@ -0,0 +1,183 @@ +data = file_get_contents($path); + + /** + * If reading the file failed, update our local stat store + * to reflect the real stat of the file, then return on failure + */ + if ($this->data === false) { + $this->stat = stat($path); + return false; + } + + /** + * Convert to long-form and to + * + */ + $this->data = preg_replace('/\<\?\=/', "data); + $this->data = preg_replace('/<\?(?!xml|php)/s', 'data); + + /** + * file_get_contents() won't update PHP's stat cache, so we grab a stat + * of the file to prevent additional reads should the script be + * requested again, which will make include() happy. + */ + $this->stat = stat($path); + + return true; + } + + /** + * Included so that __FILE__ returns the appropriate info + * + * @return array + */ + public function url_stat() + { + return $this->stat; + } + + /** + * Reads from the stream. + * + * @param int $count + * @return string + */ + public function stream_read($count) + { + $ret = substr($this->data, $this->pos, $count); + $this->pos += strlen($ret); + return $ret; + } + + /** + * Tells the current position in the stream. + * + * @return int + */ + public function stream_tell() + { + return $this->pos; + } + + /** + * Tells if we are at the end of the stream. + * + * @return bool + */ + public function stream_eof() + { + return $this->pos >= strlen($this->data); + } + + /** + * Stream statistics. + * + * @return array + */ + public function stream_stat() + { + return $this->stat; + } + + /** + * Seek to a specific point in the stream. + * + * @param $offset + * @param $whence + * @return bool + */ + public function stream_seek($offset, $whence) + { + switch ($whence) { + case SEEK_SET: + if ($offset < strlen($this->data) && $offset >= 0) { + $this->pos = $offset; + return true; + } else { + return false; + } + break; + + case SEEK_CUR: + if ($offset >= 0) { + $this->pos += $offset; + return true; + } else { + return false; + } + break; + + case SEEK_END: + if (strlen($this->data) + $offset >= 0) { + $this->pos = strlen($this->data) + $offset; + return true; + } else { + return false; + } + break; + + default: + return false; + } + } +} diff --git a/library/Zend/View/Variables.php b/library/Zend/View/Variables.php new file mode 100755 index 000000000..17b14724c --- /dev/null +++ b/library/Zend/View/Variables.php @@ -0,0 +1,162 @@ +setOptions($options); + } + + /** + * Configure object + * + * @param array $options + * @return Variables + */ + public function setOptions(array $options) + { + foreach ($options as $key => $value) { + switch (strtolower($key)) { + case 'strict_vars': + $this->setStrictVars($value); + break; + default: + // Unknown options are considered variables + $this[$key] = $value; + break; + } + } + return $this; + } + + /** + * Set status of "strict vars" flag + * + * @param bool $flag + * @return Variables + */ + public function setStrictVars($flag) + { + $this->strictVars = (bool) $flag; + return $this; + } + + /** + * Are we operating with strict variables? + * + * @return bool + */ + public function isStrict() + { + return $this->strictVars; + } + + /** + * Assign many values at once + * + * @param array|object $spec + * @return Variables + * @throws Exception\InvalidArgumentException + */ + public function assign($spec) + { + if (is_object($spec)) { + if (method_exists($spec, 'toArray')) { + $spec = $spec->toArray(); + } else { + $spec = (array) $spec; + } + } + if (!is_array($spec)) { + throw new Exception\InvalidArgumentException(sprintf( + 'assign() expects either an array or an object as an argument; received "%s"', + gettype($spec) + )); + } + foreach ($spec as $key => $value) { + $this[$key] = $value; + } + + return $this; + } + + /** + * Get the variable value + * + * If the value has not been defined, a null value will be returned; if + * strict vars on in place, a notice will also be raised. + * + * Otherwise, returns _escaped_ version of the value. + * + * @param mixed $key + * @return mixed + */ + public function offsetGet($key) + { + if (!$this->offsetExists($key)) { + if ($this->isStrict()) { + trigger_error(sprintf( + 'View variable "%s" does not exist', $key + ), E_USER_NOTICE); + } + return null; + } + + $return = parent::offsetGet($key); + + // If we have a closure/functor, invoke it, and return its return value + if (is_object($return) && is_callable($return)) { + $return = call_user_func($return); + } + + return $return; + } + + /** + * Clear all variables + * + * @return void + */ + public function clear() + { + $this->exchangeArray(array()); + } +} diff --git a/library/Zend/View/View.php b/library/Zend/View/View.php new file mode 100755 index 000000000..aebae58a0 --- /dev/null +++ b/library/Zend/View/View.php @@ -0,0 +1,264 @@ +request = $request; + return $this; + } + + /** + * Set MVC response object + * + * @param Response $response + * @return View + */ + public function setResponse(Response $response) + { + $this->response = $response; + return $this; + } + + /** + * Get MVC request object + * + * @return null|Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get MVC response object + * + * @return null|Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Set the event manager instance + * + * @param EventManagerInterface $events + * @return View + */ + public function setEventManager(EventManagerInterface $events) + { + $events->setIdentifiers(array( + __CLASS__, + get_class($this), + )); + $this->events = $events; + return $this; + } + + /** + * Retrieve the event manager instance + * + * Lazy-loads a default instance if none available + * + * @return EventManagerInterface + */ + public function getEventManager() + { + if (!$this->events instanceof EventManagerInterface) { + $this->setEventManager(new EventManager()); + } + return $this->events; + } + + /** + * Add a rendering strategy + * + * Expects a callable. Strategies should accept a ViewEvent object, and should + * return a Renderer instance if the strategy is selected. + * + * Internally, the callable provided will be subscribed to the "renderer" + * event, at the priority specified. + * + * @param callable $callable + * @param int $priority + * @return View + */ + public function addRenderingStrategy($callable, $priority = 1) + { + $this->getEventManager()->attach(ViewEvent::EVENT_RENDERER, $callable, $priority); + return $this; + } + + /** + * Add a response strategy + * + * Expects a callable. Strategies should accept a ViewEvent object. The return + * value will be ignored. + * + * Typical usages for a response strategy are to populate the Response object. + * + * Internally, the callable provided will be subscribed to the "response" + * event, at the priority specified. + * + * @param callable $callable + * @param int $priority + * @return View + */ + public function addResponseStrategy($callable, $priority = 1) + { + $this->getEventManager()->attach(ViewEvent::EVENT_RESPONSE, $callable, $priority); + return $this; + } + + /** + * Render the provided model. + * + * Internally, the following workflow is used: + * + * - Trigger the "renderer" event to select a renderer. + * - Call the selected renderer with the provided Model + * - Trigger the "response" event + * + * @triggers renderer(ViewEvent) + * @triggers response(ViewEvent) + * @param Model $model + * @throws Exception\RuntimeException + * @return void + */ + public function render(Model $model) + { + $event = $this->getEvent(); + $event->setModel($model); + $events = $this->getEventManager(); + $results = $events->trigger(ViewEvent::EVENT_RENDERER, $event, function ($result) { + return ($result instanceof Renderer); + }); + $renderer = $results->last(); + if (!$renderer instanceof Renderer) { + throw new Exception\RuntimeException(sprintf( + '%s: no renderer selected!', + __METHOD__ + )); + } + + $event->setRenderer($renderer); + $events->trigger(ViewEvent::EVENT_RENDERER_POST, $event); + + // If EVENT_RENDERER or EVENT_RENDERER_POST changed the model, make sure + // we use this new model instead of the current $model + $model = $event->getModel(); + + // If we have children, render them first, but only if: + // a) the renderer does not implement TreeRendererInterface, or + // b) it does, but canRenderTrees() returns false + if ($model->hasChildren() + && (!$renderer instanceof TreeRendererInterface + || !$renderer->canRenderTrees()) + ) { + $this->renderChildren($model); + } + + // Reset the model, in case it has changed, and set the renderer + $event->setModel($model); + $event->setRenderer($renderer); + + $rendered = $renderer->render($model); + + // If this is a child model, return the rendered content; do not + // invoke the response strategy. + $options = $model->getOptions(); + if (array_key_exists('has_parent', $options) && $options['has_parent']) { + return $rendered; + } + + $event->setResult($rendered); + + $events->trigger(ViewEvent::EVENT_RESPONSE, $event); + } + + /** + * Loop through children, rendering each + * + * @param Model $model + * @throws Exception\DomainException + * @return void + */ + protected function renderChildren(Model $model) + { + foreach ($model as $child) { + if ($child->terminate()) { + throw new Exception\DomainException('Inconsistent state; child view model is marked as terminal'); + } + $child->setOption('has_parent', true); + $result = $this->render($child); + $child->setOption('has_parent', null); + $capture = $child->captureTo(); + if (!empty($capture)) { + if ($child->isAppend()) { + $oldResult=$model->{$capture}; + $model->setVariable($capture, $oldResult . $result); + } else { + $model->setVariable($capture, $result); + } + } + } + } + + /** + * Create and return ViewEvent used by render() + * + * @return ViewEvent + */ + protected function getEvent() + { + $event = new ViewEvent(); + $event->setTarget($this); + if (null !== ($request = $this->getRequest())) { + $event->setRequest($request); + } + if (null !== ($response = $this->getResponse())) { + $event->setResponse($response); + } + return $event; + } +} diff --git a/library/Zend/View/ViewEvent.php b/library/Zend/View/ViewEvent.php new file mode 100755 index 000000000..34baa2136 --- /dev/null +++ b/library/Zend/View/ViewEvent.php @@ -0,0 +1,258 @@ +model = $model; + return $this; + } + + /** + * Set the MVC request object + * + * @param Request $request + * @return ViewEvent + */ + public function setRequest(Request $request) + { + $this->request = $request; + return $this; + } + + /** + * Set the MVC response object + * + * @param Response $response + * @return ViewEvent + */ + public function setResponse(Response $response) + { + $this->response = $response; + return $this; + } + + /** + * Set result of rendering + * + * @param mixed $result + * @return ViewEvent + */ + public function setResult($result) + { + $this->result = $result; + return $this; + } + + /** + * Retrieve the view model + * + * @return null|Model + */ + public function getModel() + { + return $this->model; + } + + /** + * Set value for renderer + * + * @param Renderer $renderer + * @return ViewEvent + */ + public function setRenderer(Renderer $renderer) + { + $this->renderer = $renderer; + return $this; + } + + /** + * Get value for renderer + * + * @return null|Renderer + */ + public function getRenderer() + { + return $this->renderer; + } + + /** + * Retrieve the MVC request object + * + * @return null|Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Retrieve the MVC response object + * + * @return null|Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Retrieve the result of rendering + * + * @return mixed + */ + public function getResult() + { + return $this->result; + } + + /** + * Get event parameter + * + * @param string $name + * @param mixed $default + * @return mixed + */ + public function getParam($name, $default = null) + { + switch ($name) { + case 'model': + return $this->getModel(); + case 'renderer': + return $this->getRenderer(); + case 'request': + return $this->getRequest(); + case 'response': + return $this->getResponse(); + case 'result': + return $this->getResult(); + default: + return parent::getParam($name, $default); + } + } + + /** + * Get all event parameters + * + * @return array|\ArrayAccess + */ + public function getParams() + { + $params = parent::getParams(); + $params['model'] = $this->getModel(); + $params['renderer'] = $this->getRenderer(); + $params['request'] = $this->getRequest(); + $params['response'] = $this->getResponse(); + $params['result'] = $this->getResult(); + return $params; + } + + /** + * Set event parameters + * + * @param array|object|ArrayAccess $params + * @return ViewEvent + */ + public function setParams($params) + { + parent::setParams($params); + if (!is_array($params) && !$params instanceof ArrayAccess) { + return $this; + } + + foreach (array('model', 'renderer', 'request', 'response', 'result') as $param) { + if (isset($params[$param])) { + $method = 'set' . $param; + $this->$method($params[$param]); + } + } + return $this; + } + + /** + * Set an individual event parameter + * + * @param string $name + * @param mixed $value + * @return ViewEvent + */ + public function setParam($name, $value) + { + switch ($name) { + case 'model': + $this->setModel($value); + break; + case 'renderer': + $this->setRenderer($value); + break; + case 'request': + $this->setRequest($value); + break; + case 'response': + $this->setResponse($value); + break; + case 'result': + $this->setResult($value); + break; + default: + parent::setParam($name, $value); + break; + } + return $this; + } +} diff --git a/library/Zend/View/composer.json b/library/Zend/View/composer.json new file mode 100755 index 000000000..44bb5c275 --- /dev/null +++ b/library/Zend/View/composer.json @@ -0,0 +1,58 @@ +{ + "name": "zendframework/zend-view", + "description": "provides a system of helpers, output filters, and variable escaping", + "license": "BSD-3-Clause", + "keywords": [ + "zf2", + "view" + ], + "homepage": "https://github.com/zendframework/zf2", + "autoload": { + "psr-0": { + "Zend\\View\\": "" + } + }, + "target-dir": "Zend/View", + "require": { + "php": ">=5.3.23", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-loader": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "zendframework/zend-authentication": "self.version", + "zendframework/zend-escaper": "self.version", + "zendframework/zend-feed": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-http": "self.version", + "zendframework/zend-i18n": "self.version", + "zendframework/zend-json": "self.version", + "zendframework/zend-mvc": "self.version", + "zendframework/zend-navigation": "self.version", + "zendframework/zend-paginator": "self.version", + "zendframework/zend-permissions-acl": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-uri": "self.version" + }, + "suggest": { + "zendframework/zend-authentication": "Zend\\Authentication component", + "zendframework/zend-escaper": "Zend\\Escaper component", + "zendframework/zend-feed": "Zend\\Feed component", + "zendframework/zend-filter": "Zend\\Filter component", + "zendframework/zend-http": "Zend\\Http component", + "zendframework/zend-i18n": "Zend\\I18n component", + "zendframework/zend-json": "Zend\\Json component", + "zendframework/zend-mvc": "Zend\\Mvc component", + "zendframework/zend-navigation": "Zend\\Navigation component", + "zendframework/zend-paginator": "Zend\\Paginator component", + "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component", + "zendframework/zend-uri": "Zend\\Uri component" + }, + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + } +} diff --git a/library/Zend/XmlRpc/AbstractValue.php b/library/Zend/XmlRpc/AbstractValue.php new file mode 100755 index 000000000..221238a09 --- /dev/null +++ b/library/Zend/XmlRpc/AbstractValue.php @@ -0,0 +1,462 @@ +type; + } + + /** + * Get XML generator instance + * + * @return \Zend\XmlRpc\Generator\GeneratorInterface + */ + public static function getGenerator() + { + if (!static::$generator) { + if (extension_loaded('xmlwriter')) { + static::$generator = new Generator\XmlWriter(); + } else { + static::$generator = new Generator\DomDocument(); + } + } + + return static::$generator; + } + + /** + * Sets XML generator instance + * + * @param null|Generator\GeneratorInterface $generator + * @return void + */ + public static function setGenerator(Generator\GeneratorInterface $generator = null) + { + static::$generator = $generator; + } + + /** + * Changes the encoding of the generator + * + * @param string $encoding + * @return void + */ + public static function setEncoding($encoding) + { + $generator = static::getGenerator(); + $newGenerator = new $generator($encoding); + static::setGenerator($newGenerator); + } + + /** + * Return the value of this object, convert the XML-RPC native value into a PHP variable + * + * @return mixed + */ + abstract public function getValue(); + + + /** + * Return the XML code that represent a native MXL-RPC value + * + * @return string + */ + public function saveXml() + { + if (!$this->xml) { + $this->generateXml(); + $this->xml = (string) $this->getGenerator(); + } + return $this->xml; + } + + /** + * Generate XML code that represent a native XML/RPC value + * + * @return void + */ + public function generateXml() + { + $this->_generateXml(); + } + + /** + * Creates a Value* object, representing a native XML-RPC value + * A XmlRpcValue object can be created in 3 ways: + * 1. Autodetecting the native type out of a PHP variable + * (if $type is not set or equal to Value::AUTO_DETECT_TYPE) + * 2. By specifying the native type ($type is one of the Value::XMLRPC_TYPE_* constants) + * 3. From a XML string ($type is set to Value::XML_STRING) + * + * By default the value type is autodetected according to it's PHP type + * + * @param mixed $value + * @param Zend\XmlRpc\Value::constant $type + * @throws Exception\ValueException + * @return AbstractValue + */ + public static function getXmlRpcValue($value, $type = self::AUTO_DETECT_TYPE) + { + switch ($type) { + case self::AUTO_DETECT_TYPE: + // Auto detect the XML-RPC native type from the PHP type of $value + return static::_phpVarToNativeXmlRpc($value); + + case self::XML_STRING: + // Parse the XML string given in $value and get the XML-RPC value in it + return static::_xmlStringToNativeXmlRpc($value); + + case self::XMLRPC_TYPE_I4: + // fall through to the next case + case self::XMLRPC_TYPE_INTEGER: + return new Value\Integer($value); + + case self::XMLRPC_TYPE_I8: + // fall through to the next case + case self::XMLRPC_TYPE_APACHEI8: + return new Value\BigInteger($value); + + case self::XMLRPC_TYPE_DOUBLE: + return new Value\Double($value); + + case self::XMLRPC_TYPE_BOOLEAN: + return new Value\Boolean($value); + + case self::XMLRPC_TYPE_STRING: + return new Value\String($value); + + case self::XMLRPC_TYPE_BASE64: + return new Value\Base64($value); + + case self::XMLRPC_TYPE_NIL: + // fall through to the next case + case self::XMLRPC_TYPE_APACHENIL: + return new Value\Nil(); + + case self::XMLRPC_TYPE_DATETIME: + return new Value\DateTime($value); + + case self::XMLRPC_TYPE_ARRAY: + return new Value\ArrayValue($value); + + case self::XMLRPC_TYPE_STRUCT: + return new Value\Struct($value); + + default: + throw new Exception\ValueException('Given type is not a '. __CLASS__ .' constant'); + } + } + + /** + * Get XML-RPC type for a PHP native variable + * + * @static + * @param mixed $value + * @throws Exception\InvalidArgumentException + * @return string + */ + public static function getXmlRpcTypeByValue($value) + { + if (is_object($value)) { + if ($value instanceof AbstractValue) { + return $value->getType(); + } elseif ($value instanceof DateTime) { + return self::XMLRPC_TYPE_DATETIME; + } + return static::getXmlRpcTypeByValue(get_object_vars($value)); + } elseif (is_array($value)) { + if (!empty($value) && is_array($value) && (array_keys($value) !== range(0, count($value) - 1))) { + return self::XMLRPC_TYPE_STRUCT; + } + return self::XMLRPC_TYPE_ARRAY; + } elseif (is_int($value)) { + return ($value > PHP_INT_MAX) ? self::XMLRPC_TYPE_I8 : self::XMLRPC_TYPE_INTEGER; + } elseif (is_double($value)) { + return self::XMLRPC_TYPE_DOUBLE; + } elseif (is_bool($value)) { + return self::XMLRPC_TYPE_BOOLEAN; + } elseif (null === $value) { + return self::XMLRPC_TYPE_NIL; + } elseif (is_string($value)) { + return self::XMLRPC_TYPE_STRING; + } + throw new Exception\InvalidArgumentException(sprintf( + 'No matching XMLRPC type found for php type %s.', + gettype($value) + )); + } + + /** + * Transform a PHP native variable into a XML-RPC native value + * + * @param mixed $value The PHP variable for conversion + * + * @throws Exception\InvalidArgumentException + * @return AbstractValue + * @static + */ + protected static function _phpVarToNativeXmlRpc($value) + { + // @see http://framework.zend.com/issues/browse/ZF-8623 + if ($value instanceof AbstractValue) { + return $value; + } + + switch (static::getXmlRpcTypeByValue($value)) { + case self::XMLRPC_TYPE_DATETIME: + return new Value\DateTime($value); + + case self::XMLRPC_TYPE_ARRAY: + return new Value\ArrayValue($value); + + case self::XMLRPC_TYPE_STRUCT: + return new Value\Struct($value); + + case self::XMLRPC_TYPE_INTEGER: + return new Value\Integer($value); + + case self::XMLRPC_TYPE_DOUBLE: + return new Value\Double($value); + + case self::XMLRPC_TYPE_BOOLEAN: + return new Value\Boolean($value); + + case self::XMLRPC_TYPE_NIL: + return new Value\Nil; + + case self::XMLRPC_TYPE_STRING: + // Fall through to the next case + default: + // If type isn't identified (or identified as string), it treated as string + return new Value\String($value); + } + } + + /** + * Transform an XML string into a XML-RPC native value + * + * @param string|\SimpleXMLElement $xml A SimpleXMLElement object represent the XML string + * It can be also a valid XML string for conversion + * + * @throws Exception\ValueException + * @return \Zend\XmlRpc\AbstractValue + * @static + */ + protected static function _xmlStringToNativeXmlRpc($xml) + { + static::_createSimpleXMLElement($xml); + + static::_extractTypeAndValue($xml, $type, $value); + + switch ($type) { + // All valid and known XML-RPC native values + case self::XMLRPC_TYPE_I4: + // Fall through to the next case + case self::XMLRPC_TYPE_INTEGER: + $xmlrpcValue = new Value\Integer($value); + break; + case self::XMLRPC_TYPE_APACHEI8: + // Fall through to the next case + case self::XMLRPC_TYPE_I8: + $xmlrpcValue = new Value\BigInteger($value); + break; + case self::XMLRPC_TYPE_DOUBLE: + $xmlrpcValue = new Value\Double($value); + break; + case self::XMLRPC_TYPE_BOOLEAN: + $xmlrpcValue = new Value\Boolean($value); + break; + case self::XMLRPC_TYPE_STRING: + $xmlrpcValue = new Value\String($value); + break; + case self::XMLRPC_TYPE_DATETIME: // The value should already be in an iso8601 format + $xmlrpcValue = new Value\DateTime($value); + break; + case self::XMLRPC_TYPE_BASE64: // The value should already be base64 encoded + $xmlrpcValue = new Value\Base64($value, true); + break; + case self::XMLRPC_TYPE_NIL: + // Fall through to the next case + case self::XMLRPC_TYPE_APACHENIL: + // The value should always be NULL + $xmlrpcValue = new Value\Nil(); + break; + case self::XMLRPC_TYPE_ARRAY: + // PHP 5.2.4 introduced a regression in how empty($xml->value) + // returns; need to look for the item specifically + $data = null; + foreach ($value->children() as $key => $value) { + if ('data' == $key) { + $data = $value; + break; + } + } + + if (null === $data) { + throw new Exception\ValueException('Invalid XML for XML-RPC native '. self::XMLRPC_TYPE_ARRAY .' type: ARRAY tag must contain DATA tag'); + } + $values = array(); + // Parse all the elements of the array from the XML string + // (simple xml element) to Value objects + foreach ($data->value as $element) { + $values[] = static::_xmlStringToNativeXmlRpc($element); + } + $xmlrpcValue = new Value\ArrayValue($values); + break; + case self::XMLRPC_TYPE_STRUCT: + $values = array(); + // Parse all the members of the struct from the XML string + // (simple xml element) to Value objects + foreach ($value->member as $member) { + // @todo? If a member doesn't have a tag, we don't add it to the struct + // Maybe we want to throw an exception here ? + if (!isset($member->value) or !isset($member->name)) { + continue; + //throw new Value_Exception('Member of the '. self::XMLRPC_TYPE_STRUCT .' XML-RPC native type must contain a VALUE tag'); + } + $values[(string) $member->name] = static::_xmlStringToNativeXmlRpc($member->value); + } + $xmlrpcValue = new Value\Struct($values); + break; + default: + throw new Exception\ValueException('Value type \''. $type .'\' parsed from the XML string is not a known XML-RPC native type'); + break; + } + $xmlrpcValue->_setXML($xml->asXML()); + + return $xmlrpcValue; + } + + protected static function _createSimpleXMLElement(&$xml) + { + if ($xml instanceof \SimpleXMLElement) { + return; + } + + try { + $xml = new \SimpleXMLElement($xml); + } catch (\Exception $e) { + // The given string is not a valid XML + throw new Exception\ValueException('Failed to create XML-RPC value from XML string: ' . $e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Extract XML/RPC type and value from SimpleXMLElement object + * + * @param \SimpleXMLElement $xml + * @param string &$type Type bind variable + * @param string &$value Value bind variable + * @return void + */ + protected static function _extractTypeAndValue(\SimpleXMLElement $xml, &$type, &$value) + { + list($type, $value) = each($xml); + if (!$type and $value === null) { + $namespaces = array('ex' => 'http://ws.apache.org/xmlrpc/namespaces/extensions'); + foreach ($namespaces as $namespaceName => $namespaceUri) { + $namespaceXml = $xml->children($namespaceUri); + list($type, $value) = each($namespaceXml); + if ($type !== null) { + $type = $namespaceName . ':' . $type; + break; + } + } + } + + // If no type was specified, the default is string + if (!$type) { + $type = self::XMLRPC_TYPE_STRING; + if (empty($value) and preg_match('#^.*$#', $xml->asXML())) { + $value = str_replace(array('', ''), '', $xml->asXML()); + } + } + } + + /** + * @param $xml + * @return void + */ + protected function _setXML($xml) + { + $this->xml = $this->getGenerator()->stripDeclaration($xml); + } +} diff --git a/library/Zend/XmlRpc/CONTRIBUTING.md b/library/Zend/XmlRpc/CONTRIBUTING.md new file mode 100755 index 000000000..e77f5d2d5 --- /dev/null +++ b/library/Zend/XmlRpc/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# CONTRIBUTING + +Please don't open pull requests against this repository, please use https://github.com/zendframework/zf2. \ No newline at end of file diff --git a/library/Zend/XmlRpc/Client.php b/library/Zend/XmlRpc/Client.php new file mode 100755 index 000000000..504a3bf23 --- /dev/null +++ b/library/Zend/XmlRpc/Client.php @@ -0,0 +1,343 @@ +httpClient = new Http\Client(); + } else { + $this->httpClient = $httpClient; + } + + $this->introspector = new Client\ServerIntrospection($this); + $this->serverAddress = $server; + } + + + /** + * Sets the HTTP client object to use for connecting the XML-RPC server. + * + * @param \Zend\Http\Client $httpClient + * @return \Zend\Http\Client + */ + public function setHttpClient(Http\Client $httpClient) + { + return $this->httpClient = $httpClient; + } + + + /** + * Gets the HTTP client object. + * + * @return \Zend\Http\Client + */ + public function getHttpClient() + { + return $this->httpClient; + } + + + /** + * Sets the object used to introspect remote servers + * + * @param \Zend\XmlRpc\Client\ServerIntrospection + * @return \Zend\XmlRpc\Client\ServerIntrospection + */ + public function setIntrospector(Client\ServerIntrospection $introspector) + { + return $this->introspector = $introspector; + } + + + /** + * Gets the introspection object. + * + * @return \Zend\XmlRpc\Client\ServerIntrospection + */ + public function getIntrospector() + { + return $this->introspector; + } + + + /** + * The request of the last method call + * + * @return \Zend\XmlRpc\Request + */ + public function getLastRequest() + { + return $this->lastRequest; + } + + + /** + * The response received from the last method call + * + * @return \Zend\XmlRpc\Response + */ + public function getLastResponse() + { + return $this->lastResponse; + } + + + /** + * Returns a proxy object for more convenient method calls + * + * @param string $namespace Namespace to proxy or empty string for none + * @return \Zend\XmlRpc\Client\ServerProxy + */ + public function getProxy($namespace = '') + { + if (empty($this->proxyCache[$namespace])) { + $proxy = new Client\ServerProxy($this, $namespace); + $this->proxyCache[$namespace] = $proxy; + } + return $this->proxyCache[$namespace]; + } + + /** + * Set skip system lookup flag + * + * @param bool $flag + * @return \Zend\XmlRpc\Client + */ + public function setSkipSystemLookup($flag = true) + { + $this->skipSystemLookup = (bool) $flag; + return $this; + } + + /** + * Skip system lookup when determining if parameter should be array or struct? + * + * @return bool + */ + public function skipSystemLookup() + { + return $this->skipSystemLookup; + } + + /** + * Perform an XML-RPC request and return a response. + * + * @param \Zend\XmlRpc\Request $request + * @param null|\Zend\XmlRpc\Response $response + * @return void + * @throws \Zend\XmlRpc\Client\Exception\HttpException + */ + public function doRequest($request, $response = null) + { + $this->lastRequest = $request; + + if (PHP_VERSION_ID < 50600) { + iconv_set_encoding('input_encoding', 'UTF-8'); + iconv_set_encoding('output_encoding', 'UTF-8'); + iconv_set_encoding('internal_encoding', 'UTF-8'); + } else { + ini_set('default_charset', 'UTF-8'); + } + + $http = $this->getHttpClient(); + $httpRequest = $http->getRequest(); + if ($httpRequest->getUriString() === null) { + $http->setUri($this->serverAddress); + } + + $headers = $httpRequest->getHeaders(); + $headers->addHeaders(array( + 'Content-Type: text/xml; charset=utf-8', + 'Accept: text/xml', + )); + + if (!$headers->get('user-agent')) { + $headers->addHeaderLine('user-agent', 'Zend_XmlRpc_Client'); + } + + $xml = $this->lastRequest->__toString(); + $http->setRawBody($xml); + $httpResponse = $http->setMethod('POST')->send(); + + if (!$httpResponse->isSuccess()) { + /** + * Exception thrown when an HTTP error occurs + */ + throw new Client\Exception\HttpException( + $httpResponse->getReasonPhrase(), + $httpResponse->getStatusCode() + ); + } + + if ($response === null) { + $response = new Response(); + } + + $this->lastResponse = $response; + $this->lastResponse->loadXml(trim($httpResponse->getBody())); + } + + /** + * Send an XML-RPC request to the service (for a specific method) + * + * @param string $method Name of the method we want to call + * @param array $params Array of parameters for the method + * @return mixed + * @throws \Zend\XmlRpc\Client\Exception\FaultException + */ + public function call($method, $params=array()) + { + if (!$this->skipSystemLookup() && ('system.' != substr($method, 0, 7))) { + // Ensure empty array/struct params are cast correctly + // If system.* methods are not available, bypass. (ZF-2978) + $success = true; + try { + $signatures = $this->getIntrospector()->getMethodSignature($method); + } catch (\Zend\XmlRpc\Exception\ExceptionInterface $e) { + $success = false; + } + if ($success) { + $validTypes = array( + AbstractValue::XMLRPC_TYPE_ARRAY, + AbstractValue::XMLRPC_TYPE_BASE64, + AbstractValue::XMLRPC_TYPE_BOOLEAN, + AbstractValue::XMLRPC_TYPE_DATETIME, + AbstractValue::XMLRPC_TYPE_DOUBLE, + AbstractValue::XMLRPC_TYPE_I4, + AbstractValue::XMLRPC_TYPE_INTEGER, + AbstractValue::XMLRPC_TYPE_NIL, + AbstractValue::XMLRPC_TYPE_STRING, + AbstractValue::XMLRPC_TYPE_STRUCT, + ); + + if (!is_array($params)) { + $params = array($params); + } + foreach ($params as $key => $param) { + if ($param instanceof AbstractValue) { + continue; + } + + if (count($signatures) > 1) { + $type = AbstractValue::getXmlRpcTypeByValue($param); + foreach ($signatures as $signature) { + if (!is_array($signature)) { + continue; + } + if (isset($signature['parameters'][$key])) { + if ($signature['parameters'][$key] == $type) { + break; + } + } + } + } elseif (isset($signatures[0]['parameters'][$key])) { + $type = $signatures[0]['parameters'][$key]; + } else { + $type = null; + } + + if (empty($type) || !in_array($type, $validTypes)) { + $type = AbstractValue::AUTO_DETECT_TYPE; + } + + $params[$key] = AbstractValue::getXmlRpcValue($param, $type); + } + } + } + + $request = $this->_createRequest($method, $params); + + $this->doRequest($request); + + if ($this->lastResponse->isFault()) { + $fault = $this->lastResponse->getFault(); + /** + * Exception thrown when an XML-RPC fault is returned + */ + throw new Client\Exception\FaultException( + $fault->getMessage(), + $fault->getCode() + ); + } + + return $this->lastResponse->getReturnValue(); + } + + /** + * Create request object + * + * @param string $method + * @param array $params + * @return \Zend\XmlRpc\Request + */ + protected function _createRequest($method, $params) + { + return new Request($method, $params); + } +} diff --git a/library/Zend/XmlRpc/Client/Exception/ExceptionInterface.php b/library/Zend/XmlRpc/Client/Exception/ExceptionInterface.php new file mode 100755 index 000000000..03c09959c --- /dev/null +++ b/library/Zend/XmlRpc/Client/Exception/ExceptionInterface.php @@ -0,0 +1,19 @@ +system = $client->getProxy('system'); + } + + /** + * Returns the signature for each method on the server, + * autodetecting whether system.multicall() is supported and + * using it if so. + * + * @return array + */ + public function getSignatureForEachMethod() + { + $methods = $this->listMethods(); + + try { + $signatures = $this->getSignatureForEachMethodByMulticall($methods); + } catch (Exception\FaultException $e) { + // degrade to looping + } + + if (empty($signatures)) { + $signatures = $this->getSignatureForEachMethodByLooping($methods); + } + + return $signatures; + } + + /** + * Attempt to get the method signatures in one request via system.multicall(). + * This is a boxcar feature of XML-RPC and is found on fewer servers. However, + * can significantly improve performance if present. + * + * @param array $methods + * @throws Exception\IntrospectException + * @return array array(array(return, param, param, param...)) + */ + public function getSignatureForEachMethodByMulticall($methods = null) + { + if ($methods === null) { + $methods = $this->listMethods(); + } + + $multicallParams = array(); + foreach ($methods as $method) { + $multicallParams[] = array('methodName' => 'system.methodSignature', + 'params' => array($method)); + } + + $serverSignatures = $this->system->multicall($multicallParams); + + if (! is_array($serverSignatures)) { + $type = gettype($serverSignatures); + $error = "Multicall return is malformed. Expected array, got $type"; + throw new Exception\IntrospectException($error); + } + + if (count($serverSignatures) != count($methods)) { + $error = 'Bad number of signatures received from multicall'; + throw new Exception\IntrospectException($error); + } + + // Create a new signatures array with the methods name as keys and the signature as value + $signatures = array(); + foreach ($serverSignatures as $i => $signature) { + $signatures[$methods[$i]] = $signature; + } + + return $signatures; + } + + /** + * Get the method signatures for every method by + * successively calling system.methodSignature + * + * @param array $methods + * @return array + */ + public function getSignatureForEachMethodByLooping($methods = null) + { + if ($methods === null) { + $methods = $this->listMethods(); + } + + $signatures = array(); + foreach ($methods as $method) { + $signatures[$method] = $this->getMethodSignature($method); + } + + return $signatures; + } + + /** + * Call system.methodSignature() for the given method + * + * @param array $method + * @throws Exception\IntrospectException + * @return array array(array(return, param, param, param...)) + */ + public function getMethodSignature($method) + { + $signature = $this->system->methodSignature($method); + if (!is_array($signature)) { + $error = 'Invalid signature for method "' . $method . '"'; + throw new Exception\IntrospectException($error); + } + return $signature; + } + + /** + * Call system.listMethods() + * + * @return array array(method, method, method...) + */ + public function listMethods() + { + return $this->system->listMethods(); + } +} diff --git a/library/Zend/XmlRpc/Client/ServerProxy.php b/library/Zend/XmlRpc/Client/ServerProxy.php new file mode 100755 index 000000000..861b61590 --- /dev/null +++ b/library/Zend/XmlRpc/Client/ServerProxy.php @@ -0,0 +1,79 @@ +foo->bar->baz()". + */ +class ServerProxy +{ + /** + * @var \Zend\XmlRpc\Client + */ + private $client = null; + + /** + * @var string + */ + private $namespace = ''; + + + /** + * @var array of \Zend\XmlRpc\Client\ServerProxy + */ + private $cache = array(); + + + /** + * Class constructor + * + * @param \Zend\XmlRpc\Client $client + * @param string $namespace + */ + public function __construct(XMLRPCClient $client, $namespace = '') + { + $this->client = $client; + $this->namespace = $namespace; + } + + + /** + * Get the next successive namespace + * + * @param string $namespace + * @return \Zend\XmlRpc\Client\ServerProxy + */ + public function __get($namespace) + { + $namespace = ltrim("$this->namespace.$namespace", '.'); + if (!isset($this->cache[$namespace])) { + $this->cache[$namespace] = new $this($this->client, $namespace); + } + return $this->cache[$namespace]; + } + + + /** + * Call a method in this namespace. + * + * @param string $method + * @param array $args + * @return mixed + */ + public function __call($method, $args) + { + $method = ltrim("{$this->namespace}.{$method}", '.'); + return $this->client->call($method, $args); + } +} diff --git a/library/Zend/XmlRpc/Exception/BadMethodCallException.php b/library/Zend/XmlRpc/Exception/BadMethodCallException.php new file mode 100755 index 000000000..db36424ab --- /dev/null +++ b/library/Zend/XmlRpc/Exception/BadMethodCallException.php @@ -0,0 +1,14 @@ + messages + * @var array + */ + protected $internal = array( + 404 => 'Unknown Error', + + // 610 - 619 reflection errors + 610 => 'Invalid method class', + 611 => 'Unable to attach function or callback; not callable', + 612 => 'Unable to load array; not an array', + 613 => 'One or more method records are corrupt or otherwise unusable', + + // 620 - 629 dispatch errors + 620 => 'Method does not exist', + 621 => 'Error instantiating class to invoke method', + 622 => 'Method missing implementation', + 623 => 'Calling parameters do not match signature', + + // 630 - 639 request errors + 630 => 'Unable to read request', + 631 => 'Failed to parse request', + 632 => 'Invalid request, no method passed; request must contain a \'methodName\' tag', + 633 => 'Param must contain a value', + 634 => 'Invalid method name', + 635 => 'Invalid XML provided to request', + 636 => 'Error creating xmlrpc value', + + // 640 - 649 system.* errors + 640 => 'Method does not exist', + + // 650 - 659 response errors + 650 => 'Invalid XML provided for response', + 651 => 'Failed to parse response', + 652 => 'Invalid response', + 653 => 'Invalid XMLRPC value in response', + ); + + /** + * Constructor + * + */ + public function __construct($code = 404, $message = '') + { + $this->setCode($code); + $code = $this->getCode(); + + if (empty($message) && isset($this->internal[$code])) { + $message = $this->internal[$code]; + } elseif (empty($message)) { + $message = 'Unknown error'; + } + $this->setMessage($message); + } + + /** + * Set the fault code + * + * @param int $code + * @return Fault + */ + public function setCode($code) + { + $this->code = (int) $code; + return $this; + } + + /** + * Return fault code + * + * @return int + */ + public function getCode() + { + return $this->code; + } + + /** + * Retrieve fault message + * + * @param string + * @return Fault + */ + public function setMessage($message) + { + $this->message = (string) $message; + return $this; + } + + /** + * Retrieve fault message + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Set encoding to use in fault response + * + * @param string $encoding + * @return Fault + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + AbstractValue::setEncoding($encoding); + return $this; + } + + /** + * Retrieve current fault encoding + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Load an XMLRPC fault from XML + * + * @param string $fault + * @return bool Returns true if successfully loaded fault response, false + * if response was not a fault response + * @throws Exception\ExceptionInterface if no or faulty XML provided, or if fault + * response does not contain either code or message + */ + public function loadXml($fault) + { + if (!is_string($fault)) { + throw new Exception\InvalidArgumentException('Invalid XML provided to fault'); + } + + $xmlErrorsFlag = libxml_use_internal_errors(true); + try { + $xml = XmlSecurity::scan($fault); + } catch (\ZendXml\Exception\RuntimeException $e) { + // Unsecure XML + throw new Exception\RuntimeException('Failed to parse XML fault: ' . $e->getMessage(), 500, $e); + } + if (!$xml instanceof SimpleXMLElement) { + $errors = libxml_get_errors(); + $errors = array_reduce($errors, function ($result, $item) { + if (empty($result)) { + return $item->message; + } + return $result . '; ' . $item->message; + }, ''); + libxml_use_internal_errors($xmlErrorsFlag); + throw new Exception\InvalidArgumentException('Failed to parse XML fault: ' . $errors, 500); + } + libxml_use_internal_errors($xmlErrorsFlag); + + // Check for fault + if (!$xml->fault) { + // Not a fault + return false; + } + + if (!$xml->fault->value->struct) { + // not a proper fault + throw new Exception\InvalidArgumentException('Invalid fault structure', 500); + } + + $structXml = $xml->fault->value->asXML(); + $struct = AbstractValue::getXmlRpcValue($structXml, AbstractValue::XML_STRING); + $struct = $struct->getValue(); + + if (isset($struct['faultCode'])) { + $code = $struct['faultCode']; + } + if (isset($struct['faultString'])) { + $message = $struct['faultString']; + } + + if (empty($code) && empty($message)) { + throw new Exception\InvalidArgumentException('Fault code and string required'); + } + + if (empty($code)) { + $code = '404'; + } + + if (empty($message)) { + if (isset($this->internal[$code])) { + $message = $this->internal[$code]; + } else { + $message = 'Unknown Error'; + } + } + + $this->setCode($code); + $this->setMessage($message); + + return true; + } + + /** + * Determine if an XML response is an XMLRPC fault + * + * @param string $xml + * @return bool + */ + public static function isFault($xml) + { + $fault = new static(); + try { + $isFault = $fault->loadXml($xml); + } catch (Exception\ExceptionInterface $e) { + $isFault = false; + } + + return $isFault; + } + + /** + * Serialize fault to XML + * + * @return string + */ + public function saveXml() + { + // Create fault value + $faultStruct = array( + 'faultCode' => $this->getCode(), + 'faultString' => $this->getMessage() + ); + $value = AbstractValue::getXmlRpcValue($faultStruct); + + $generator = AbstractValue::getGenerator(); + $generator->openElement('methodResponse') + ->openElement('fault'); + $value->generateXml(); + $generator->closeElement('fault') + ->closeElement('methodResponse'); + + return $generator->flush(); + } + + /** + * Return XML fault response + * + * @return string + */ + public function __toString() + { + return $this->saveXML(); + } +} diff --git a/library/Zend/XmlRpc/Generator/AbstractGenerator.php b/library/Zend/XmlRpc/Generator/AbstractGenerator.php new file mode 100755 index 000000000..693f026c0 --- /dev/null +++ b/library/Zend/XmlRpc/Generator/AbstractGenerator.php @@ -0,0 +1,151 @@ +setEncoding($encoding); + $this->_init(); + } + + /** + * Initialize internal objects + * + * @return void + */ + abstract protected function _init(); + + /** + * Start XML element + * + * Method opens a new XML element with an element name and an optional value + * + * @param string $name XML tag name + * @param string $value Optional value of the XML tag + * @return AbstractGenerator Fluent interface + */ + public function openElement($name, $value = null) + { + $this->_openElement($name); + if ($value !== null) { + $this->_writeTextData($value); + } + + return $this; + } + + /** + * End of an XML element + * + * Method marks the end of an XML element + * + * @param string $name XML tag name + * @return AbstractGenerator Fluent interface + */ + public function closeElement($name) + { + $this->_closeElement($name); + + return $this; + } + + /** + * Return encoding + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Set XML encoding + * + * @param string $encoding + * @return AbstractGenerator + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + return $this; + } + + /** + * Returns the XML as a string and flushes all internal buffers + * + * @return string + */ + public function flush() + { + $xml = $this->saveXml(); + $this->_init(); + return $xml; + } + + /** + * Returns XML without document declaration + * + * @return string + */ + public function __toString() + { + return $this->stripDeclaration($this->saveXml()); + } + + /** + * Removes XML declaration from a string + * + * @param string $xml + * @return string + */ + public function stripDeclaration($xml) + { + return preg_replace('/<\?xml version="1.0"( encoding="[^\"]*")?\?>\n/u', '', $xml); + } + + /** + * Start XML element + * + * @param string $name XML element name + */ + abstract protected function _openElement($name); + + /** + * Write XML text data into the currently opened XML element + * + * @param string $text + */ + abstract protected function _writeTextData($text); + + /** + * End XML element + * + * @param string $name + */ + abstract protected function _closeElement($name); +} diff --git a/library/Zend/XmlRpc/Generator/DomDocument.php b/library/Zend/XmlRpc/Generator/DomDocument.php new file mode 100755 index 000000000..47e7a7223 --- /dev/null +++ b/library/Zend/XmlRpc/Generator/DomDocument.php @@ -0,0 +1,85 @@ +dom->createElement($name); + + $this->currentElement = $this->currentElement->appendChild($newElement); + } + + /** + * Write XML text data into the currently opened XML element + * + * @param string $text + */ + protected function _writeTextData($text) + { + $this->currentElement->appendChild($this->dom->createTextNode($text)); + } + + /** + * Close a previously opened XML element + * + * Resets $currentElement to the next parent node in the hierarchy + * + * @param string $name + * @return void + */ + protected function _closeElement($name) + { + if (isset($this->currentElement->parentNode)) { + $this->currentElement = $this->currentElement->parentNode; + } + } + + /** + * Save XML as a string + * + * @return string + */ + public function saveXml() + { + return $this->dom->saveXml(); + } + + /** + * Initializes internal objects + * + * @return void + */ + protected function _init() + { + $this->dom = new \DOMDocument('1.0', $this->encoding); + $this->currentElement = $this->dom; + } +} diff --git a/library/Zend/XmlRpc/Generator/GeneratorInterface.php b/library/Zend/XmlRpc/Generator/GeneratorInterface.php new file mode 100755 index 000000000..ca2af243b --- /dev/null +++ b/library/Zend/XmlRpc/Generator/GeneratorInterface.php @@ -0,0 +1,32 @@ +xmlWriter = new \XMLWriter(); + $this->xmlWriter->openMemory(); + $this->xmlWriter->startDocument('1.0', $this->encoding); + } + + + /** + * Open a new XML element + * + * @param string $name XML element name + * @return void + */ + protected function _openElement($name) + { + $this->xmlWriter->startElement($name); + } + + /** + * Write XML text data into the currently opened XML element + * + * @param string $text XML text data + * @return void + */ + protected function _writeTextData($text) + { + $this->xmlWriter->text($text); + } + + /** + * Close a previously opened XML element + * + * @param string $name + * @return XmlWriter + */ + protected function _closeElement($name) + { + $this->xmlWriter->endElement(); + + return $this; + } + + /** + * Emit XML document + * + * @return string + */ + public function saveXml() + { + return $this->xmlWriter->flush(false); + } +} diff --git a/library/Zend/XmlRpc/README.md b/library/Zend/XmlRpc/README.md new file mode 100755 index 000000000..f003296a4 --- /dev/null +++ b/library/Zend/XmlRpc/README.md @@ -0,0 +1,15 @@ +XML-RPC Component from ZF2 +========================== + +This is the XML-RPC component for ZF2. + +- File issues at https://github.com/zendframework/zf2/issues +- Create pull requests against https://github.com/zendframework/zf2 +- Documentation is at http://framework.zend.com/docs + +LICENSE +------- + +The files in this archive are released under the [Zend Framework +license](http://framework.zend.com/license), which is a 3-clause BSD license. + diff --git a/library/Zend/XmlRpc/Request.php b/library/Zend/XmlRpc/Request.php new file mode 100755 index 000000000..82a7eedbe --- /dev/null +++ b/library/Zend/XmlRpc/Request.php @@ -0,0 +1,444 @@ +setMethod($method); + } + + if ($params !== null) { + $this->setParams($params); + } + } + + + /** + * Set encoding to use in request + * + * @param string $encoding + * @return \Zend\XmlRpc\Request + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + AbstractValue::setEncoding($encoding); + return $this; + } + + /** + * Retrieve current request encoding + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Set method to call + * + * @param string $method + * @return bool Returns true on success, false if method name is invalid + */ + public function setMethod($method) + { + if (!is_string($method) || !preg_match('/^[a-z0-9_.:\\\\\/]+$/i', $method)) { + $this->fault = new Fault(634, 'Invalid method name ("' . $method . '")'); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + $this->method = $method; + return true; + } + + /** + * Retrieve call method + * + * @return string + */ + public function getMethod() + { + return $this->method; + } + + /** + * Add a parameter to the parameter stack + * + * Adds a parameter to the parameter stack, associating it with the type + * $type if provided + * + * @param mixed $value + * @param string $type Optional; type hinting + * @return void + */ + public function addParam($value, $type = null) + { + $this->params[] = $value; + if (null === $type) { + // Detect type if not provided explicitly + if ($value instanceof AbstractValue) { + $type = $value->getType(); + } else { + $xmlRpcValue = AbstractValue::getXmlRpcValue($value); + $type = $xmlRpcValue->getType(); + } + } + $this->types[] = $type; + $this->xmlRpcParams[] = array('value' => $value, 'type' => $type); + } + + /** + * Set the parameters array + * + * If called with a single, array value, that array is used to set the + * parameters stack. If called with multiple values or a single non-array + * value, the arguments are used to set the parameters stack. + * + * Best is to call with array of the format, in order to allow type hinting + * when creating the XMLRPC values for each parameter: + * + * $array = array( + * array( + * 'value' => $value, + * 'type' => $type + * )[, ... ] + * ); + * + * + * @access public + * @return void + */ + public function setParams() + { + $argc = func_num_args(); + $argv = func_get_args(); + if (0 == $argc) { + return; + } + + if ((1 == $argc) && is_array($argv[0])) { + $params = array(); + $types = array(); + $wellFormed = true; + foreach ($argv[0] as $arg) { + if (!is_array($arg) || !isset($arg['value'])) { + $wellFormed = false; + break; + } + $params[] = $arg['value']; + + if (!isset($arg['type'])) { + $xmlRpcValue = AbstractValue::getXmlRpcValue($arg['value']); + $arg['type'] = $xmlRpcValue->getType(); + } + $types[] = $arg['type']; + } + if ($wellFormed) { + $this->xmlRpcParams = $argv[0]; + $this->params = $params; + $this->types = $types; + } else { + $this->params = $argv[0]; + $this->types = array(); + $xmlRpcParams = array(); + foreach ($argv[0] as $arg) { + if ($arg instanceof AbstractValue) { + $type = $arg->getType(); + } else { + $xmlRpcValue = AbstractValue::getXmlRpcValue($arg); + $type = $xmlRpcValue->getType(); + } + $xmlRpcParams[] = array('value' => $arg, 'type' => $type); + $this->types[] = $type; + } + $this->xmlRpcParams = $xmlRpcParams; + } + return; + } + + $this->params = $argv; + $this->types = array(); + $xmlRpcParams = array(); + foreach ($argv as $arg) { + if ($arg instanceof AbstractValue) { + $type = $arg->getType(); + } else { + $xmlRpcValue = AbstractValue::getXmlRpcValue($arg); + $type = $xmlRpcValue->getType(); + } + $xmlRpcParams[] = array('value' => $arg, 'type' => $type); + $this->types[] = $type; + } + $this->xmlRpcParams = $xmlRpcParams; + } + + /** + * Retrieve the array of parameters + * + * @return array + */ + public function getParams() + { + return $this->params; + } + + /** + * Return parameter types + * + * @return array + */ + public function getTypes() + { + return $this->types; + } + + /** + * Load XML and parse into request components + * + * @param string $request + * @throws Exception\ValueException if invalid XML + * @return bool True on success, false if an error occurred. + */ + public function loadXml($request) + { + if (!is_string($request)) { + $this->fault = new Fault(635); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + // @see ZF-12293 - disable external entities for security purposes + $loadEntities = libxml_disable_entity_loader(true); + $xmlErrorsFlag = libxml_use_internal_errors(true); + try { + $dom = new DOMDocument; + $dom->loadXML($request); + foreach ($dom->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + throw new Exception\ValueException( + 'Invalid XML: Detected use of illegal DOCTYPE' + ); + } + } + ErrorHandler::start(); + $xml = simplexml_import_dom($dom); + $error = ErrorHandler::stop(); + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($xmlErrorsFlag); + } catch (\Exception $e) { + // Not valid XML + $this->fault = new Fault(631); + $this->fault->setEncoding($this->getEncoding()); + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($xmlErrorsFlag); + return false; + } + if (!$xml instanceof SimpleXMLElement || $error) { + // Not valid XML + $this->fault = new Fault(631); + $this->fault->setEncoding($this->getEncoding()); + libxml_use_internal_errors($xmlErrorsFlag); + return false; + } + + // Check for method name + if (empty($xml->methodName)) { + // Missing method name + $this->fault = new Fault(632); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + $this->method = (string) $xml->methodName; + + // Check for parameters + if (!empty($xml->params)) { + $types = array(); + $argv = array(); + foreach ($xml->params->children() as $param) { + if (!isset($param->value)) { + $this->fault = new Fault(633); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + try { + $param = AbstractValue::getXmlRpcValue($param->value, AbstractValue::XML_STRING); + $types[] = $param->getType(); + $argv[] = $param->getValue(); + } catch (\Exception $e) { + $this->fault = new Fault(636); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + } + + $this->types = $types; + $this->params = $argv; + } + + $this->xml = $request; + + return true; + } + + /** + * Does the current request contain errors and should it return a fault + * response? + * + * @return bool + */ + public function isFault() + { + return $this->fault instanceof Fault; + } + + /** + * Retrieve the fault response, if any + * + * @return null|\Zend\XmlRpc\Fault + */ + public function getFault() + { + return $this->fault; + } + + /** + * Retrieve method parameters as XMLRPC values + * + * @return array + */ + protected function _getXmlRpcParams() + { + $params = array(); + if (is_array($this->xmlRpcParams)) { + foreach ($this->xmlRpcParams as $param) { + $value = $param['value']; + $type = $param['type'] ?: AbstractValue::AUTO_DETECT_TYPE; + + if (!$value instanceof AbstractValue) { + $value = AbstractValue::getXmlRpcValue($value, $type); + } + $params[] = $value; + } + } + + return $params; + } + + /** + * Create XML request + * + * @return string + */ + public function saveXml() + { + $args = $this->_getXmlRpcParams(); + $method = $this->getMethod(); + + $generator = AbstractValue::getGenerator(); + $generator->openElement('methodCall') + ->openElement('methodName', $method) + ->closeElement('methodName'); + + if (is_array($args) && count($args)) { + $generator->openElement('params'); + + foreach ($args as $arg) { + $generator->openElement('param'); + $arg->generateXml(); + $generator->closeElement('param'); + } + $generator->closeElement('params'); + } + $generator->closeElement('methodCall'); + + return $generator->flush(); + } + + /** + * Return XML request + * + * @return string + */ + public function __toString() + { + return $this->saveXML(); + } +} diff --git a/library/Zend/XmlRpc/Request/Http.php b/library/Zend/XmlRpc/Request/Http.php new file mode 100755 index 000000000..d42a8331d --- /dev/null +++ b/library/Zend/XmlRpc/Request/Http.php @@ -0,0 +1,108 @@ +fault = new Fault(630); + return; + } + + $this->xml = $xml; + + $this->loadXml($xml); + } + + /** + * Retrieve the raw XML request + * + * @return string + */ + public function getRawRequest() + { + return $this->xml; + } + + /** + * Get headers + * + * Gets all headers as key => value pairs and returns them. + * + * @return array + */ + public function getHeaders() + { + if (null === $this->headers) { + $this->headers = array(); + foreach ($_SERVER as $key => $value) { + if ('HTTP_' == substr($key, 0, 5)) { + $header = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5))))); + $this->headers[$header] = $value; + } + } + } + + return $this->headers; + } + + /** + * Retrieve the full HTTP request, including headers and XML + * + * @return string + */ + public function getFullRequest() + { + $request = ''; + foreach ($this->getHeaders() as $key => $value) { + $request .= $key . ': ' . $value . "\n"; + } + + $request .= $this->xml; + + return $request; + } +} diff --git a/library/Zend/XmlRpc/Request/Stdin.php b/library/Zend/XmlRpc/Request/Stdin.php new file mode 100755 index 000000000..bcf748e95 --- /dev/null +++ b/library/Zend/XmlRpc/Request/Stdin.php @@ -0,0 +1,66 @@ +fault = new Fault(630); + return; + } + + $xml = ''; + while (!feof($fh)) { + $xml .= fgets($fh); + } + fclose($fh); + + $this->xml = $xml; + + $this->loadXml($xml); + } + + /** + * Retrieve the raw XML request + * + * @return string + */ + public function getRawRequest() + { + return $this->xml; + } +} diff --git a/library/Zend/XmlRpc/Response.php b/library/Zend/XmlRpc/Response.php new file mode 100755 index 000000000..f8537584e --- /dev/null +++ b/library/Zend/XmlRpc/Response.php @@ -0,0 +1,224 @@ +setReturnValue($return, $type); + } + + /** + * Set encoding to use in response + * + * @param string $encoding + * @return \Zend\XmlRpc\Response + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + AbstractValue::setEncoding($encoding); + return $this; + } + + /** + * Retrieve current response encoding + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Set the return value + * + * Sets the return value, with optional type hinting if provided. + * + * @param mixed $value + * @param string $type + * @return void + */ + public function setReturnValue($value, $type = null) + { + $this->return = $value; + $this->type = (string) $type; + } + + /** + * Retrieve the return value + * + * @return mixed + */ + public function getReturnValue() + { + return $this->return; + } + + /** + * Retrieve the XMLRPC value for the return value + * + * @return \Zend\XmlRpc\AbstractValue + */ + protected function _getXmlRpcReturn() + { + return AbstractValue::getXmlRpcValue($this->return); + } + + /** + * Is the response a fault response? + * + * @return bool + */ + public function isFault() + { + return $this->fault instanceof Fault; + } + + /** + * Returns the fault, if any. + * + * @return null|\Zend\XmlRpc\Fault + */ + public function getFault() + { + return $this->fault; + } + + /** + * Load a response from an XML response + * + * Attempts to load a response from an XMLRPC response, autodetecting if it + * is a fault response. + * + * @param string $response + * @throws Exception\ValueException if invalid XML + * @return bool True if a valid XMLRPC response, false if a fault + * response or invalid input + */ + public function loadXml($response) + { + if (!is_string($response)) { + $this->fault = new Fault(650); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + try { + $xml = XmlSecurity::scan($response); + } catch (\ZendXml\Exception\RuntimeException $e) { + $this->fault = new Fault(651); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + if (!empty($xml->fault)) { + // fault response + $this->fault = new Fault(); + $this->fault->setEncoding($this->getEncoding()); + $this->fault->loadXml($response); + return false; + } + + if (empty($xml->params)) { + // Invalid response + $this->fault = new Fault(652); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + try { + if (!isset($xml->params) || !isset($xml->params->param) || !isset($xml->params->param->value)) { + throw new Exception\ValueException('Missing XML-RPC value in XML'); + } + $valueXml = $xml->params->param->value->asXML(); + $value = AbstractValue::getXmlRpcValue($valueXml, AbstractValue::XML_STRING); + } catch (Exception\ValueException $e) { + $this->fault = new Fault(653); + $this->fault->setEncoding($this->getEncoding()); + return false; + } + + $this->setReturnValue($value->getValue()); + return true; + } + + /** + * Return response as XML + * + * @return string + */ + public function saveXml() + { + $value = $this->_getXmlRpcReturn(); + $generator = AbstractValue::getGenerator(); + $generator->openElement('methodResponse') + ->openElement('params') + ->openElement('param'); + $value->generateXml(); + $generator->closeElement('param') + ->closeElement('params') + ->closeElement('methodResponse'); + + return $generator->flush(); + } + + /** + * Return XML response + * + * @return string + */ + public function __toString() + { + return $this->saveXML(); + } +} diff --git a/library/Zend/XmlRpc/Response/Http.php b/library/Zend/XmlRpc/Response/Http.php new file mode 100755 index 000000000..00ae07cc9 --- /dev/null +++ b/library/Zend/XmlRpc/Response/Http.php @@ -0,0 +1,32 @@ +getEncoding())); + } + + return parent::__toString(); + } +} diff --git a/library/Zend/XmlRpc/Server.php b/library/Zend/XmlRpc/Server.php new file mode 100755 index 000000000..e2cfcea6b --- /dev/null +++ b/library/Zend/XmlRpc/Server.php @@ -0,0 +1,609 @@ + + * use Zend\XmlRpc; + * + * // Instantiate server + * $server = new XmlRpc\Server(); + * + * // Allow some exceptions to report as fault responses: + * XmlRpc\Server\Fault::attachFaultException('My\\Exception'); + * XmlRpc\Server\Fault::attachObserver('My\\Fault\\Observer'); + * + * // Get or build dispatch table: + * if (!XmlRpc\Server\Cache::get($filename, $server)) { + * + * // Attach Some_Service_Class in 'some' namespace + * $server->setClass('Some\\Service\\Class', 'some'); + * + * // Attach Another_Service_Class in 'another' namespace + * $server->setClass('Another\\Service\\Class', 'another'); + * + * // Create dispatch table cache file + * XmlRpc\Server\Cache::save($filename, $server); + * } + * + * $response = $server->handle(); + * echo $response; + * + */ +class Server extends AbstractServer +{ + /** + * Character encoding + * @var string + */ + protected $encoding = 'UTF-8'; + + /** + * Request processed + * @var null|Request + */ + protected $request = null; + + /** + * Class to use for responses; defaults to {@link Response\Http} + * @var string + */ + protected $responseClass = 'Zend\XmlRpc\Response\Http'; + + /** + * Dispatch table of name => method pairs + * @var Definition + */ + protected $table; + + /** + * PHP types => XML-RPC types + * @var array + */ + protected $typeMap = array( + 'i4' => 'i4', + 'int' => 'int', + 'integer' => 'int', + 'i8' => 'i8', + 'ex:i8' => 'i8', + 'double' => 'double', + 'float' => 'double', + 'real' => 'double', + 'boolean' => 'boolean', + 'bool' => 'boolean', + 'true' => 'boolean', + 'false' => 'boolean', + 'string' => 'string', + 'str' => 'string', + 'base64' => 'base64', + 'dateTime.iso8601' => 'dateTime.iso8601', + 'date' => 'dateTime.iso8601', + 'time' => 'dateTime.iso8601', + 'DateTime' => 'dateTime.iso8601', + 'array' => 'array', + 'struct' => 'struct', + 'null' => 'nil', + 'nil' => 'nil', + 'ex:nil' => 'nil', + 'void' => 'void', + 'mixed' => 'struct', + ); + + /** + * Send arguments to all methods or just constructor? + * + * @var bool + */ + protected $sendArgumentsToAllMethods = true; + + /** + * Flag: whether or not {@link handle()} should return a response instead + * of automatically emitting it. + * @var bool + */ + protected $returnResponse = false; + + /** + * Last response results. + * @var Response + */ + protected $response; + + /** + * Constructor + * + * Creates system.* methods. + * + */ + public function __construct() + { + $this->table = new Definition(); + $this->registerSystemMethods(); + } + + /** + * Proxy calls to system object + * + * @param string $method + * @param array $params + * @return mixed + * @throws Server\Exception\BadMethodCallException + */ + public function __call($method, $params) + { + $system = $this->getSystem(); + if (!method_exists($system, $method)) { + throw new Server\Exception\BadMethodCallException('Unknown instance method called on server: ' . $method); + } + return call_user_func_array(array($system, $method), $params); + } + + /** + * Attach a callback as an XMLRPC method + * + * Attaches a callback as an XMLRPC method, prefixing the XMLRPC method name + * with $namespace, if provided. Reflection is done on the callback's + * docblock to create the methodHelp for the XMLRPC method. + * + * Additional arguments to pass to the function at dispatch may be passed; + * any arguments following the namespace will be aggregated and passed at + * dispatch time. + * + * @param string|array|callable $function Valid callback + * @param string $namespace Optional namespace prefix + * @throws Server\Exception\InvalidArgumentException + * @return void + */ + public function addFunction($function, $namespace = '') + { + if (!is_string($function) && !is_array($function)) { + throw new Server\Exception\InvalidArgumentException('Unable to attach function; invalid', 611); + } + + $argv = null; + if (2 < func_num_args()) { + $argv = func_get_args(); + $argv = array_slice($argv, 2); + } + + $function = (array) $function; + foreach ($function as $func) { + if (!is_string($func) || !function_exists($func)) { + throw new Server\Exception\InvalidArgumentException('Unable to attach function; invalid', 611); + } + $reflection = Reflection::reflectFunction($func, $argv, $namespace); + $this->_buildSignature($reflection); + } + } + + /** + * Attach class methods as XMLRPC method handlers + * + * $class may be either a class name or an object. Reflection is done on the + * class or object to determine the available public methods, and each is + * attached to the server as an available method; if a $namespace has been + * provided, that namespace is used to prefix the XMLRPC method names. + * + * Any additional arguments beyond $namespace will be passed to a method at + * invocation. + * + * @param string|object $class + * @param string $namespace Optional + * @param mixed $argv Optional arguments to pass to methods + * @return void + * @throws Server\Exception\InvalidArgumentException on invalid input + */ + public function setClass($class, $namespace = '', $argv = null) + { + if (is_string($class) && !class_exists($class)) { + throw new Server\Exception\InvalidArgumentException('Invalid method class', 610); + } + + if (2 < func_num_args()) { + $argv = func_get_args(); + $argv = array_slice($argv, 2); + } + + $dispatchable = Reflection::reflectClass($class, $argv, $namespace); + foreach ($dispatchable->getMethods() as $reflection) { + $this->_buildSignature($reflection, $class); + } + } + + /** + * Raise an xmlrpc server fault + * + * @param string|\Exception $fault + * @param int $code + * @return Server\Fault + */ + public function fault($fault = null, $code = 404) + { + if (!$fault instanceof \Exception) { + $fault = (string) $fault; + if (empty($fault)) { + $fault = 'Unknown Error'; + } + $fault = new Server\Exception\RuntimeException($fault, $code); + } + + return Server\Fault::getInstance($fault); + } + + /** + * Set return response flag + * + * If true, {@link handle()} will return the response instead of + * automatically sending it back to the requesting client. + * + * The response is always available via {@link getResponse()}. + * + * @param bool $flag + * @return Server + */ + public function setReturnResponse($flag = true) + { + $this->returnResponse = ($flag) ? true : false; + return $this; + } + + /** + * Retrieve return response flag + * + * @return bool + */ + public function getReturnResponse() + { + return $this->returnResponse; + } + + /** + * Handle an xmlrpc call + * + * @param Request $request Optional + * @return Response|Fault + */ + public function handle($request = false) + { + // Get request + if ((!$request || !$request instanceof Request) + && (null === ($request = $this->getRequest())) + ) { + $request = new Request\Http(); + $request->setEncoding($this->getEncoding()); + } + + $this->setRequest($request); + + if ($request->isFault()) { + $response = $request->getFault(); + } else { + try { + $response = $this->handleRequest($request); + } catch (\Exception $e) { + $response = $this->fault($e); + } + } + + // Set output encoding + $response->setEncoding($this->getEncoding()); + $this->response = $response; + + if (!$this->returnResponse) { + echo $response; + return; + } + + return $response; + } + + /** + * Load methods as returned from {@link getFunctions} + * + * Typically, you will not use this method; it will be called using the + * results pulled from {@link Zend\XmlRpc\Server\Cache::get()}. + * + * @param array|Definition $definition + * @return void + * @throws Server\Exception\InvalidArgumentException on invalid input + */ + public function loadFunctions($definition) + { + if (!is_array($definition) && (!$definition instanceof Definition)) { + if (is_object($definition)) { + $type = get_class($definition); + } else { + $type = gettype($definition); + } + throw new Server\Exception\InvalidArgumentException('Unable to load server definition; must be an array or Zend\Server\Definition, received ' . $type, 612); + } + + $this->table->clearMethods(); + $this->registerSystemMethods(); + + if ($definition instanceof Definition) { + $definition = $definition->getMethods(); + } + + foreach ($definition as $key => $method) { + if ('system.' == substr($key, 0, 7)) { + continue; + } + $this->table->addMethod($method, $key); + } + } + + /** + * Set encoding + * + * @param string $encoding + * @return Server + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + AbstractValue::setEncoding($encoding); + return $this; + } + + /** + * Retrieve current encoding + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Do nothing; persistence is handled via {@link Zend\XmlRpc\Server\Cache} + * + * @param mixed $mode + * @return void + */ + public function setPersistence($mode) + { + } + + /** + * Set the request object + * + * @param string|Request $request + * @return Server + * @throws Server\Exception\InvalidArgumentException on invalid request class or object + */ + public function setRequest($request) + { + if (is_string($request) && class_exists($request)) { + $request = new $request(); + if (!$request instanceof Request) { + throw new Server\Exception\InvalidArgumentException('Invalid request class'); + } + $request->setEncoding($this->getEncoding()); + } elseif (!$request instanceof Request) { + throw new Server\Exception\InvalidArgumentException('Invalid request object'); + } + + $this->request = $request; + return $this; + } + + /** + * Return currently registered request object + * + * @return null|Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Last response. + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Set the class to use for the response + * + * @param string $class + * @throws Server\Exception\InvalidArgumentException if invalid response class + * @return bool True if class was set, false if not + */ + public function setResponseClass($class) + { + if (!class_exists($class) || !static::isSubclassOf($class, 'Zend\XmlRpc\Response')) { + throw new Server\Exception\InvalidArgumentException('Invalid response class'); + } + $this->responseClass = $class; + return true; + } + + /** + * Retrieve current response class + * + * @return string + */ + public function getResponseClass() + { + return $this->responseClass; + } + + /** + * Retrieve dispatch table + * + * @return array + */ + public function getDispatchTable() + { + return $this->table; + } + + /** + * Returns a list of registered methods + * + * Returns an array of dispatchables (Zend\Server\Reflection\ReflectionFunction, + * ReflectionMethod, and ReflectionClass items). + * + * @return array + */ + public function getFunctions() + { + return $this->table->toArray(); + } + + /** + * Retrieve system object + * + * @return Server\System + */ + public function getSystem() + { + return $this->system; + } + + /** + * Send arguments to all methods? + * + * If setClass() is used to add classes to the server, this flag defined + * how to handle arguments. If set to true, all methods including constructor + * will receive the arguments. If set to false, only constructor will receive the + * arguments + */ + public function sendArgumentsToAllMethods($flag = null) + { + if ($flag === null) { + return $this->sendArgumentsToAllMethods; + } + + $this->sendArgumentsToAllMethods = (bool) $flag; + return $this; + } + + /** + * Map PHP type to XML-RPC type + * + * @param string $type + * @return string + */ + protected function _fixType($type) + { + if (isset($this->typeMap[$type])) { + return $this->typeMap[$type]; + } + return 'void'; + } + + /** + * Handle an xmlrpc call (actual work) + * + * @param Request $request + * @return Response + * @throws Server\Exception\RuntimeException + * Zend\XmlRpc\Server\Exceptions are thrown for internal errors; otherwise, + * any other exception may be thrown by the callback + */ + protected function handleRequest(Request $request) + { + $method = $request->getMethod(); + + // Check for valid method + if (!$this->table->hasMethod($method)) { + throw new Server\Exception\RuntimeException('Method "' . $method . '" does not exist', 620); + } + + $info = $this->table->getMethod($method); + $params = $request->getParams(); + $argv = $info->getInvokeArguments(); + if (0 < count($argv) and $this->sendArgumentsToAllMethods()) { + $params = array_merge($params, $argv); + } + + // Check calling parameters against signatures + $matched = false; + $sigCalled = $request->getTypes(); + + $sigLength = count($sigCalled); + $paramsLen = count($params); + if ($sigLength < $paramsLen) { + for ($i = $sigLength; $i < $paramsLen; ++$i) { + $xmlRpcValue = AbstractValue::getXmlRpcValue($params[$i]); + $sigCalled[] = $xmlRpcValue->getType(); + } + } + + $signatures = $info->getPrototypes(); + foreach ($signatures as $signature) { + $sigParams = $signature->getParameters(); + if ($sigCalled === $sigParams) { + $matched = true; + break; + } + } + if (!$matched) { + throw new Server\Exception\RuntimeException('Calling parameters do not match signature', 623); + } + + $return = $this->_dispatch($info, $params); + $responseClass = $this->getResponseClass(); + return new $responseClass($return); + } + + /** + * Register system methods with the server + * + * @return void + */ + protected function registerSystemMethods() + { + $system = new Server\System($this); + $this->system = $system; + $this->setClass($system, 'system'); + } + + /** + * Checks if the object has this class as one of its parents + * + * @see https://bugs.php.net/bug.php?id=53727 + * @see https://github.com/zendframework/zf2/pull/1807 + * + * @param string $className + * @param string $type + * @return bool + */ + protected static function isSubclassOf($className, $type) + { + if (is_subclass_of($className, $type)) { + return true; + } + if (PHP_VERSION_ID >= 50307) { + return false; + } + if (!interface_exists($type)) { + return false; + } + $r = new ReflectionClass($className); + return $r->implementsInterface($type); + } +} diff --git a/library/Zend/XmlRpc/Server/Cache.php b/library/Zend/XmlRpc/Server/Cache.php new file mode 100755 index 000000000..f4e643ea6 --- /dev/null +++ b/library/Zend/XmlRpc/Server/Cache.php @@ -0,0 +1,26 @@ + true); + + /** + * @var array Array of fault observers + */ + protected static $observers = array(); + + /** + * Constructor + * + * @param \Exception $e + * @return Fault + */ + public function __construct(\Exception $e) + { + $this->exception = $e; + $code = 404; + $message = 'Unknown error'; + + foreach (array_keys(static::$faultExceptionClasses) as $class) { + if ($e instanceof $class) { + $code = $e->getCode(); + $message = $e->getMessage(); + break; + } + } + + parent::__construct($code, $message); + + // Notify exception observers, if present + if (!empty(static::$observers)) { + foreach (array_keys(static::$observers) as $observer) { + $observer::observe($this); + } + } + } + + /** + * Return Zend\XmlRpc\Server\Fault instance + * + * @param \Exception $e + * @return Fault + */ + public static function getInstance(\Exception $e) + { + return new static($e); + } + + /** + * Attach valid exceptions that can be used to define xmlrpc faults + * + * @param string|array $classes Class name or array of class names + * @return void + */ + public static function attachFaultException($classes) + { + if (!is_array($classes)) { + $classes = (array) $classes; + } + + foreach ($classes as $class) { + if (is_string($class) && class_exists($class)) { + static::$faultExceptionClasses[$class] = true; + } + } + } + + /** + * Detach fault exception classes + * + * @param string|array $classes Class name or array of class names + * @return void + */ + public static function detachFaultException($classes) + { + if (!is_array($classes)) { + $classes = (array) $classes; + } + + foreach ($classes as $class) { + if (is_string($class) && isset(static::$faultExceptionClasses[$class])) { + unset(static::$faultExceptionClasses[$class]); + } + } + } + + /** + * Attach an observer class + * + * Allows observation of xmlrpc server faults, thus allowing logging or mail + * notification of fault responses on the xmlrpc server. + * + * Expects a valid class name; that class must have a public static method + * 'observe' that accepts an exception as its sole argument. + * + * @param string $class + * @return bool + */ + public static function attachObserver($class) + { + if (!is_string($class) + || !class_exists($class) + || !is_callable(array($class, 'observe')) + ) { + return false; + } + + if (!isset(static::$observers[$class])) { + static::$observers[$class] = true; + } + + return true; + } + + /** + * Detach an observer + * + * @param string $class + * @return bool + */ + public static function detachObserver($class) + { + if (!isset(static::$observers[$class])) { + return false; + } + + unset(static::$observers[$class]); + return true; + } + + /** + * Retrieve the exception + * + * @access public + * @return \Exception + */ + public function getException() + { + return $this->exception; + } +} diff --git a/library/Zend/XmlRpc/Server/System.php b/library/Zend/XmlRpc/Server/System.php new file mode 100755 index 000000000..8a57838b1 --- /dev/null +++ b/library/Zend/XmlRpc/Server/System.php @@ -0,0 +1,144 @@ +server = $server; + } + + /** + * List all available XMLRPC methods + * + * Returns an array of methods. + * + * @return array + */ + public function listMethods() + { + $table = $this->server->getDispatchTable()->getMethods(); + return array_keys($table); + } + + /** + * Display help message for an XMLRPC method + * + * @param string $method + * @throws Exception\InvalidArgumentException + * @return string + */ + public function methodHelp($method) + { + $table = $this->server->getDispatchTable(); + if (!$table->hasMethod($method)) { + throw new Exception\InvalidArgumentException('Method "' . $method . '" does not exist', 640); + } + + return $table->getMethod($method)->getMethodHelp(); + } + + /** + * Return a method signature + * + * @param string $method + * @throws Exception\InvalidArgumentException + * @return array + */ + public function methodSignature($method) + { + $table = $this->server->getDispatchTable(); + if (!$table->hasMethod($method)) { + throw new Exception\InvalidArgumentException('Method "' . $method . '" does not exist', 640); + } + $method = $table->getMethod($method)->toArray(); + return $method['prototypes']; + } + + /** + * Multicall - boxcar feature of XML-RPC for calling multiple methods + * in a single request. + * + * Expects an array of structs representing method calls, each element + * having the keys: + * - methodName + * - params + * + * Returns an array of responses, one for each method called, with the value + * returned by the method. If an error occurs for a given method, returns a + * struct with a fault response. + * + * @see http://www.xmlrpc.com/discuss/msgReader$1208 + * @param array $methods + * @return array + */ + public function multicall($methods) + { + $responses = array(); + foreach ($methods as $method) { + $fault = false; + if (!is_array($method)) { + $fault = $this->server->fault('system.multicall expects each method to be a struct', 601); + } elseif (!isset($method['methodName'])) { + $fault = $this->server->fault('Missing methodName: ' . var_export($methods, 1), 602); + } elseif (!isset($method['params'])) { + $fault = $this->server->fault('Missing params', 603); + } elseif (!is_array($method['params'])) { + $fault = $this->server->fault('Params must be an array', 604); + } else { + if ('system.multicall' == $method['methodName']) { + // don't allow recursive calls to multicall + $fault = $this->server->fault('Recursive system.multicall forbidden', 605); + } + } + + if (!$fault) { + try { + $request = new \Zend\XmlRpc\Request(); + $request->setMethod($method['methodName']); + $request->setParams($method['params']); + $response = $this->server->handle($request); + if ($response instanceof \Zend\XmlRpc\Fault + || $response->isFault() + ) { + $fault = $response; + } else { + $responses[] = $response->getReturnValue(); + } + } catch (\Exception $e) { + $fault = $this->server->fault($e); + } + } + + if ($fault) { + $responses[] = array( + 'faultCode' => $fault->getCode(), + 'faultString' => $fault->getMessage() + ); + } + } + + return $responses; + } +} diff --git a/library/Zend/XmlRpc/Value/AbstractCollection.php b/library/Zend/XmlRpc/Value/AbstractCollection.php new file mode 100755 index 000000000..ed4f8193f --- /dev/null +++ b/library/Zend/XmlRpc/Value/AbstractCollection.php @@ -0,0 +1,48 @@ + $value) { + // If the elements of the given array are not Zend\XmlRpc\Value objects, + // we need to convert them as such (using auto-detection from PHP value) + if (!$value instanceof parent) { + $value = static::getXmlRpcValue($value, self::AUTO_DETECT_TYPE); + } + $this->value[$key] = $value; + } + } + + + /** + * Return the value of this object, convert the XML-RPC native collection values into a PHP array + * + * @return array + */ + public function getValue() + { + $values = (array) $this->value; + foreach ($values as $key => $value) { + $values[$key] = $value->getValue(); + } + return $values; + } +} diff --git a/library/Zend/XmlRpc/Value/AbstractScalar.php b/library/Zend/XmlRpc/Value/AbstractScalar.php new file mode 100755 index 000000000..3e7b8eacb --- /dev/null +++ b/library/Zend/XmlRpc/Value/AbstractScalar.php @@ -0,0 +1,30 @@ +getGenerator(); + + $generator->openElement('value') + ->openElement($this->type, $this->value) + ->closeElement($this->type) + ->closeElement('value'); + } +} diff --git a/library/Zend/XmlRpc/Value/ArrayValue.php b/library/Zend/XmlRpc/Value/ArrayValue.php new file mode 100755 index 000000000..99d77437b --- /dev/null +++ b/library/Zend/XmlRpc/Value/ArrayValue.php @@ -0,0 +1,47 @@ +type = self::XMLRPC_TYPE_ARRAY; + parent::__construct($value); + } + + + /** + * Generate the XML code that represent an array native MXL-RPC value + * + * @return void + */ + protected function _generateXml() + { + $generator = $this->getGenerator(); + $generator->openElement('value') + ->openElement('array') + ->openElement('data'); + + if (is_array($this->value)) { + foreach ($this->value as $val) { + $val->generateXml(); + } + } + $generator->closeElement('data') + ->closeElement('array') + ->closeElement('value'); + } +} diff --git a/library/Zend/XmlRpc/Value/Base64.php b/library/Zend/XmlRpc/Value/Base64.php new file mode 100755 index 000000000..9ba44acea --- /dev/null +++ b/library/Zend/XmlRpc/Value/Base64.php @@ -0,0 +1,42 @@ +type = self::XMLRPC_TYPE_BASE64; + + $value = (string) $value; // Make sure this value is string + if (!$alreadyEncoded) { + $value = base64_encode($value); // We encode it in base64 + } + $this->value = $value; + } + + /** + * Return the value of this object, convert the XML-RPC native base64 value into a PHP string + * We return this value decoded (a normal string) + * + * @return string + */ + public function getValue() + { + return base64_decode($this->value); + } +} diff --git a/library/Zend/XmlRpc/Value/BigInteger.php b/library/Zend/XmlRpc/Value/BigInteger.php new file mode 100755 index 000000000..e85ef9537 --- /dev/null +++ b/library/Zend/XmlRpc/Value/BigInteger.php @@ -0,0 +1,34 @@ +value = BigIntegerMath::factory()->init($value, 10); + $this->type = self::XMLRPC_TYPE_I8; + } + + /** + * Return bigint value object + * + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/library/Zend/XmlRpc/Value/Boolean.php b/library/Zend/XmlRpc/Value/Boolean.php new file mode 100755 index 000000000..5ec6b7932 --- /dev/null +++ b/library/Zend/XmlRpc/Value/Boolean.php @@ -0,0 +1,37 @@ +type = self::XMLRPC_TYPE_BOOLEAN; + // Make sure the value is boolean and then convert it into an integer + // The double conversion is because a bug in the ZendOptimizer in PHP version 5.0.4 + $this->value = (int)(bool) $value; + } + + /** + * Return the value of this object, convert the XML-RPC native boolean value into a PHP boolean + * + * @return bool + */ + public function getValue() + { + return (bool) $this->value; + } +} diff --git a/library/Zend/XmlRpc/Value/DateTime.php b/library/Zend/XmlRpc/Value/DateTime.php new file mode 100755 index 000000000..9ec7253e6 --- /dev/null +++ b/library/Zend/XmlRpc/Value/DateTime.php @@ -0,0 +1,67 @@ +type = self::XMLRPC_TYPE_DATETIME; + + if ($value instanceof \DateTime) { + $this->value = $value->format($this->phpFormatString); + } elseif (is_numeric($value)) { // The value is numeric, we make sure it is an integer + $this->value = date($this->phpFormatString, (int) $value); + } else { + try { + $dateTime = new \DateTime($value); + } catch (\Exception $e) { + throw new Exception\ValueException($e->getMessage(), $e->getCode(), $e); + } + + $this->value = $dateTime->format($this->phpFormatString); // Convert the DateTime to iso8601 format + } + } + + /** + * Return the value of this object as iso8601 dateTime value + * + * @return int As a Unix timestamp + */ + public function getValue() + { + return $this->value; + } +} diff --git a/library/Zend/XmlRpc/Value/Double.php b/library/Zend/XmlRpc/Value/Double.php new file mode 100755 index 000000000..722012000 --- /dev/null +++ b/library/Zend/XmlRpc/Value/Double.php @@ -0,0 +1,36 @@ +type = self::XMLRPC_TYPE_DOUBLE; + $precision = (int) ini_get('precision'); + $formatString = '%1.' . $precision . 'F'; + $this->value = rtrim(sprintf($formatString, (float) $value), '0'); + } + + /** + * Return the value of this object, convert the XML-RPC native double value into a PHP float + * + * @return float + */ + public function getValue() + { + return (float) $this->value; + } +} diff --git a/library/Zend/XmlRpc/Value/Integer.php b/library/Zend/XmlRpc/Value/Integer.php new file mode 100755 index 000000000..40d938664 --- /dev/null +++ b/library/Zend/XmlRpc/Value/Integer.php @@ -0,0 +1,41 @@ + PHP_INT_MAX) { + throw new Exception\ValueException('Overlong integer given'); + } + + $this->type = self::XMLRPC_TYPE_INTEGER; + $this->value = (int) $value; // Make sure this value is integer + } + + /** + * Return the value of this object, convert the XML-RPC native integer value into a PHP integer + * + * @return int + */ + public function getValue() + { + return $this->value; + } +} diff --git a/library/Zend/XmlRpc/Value/Nil.php b/library/Zend/XmlRpc/Value/Nil.php new file mode 100755 index 000000000..49f3c7511 --- /dev/null +++ b/library/Zend/XmlRpc/Value/Nil.php @@ -0,0 +1,33 @@ +type = self::XMLRPC_TYPE_NIL; + $this->value = null; + } + + /** + * Return the value of this object, convert the XML-RPC native nill value into a PHP NULL + * + * @return null + */ + public function getValue() + { + return null; + } +} diff --git a/library/Zend/XmlRpc/Value/String.php b/library/Zend/XmlRpc/Value/String.php new file mode 100755 index 000000000..66fa441b0 --- /dev/null +++ b/library/Zend/XmlRpc/Value/String.php @@ -0,0 +1,36 @@ +type = self::XMLRPC_TYPE_STRING; + + // Make sure this value is string and all XML characters are encoded + $this->value = (string) $value; + } + + /** + * Return the value of this object, convert the XML-RPC native string value into a PHP string + * + * @return string + */ + public function getValue() + { + return (string) $this->value; + } +} diff --git a/library/Zend/XmlRpc/Value/Struct.php b/library/Zend/XmlRpc/Value/Struct.php new file mode 100755 index 000000000..99d55bb84 --- /dev/null +++ b/library/Zend/XmlRpc/Value/Struct.php @@ -0,0 +1,49 @@ +type = self::XMLRPC_TYPE_STRUCT; + parent::__construct($value); + } + + + /** + * Generate the XML code that represent struct native MXL-RPC value + * + * @return void + */ + protected function _generateXML() + { + $generator = $this->getGenerator(); + $generator->openElement('value') + ->openElement('struct'); + + if (is_array($this->value)) { + foreach ($this->value as $name => $val) { + $generator->openElement('member') + ->openElement('name', $name) + ->closeElement('name'); + $val->generateXml(); + $generator->closeElement('member'); + } + } + $generator->closeElement('struct') + ->closeElement('value'); + } +} diff --git a/library/Zend/XmlRpc/composer.json b/library/Zend/XmlRpc/composer.json new file mode 100755 index 000000000..1520a41e4 --- /dev/null +++ b/library/Zend/XmlRpc/composer.json @@ -0,0 +1,30 @@ +{ + "name": "zendframework/zend-xmlrpc", + "description": " ", + "license": "BSD-3-Clause", + "keywords": [ + "zf2", + "xmlrpc" + ], + "homepage": "https://github.com/zendframework/zf2", + "autoload": { + "psr-0": { + "Zend\\XmlRpc\\": "" + } + }, + "target-dir": "Zend/XmlRpc", + "require": { + "php": ">=5.3.23", + "zendframework/zend-http": "self.version", + "zendframework/zend-math": "self.version", + "zendframework/zend-server": "self.version", + "zendframework/zend-stdlib": "self.version", + "zendframework/zendxml": "1.*" + }, + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + } +} diff --git a/upload/library/ajax/avatar.php b/library/ajax/avatar.php similarity index 100% rename from upload/library/ajax/avatar.php rename to library/ajax/avatar.php diff --git a/upload/library/ajax/change_tor_status.php b/library/ajax/change_tor_status.php similarity index 100% rename from upload/library/ajax/change_tor_status.php rename to library/ajax/change_tor_status.php diff --git a/upload/library/ajax/change_torrent.php b/library/ajax/change_torrent.php similarity index 100% rename from upload/library/ajax/change_torrent.php rename to library/ajax/change_torrent.php diff --git a/upload/library/ajax/change_user_opt.php b/library/ajax/change_user_opt.php similarity index 100% rename from upload/library/ajax/change_user_opt.php rename to library/ajax/change_user_opt.php diff --git a/upload/library/ajax/change_user_rank.php b/library/ajax/change_user_rank.php similarity index 100% rename from upload/library/ajax/change_user_rank.php rename to library/ajax/change_user_rank.php diff --git a/upload/library/ajax/edit_group_profile.php b/library/ajax/edit_group_profile.php similarity index 100% rename from upload/library/ajax/edit_group_profile.php rename to library/ajax/edit_group_profile.php diff --git a/upload/library/ajax/edit_user_profile.php b/library/ajax/edit_user_profile.php similarity index 100% rename from upload/library/ajax/edit_user_profile.php rename to library/ajax/edit_user_profile.php diff --git a/upload/library/ajax/gen_passkey.php b/library/ajax/gen_passkey.php similarity index 100% rename from upload/library/ajax/gen_passkey.php rename to library/ajax/gen_passkey.php diff --git a/upload/library/ajax/group_membership.php b/library/ajax/group_membership.php similarity index 100% rename from upload/library/ajax/group_membership.php rename to library/ajax/group_membership.php diff --git a/upload/library/ajax/index_data.php b/library/ajax/index_data.php similarity index 100% rename from upload/library/ajax/index_data.php rename to library/ajax/index_data.php diff --git a/upload/library/ajax/manage_admin.php b/library/ajax/manage_admin.php similarity index 100% rename from upload/library/ajax/manage_admin.php rename to library/ajax/manage_admin.php diff --git a/upload/library/ajax/manage_user.php b/library/ajax/manage_user.php similarity index 100% rename from upload/library/ajax/manage_user.php rename to library/ajax/manage_user.php diff --git a/upload/library/ajax/mod_action.php b/library/ajax/mod_action.php similarity index 100% rename from upload/library/ajax/mod_action.php rename to library/ajax/mod_action.php diff --git a/upload/library/ajax/post_mod_comment.php b/library/ajax/post_mod_comment.php similarity index 100% rename from upload/library/ajax/post_mod_comment.php rename to library/ajax/post_mod_comment.php diff --git a/upload/library/ajax/posts.php b/library/ajax/posts.php similarity index 100% rename from upload/library/ajax/posts.php rename to library/ajax/posts.php diff --git a/upload/library/ajax/sitemap.php b/library/ajax/sitemap.php similarity index 100% rename from upload/library/ajax/sitemap.php rename to library/ajax/sitemap.php diff --git a/upload/library/ajax/topic_tpl.php b/library/ajax/topic_tpl.php similarity index 100% rename from upload/library/ajax/topic_tpl.php rename to library/ajax/topic_tpl.php diff --git a/upload/library/ajax/user_register.php b/library/ajax/user_register.php similarity index 100% rename from upload/library/ajax/user_register.php rename to library/ajax/user_register.php diff --git a/upload/library/ajax/view_post.php b/library/ajax/view_post.php similarity index 100% rename from upload/library/ajax/view_post.php rename to library/ajax/view_post.php diff --git a/upload/library/ajax/view_torrent.php b/library/ajax/view_torrent.php similarity index 100% rename from upload/library/ajax/view_torrent.php rename to library/ajax/view_torrent.php diff --git a/upload/library/attach_mod/includes/.htaccess b/library/attach_mod/.htaccess similarity index 100% rename from upload/library/attach_mod/includes/.htaccess rename to library/attach_mod/.htaccess diff --git a/upload/library/attach_mod/attachment_mod.php b/library/attach_mod/attachment_mod.php similarity index 100% rename from upload/library/attach_mod/attachment_mod.php rename to library/attach_mod/attachment_mod.php diff --git a/upload/library/attach_mod/displaying.php b/library/attach_mod/displaying.php similarity index 100% rename from upload/library/attach_mod/displaying.php rename to library/attach_mod/displaying.php diff --git a/upload/library/attach_mod/displaying_torrent.php b/library/attach_mod/displaying_torrent.php similarity index 100% rename from upload/library/attach_mod/displaying_torrent.php rename to library/attach_mod/displaying_torrent.php diff --git a/upload/library/includes/.htaccess b/library/attach_mod/includes/.htaccess similarity index 100% rename from upload/library/includes/.htaccess rename to library/attach_mod/includes/.htaccess diff --git a/upload/library/attach_mod/includes/functions_admin.php b/library/attach_mod/includes/functions_admin.php similarity index 100% rename from upload/library/attach_mod/includes/functions_admin.php rename to library/attach_mod/includes/functions_admin.php diff --git a/upload/library/attach_mod/includes/functions_attach.php b/library/attach_mod/includes/functions_attach.php similarity index 100% rename from upload/library/attach_mod/includes/functions_attach.php rename to library/attach_mod/includes/functions_attach.php diff --git a/upload/library/attach_mod/includes/functions_delete.php b/library/attach_mod/includes/functions_delete.php similarity index 100% rename from upload/library/attach_mod/includes/functions_delete.php rename to library/attach_mod/includes/functions_delete.php diff --git a/upload/library/attach_mod/includes/functions_filetypes.php b/library/attach_mod/includes/functions_filetypes.php similarity index 100% rename from upload/library/attach_mod/includes/functions_filetypes.php rename to library/attach_mod/includes/functions_filetypes.php diff --git a/upload/library/attach_mod/includes/functions_includes.php b/library/attach_mod/includes/functions_includes.php similarity index 100% rename from upload/library/attach_mod/includes/functions_includes.php rename to library/attach_mod/includes/functions_includes.php diff --git a/upload/library/attach_mod/includes/functions_selects.php b/library/attach_mod/includes/functions_selects.php similarity index 100% rename from upload/library/attach_mod/includes/functions_selects.php rename to library/attach_mod/includes/functions_selects.php diff --git a/upload/library/attach_mod/includes/functions_thumbs.php b/library/attach_mod/includes/functions_thumbs.php similarity index 100% rename from upload/library/attach_mod/includes/functions_thumbs.php rename to library/attach_mod/includes/functions_thumbs.php diff --git a/upload/library/attach_mod/posting_attachments.php b/library/attach_mod/posting_attachments.php similarity index 99% rename from upload/library/attach_mod/posting_attachments.php rename to library/attach_mod/posting_attachments.php index df5091e0d..368706b3a 100644 --- a/upload/library/attach_mod/posting_attachments.php +++ b/library/attach_mod/posting_attachments.php @@ -972,7 +972,7 @@ class attach_parent $this->attach_filename = str_replace(array(',', '.', '!', '?', 'ь', 'Ь', 'ц', 'Ц', 'д', 'Д', ';', ':', '@', "'", '"', '&'), array('', '', '', '', 'ue', 'ue', 'oe', 'oe', 'ae', 'ae', '', '', '', '', '', 'and'), $this->attach_filename); $this->attach_filename = str_replace(array('$', 'Я', '>','<','§','%','=','/','(',')','#','*','+',"\\",'{','}','[',']'), array('dollar', 'ss','greater','lower','paragraph','percent','equal','','','','','','','','','','',''), $this->attach_filename); // Remove non-latin characters - $this->attach_filename = preg_replace("/([\xC2\xC3])([\x80-\xBF])/e", "chr(ord('\\1')<<6&0xC0|ord('\\2')&0x3F)", $this->attach_filename); + $this->attach_filename = preg_replace('#([\xC2\xC3])([\x80-\xBF])#', 'chr(ord(\'$1\')<<6&0xC0|ord(\'$2\')&0x3F)', $this->attach_filename); $this->attach_filename = rawurlencode($this->attach_filename); $this->attach_filename = preg_replace("/(%[0-9A-F]{1,2})/i", '', $this->attach_filename); $this->attach_filename = trim($this->attach_filename); diff --git a/upload/library/config.php b/library/config.php similarity index 92% rename from upload/library/config.php rename to library/config.php index ff89baf9d..2e7d35932 100644 --- a/upload/library/config.php +++ b/library/config.php @@ -37,7 +37,6 @@ * Error reporting * Triggers * Date format - * Subforums * Forums * Topics @@ -48,13 +47,13 @@ * Actions log * Users * Groups - * Tidy * Ads + * Misc + * Extensions * Attachments * Avatars * Group avatars - * Misc * Captcha * Atom feed * Nofollow @@ -73,9 +72,10 @@ $domain_name = 'torrentpier.me'; // enter here your primary domain name of your $domain_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : $domain_name; // Version info -$bb_cfg['tp_version'] = '2.1.3'; -$bb_cfg['tp_release_date'] = '24-10-2014'; +$bb_cfg['tp_version'] = '2.1.4'; +$bb_cfg['tp_release_date'] = '26-11-2014'; $bb_cfg['tp_release_state'] = 'ALPHA'; +$bb_cfg['tp_zf_version'] = '2.3.3'; // Database $charset = 'utf8'; @@ -263,6 +263,7 @@ define('ATTACH_DIR', BB_PATH .'/library/attach_mod/' ); define('CFG_DIR', BB_PATH .'/library/config/' ); define('INC_DIR', BB_PATH .'/library/includes/' ); define('CLASS_DIR', BB_PATH .'/library/includes/classes/'); +define('CORE_DIR', BB_PATH .'/library/includes/core/' ); define('UCP_DIR', BB_PATH .'/library/includes/ucp/' ); define('LANG_ROOT_DIR', BB_PATH .'/library/language/' ); define('IMAGES_DIR', BB_PATH .'/styles/images/' ); @@ -456,23 +457,23 @@ $bb_cfg['sf_on_first_page_only'] = true; $bb_cfg['allowed_topics_per_page'] = array(50, 100, 150, 200, 250, 300); // Topics -$bb_cfg['show_quick_reply'] = true; -$bb_cfg['show_rank_text'] = false; -$bb_cfg['show_rank_image'] = true; -$bb_cfg['show_poster_joined'] = true; -$bb_cfg['show_poster_posts'] = true; -$bb_cfg['show_poster_from'] = true; -$bb_cfg['show_bot_nick'] = false; -$bb_cfg['text_buttons'] = false; // replace EDIT, QUOTE... images with text links -$bb_cfg['parse_ed2k_links'] = true; // make ed2k links clickable -$bb_cfg['post_date_format'] = 'd-M-Y H:i'; -$bb_cfg['ext_link_new_win'] = true; // open external links in new window +$bb_cfg['show_quick_reply'] = true; +$bb_cfg['show_rank_text'] = false; +$bb_cfg['show_rank_image'] = true; +$bb_cfg['show_poster_joined'] = true; +$bb_cfg['show_poster_posts'] = true; +$bb_cfg['show_poster_from'] = true; +$bb_cfg['show_bot_nick'] = false; +$bb_cfg['text_buttons'] = false; // replace EDIT, QUOTE... images with text links +$bb_cfg['parse_ed2k_links'] = true; // make ed2k links clickable +$bb_cfg['post_date_format'] = 'd-M-Y H:i'; +$bb_cfg['ext_link_new_win'] = true; // open external links in new window -$bb_cfg['topic_moved_days_keep'] = 7; // remove topic moved links after xx days (or FALSE to disable) +$bb_cfg['topic_moved_days_keep'] = 7; // remove topic moved links after xx days (or FALSE to disable) $bb_cfg['allowed_posts_per_page'] = array(15, 30, 50, 100); $bb_cfg['user_signature_start'] = '

    _________________
    '; -$bb_cfg['user_signature_end'] = '
    '; // Это позволит использовать html теги, которые требуют закрытия. Например или +$bb_cfg['user_signature_end'] = ''; // Это позволит использовать html теги, которые требуют закрытия. Например
    или // Posts $bb_cfg['use_posts_cache'] = true; // if you switch from ON to OFF, you need to TRUNCATE `bb_posts_html` table @@ -538,50 +539,6 @@ $bb_cfg['ad_blocks'] = array( ), ); -// Attachments -$bb_cfg['attach'] = array( - 'upload_path' => DATA_DIR . 'torrent_files', // without '/' - 'max_size' => 250*1024, // bytes -); - -$bb_cfg['file_id_ext'] = array( - 1 => 'gif', - 2 => 'gz', - 3 => 'jpg', - 4 => 'png', - 5 => 'rar', - 6 => 'tar', - 7 => 'tiff', - 8 => 'torrent', - 9 => 'zip', -); - -$bb_cfg['tor_forums_allowed_ext'] = array('torrent', 'zip', 'rar'); // для разделов с раздачами -$bb_cfg['gen_forums_allowed_ext'] = array('zip', 'rar'); // для обычных разделов - -// Avatars -$bb_cfg['avatars'] = array( - 'allowed_ext' => array('gif','jpg','jpeg','png'), // разрешенные форматы файлов - 'bot_avatar' => 'gallery/bot.gif', // аватара бота - 'max_size' => 100*1024, // размер аватары в байтах - 'max_height' => 100, // высота аватара в px - 'max_width' => 100, // ширина аватара в px - 'no_avatar' => 'gallery/noavatar.png', // дефолтная аватара - 'upload_path' => BB_ROOT . 'data/avatars/', // путь к директории с аватарами - 'up_allowed' => true, // разрешить загрузку аватар -); - -// Group avatars -$bb_cfg['group_avatars'] = array( - 'allowed_ext' => array('gif','jpg','jpeg','png'), // разрешенные форматы файлов - 'max_size' => 300*1024, // размер аватары в байтах - 'max_height' => 300, // высота аватара в px - 'max_width' => 300, // ширина аватара в px - 'no_avatar' => 'gallery/noavatar.png', // дефолтная аватара - 'upload_path' => BB_ROOT . 'data/avatars/', // путь к директории с аватарами - 'up_allowed' => true, // разрешить загрузку аватар -); - // Misc define('MEM_USAGE', function_exists('memory_get_usage')); @@ -618,24 +575,69 @@ $bb_cfg['user_agreement_url'] = 'info.php?show=user_agreement'; $bb_cfg['copyright_holders_url'] = 'info.php?show=copyright_holders'; $bb_cfg['advert_url'] = 'info.php?show=advert'; +// Extensions +$bb_cfg['file_id_ext'] = array( + 1 => 'gif', + 2 => 'gz', + 3 => 'jpg', + 4 => 'png', + 5 => 'rar', + 6 => 'tar', + 7 => 'tiff', + 8 => 'torrent', + 9 => 'zip', +); + +// Attachments +$bb_cfg['attach'] = array( + 'upload_path' => DATA_DIR . 'torrent_files', // путь к директории с torrent файлами + 'max_size' => 250*1024, // размер аватары в байтах +); + +$bb_cfg['tor_forums_allowed_ext'] = array('torrent', 'zip', 'rar'); // для разделов с раздачами +$bb_cfg['gen_forums_allowed_ext'] = array('zip', 'rar'); // для обычных разделов + +// Avatars +$bb_cfg['avatars'] = array( + 'allowed_ext' => array('gif','jpg','jpeg','png'), // разрешенные форматы файлов + 'bot_avatar' => 'gallery/bot.gif', // аватара бота + 'max_size' => 100*1024, // размер аватары в байтах + 'max_height' => 100, // высота аватара в px + 'max_width' => 100, // ширина аватара в px + 'no_avatar' => 'gallery/noavatar.png', // дефолтная аватара + 'upload_path' => BB_ROOT . 'data/avatars/', // путь к директории с аватарами + 'up_allowed' => true, // разрешить загрузку аватар +); + +// Group avatars +$bb_cfg['group_avatars'] = array( + 'allowed_ext' => array('gif','jpg','jpeg','png'), // разрешенные форматы файлов + 'max_size' => 300*1024, // размер аватары в байтах + 'max_height' => 300, // высота аватара в px + 'max_width' => 300, // ширина аватара в px + 'no_avatar' => 'gallery/noavatar.png', // дефолтная аватара + 'upload_path' => BB_ROOT . 'data/avatars/', // путь к директории с аватарами + 'up_allowed' => true, // разрешить загрузку аватар +); + // Captcha $bb_cfg['captcha'] = array( 'disabled' => false, 'secret_key' => 'secret_key', - 'img_path' => INT_DATA_DIR .'captcha/', # with '/' - 'img_url' => './internal_data/captcha/', # with '/' + 'img_path' => INT_DATA_DIR .'captcha/', // with ending slash + 'img_url' => './internal_data/captcha/', // with ending slash ); // Atom feed $bb_cfg['atom'] = array( - 'path' => INT_DATA_DIR .'atom', # without '/' - 'url' => './internal_data/atom', # without '/' + 'path' => INT_DATA_DIR .'atom', // without ending slash + 'url' => './internal_data/atom', // without ending slash ); // Nofollow $bb_cfg['nofollow'] = array( 'disabled' => false, - 'allowed_url' => array($domain_name), // 'allowed.site', 'www.allowed.site' + 'allowed_url' => array($domain_name), // 'allowed.site', 'www.allowed.site' ); // Local config diff --git a/upload/library/includes/captcha/.htaccess b/library/includes/.htaccess similarity index 100% rename from upload/library/includes/captcha/.htaccess rename to library/includes/.htaccess diff --git a/upload/library/includes/api/sphinx.php b/library/includes/api/sphinx.php similarity index 99% rename from upload/library/includes/api/sphinx.php rename to library/includes/api/sphinx.php index a3afb1614..68b29869e 100644 --- a/upload/library/includes/api/sphinx.php +++ b/library/includes/api/sphinx.php @@ -1,5 +1,7 @@ is_installed()) + { + die('Error: APC extension not installed'); + } + $this->dbg_enabled = sql_dbg_enabled(); + $this->prefix = $prefix; + } + + function get ($name, $get_miss_key_callback = '', $ttl = 0) + { + $this->cur_query = "cache->get('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return apc_fetch($this->prefix . $name); + } + + function set ($name, $value, $ttl = 0) + { + $this->cur_query = "cache->set('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return apc_store($this->prefix . $name, $value, $ttl); + } + + function rm ($name = '') + { + if ($name) + { + $this->cur_query = "cache->rm('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return apc_delete($this->prefix . $name); + } + else + { + return apc_clear_cache(); + } + } + + function is_installed () + { + return function_exists('apc_fetch'); + } +} \ No newline at end of file diff --git a/library/includes/cache/common.php b/library/includes/cache/common.php new file mode 100644 index 000000000..79a0ff525 --- /dev/null +++ b/library/includes/cache/common.php @@ -0,0 +1,84 @@ +dbg_enabled) return; + + $id =& $this->dbg_id; + $dbg =& $this->dbg[$id]; + + if ($mode == 'start') + { + $this->sql_starttime = utime(); + + $dbg['sql'] = isset($cur_query) ? short_query($cur_query) : short_query($this->cur_query); + $dbg['src'] = $this->debug_find_source(); + $dbg['file'] = $this->debug_find_source('file'); + $dbg['line'] = $this->debug_find_source('line'); + $dbg['time'] = ''; + } + else if ($mode == 'stop') + { + $this->cur_query_time = utime() - $this->sql_starttime; + $this->sql_timetotal += $this->cur_query_time; + $dbg['time'] = $this->cur_query_time; + $id++; + } + } + + function debug_find_source ($mode = '') + { + foreach (debug_backtrace() as $trace) + { + if ($trace['file'] !== __FILE__) + { + switch ($mode) + { + case 'file': return $trace['file']; + case 'line': return $trace['line']; + default: return hide_bb_path($trace['file']) .'('. $trace['line'] .')'; + } + } + } + return 'src not found'; + } +} \ No newline at end of file diff --git a/library/includes/cache/file.php b/library/includes/cache/file.php new file mode 100644 index 000000000..8538d22c6 --- /dev/null +++ b/library/includes/cache/file.php @@ -0,0 +1,136 @@ +dir = $dir; + $this->prefix = $prefix; + $this->dbg_enabled = sql_dbg_enabled(); + } + + function get ($name, $get_miss_key_callback = '', $ttl = 0) + { + $filename = $this->dir . clean_filename($this->prefix . $name) . '.php'; + + $this->cur_query = "cache->set('$name')"; + $this->debug('start'); + + if (file_exists($filename)) + { + require($filename); + } + + $this->debug('stop'); + $this->cur_query = null; + + return (!empty($filecache['value'])) ? $filecache['value'] : false; + } + + function set ($name, $value, $ttl = 86400) + { + if (!function_exists('var_export')) + { + return false; + } + + $this->cur_query = "cache->set('$name')"; + $this->debug('start'); + + $filename = $this->dir . clean_filename($this->prefix . $name) . '.php'; + $expire = TIMENOW + $ttl; + $cache_data = array( + 'expire' => $expire, + 'value' => $value, + ); + + $filecache = "'; + + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return (bool) file_write($filecache, $filename, false, true, true); + } + + function rm ($name = '') + { + $clear = false; + if ($name) + { + $this->cur_query = "cache->rm('$name')"; + $this->debug('start'); + + $filename = $this->dir . clean_filename($this->prefix . $name) . '.php'; + if (file_exists($filename)) + { + $clear = (bool) unlink($filename); + } + + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + } + else + { + if (is_dir($this->dir)) + { + if ($dh = opendir($this->dir)) + { + while (($file = readdir($dh)) !== false) + { + if ($file != "." && $file != "..") + { + $filename = $this->dir . $file; + + unlink($filename); + $clear = true; + } + } + closedir($dh); + } + } + } + return $clear; + } + + function gc ($expire_time = TIMENOW) + { + $clear = false; + + if (is_dir($this->dir)) + { + if ($dh = opendir($this->dir)) + { + while (($file = readdir($dh)) !== false) + { + if ($file != "." && $file != "..") + { + $filename = $this->dir . $file; + + require($filename); + + if(!empty($filecache['expire']) && ($filecache['expire'] < $expire_time)) + { + unlink($filename); + $clear = true; + } + } + } + closedir($dh); + } + } + + return $clear; + } +} \ No newline at end of file diff --git a/library/includes/cache/memcache.php b/library/includes/cache/memcache.php new file mode 100644 index 000000000..0c76fecf6 --- /dev/null +++ b/library/includes/cache/memcache.php @@ -0,0 +1,100 @@ +is_installed()) + { + die('Error: Memcached extension not installed'); + } + + $this->cfg = $cfg; + $this->prefix = $prefix; + $this->memcache = new Memcache; + $this->dbg_enabled = sql_dbg_enabled(); + } + + function connect () + { + $connect_type = ($this->cfg['pconnect']) ? 'pconnect' : 'connect'; + + $this->cur_query = $connect_type .' '. $this->cfg['host'] .':'. $this->cfg['port']; + $this->debug('start'); + + if (@$this->memcache->$connect_type($this->cfg['host'], $this->cfg['port'])) + { + $this->connected = true; + } + + if (DBG_LOG) dbg_log(' ', 'CACHE-connect'. ($this->connected ? '' : '-FAIL')); + + if (!$this->connected && $this->cfg['con_required']) + { + die('Could not connect to memcached server'); + } + + $this->debug('stop'); + $this->cur_query = null; + } + + function get ($name, $get_miss_key_callback = '', $ttl = 0) + { + if (!$this->connected) $this->connect(); + + $this->cur_query = "cache->get('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return ($this->connected) ? $this->memcache->get($this->prefix . $name) : false; + } + + function set ($name, $value, $ttl = 0) + { + if (!$this->connected) $this->connect(); + + $this->cur_query = "cache->set('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return ($this->connected) ? $this->memcache->set($this->prefix . $name, $value, false, $ttl) : false; + } + + function rm ($name = '') + { + if (!$this->connected) $this->connect(); + + if ($name) + { + $this->cur_query = "cache->rm('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return ($this->connected) ? $this->memcache->delete($this->prefix . $name, 0) : false; + } + else + { + return ($this->connected) ? $this->memcache->flush() : false; + } + } + + function is_installed () + { + return class_exists('Memcache'); + } +} \ No newline at end of file diff --git a/library/includes/cache/redis.php b/library/includes/cache/redis.php new file mode 100644 index 000000000..431dade3a --- /dev/null +++ b/library/includes/cache/redis.php @@ -0,0 +1,109 @@ +is_installed()) + { + die('Error: Redis extension not installed'); + } + + $this->cfg = $cfg; + $this->prefix = $prefix; + $this->redis = new Redis(); + $this->dbg_enabled = sql_dbg_enabled(); + } + + function connect () + { + $this->cur_query = 'connect '. $this->cfg['host'] .':'. $this->cfg['port']; + $this->debug('start'); + + if (@$this->redis->connect($this->cfg['host'], $this->cfg['port'])) + { + $this->connected = true; + } + + if (!$this->connected && $this->cfg['con_required']) + { + die('Could not connect to redis server'); + } + + $this->debug('stop'); + $this->cur_query = null; + } + + function get ($name, $get_miss_key_callback = '', $ttl = 0) + { + if (!$this->connected) $this->connect(); + + $this->cur_query = "cache->get('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return ($this->connected) ? unserialize($this->redis->get($this->prefix . $name)) : false; + } + + function set ($name, $value, $ttl = 0) + { + if (!$this->connected) $this->connect(); + + $this->cur_query = "cache->set('$name')"; + $this->debug('start'); + + if ($this->redis->set($this->prefix . $name, serialize($value))) + { + if ($ttl > 0) + { + $this->redis->expire($this->prefix . $name, $ttl); + } + + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return true; + } + else + { + return false; + } + } + + function rm ($name = '') + { + if (!$this->connected) $this->connect(); + + if ($name) + { + $this->cur_query = "cache->rm('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return ($this->connected) ? $this->redis->del($this->prefix . $name) : false; + } + else + { + return ($this->connected) ? $this->redis->flushdb() : false; + } + } + + function is_installed () + { + return class_exists('Redis'); + } +} \ No newline at end of file diff --git a/library/includes/cache/sqlite.php b/library/includes/cache/sqlite.php new file mode 100644 index 000000000..cc9004bcf --- /dev/null +++ b/library/includes/cache/sqlite.php @@ -0,0 +1,288 @@ + '/path/to/cache.db.sqlite', + 'table_name' => 'cache', + 'table_schema' => 'CREATE TABLE cache ( + cache_name VARCHAR(255), + cache_expire_time INT, + cache_value TEXT, + PRIMARY KEY (cache_name) + )', + 'pconnect' => true, + 'con_required' => true, + 'log_name' => 'CACHE', + ); + + function cache_sqlite ($cfg, $prefix = null) + { + $this->cfg = array_merge($this->cfg, $cfg); + $this->db = new sqlite_common($this->cfg); + $this->prefix = $prefix; + } + + function get ($name, $get_miss_key_callback = '', $ttl = 604800) + { + if (empty($name)) + { + return is_array($name) ? array() : false; + } + $this->db->shard($name); + $cached_items = array(); + $this->prefix_len = strlen($this->prefix); + $this->prefix_sql = SQLite3::escapeString($this->prefix); + + $name_ary = $name_sql = (array) $name; + array_deep($name_sql, 'SQLite3::escapeString'); + + // get available items + $rowset = $this->db->fetch_rowset(" + SELECT cache_name, cache_value + FROM ". $this->cfg['table_name'] ." + WHERE cache_name IN('$this->prefix_sql". join("','$this->prefix_sql", $name_sql) ."') AND cache_expire_time > ". TIMENOW ." + LIMIT ". count($name) ." + "); + + $this->db->debug('start', 'unserialize()'); + foreach ($rowset as $row) + { + $cached_items[substr($row['cache_name'], $this->prefix_len)] = unserialize($row['cache_value']); + } + $this->db->debug('stop'); + + // get miss items + if ($get_miss_key_callback AND $miss_key = array_diff($name_ary, array_keys($cached_items))) + { + foreach ($get_miss_key_callback($miss_key) as $k => $v) + { + $this->set($this->prefix . $k, $v, $ttl); + $cached_items[$k] = $v; + } + } + // return + if (is_array($this->prefix . $name)) + { + return $cached_items; + } + else + { + return isset($cached_items[$name]) ? $cached_items[$name] : false; + } + } + + function set ($name, $value, $ttl = 604800) + { + $this->db->shard($this->prefix . $name); + $name_sql = SQLite3::escapeString($this->prefix . $name); + $expire = TIMENOW + $ttl; + $value_sql = SQLite3::escapeString(serialize($value)); + + $result = $this->db->query("REPLACE INTO ". $this->cfg['table_name'] ." (cache_name, cache_expire_time, cache_value) VALUES ('$name_sql', $expire, '$value_sql')"); + return (bool) $result; + } + + function rm ($name = '') + { + if ($name) + { + $this->db->shard($this->prefix . $name); + $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_name = '". SQLite3::escapeString($this->prefix . $name) ."'"); + } + else + { + $result = $this->db->query("DELETE FROM ". $this->cfg['table_name']); + } + return (bool) $result; + } + + function gc ($expire_time = TIMENOW) + { + $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_expire_time < $expire_time"); + return ($result) ? $this->db->changes() : 0; + } +} + +class sqlite_common extends cache_common +{ + var $cfg = array( + 'db_file_path' => 'sqlite.db', + 'table_name' => 'table_name', + 'table_schema' => 'CREATE TABLE table_name (...)', + 'pconnect' => true, + 'con_required' => true, + 'log_name' => 'SQLite', + 'shard_type' => 'none', # none, string, int (тип перевичного ключа для шардинга) + 'shard_val' => 0, # для string - кол. начальных символов, для int - делитель (будет использован остаток от деления) + ); + var $engine = 'SQLite'; + var $dbh = null; + var $connected = false; + var $shard_val = false; + + var $table_create_attempts = 0; + + function sqlite_common ($cfg) + { + $this->cfg = array_merge($this->cfg, $cfg); + $this->dbg_enabled = sql_dbg_enabled(); + } + + function connect () + { + $this->cur_query = ($this->dbg_enabled) ? 'connect to: '. $this->cfg['db_file_path'] : 'connect'; + $this->debug('start'); + + if (@$this->dbh = new SQLite3($this->cfg['db_file_path'])) + { + $this->connected = true; + } + + if (DBG_LOG) dbg_log(' ', $this->cfg['log_name'] .'-connect'. ($this->connected ? '' : '-FAIL')); + + if (!$this->connected && $this->cfg['con_required']) + { + trigger_error('SQLite not connected', E_USER_ERROR); + } + + $this->debug('stop'); + $this->cur_query = null; + } + + function create_table () + { + $this->table_create_attempts++; + return $this->dbh->query($this->cfg['table_schema']); + } + + function shard ($name) + { + $type = $this->cfg['shard_type']; + + if ($type == 'none') return; + if (is_array($name)) trigger_error('cannot shard: $name is array', E_USER_ERROR); + + // define shard_val + if ($type == 'string') + { + $shard_val = substr($name, 0, $this->cfg['shard_val']); + } + else + { + $shard_val = $name % $this->cfg['shard_val']; + } + // все запросы должны быть к одному и тому же шарду + if ($this->shard_val !== false) + { + if ($shard_val != $this->shard_val) + { + trigger_error("shard cannot be reassigned. [{$this->shard_val}, $shard_val, $name]", E_USER_ERROR); + } + else + { + return; + } + } + $this->shard_val = $shard_val; + $this->cfg['db_file_path'] = str_replace('*', $shard_val, $this->cfg['db_file_path']); + } + + function query ($query) + { + if (!$this->connected) $this->connect(); + + $this->cur_query = $query; + $this->debug('start'); + + if (!$result = @$this->dbh->query($query)) + { + $rowsresult = $this->dbh->query("PRAGMA table_info({$this->cfg['table_name']})"); + $rowscount = 0; + while ($row = $rowsresult->fetchArray(SQLITE3_ASSOC)) + { + $rowscount++; + } + if (!$this->table_create_attempts && !$rowscount) + { + if ($this->create_table()) + { + $result = $this->dbh->query($query); + } + } + if (!$result) + { + $this->trigger_error($this->get_error_msg()); + } + } + + $this->debug('stop'); + $this->cur_query = null; + + $this->num_queries++; + + return $result; + } + + function fetch_row ($query) + { + $result = $this->query($query); + return is_resource($result) ? $result->fetchArray(SQLITE3_ASSOC) : false; + } + + function fetch_rowset ($query) + { + $result = $this->query($query); + $rowset = array(); + while ($row = $result->fetchArray(SQLITE3_ASSOC)) + { + $rowset[] = $row; + } + return $rowset; + } + + function changes () + { + return is_resource($this->dbh) ? $this->dbh->changes() : 0; + } + + function escape ($str) + { + return SQLite3::escapeString($str); + } + + function get_error_msg () + { + return 'SQLite error #'. ($err_code = $this->dbh->lastErrorCode()) .': '. $this->dbh->lastErrorMsg(); + } + + function rm ($name = '') + { + if ($name) + { + $this->db->shard($this->prefix . $name); + $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_name = '". SQLite3::escapeString($this->prefix . $name) ."'"); + } + else + { + $result = $this->db->query("DELETE FROM ". $this->cfg['table_name']); + } + return (bool) $result; + } + + function gc ($expire_time = TIMENOW) + { + $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_expire_time < $expire_time"); + return ($result) ? sqlite_changes($this->db->dbh) : 0; + } + + function trigger_error ($msg = 'DB Error') + { + if (error_reporting()) trigger_error($msg, E_USER_ERROR); + } +} \ No newline at end of file diff --git a/library/includes/cache/xcache.php b/library/includes/cache/xcache.php new file mode 100644 index 000000000..b1e391f72 --- /dev/null +++ b/library/includes/cache/xcache.php @@ -0,0 +1,67 @@ +is_installed()) + { + die('Error: XCache extension not installed'); + } + $this->dbg_enabled = sql_dbg_enabled(); + $this->prefix = $prefix; + } + + function get ($name, $get_miss_key_callback = '', $ttl = 0) + { + $this->cur_query = "cache->get('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return xcache_get($this->prefix . $name); + } + + function set ($name, $value, $ttl = 0) + { + $this->cur_query = "cache->set('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return xcache_set($this->prefix . $name, $value, $ttl); + } + + function rm ($name = '') + { + if ($name) + { + $this->cur_query = "cache->rm('$name')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return xcache_unset($this->prefix . $name); + } + else + { + xcache_clear_cache(XC_TYPE_PHP, 0); + xcache_clear_cache(XC_TYPE_VAR, 0); + return; + } + } + + function is_installed () + { + return function_exists('xcache_get'); + } +} \ No newline at end of file diff --git a/upload/library/includes/classes/.htaccess b/library/includes/captcha/.htaccess similarity index 100% rename from upload/library/includes/classes/.htaccess rename to library/includes/captcha/.htaccess diff --git a/upload/library/includes/captcha/captcha.php b/library/includes/captcha/captcha.php similarity index 99% rename from upload/library/includes/captcha/captcha.php rename to library/includes/captcha/captcha.php index 4ecfcdc34..4b8366ca2 100644 --- a/upload/library/includes/captcha/captcha.php +++ b/library/includes/captcha/captcha.php @@ -1,5 +1,7 @@ home = 'http://'.$bb_cfg['server_name'].'/'; + $this->home = make_url(); } function build_map () { @@ -27,10 +28,10 @@ class sitemap function build_index ($count) { $lm = date('c'); $map = "\n\n"; - $map .= "\n{$this->home}sitemap/sitemap1.xml\n{$lm}\n\n"; + $map .= "\n{$this->home}internal_data/sitemap/sitemap1.xml\n{$lm}\n\n"; for ($i = 0; $i < $count; $i++) { $t = $i + 2; - $map .= "\n{$this->home}sitemap/sitemap{$t}.xml\n{$lm}\n\n"; + $map .= "\n{$this->home}internal_data/sitemap/sitemap{$t}.xml\n{$lm}\n\n"; } $map .= ""; diff --git a/upload/library/includes/classes/utf8.php b/library/includes/classes/utf8.php similarity index 99% rename from upload/library/includes/classes/utf8.php rename to library/includes/classes/utf8.php index ad740d657..af5bfb2ae 100644 --- a/upload/library/includes/classes/utf8.php +++ b/library/includes/classes/utf8.php @@ -1,4 +1,7 @@ кеш_объект) + + function CACHES ($cfg) + { + $this->cfg = $cfg['cache']; + $this->obj['__stub'] = new cache_common(); + } + + function get_cache_obj ($cache_name) + { + if (!isset($this->ref[$cache_name])) + { + if (!$engine_cfg =& $this->cfg['engines'][$cache_name]) + { + $this->ref[$cache_name] =& $this->obj['__stub']; + } + else + { + $cache_type =& $engine_cfg[0]; + $cache_cfg =& $engine_cfg[1]; + + switch ($cache_type) + { + case 'memcache': + if (!isset($this->obj[$cache_name])) + { + $this->obj[$cache_name] = new cache_memcache($this->cfg['memcache'], $this->cfg['prefix']); + } + $this->ref[$cache_name] =& $this->obj[$cache_name]; + break; + + case 'sqlite': + if (!isset($this->obj[$cache_name])) + { + $cache_cfg['pconnect'] = $this->cfg['pconnect']; + $cache_cfg['db_file_path'] = $this->get_db_path($cache_name, $cache_cfg, '.sqlite.db'); + + $this->obj[$cache_name] = new cache_sqlite($cache_cfg, $this->cfg['prefix']); + } + $this->ref[$cache_name] =& $this->obj[$cache_name]; + break; + + case 'db_sqlite': + if (!isset($this->obj[$cache_name])) + { + $cache_cfg['pconnect'] = $this->cfg['pconnect']; + $cache_cfg['db_file_path'] = $this->get_db_path($cache_name, $cache_cfg, '.sqlite.db'); + $cache_cfg['table_name'] = $cache_name; + $cache_cfg['table_schema'] = $this->get_table_schema($cache_cfg); + + $this->obj[$cache_name] = new sqlite_common($cache_cfg); + } + $this->ref[$cache_name] =& $this->obj[$cache_name]; + break; + + case 'redis': + if (!isset($this->obj[$cache_name])) + { + $this->obj[$cache_name] = new cache_redis($this->cfg['redis'], $this->cfg['prefix']); + } + $this->ref[$cache_name] =& $this->obj[$cache_name]; + break; + + case 'apc': + if (!isset($this->obj[$cache_name])) + { + $this->obj[$cache_name] = new cache_apc($this->cfg['prefix']); + } + $this->ref[$cache_name] =& $this->obj[$cache_name]; + break; + + case 'xcache': + if (!isset($this->obj[$cache_name])) + { + $this->obj[$cache_name] = new cache_xcache($this->cfg['prefix']); + } + $this->ref[$cache_name] =& $this->obj[$cache_name]; + break; + + default: //filecache + if (!isset($this->obj[$cache_name])) + { + $this->obj[$cache_name] = new cache_file($this->cfg['db_dir'] . $cache_name .'/', $this->cfg['prefix']); + } + $this->ref[$cache_name] =& $this->obj[$cache_name]; + break; + } + } + } + + return $this->ref[$cache_name]; + } + + function get_db_path ($name, $cfg, $ext) + { + if (!empty($cfg['shard_type']) && $cfg['shard_type'] != 'none') + { + return $this->cfg['db_dir'] . $name .'_*'. $ext; + } + else + { + return $this->cfg['db_dir'] . $name . $ext; + } + } + + function get_table_schema ($cfg) + { + return "CREATE TABLE {$cfg['table_name']} ( {$cfg['columns']} )"; + } +} \ No newline at end of file diff --git a/library/includes/core/dbs.php b/library/includes/core/dbs.php new file mode 100644 index 000000000..a3e9ef913 --- /dev/null +++ b/library/includes/core/dbs.php @@ -0,0 +1,58 @@ + $srv_cfg + var $srv = array(); // $srv_name => $db_obj + var $alias = array(); // $srv_alias => $srv_name + + var $log_file = 'sql_queries'; + var $log_counter = 0; + var $num_queries = 0; + var $sql_inittime = 0; + var $sql_timetotal = 0; + + function DBS ($cfg) + { + $this->cfg = $cfg['db']; + $this->alias = $cfg['db_alias']; + + foreach ($this->cfg as $srv_name => $srv_cfg) + { + $this->srv[$srv_name] = null; + } + } + + // получение/инициализация класса для сервера $srv_name + function get_db_obj ($srv_name_or_alias = 'db1') + { + $srv_name = $this->get_srv_name($srv_name_or_alias); + + if (!is_object($this->srv[$srv_name])) + { + $this->srv[$srv_name] = new sql_db($this->cfg[$srv_name]); + $this->srv[$srv_name]->db_server = $srv_name; + } + return $this->srv[$srv_name]; + } + + // определение имени сервера + function get_srv_name ($name) + { + if (isset($this->alias[$name])) + { + $srv_name = $this->alias[$name]; + } + else if (isset($this->cfg[$name])) + { + $srv_name = $name; + } + else + { + $srv_name = 'db1'; + } + return $srv_name; + } +} \ No newline at end of file diff --git a/upload/library/includes/core/mysql.php b/library/includes/core/mysql.php similarity index 100% rename from upload/library/includes/core/mysql.php rename to library/includes/core/mysql.php diff --git a/upload/library/includes/cron/jobs/.htaccess b/library/includes/cron/.htaccess similarity index 100% rename from upload/library/includes/cron/jobs/.htaccess rename to library/includes/cron/.htaccess diff --git a/upload/library/includes/cron/cron_check.php b/library/includes/cron/cron_check.php similarity index 100% rename from upload/library/includes/cron/cron_check.php rename to library/includes/cron/cron_check.php diff --git a/upload/library/includes/cron/cron_init.php b/library/includes/cron/cron_init.php similarity index 100% rename from upload/library/includes/cron/cron_init.php rename to library/includes/cron/cron_init.php diff --git a/upload/library/includes/cron/cron_run.php b/library/includes/cron/cron_run.php similarity index 100% rename from upload/library/includes/cron/cron_run.php rename to library/includes/cron/cron_run.php diff --git a/upload/library/includes/datastore/.htaccess b/library/includes/cron/jobs/.htaccess similarity index 100% rename from upload/library/includes/datastore/.htaccess rename to library/includes/cron/jobs/.htaccess diff --git a/upload/library/includes/cron/jobs/attach_maintenance.php b/library/includes/cron/jobs/attach_maintenance.php similarity index 100% rename from upload/library/includes/cron/jobs/attach_maintenance.php rename to library/includes/cron/jobs/attach_maintenance.php diff --git a/upload/library/includes/cron/jobs/board_maintenance.php b/library/includes/cron/jobs/board_maintenance.php similarity index 100% rename from upload/library/includes/cron/jobs/board_maintenance.php rename to library/includes/cron/jobs/board_maintenance.php diff --git a/upload/library/includes/cron/jobs/cache_gc.php b/library/includes/cron/jobs/cache_gc.php similarity index 100% rename from upload/library/includes/cron/jobs/cache_gc.php rename to library/includes/cron/jobs/cache_gc.php diff --git a/upload/library/includes/cron/jobs/captcha_gen_gc.php b/library/includes/cron/jobs/captcha_gen_gc.php similarity index 100% rename from upload/library/includes/cron/jobs/captcha_gen_gc.php rename to library/includes/cron/jobs/captcha_gen_gc.php diff --git a/upload/library/includes/cron/jobs/clean_dlstat.php b/library/includes/cron/jobs/clean_dlstat.php similarity index 100% rename from upload/library/includes/cron/jobs/clean_dlstat.php rename to library/includes/cron/jobs/clean_dlstat.php diff --git a/upload/library/includes/cron/jobs/clean_log.php b/library/includes/cron/jobs/clean_log.php similarity index 100% rename from upload/library/includes/cron/jobs/clean_log.php rename to library/includes/cron/jobs/clean_log.php diff --git a/upload/library/includes/cron/jobs/clean_search_results.php b/library/includes/cron/jobs/clean_search_results.php similarity index 100% rename from upload/library/includes/cron/jobs/clean_search_results.php rename to library/includes/cron/jobs/clean_search_results.php diff --git a/upload/library/includes/cron/jobs/ds_update_cat_forums.php b/library/includes/cron/jobs/ds_update_cat_forums.php similarity index 100% rename from upload/library/includes/cron/jobs/ds_update_cat_forums.php rename to library/includes/cron/jobs/ds_update_cat_forums.php diff --git a/upload/library/includes/cron/jobs/ds_update_stats.php b/library/includes/cron/jobs/ds_update_stats.php similarity index 100% rename from upload/library/includes/cron/jobs/ds_update_stats.php rename to library/includes/cron/jobs/ds_update_stats.php diff --git a/upload/library/includes/cron/jobs/flash_topic_view.php b/library/includes/cron/jobs/flash_topic_view.php similarity index 100% rename from upload/library/includes/cron/jobs/flash_topic_view.php rename to library/includes/cron/jobs/flash_topic_view.php diff --git a/upload/library/includes/cron/jobs/prune_forums.php b/library/includes/cron/jobs/prune_forums.php similarity index 100% rename from upload/library/includes/cron/jobs/prune_forums.php rename to library/includes/cron/jobs/prune_forums.php diff --git a/upload/library/includes/cron/jobs/prune_inactive_users.php b/library/includes/cron/jobs/prune_inactive_users.php similarity index 100% rename from upload/library/includes/cron/jobs/prune_inactive_users.php rename to library/includes/cron/jobs/prune_inactive_users.php diff --git a/upload/library/includes/cron/jobs/prune_topic_moved.php b/library/includes/cron/jobs/prune_topic_moved.php similarity index 100% rename from upload/library/includes/cron/jobs/prune_topic_moved.php rename to library/includes/cron/jobs/prune_topic_moved.php diff --git a/upload/library/includes/cron/jobs/sessions_cleanup.php b/library/includes/cron/jobs/sessions_cleanup.php similarity index 100% rename from upload/library/includes/cron/jobs/sessions_cleanup.php rename to library/includes/cron/jobs/sessions_cleanup.php diff --git a/upload/library/includes/cron/jobs/sitemap.php b/library/includes/cron/jobs/sitemap.php similarity index 100% rename from upload/library/includes/cron/jobs/sitemap.php rename to library/includes/cron/jobs/sitemap.php diff --git a/upload/library/includes/cron/jobs/tr_cleanup_and_dlstat.php b/library/includes/cron/jobs/tr_cleanup_and_dlstat.php similarity index 87% rename from upload/library/includes/cron/jobs/tr_cleanup_and_dlstat.php rename to library/includes/cron/jobs/tr_cleanup_and_dlstat.php index 9f905f68f..3fee3a01f 100644 --- a/upload/library/includes/cron/jobs/tr_cleanup_and_dlstat.php +++ b/library/includes/cron/jobs/tr_cleanup_and_dlstat.php @@ -71,15 +71,15 @@ if ($tr_cfg['update_dlstat']) // Set "only 1 seeder" bonus DB()->query(" UPDATE - ". NEW_BB_BT_LAST_TORSTAT ." tb, - ". BB_BT_TRACKER_SNAP ." sn + ". NEW_BB_BT_LAST_TORSTAT ." tb, + ". BB_BT_TRACKER_SNAP ." sn SET - tb.bonus_add = tb.up_add + tb.bonus_add = tb.up_add WHERE - tb.topic_id = sn.topic_id - AND sn.seeders = 1 - AND tb.up_add != 0 - AND tb.dl_status = ". DL_STATUS_COMPLETE ." + tb.topic_id = sn.topic_id + AND sn.seeders = 1 + AND tb.up_add != 0 + AND tb.dl_status = ". DL_STATUS_COMPLETE ." "); // Get SUMMARIZED user's dlstat @@ -154,7 +154,7 @@ DB()->query("DROP TABLE IF EXISTS ". NEW_BB_BT_LAST_USERSTAT .", ". OLD_BB_BT_LA DB()->expect_slow_query(10); -if($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['seed_bonus_release']) +if ($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['seed_bonus_release']) { DB()->query(" CREATE TEMPORARY TABLE tmp_bonus ( @@ -163,7 +163,7 @@ if($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['see ) ENGINE = MEMORY "); - $tor_size = ($bb_cfg['seed_bonus_tor_size'] * 1073741824); + $tor_size = ($bb_cfg['seed_bonus_tor_size'] * 1073741824); DB()->query("INSERT INTO tmp_bonus SELECT bt.user_id, count(bt.seeder) AS release_count @@ -174,18 +174,18 @@ if($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['see GROUP BY user_id "); - $seed_bonus = unserialize($bb_cfg['seed_bonus_points']); - $seed_release = unserialize($bb_cfg['seed_bonus_release']); + $seed_bonus = (float) unserialize($bb_cfg['seed_bonus_points']); + $seed_release = (int) unserialize($bb_cfg['seed_bonus_release']); - foreach($seed_bonus as $i => $points) - { - if(!$points || !$seed_release[$i]) continue; + foreach($seed_bonus as $i => $points) + { + if (!$points || !$seed_release[$i]) continue; - $user_points = ($points / 4); - $release = $seed_release[$i]; - $user_regdate = (TIMENOW - $bb_cfg['seed_bonus_user_regdate'] * 86400); + $user_points = ($points / 4); + $release = $seed_release[$i]; + $user_regdate = (TIMENOW - $bb_cfg['seed_bonus_user_regdate'] * 86400); - DB()->query(" + DB()->query(" UPDATE ". BB_USERS ." u, ". BB_BT_USERS ." bu, tmp_bonus b SET u.user_points = u.user_points + $user_points, @@ -199,7 +199,7 @@ if($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['see AND u.user_active = 1 AND u.user_id not IN(". EXCLUDED_USERS_CSV .") "); - } + } DB()->query("DROP TEMPORARY TABLE IF EXISTS tmp_bonus"); } \ No newline at end of file diff --git a/upload/library/includes/cron/jobs/tr_complete_count.php b/library/includes/cron/jobs/tr_complete_count.php similarity index 100% rename from upload/library/includes/cron/jobs/tr_complete_count.php rename to library/includes/cron/jobs/tr_complete_count.php diff --git a/upload/library/includes/cron/jobs/tr_maintenance.php b/library/includes/cron/jobs/tr_maintenance.php similarity index 100% rename from upload/library/includes/cron/jobs/tr_maintenance.php rename to library/includes/cron/jobs/tr_maintenance.php diff --git a/upload/library/includes/cron/jobs/tr_make_snapshot.php b/library/includes/cron/jobs/tr_make_snapshot.php similarity index 100% rename from upload/library/includes/cron/jobs/tr_make_snapshot.php rename to library/includes/cron/jobs/tr_make_snapshot.php diff --git a/upload/library/includes/cron/jobs/tr_update_seeder_last_seen.php b/library/includes/cron/jobs/tr_update_seeder_last_seen.php similarity index 100% rename from upload/library/includes/cron/jobs/tr_update_seeder_last_seen.php rename to library/includes/cron/jobs/tr_update_seeder_last_seen.php diff --git a/upload/library/includes/cron/jobs/update_forums_atom.php b/library/includes/cron/jobs/update_forums_atom.php similarity index 100% rename from upload/library/includes/cron/jobs/update_forums_atom.php rename to library/includes/cron/jobs/update_forums_atom.php diff --git a/upload/library/includes/ucp/.htaccess b/library/includes/datastore/.htaccess similarity index 100% rename from upload/library/includes/ucp/.htaccess rename to library/includes/datastore/.htaccess diff --git a/library/includes/datastore/apc.php b/library/includes/datastore/apc.php new file mode 100644 index 000000000..03a52c1f2 --- /dev/null +++ b/library/includes/datastore/apc.php @@ -0,0 +1,71 @@ +is_installed()) + { + die('Error: APC extension not installed'); + } + $this->dbg_enabled = sql_dbg_enabled(); + $this->prefix = $prefix; + } + + function store ($title, $var) + { + $this->data[$title] = $var; + + $this->cur_query = "cache->set('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return (bool) apc_store($this->prefix . $title, $var); + } + + function clean () + { + foreach ($this->known_items as $title => $script_name) + { + $this->cur_query = "cache->rm('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + apc_delete($this->prefix . $title); + } + } + + function _fetch_from_store () + { + if (!$items = $this->queued_items) + { + $src = $this->_debug_find_caller('enqueue'); + trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); + } + + foreach ($items as $item) + { + $this->cur_query = "cache->get('$item')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + $this->data[$item] = apc_fetch($this->prefix . $item); + } + } + + function is_installed () + { + return function_exists('apc_fetch'); + } +} \ No newline at end of file diff --git a/upload/library/includes/datastore/build_attach_extensions.php b/library/includes/datastore/build_attach_extensions.php similarity index 100% rename from upload/library/includes/datastore/build_attach_extensions.php rename to library/includes/datastore/build_attach_extensions.php diff --git a/upload/library/includes/datastore/build_cat_forums.php b/library/includes/datastore/build_cat_forums.php similarity index 100% rename from upload/library/includes/datastore/build_cat_forums.php rename to library/includes/datastore/build_cat_forums.php diff --git a/upload/library/includes/datastore/build_moderators.php b/library/includes/datastore/build_moderators.php similarity index 100% rename from upload/library/includes/datastore/build_moderators.php rename to library/includes/datastore/build_moderators.php diff --git a/upload/library/includes/datastore/build_ranks.php b/library/includes/datastore/build_ranks.php similarity index 100% rename from upload/library/includes/datastore/build_ranks.php rename to library/includes/datastore/build_ranks.php diff --git a/upload/library/includes/datastore/build_smilies.php b/library/includes/datastore/build_smilies.php similarity index 100% rename from upload/library/includes/datastore/build_smilies.php rename to library/includes/datastore/build_smilies.php diff --git a/upload/library/includes/datastore/build_stats.php b/library/includes/datastore/build_stats.php similarity index 100% rename from upload/library/includes/datastore/build_stats.php rename to library/includes/datastore/build_stats.php diff --git a/library/includes/datastore/common.php b/library/includes/datastore/common.php new file mode 100644 index 000000000..8b7232582 --- /dev/null +++ b/library/includes/datastore/common.php @@ -0,0 +1,175 @@ + data) + */ + var $data = array(); + /** + * Список элементов, которые будут извлечены из хранилища при первом же запросе get() + * до этого момента они ставятся в очередь $queued_items для дальнейшего извлечения _fetch()'ем + * всех элементов одним запросом + * array('title1', 'title2'...) + */ + var $queued_items = array(); + + /** + * 'title' => 'builder script name' inside "includes/datastore" dir + */ + var $known_items = array( + 'cat_forums' => 'build_cat_forums.php', + 'jumpbox' => 'build_cat_forums.php', + 'viewtopic_forum_select' => 'build_cat_forums.php', + 'latest_news' => 'build_cat_forums.php', + 'network_news' => 'build_cat_forums.php', + 'ads' => 'build_cat_forums.php', + 'moderators' => 'build_moderators.php', + 'stats' => 'build_stats.php', + 'ranks' => 'build_ranks.php', + 'attach_extensions' => 'build_attach_extensions.php', + 'smile_replacements' => 'build_smilies.php', + ); + + /** + * Constructor + */ + function datastore_common () {} + + /** + * @param array(item1_title, item2_title...) or single item's title + */ + function enqueue ($items) + { + foreach ((array) $items as $item) + { + // игнор уже поставленного в очередь либо уже извлеченного + if (!in_array($item, $this->queued_items) && !isset($this->data[$item])) + { + $this->queued_items[] = $item; + } + } + } + + function &get ($title) + { + if (!isset($this->data[$title])) + { + $this->enqueue($title); + $this->_fetch(); + } + return $this->data[$title]; + } + + function store ($item_name, $item_data) {} + + function rm ($items) + { + foreach ((array) $items as $item) + { + unset($this->data[$item]); + } + } + + function update ($items) + { + if ($items == 'all') + { + $items = array_keys(array_unique($this->known_items)); + } + foreach ((array) $items as $item) + { + $this->_build_item($item); + } + } + + function _fetch () + { + $this->_fetch_from_store(); + + foreach ($this->queued_items as $title) + { + if (!isset($this->data[$title]) || $this->data[$title] === false) + { + $this->_build_item($title); + } + } + + $this->queued_items = array(); + } + + function _fetch_from_store () {} + + function _build_item ($title) + { + if (!empty($this->known_items[$title])) + { + require(INC_DIR . $this->ds_dir . $this->known_items[$title]); + } + else + { + trigger_error("Unknown datastore item: $title", E_USER_ERROR); + } + } + + var $num_queries = 0; + var $sql_starttime = 0; + var $sql_inittime = 0; + var $sql_timetotal = 0; + var $cur_query_time = 0; + + var $dbg = array(); + var $dbg_id = 0; + var $dbg_enabled = false; + var $cur_query = null; + + function debug ($mode, $cur_query = null) + { + if (!$this->dbg_enabled) return; + + $id =& $this->dbg_id; + $dbg =& $this->dbg[$id]; + + if ($mode == 'start') + { + $this->sql_starttime = utime(); + + $dbg['sql'] = isset($cur_query) ? short_query($cur_query) : short_query($this->cur_query); + $dbg['src'] = $this->debug_find_source(); + $dbg['file'] = $this->debug_find_source('file'); + $dbg['line'] = $this->debug_find_source('line'); + $dbg['time'] = ''; + } + else if ($mode == 'stop') + { + $this->cur_query_time = utime() - $this->sql_starttime; + $this->sql_timetotal += $this->cur_query_time; + $dbg['time'] = $this->cur_query_time; + $id++; + } + } + + function debug_find_source ($mode = '') + { + foreach (debug_backtrace() as $trace) + { + if ($trace['file'] !== __FILE__) + { + switch ($mode) + { + case 'file': return $trace['file']; + case 'line': return $trace['line']; + default: return hide_bb_path($trace['file']) .'('. $trace['line'] .')'; + } + } + } + return 'src not found'; + } +} \ No newline at end of file diff --git a/library/includes/datastore/file.php b/library/includes/datastore/file.php new file mode 100644 index 000000000..8eeddb7d5 --- /dev/null +++ b/library/includes/datastore/file.php @@ -0,0 +1,87 @@ +prefix = $prefix; + $this->dir = $dir; + $this->dbg_enabled = sql_dbg_enabled(); + } + + function store ($title, $var) + { + $this->cur_query = "cache->set('$title')"; + $this->debug('start'); + + $this->data[$title] = $var; + + $filename = $this->dir . clean_filename($this->prefix . $title) . '.php'; + + $filecache = "'; + + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return (bool) file_write($filecache, $filename, false, true, true); + } + + function clean () + { + $dir = $this->dir; + + if (is_dir($dir)) + { + if ($dh = opendir($dir)) + { + while (($file = readdir($dh)) !== false) + { + if ($file != "." && $file != "..") + { + $filename = $dir . $file; + + unlink($filename); + } + } + closedir($dh); + } + } + } + + function _fetch_from_store () + { + if (!$items = $this->queued_items) + { + $src = $this->_debug_find_caller('enqueue'); + trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); + } + + foreach($items as $item) + { + $filename = $this->dir . $this->prefix . $item . '.php'; + + $this->cur_query = "cache->get('$item')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + if(file_exists($filename)) + { + require($filename); + + $this->data[$item] = $filecache; + } + } + } +} \ No newline at end of file diff --git a/library/includes/datastore/memcache.php b/library/includes/datastore/memcache.php new file mode 100644 index 000000000..96fdf9242 --- /dev/null +++ b/library/includes/datastore/memcache.php @@ -0,0 +1,103 @@ +is_installed()) + { + die('Error: Memcached extension not installed'); + } + + $this->cfg = $cfg; + $this->prefix = $prefix; + $this->memcache = new Memcache; + $this->dbg_enabled = sql_dbg_enabled(); + } + + function connect () + { + $connect_type = ($this->cfg['pconnect']) ? 'pconnect' : 'connect'; + + $this->cur_query = $connect_type .' '. $this->cfg['host'] .':'. $this->cfg['port']; + $this->debug('start'); + + if (@$this->memcache->$connect_type($this->cfg['host'], $this->cfg['port'])) + { + $this->connected = true; + } + + if (DBG_LOG) dbg_log(' ', 'CACHE-connect'. ($this->connected ? '' : '-FAIL')); + + if (!$this->connected && $this->cfg['con_required']) + { + die('Could not connect to memcached server'); + } + + $this->debug('stop'); + $this->cur_query = null; + } + + function store ($title, $var) + { + if (!$this->connected) $this->connect(); + $this->data[$title] = $var; + + $this->cur_query = "cache->set('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return (bool) $this->memcache->set($this->prefix . $title, $var); + } + + function clean () + { + if (!$this->connected) $this->connect(); + foreach ($this->known_items as $title => $script_name) + { + $this->cur_query = "cache->rm('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + $this->memcache->delete($this->prefix . $title, 0); + } + } + + function _fetch_from_store () + { + if (!$items = $this->queued_items) + { + $src = $this->_debug_find_caller('enqueue'); + trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); + } + + if (!$this->connected) $this->connect(); + foreach ($items as $item) + { + $this->cur_query = "cache->get('$item')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + $this->data[$item] = $this->memcache->get($this->prefix . $item); + } + } + + function is_installed () + { + return class_exists('Memcache'); + } +} \ No newline at end of file diff --git a/library/includes/datastore/redis.php b/library/includes/datastore/redis.php new file mode 100644 index 000000000..10b71019d --- /dev/null +++ b/library/includes/datastore/redis.php @@ -0,0 +1,99 @@ +is_installed()) + { + die('Error: Redis extension not installed'); + } + + $this->cfg = $cfg; + $this->redis = new Redis(); + $this->dbg_enabled = sql_dbg_enabled(); + $this->prefix = $prefix; + } + + function connect () + { + $this->cur_query = 'connect '. $this->cfg['host'] .':'. $this->cfg['port']; + $this->debug('start'); + + if (@$this->redis->connect($this->cfg['host'],$this->cfg['port'])) + { + $this->connected = true; + } + + if (!$this->connected && $this->cfg['con_required']) + { + die('Could not connect to redis server'); + } + + $this->debug('stop'); + $this->cur_query = null; + } + + function store ($title, $var) + { + if (!$this->connected) $this->connect(); + $this->data[$title] = $var; + + $this->cur_query = "cache->set('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return (bool) $this->redis->set($this->prefix . $title, serialize($var)); + } + + function clean () + { + if (!$this->connected) $this->connect(); + foreach ($this->known_items as $title => $script_name) + { + $this->cur_query = "cache->rm('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + $this->redis->del($this->prefix . $title); + } + } + + function _fetch_from_store () + { + if (!$items = $this->queued_items) + { + $src = $this->_debug_find_caller('enqueue'); + trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); + } + + if (!$this->connected) $this->connect(); + foreach ($items as $item) + { + $this->cur_query = "cache->get('$item')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + $this->data[$item] = unserialize($this->redis->get($this->prefix . $item)); + } + } + + function is_installed () + { + return class_exists('Redis'); + } +} \ No newline at end of file diff --git a/library/includes/datastore/sqlite.php b/library/includes/datastore/sqlite.php new file mode 100644 index 000000000..ecc5616e1 --- /dev/null +++ b/library/includes/datastore/sqlite.php @@ -0,0 +1,66 @@ + '/path/to/datastore.db.sqlite', + 'table_name' => 'datastore', + 'table_schema' => 'CREATE TABLE datastore ( + ds_title VARCHAR(255), + ds_data TEXT, + PRIMARY KEY (ds_title) + )', + 'pconnect' => true, + 'con_required' => true, + 'log_name' => 'DATASTORE', + ); + + function datastore_sqlite ($cfg, $prefix = null) + { + $this->cfg = array_merge($this->cfg, $cfg); + $this->db = new sqlite_common($this->cfg); + $this->prefix = $prefix; + } + + function store ($item_name, $item_data) + { + $this->data[$item_name] = $item_data; + + $ds_title = SQLite3::escapeString($this->prefix . $item_name); + $ds_data = SQLite3::escapeString(serialize($item_data)); + + $result = $this->db->query("REPLACE INTO ". $this->cfg['table_name'] ." (ds_title, ds_data) VALUES ('$ds_title', '$ds_data')"); + + return (bool) $result; + } + + function clean () + { + $this->db->query("DELETE FROM ". $this->cfg['table_name']); + } + + function _fetch_from_store () + { + if (!$items = $this->queued_items) return; + + $prefix_len = strlen($this->prefix); + $prefix_sql = SQLite3::escapeString($this->prefix); + + array_deep($items, 'SQLite3::escapeString'); + $items_list = $prefix_sql . join("','$prefix_sql", $items); + + $rowset = $this->db->fetch_rowset("SELECT ds_title, ds_data FROM ". $this->cfg['table_name'] ." WHERE ds_title IN ('$items_list')"); + + $this->db->debug('start', "unserialize()"); + foreach ($rowset as $row) + { + $this->data[substr($row['ds_title'], $prefix_len)] = unserialize($row['ds_data']); + } + $this->db->debug('stop'); + } +} \ No newline at end of file diff --git a/library/includes/datastore/xcache.php b/library/includes/datastore/xcache.php new file mode 100644 index 000000000..2bd5f3fb8 --- /dev/null +++ b/library/includes/datastore/xcache.php @@ -0,0 +1,72 @@ +is_installed()) + { + die('Error: XCache extension not installed'); + } + + $this->dbg_enabled = sql_dbg_enabled(); + $this->prefix = $prefix; + } + + function store ($title, $var) + { + $this->data[$title] = $var; + + $this->cur_query = "cache->set('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + return (bool) xcache_set($this->prefix . $title, $var); + } + + function clean () + { + foreach ($this->known_items as $title => $script_name) + { + $this->cur_query = "cache->rm('$title')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + xcache_unset($this->prefix . $title); + } + } + + function _fetch_from_store () + { + if (!$items = $this->queued_items) + { + $src = $this->_debug_find_caller('enqueue'); + trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); + } + + foreach ($items as $item) + { + $this->cur_query = "cache->set('$item')"; + $this->debug('start'); + $this->debug('stop'); + $this->cur_query = null; + $this->num_queries++; + + $this->data[$item] = xcache_get($this->prefix . $item); + } + } + + function is_installed () + { + return function_exists('xcache_get'); + } +} \ No newline at end of file diff --git a/upload/library/includes/functions.php b/library/includes/functions.php similarity index 98% rename from upload/library/includes/functions.php rename to library/includes/functions.php index ff885e61c..96dc8bb83 100644 --- a/upload/library/includes/functions.php +++ b/library/includes/functions.php @@ -9,24 +9,26 @@ function get_path_from_id ($id, $ext_id, $base_path, $first_div, $sec_div) return ($base_path ? "$base_path/" : '') . floor($id/$first_div) .'/'. ($id % $sec_div) .'/'. $id . ($ext ? ".$ext" : ''); } -function get_avatar_path ($id, $ext_id, $base_path = '') +function get_avatar_path ($id, $ext_id, $base_path = null, $first_div = 10000, $sec_div = 100) { - return get_path_from_id($id, $ext_id, $base_path, 5000, 100); + global $bb_cfg; + $base_path = isset($base_path) ? $base_path : $bb_cfg['avatars']['upload_path']; + return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div); +} + +function get_attach_path ($id, $ext_id = '', $base_path = null, $first_div = 10000, $sec_div = 100) +{ + global $bb_cfg; + $base_path = isset($base_path) ? $base_path : $bb_cfg['attach']['upload_path']; + return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div); } function delete_avatar ($user_id, $avatar_ext_id) { - global $bb_cfg; - $avatar_file = ($avatar_ext_id) ? get_avatar_path($user_id, $avatar_ext_id, $bb_cfg['avatars']['upload_path']) : ''; + $avatar_file = ($avatar_ext_id) ? get_avatar_path($user_id, $avatar_ext_id) : ''; return ($avatar_file && file_exists($avatar_file)) ? @unlink($avatar_file) : false; } -function get_attach_path ($id) -{ - global $bb_cfg; - return get_path_from_id($id, '', $bb_cfg['attach']['upload_path'], 1000, 100); -} - function get_tracks ($type) { static $pattern = '#^a:\d+:{[i:;\d]+}$#'; @@ -1868,7 +1870,7 @@ function bb_realpath ($path) function login_redirect ($url = '') { - redirect(LOGIN_URL . '?redirect='. (($url) ? $url : $_SERVER['REQUEST_URI'])); + redirect(LOGIN_URL . '?redirect='. (($url) ? $url : (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'))); } function meta_refresh ($url, $time = 5) @@ -2755,17 +2757,17 @@ function get_avatar ($user_id, $ext_id, $allow_avatar = true, $size = true, $hei $height = ($height != '') ? 'height="'. $height .'"' : ''; $width = ($width != '') ? 'width="'. $width .'"' : ''; - $user_avatar = ''. $user_id .''; + $user_avatar = ''. $user_id .''; if ($user_id == BOT_UID && $bb_cfg['avatars']['bot_avatar']) { - $user_avatar = ''. $user_id .''; + $user_avatar = ''. $user_id .''; } - elseif ($allow_avatar && $ext_id) + else if ($allow_avatar && $ext_id) { - if (file_exists($bb_cfg['avatars']['upload_path'] . get_avatar_path($user_id, $ext_id))) + if (file_exists(get_avatar_path($user_id, $ext_id))) { - $user_avatar = ''. $user_id .''; + $user_avatar = ''. $user_id .''; } } diff --git a/upload/library/includes/functions_admin.php b/library/includes/functions_admin.php similarity index 100% rename from upload/library/includes/functions_admin.php rename to library/includes/functions_admin.php diff --git a/upload/library/includes/functions_admin_cron.php b/library/includes/functions_admin_cron.php similarity index 100% rename from upload/library/includes/functions_admin_cron.php rename to library/includes/functions_admin_cron.php diff --git a/upload/library/includes/functions_admin_torrent.php b/library/includes/functions_admin_torrent.php similarity index 100% rename from upload/library/includes/functions_admin_torrent.php rename to library/includes/functions_admin_torrent.php diff --git a/upload/library/includes/functions_atom.php b/library/includes/functions_atom.php similarity index 100% rename from upload/library/includes/functions_atom.php rename to library/includes/functions_atom.php diff --git a/upload/library/includes/functions_dev.php b/library/includes/functions_dev.php similarity index 100% rename from upload/library/includes/functions_dev.php rename to library/includes/functions_dev.php diff --git a/upload/library/includes/functions_group.php b/library/includes/functions_group.php similarity index 100% rename from upload/library/includes/functions_group.php rename to library/includes/functions_group.php diff --git a/upload/library/includes/functions_post.php b/library/includes/functions_post.php similarity index 100% rename from upload/library/includes/functions_post.php rename to library/includes/functions_post.php diff --git a/upload/library/includes/functions_selects.php b/library/includes/functions_selects.php similarity index 100% rename from upload/library/includes/functions_selects.php rename to library/includes/functions_selects.php diff --git a/upload/library/includes/functions_torrent.php b/library/includes/functions_torrent.php similarity index 100% rename from upload/library/includes/functions_torrent.php rename to library/includes/functions_torrent.php diff --git a/upload/library/includes/functions_upload.php b/library/includes/functions_upload.php similarity index 98% rename from upload/library/includes/functions_upload.php rename to library/includes/functions_upload.php index cd335b294..9c2762217 100644 --- a/upload/library/includes/functions_upload.php +++ b/library/includes/functions_upload.php @@ -1,5 +1,7 @@ file_ext_id, $bb_cfg['avatars']['upload_path']); + $file_path = get_avatar_path($params['user_id'], $this->file_ext_id); return $this->_move($file_path); } else if ($mode == 'attach') diff --git a/upload/library/includes/functions_validate.php b/library/includes/functions_validate.php similarity index 100% rename from upload/library/includes/functions_validate.php rename to library/includes/functions_validate.php diff --git a/upload/library/includes/init_bb.php b/library/includes/init_bb.php similarity index 99% rename from upload/library/includes/init_bb.php rename to library/includes/init_bb.php index 47d10263e..bc72a297f 100644 --- a/upload/library/includes/init_bb.php +++ b/library/includes/init_bb.php @@ -421,7 +421,7 @@ function html_ent_decode ($txt, $quote_style = ENT_QUOTES, $charset = 'UTF-8') return (string) html_entity_decode($txt, $quote_style, $charset); } -function make_url ($path) +function make_url ($path = '') { return FULL_URL . preg_replace('#^\/?(.*?)\/?$#', '\1', $path); } @@ -429,7 +429,7 @@ function make_url ($path) require(INC_DIR .'functions.php'); require(INC_DIR .'sessions.php'); require(INC_DIR .'template.php'); -require(INC_DIR .'core/mysql.php'); +require(CORE_DIR .'mysql.php'); define('SQL_LAYER', 'mysql'); diff --git a/upload/library/includes/online_userlist.php b/library/includes/online_userlist.php similarity index 100% rename from upload/library/includes/online_userlist.php rename to library/includes/online_userlist.php diff --git a/upload/library/includes/page_footer.php b/library/includes/page_footer.php similarity index 100% rename from upload/library/includes/page_footer.php rename to library/includes/page_footer.php diff --git a/upload/library/includes/page_footer_dev.php b/library/includes/page_footer_dev.php similarity index 100% rename from upload/library/includes/page_footer_dev.php rename to library/includes/page_footer_dev.php diff --git a/upload/library/includes/page_header.php b/library/includes/page_header.php similarity index 100% rename from upload/library/includes/page_header.php rename to library/includes/page_header.php diff --git a/upload/library/includes/posting_tpl.php b/library/includes/posting_tpl.php similarity index 100% rename from upload/library/includes/posting_tpl.php rename to library/includes/posting_tpl.php diff --git a/upload/library/includes/sessions.php b/library/includes/sessions.php similarity index 99% rename from upload/library/includes/sessions.php rename to library/includes/sessions.php index c9f7ba926..8442d27e5 100644 --- a/upload/library/includes/sessions.php +++ b/library/includes/sessions.php @@ -257,7 +257,7 @@ class user_common if (DB()->fetch_row($sql)) { - header('Location: http://torrentpier.me/pages/banned/'); + header('Location: https://torrentpier.me/banned/'); } } diff --git a/upload/library/includes/smtp.php b/library/includes/smtp.php similarity index 100% rename from upload/library/includes/smtp.php rename to library/includes/smtp.php diff --git a/upload/library/includes/template.php b/library/includes/template.php similarity index 100% rename from upload/library/includes/template.php rename to library/includes/template.php diff --git a/upload/library/includes/torrent_announce_urls.php b/library/includes/torrent_announce_urls.php similarity index 100% rename from upload/library/includes/torrent_announce_urls.php rename to library/includes/torrent_announce_urls.php diff --git a/upload/library/includes/torrent_show_dl_list.php b/library/includes/torrent_show_dl_list.php similarity index 100% rename from upload/library/includes/torrent_show_dl_list.php rename to library/includes/torrent_show_dl_list.php diff --git a/upload/library/language/.htaccess b/library/includes/ucp/.htaccess similarity index 100% rename from upload/library/language/.htaccess rename to library/includes/ucp/.htaccess diff --git a/upload/library/includes/ucp/activate.php b/library/includes/ucp/activate.php similarity index 100% rename from upload/library/includes/ucp/activate.php rename to library/includes/ucp/activate.php diff --git a/upload/library/includes/ucp/bonus.php b/library/includes/ucp/bonus.php similarity index 100% rename from upload/library/includes/ucp/bonus.php rename to library/includes/ucp/bonus.php diff --git a/upload/library/includes/ucp/email.php b/library/includes/ucp/email.php similarity index 100% rename from upload/library/includes/ucp/email.php rename to library/includes/ucp/email.php diff --git a/upload/library/includes/ucp/register.php b/library/includes/ucp/register.php similarity index 99% rename from upload/library/includes/ucp/register.php rename to library/includes/ucp/register.php index 46db112b4..84401a0a1 100644 --- a/upload/library/includes/ucp/register.php +++ b/library/includes/ucp/register.php @@ -766,14 +766,13 @@ $template->assign_vars(array( 'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], $bb_cfg['avatars']['max_width'], $bb_cfg['avatars']['max_height'], (round($bb_cfg['avatars']['max_size'] / 1024))), 'AVATAR_DISALLOWED' => bf($pr_data['user_opt'], 'user_opt', 'dis_avatar'), 'AVATAR_DIS_EXPLAIN' => sprintf($lang['AVATAR_DISABLE'], $bb_cfg['terms_and_conditions_url']), + 'AVATAR_IMG' => get_avatar($pr_data['user_id'], $pr_data['avatar_ext_id'], !bf($pr_data['user_opt'], 'user_opt', 'dis_avatar')), 'SIGNATURE_EXPLAIN' => sprintf($lang['SIGNATURE_EXPLAIN'], $bb_cfg['max_sig_chars']), 'SIG_DISALLOWED' => bf($pr_data['user_opt'], 'user_opt', 'dis_sig'), 'PR_USER_ID' => $pr_data['user_id'], 'U_RESET_AUTOLOGIN' => LOGIN_URL . "?logout=1&reset_autologin=1&sid={$userdata['session_id']}", - - 'AVATAR_URL_PATH' => ($pr_data['avatar_ext_id']) ? get_avatar_path($pr_data['user_id'], $pr_data['avatar_ext_id']) : '', )); print_page('usercp_register.tpl'); \ No newline at end of file diff --git a/upload/library/includes/ucp/sendpasswd.php b/library/includes/ucp/sendpasswd.php similarity index 100% rename from upload/library/includes/ucp/sendpasswd.php rename to library/includes/ucp/sendpasswd.php diff --git a/upload/library/includes/ucp/topic_watch.php b/library/includes/ucp/topic_watch.php similarity index 99% rename from upload/library/includes/ucp/topic_watch.php rename to library/includes/ucp/topic_watch.php index 593f27bee..4fad6c02b 100644 --- a/upload/library/includes/ucp/topic_watch.php +++ b/library/includes/ucp/topic_watch.php @@ -93,7 +93,7 @@ if ($watch_count > 0) } else { - meta_refresh(BB_ROOT, '3'); + meta_refresh('index.php', 3); bb_die($lang['NO_WATCHED_TOPICS']); } diff --git a/upload/library/includes/ucp/viewprofile.php b/library/includes/ucp/viewprofile.php similarity index 100% rename from upload/library/includes/ucp/viewprofile.php rename to library/includes/ucp/viewprofile.php diff --git a/upload/library/includes/ucp/viewtorrent.php b/library/includes/ucp/viewtorrent.php similarity index 100% rename from upload/library/includes/ucp/viewtorrent.php rename to library/includes/ucp/viewtorrent.php diff --git a/upload/library/language/en/.htaccess b/library/language/.htaccess similarity index 100% rename from upload/library/language/en/.htaccess rename to library/language/.htaccess diff --git a/upload/library/language/en/email/.htaccess b/library/language/en/.htaccess similarity index 100% rename from upload/library/language/en/email/.htaccess rename to library/language/en/.htaccess diff --git a/upload/library/language/en/html/.htaccess b/library/language/en/email/.htaccess similarity index 100% rename from upload/library/language/en/html/.htaccess rename to library/language/en/email/.htaccess diff --git a/upload/library/language/en/email/admin_send_email.tpl b/library/language/en/email/admin_send_email.tpl similarity index 100% rename from upload/library/language/en/email/admin_send_email.tpl rename to library/language/en/email/admin_send_email.tpl diff --git a/upload/library/language/en/email/blank.tpl b/library/language/en/email/blank.tpl similarity index 100% rename from upload/library/language/en/email/blank.tpl rename to library/language/en/email/blank.tpl diff --git a/upload/library/language/en/email/group_added.tpl b/library/language/en/email/group_added.tpl similarity index 100% rename from upload/library/language/en/email/group_added.tpl rename to library/language/en/email/group_added.tpl diff --git a/upload/library/language/en/email/group_approved.tpl b/library/language/en/email/group_approved.tpl similarity index 100% rename from upload/library/language/en/email/group_approved.tpl rename to library/language/en/email/group_approved.tpl diff --git a/upload/library/language/en/email/group_request.tpl b/library/language/en/email/group_request.tpl similarity index 100% rename from upload/library/language/en/email/group_request.tpl rename to library/language/en/email/group_request.tpl diff --git a/upload/library/language/en/email/privmsg_notify.tpl b/library/language/en/email/privmsg_notify.tpl similarity index 100% rename from upload/library/language/en/email/privmsg_notify.tpl rename to library/language/en/email/privmsg_notify.tpl diff --git a/upload/library/language/en/email/profile_send_email.tpl b/library/language/en/email/profile_send_email.tpl similarity index 100% rename from upload/library/language/en/email/profile_send_email.tpl rename to library/language/en/email/profile_send_email.tpl diff --git a/upload/library/language/en/email/topic_notify.tpl b/library/language/en/email/topic_notify.tpl similarity index 100% rename from upload/library/language/en/email/topic_notify.tpl rename to library/language/en/email/topic_notify.tpl diff --git a/upload/library/language/en/email/user_activate.tpl b/library/language/en/email/user_activate.tpl similarity index 100% rename from upload/library/language/en/email/user_activate.tpl rename to library/language/en/email/user_activate.tpl diff --git a/upload/library/language/en/email/user_activate_passwd.tpl b/library/language/en/email/user_activate_passwd.tpl similarity index 100% rename from upload/library/language/en/email/user_activate_passwd.tpl rename to library/language/en/email/user_activate_passwd.tpl diff --git a/upload/library/language/en/email/user_welcome.tpl b/library/language/en/email/user_welcome.tpl similarity index 100% rename from upload/library/language/en/email/user_welcome.tpl rename to library/language/en/email/user_welcome.tpl diff --git a/upload/library/language/en/email/user_welcome_inactive.tpl b/library/language/en/email/user_welcome_inactive.tpl similarity index 100% rename from upload/library/language/en/email/user_welcome_inactive.tpl rename to library/language/en/email/user_welcome_inactive.tpl diff --git a/upload/library/language/ru/.htaccess b/library/language/en/html/.htaccess similarity index 100% rename from upload/library/language/ru/.htaccess rename to library/language/en/html/.htaccess diff --git a/upload/library/language/en/html/advert.html b/library/language/en/html/advert.html similarity index 100% rename from upload/library/language/en/html/advert.html rename to library/language/en/html/advert.html diff --git a/upload/library/language/en/html/copyright_holders.html b/library/language/en/html/copyright_holders.html similarity index 100% rename from upload/library/language/en/html/copyright_holders.html rename to library/language/en/html/copyright_holders.html diff --git a/upload/library/language/en/html/not_found.html b/library/language/en/html/not_found.html similarity index 100% rename from upload/library/language/en/html/not_found.html rename to library/language/en/html/not_found.html diff --git a/upload/library/language/en/html/sidebar1.html b/library/language/en/html/sidebar1.html similarity index 50% rename from upload/library/language/en/html/sidebar1.html rename to library/language/en/html/sidebar1.html index 08fa4f64d..421d37134 100644 --- a/upload/library/language/en/html/sidebar1.html +++ b/library/language/en/html/sidebar1.html @@ -1,9 +1,9 @@

    BitTorrent clients

    @@ -13,8 +13,8 @@

    FAQ

    diff --git a/upload/library/language/en/html/sidebar2.html b/library/language/en/html/sidebar2.html similarity index 100% rename from upload/library/language/en/html/sidebar2.html rename to library/language/en/html/sidebar2.html diff --git a/upload/library/language/en/html/user_agreement.html b/library/language/en/html/user_agreement.html similarity index 100% rename from upload/library/language/en/html/user_agreement.html rename to library/language/en/html/user_agreement.html diff --git a/upload/library/language/en/main.php b/library/language/en/main.php similarity index 99% rename from upload/library/language/en/main.php rename to library/language/en/main.php index c4bf20e7e..cb9583d3f 100644 --- a/upload/library/language/en/main.php +++ b/library/language/en/main.php @@ -1620,6 +1620,7 @@ $lang['CREATE_PROFILE'] = 'Create profile'; $lang['TP_VERSION'] = 'TorrentPier version'; $lang['TP_RELEASE_DATE'] = 'Release date'; +$lang['ZF_VERSION'] = 'Zend Framework version'; $lang['PHP_INFO'] = 'Information about PHP'; $lang['CLICK_RETURN_ADMIN_INDEX'] = 'Click %sHere%s to return to the Admin Index'; diff --git a/upload/library/language/en/search_stopwords.txt b/library/language/en/search_stopwords.txt similarity index 100% rename from upload/library/language/en/search_stopwords.txt rename to library/language/en/search_stopwords.txt diff --git a/upload/library/language/en/search_synonyms.txt b/library/language/en/search_synonyms.txt similarity index 100% rename from upload/library/language/en/search_synonyms.txt rename to library/language/en/search_synonyms.txt diff --git a/upload/library/language/ru/email/.htaccess b/library/language/ru/.htaccess similarity index 100% rename from upload/library/language/ru/email/.htaccess rename to library/language/ru/.htaccess diff --git a/upload/library/language/ru/html/.htaccess b/library/language/ru/email/.htaccess similarity index 100% rename from upload/library/language/ru/html/.htaccess rename to library/language/ru/email/.htaccess diff --git a/upload/library/language/ru/email/admin_send_email.tpl b/library/language/ru/email/admin_send_email.tpl similarity index 100% rename from upload/library/language/ru/email/admin_send_email.tpl rename to library/language/ru/email/admin_send_email.tpl diff --git a/upload/library/language/ru/email/blank.tpl b/library/language/ru/email/blank.tpl similarity index 100% rename from upload/library/language/ru/email/blank.tpl rename to library/language/ru/email/blank.tpl diff --git a/upload/library/language/ru/email/group_added.tpl b/library/language/ru/email/group_added.tpl similarity index 100% rename from upload/library/language/ru/email/group_added.tpl rename to library/language/ru/email/group_added.tpl diff --git a/upload/library/language/ru/email/group_approved.tpl b/library/language/ru/email/group_approved.tpl similarity index 100% rename from upload/library/language/ru/email/group_approved.tpl rename to library/language/ru/email/group_approved.tpl diff --git a/upload/library/language/ru/email/group_request.tpl b/library/language/ru/email/group_request.tpl similarity index 100% rename from upload/library/language/ru/email/group_request.tpl rename to library/language/ru/email/group_request.tpl diff --git a/upload/library/language/ru/email/privmsg_notify.tpl b/library/language/ru/email/privmsg_notify.tpl similarity index 100% rename from upload/library/language/ru/email/privmsg_notify.tpl rename to library/language/ru/email/privmsg_notify.tpl diff --git a/upload/library/language/ru/email/profile_send_email.tpl b/library/language/ru/email/profile_send_email.tpl similarity index 100% rename from upload/library/language/ru/email/profile_send_email.tpl rename to library/language/ru/email/profile_send_email.tpl diff --git a/upload/library/language/ru/email/topic_notify.tpl b/library/language/ru/email/topic_notify.tpl similarity index 100% rename from upload/library/language/ru/email/topic_notify.tpl rename to library/language/ru/email/topic_notify.tpl diff --git a/upload/library/language/ru/email/user_activate.tpl b/library/language/ru/email/user_activate.tpl similarity index 100% rename from upload/library/language/ru/email/user_activate.tpl rename to library/language/ru/email/user_activate.tpl diff --git a/upload/library/language/ru/email/user_activate_passwd.tpl b/library/language/ru/email/user_activate_passwd.tpl similarity index 100% rename from upload/library/language/ru/email/user_activate_passwd.tpl rename to library/language/ru/email/user_activate_passwd.tpl diff --git a/upload/library/language/ru/email/user_welcome.tpl b/library/language/ru/email/user_welcome.tpl similarity index 100% rename from upload/library/language/ru/email/user_welcome.tpl rename to library/language/ru/email/user_welcome.tpl diff --git a/upload/library/language/ru/email/user_welcome_inactive.tpl b/library/language/ru/email/user_welcome_inactive.tpl similarity index 100% rename from upload/library/language/ru/email/user_welcome_inactive.tpl rename to library/language/ru/email/user_welcome_inactive.tpl diff --git a/upload/library/language/uk/.htaccess b/library/language/ru/html/.htaccess similarity index 100% rename from upload/library/language/uk/.htaccess rename to library/language/ru/html/.htaccess diff --git a/upload/library/language/ru/html/advert.html b/library/language/ru/html/advert.html similarity index 100% rename from upload/library/language/ru/html/advert.html rename to library/language/ru/html/advert.html diff --git a/upload/library/language/ru/html/copyright_holders.html b/library/language/ru/html/copyright_holders.html similarity index 100% rename from upload/library/language/ru/html/copyright_holders.html rename to library/language/ru/html/copyright_holders.html diff --git a/upload/library/language/ru/html/not_found.html b/library/language/ru/html/not_found.html similarity index 100% rename from upload/library/language/ru/html/not_found.html rename to library/language/ru/html/not_found.html diff --git a/library/language/ru/html/sidebar1.html b/library/language/ru/html/sidebar1.html new file mode 100644 index 000000000..0810f75f1 --- /dev/null +++ b/library/language/ru/html/sidebar1.html @@ -0,0 +1,23 @@ +
    +

    BitTorrent клиенты

    + +
    + +
    + + + +
    + + \ No newline at end of file diff --git a/upload/library/language/ru/html/sidebar2.html b/library/language/ru/html/sidebar2.html similarity index 100% rename from upload/library/language/ru/html/sidebar2.html rename to library/language/ru/html/sidebar2.html diff --git a/upload/library/language/ru/html/user_agreement.html b/library/language/ru/html/user_agreement.html similarity index 100% rename from upload/library/language/ru/html/user_agreement.html rename to library/language/ru/html/user_agreement.html diff --git a/upload/library/language/ru/main.php b/library/language/ru/main.php similarity index 99% rename from upload/library/language/ru/main.php rename to library/language/ru/main.php index e021e08a2..6977eeba7 100644 --- a/upload/library/language/ru/main.php +++ b/library/language/ru/main.php @@ -884,7 +884,7 @@ $lang['LOOKUP_IP'] = 'Посмотреть хост для IP'; // Timezones ... for display on each page $lang['ALL_TIMES'] = 'Часовой пояс: %s'; // This is followed by UTC and the timezone offset -// это для выпадающего меню, раньше тут еще были города +// это для выпадающего меню часовых поясов $lang['TZ']['-12'] = 'UTC - 12'; $lang['TZ']['-11'] = 'UTC - 11'; $lang['TZ']['-10'] = 'UTC - 10'; @@ -900,22 +900,22 @@ $lang['TZ']['-2'] = 'UTC - 2'; $lang['TZ']['-1'] = 'UTC - 1'; $lang['TZ']['0'] = 'UTC ± 0'; $lang['TZ']['1'] = 'UTC + 1'; -$lang['TZ']['2'] = 'UTC + 2'; -$lang['TZ']['3'] = 'UTC + 3'; +$lang['TZ']['2'] = 'UTC + 2 (Калининградское время | USZ1)'; +$lang['TZ']['3'] = 'UTC + 3 (Московское время | MSK)'; $lang['TZ']['3.5'] = 'UTC + 3:30'; -$lang['TZ']['4'] = 'UTC + 4 (Московское время)'; +$lang['TZ']['4'] = 'UTC + 4 (Самарское время | SAMT)'; $lang['TZ']['4.5'] = 'UTC + 4:30'; -$lang['TZ']['5'] = 'UTC + 5'; +$lang['TZ']['5'] = 'UTC + 5 (Екатеринбургское время | YEKT)'; $lang['TZ']['5.5'] = 'UTC + 5:30'; -$lang['TZ']['6'] = 'UTC + 6'; +$lang['TZ']['6'] = 'UTC + 6 (Омское время | OMST)'; $lang['TZ']['6.5'] = 'UTC + 6:30'; -$lang['TZ']['7'] = 'UTC + 7'; -$lang['TZ']['8'] = 'UTC + 8'; -$lang['TZ']['9'] = 'UTC + 9'; +$lang['TZ']['7'] = 'UTC + 7 (Красноярское время | KRAT)'; +$lang['TZ']['8'] = 'UTC + 8 (Иркутское время | IRKT)'; +$lang['TZ']['9'] = 'UTC + 9 (Якутское время | YAKT)'; $lang['TZ']['9.5'] = 'UTC + 9:30'; -$lang['TZ']['10'] = 'UTC + 10'; -$lang['TZ']['11'] = 'UTC + 11'; -$lang['TZ']['12'] = 'UTC + 12'; +$lang['TZ']['10'] = 'UTC + 10 (Владивостокское время | VLAT)'; +$lang['TZ']['11'] = 'UTC + 11 (Среднеколымское время | SRET)'; +$lang['TZ']['12'] = 'UTC + 12 (Камчатское время | PETT)'; $lang['TZ']['13'] = 'UTC + 13'; $lang['DATETIME']['TODAY'] = 'Сегодня, в'; @@ -1620,6 +1620,7 @@ $lang['CREATE_PROFILE'] = 'Создать аккаунт'; $lang['TP_VERSION'] = 'Версия TorrentPier II'; $lang['TP_RELEASE_DATE'] = 'Дата выпуска'; +$lang['ZF_VERSION'] = 'Версия Zend Framework'; $lang['PHP_INFO'] = 'Информация о PHP'; $lang['CLICK_RETURN_ADMIN_INDEX'] = '%sВернуться на главную страницу администраторского раздела%s'; diff --git a/upload/library/language/ru/search_stopwords.txt b/library/language/ru/search_stopwords.txt similarity index 100% rename from upload/library/language/ru/search_stopwords.txt rename to library/language/ru/search_stopwords.txt diff --git a/upload/library/language/ru/search_synonyms.txt b/library/language/ru/search_synonyms.txt similarity index 100% rename from upload/library/language/ru/search_synonyms.txt rename to library/language/ru/search_synonyms.txt diff --git a/upload/library/language/uk/email/.htaccess b/library/language/uk/.htaccess similarity index 100% rename from upload/library/language/uk/email/.htaccess rename to library/language/uk/.htaccess diff --git a/upload/library/language/uk/html/.htaccess b/library/language/uk/email/.htaccess similarity index 100% rename from upload/library/language/uk/html/.htaccess rename to library/language/uk/email/.htaccess diff --git a/upload/library/language/uk/email/admin_send_email.tpl b/library/language/uk/email/admin_send_email.tpl similarity index 100% rename from upload/library/language/uk/email/admin_send_email.tpl rename to library/language/uk/email/admin_send_email.tpl diff --git a/upload/library/language/uk/email/blank.tpl b/library/language/uk/email/blank.tpl similarity index 100% rename from upload/library/language/uk/email/blank.tpl rename to library/language/uk/email/blank.tpl diff --git a/upload/library/language/uk/email/group_added.tpl b/library/language/uk/email/group_added.tpl similarity index 100% rename from upload/library/language/uk/email/group_added.tpl rename to library/language/uk/email/group_added.tpl diff --git a/upload/library/language/uk/email/group_approved.tpl b/library/language/uk/email/group_approved.tpl similarity index 100% rename from upload/library/language/uk/email/group_approved.tpl rename to library/language/uk/email/group_approved.tpl diff --git a/upload/library/language/uk/email/group_request.tpl b/library/language/uk/email/group_request.tpl similarity index 100% rename from upload/library/language/uk/email/group_request.tpl rename to library/language/uk/email/group_request.tpl diff --git a/upload/library/language/uk/email/privmsg_notify.tpl b/library/language/uk/email/privmsg_notify.tpl similarity index 100% rename from upload/library/language/uk/email/privmsg_notify.tpl rename to library/language/uk/email/privmsg_notify.tpl diff --git a/upload/library/language/uk/email/profile_send_email.tpl b/library/language/uk/email/profile_send_email.tpl similarity index 100% rename from upload/library/language/uk/email/profile_send_email.tpl rename to library/language/uk/email/profile_send_email.tpl diff --git a/upload/library/language/uk/email/topic_notify.tpl b/library/language/uk/email/topic_notify.tpl similarity index 100% rename from upload/library/language/uk/email/topic_notify.tpl rename to library/language/uk/email/topic_notify.tpl diff --git a/upload/library/language/uk/email/user_activate.tpl b/library/language/uk/email/user_activate.tpl similarity index 100% rename from upload/library/language/uk/email/user_activate.tpl rename to library/language/uk/email/user_activate.tpl diff --git a/upload/library/language/uk/email/user_activate_passwd.tpl b/library/language/uk/email/user_activate_passwd.tpl similarity index 100% rename from upload/library/language/uk/email/user_activate_passwd.tpl rename to library/language/uk/email/user_activate_passwd.tpl diff --git a/upload/library/language/uk/email/user_welcome.tpl b/library/language/uk/email/user_welcome.tpl similarity index 100% rename from upload/library/language/uk/email/user_welcome.tpl rename to library/language/uk/email/user_welcome.tpl diff --git a/upload/library/language/uk/email/user_welcome_inactive.tpl b/library/language/uk/email/user_welcome_inactive.tpl similarity index 100% rename from upload/library/language/uk/email/user_welcome_inactive.tpl rename to library/language/uk/email/user_welcome_inactive.tpl diff --git a/library/language/uk/html/.htaccess b/library/language/uk/html/.htaccess new file mode 100644 index 000000000..baa56e5a3 --- /dev/null +++ b/library/language/uk/html/.htaccess @@ -0,0 +1,2 @@ +order allow,deny +deny from all \ No newline at end of file diff --git a/upload/library/language/uk/html/advert.html b/library/language/uk/html/advert.html similarity index 100% rename from upload/library/language/uk/html/advert.html rename to library/language/uk/html/advert.html diff --git a/upload/library/language/uk/html/copyright_holders.html b/library/language/uk/html/copyright_holders.html similarity index 100% rename from upload/library/language/uk/html/copyright_holders.html rename to library/language/uk/html/copyright_holders.html diff --git a/upload/library/language/uk/html/not_found.html b/library/language/uk/html/not_found.html similarity index 100% rename from upload/library/language/uk/html/not_found.html rename to library/language/uk/html/not_found.html diff --git a/library/language/uk/html/sidebar1.html b/library/language/uk/html/sidebar1.html new file mode 100644 index 000000000..c123a61e2 --- /dev/null +++ b/library/language/uk/html/sidebar1.html @@ -0,0 +1,23 @@ +
    +

    BitTorrent клієнти

    + +
    + +
    + + + +
    + + \ No newline at end of file diff --git a/upload/library/language/uk/html/sidebar2.html b/library/language/uk/html/sidebar2.html similarity index 100% rename from upload/library/language/uk/html/sidebar2.html rename to library/language/uk/html/sidebar2.html diff --git a/upload/library/language/uk/html/user_agreement.html b/library/language/uk/html/user_agreement.html similarity index 100% rename from upload/library/language/uk/html/user_agreement.html rename to library/language/uk/html/user_agreement.html diff --git a/upload/library/language/uk/main.php b/library/language/uk/main.php similarity index 99% rename from upload/library/language/uk/main.php rename to library/language/uk/main.php index 6abd28883..90202f122 100644 --- a/upload/library/language/uk/main.php +++ b/library/language/uk/main.php @@ -1620,6 +1620,7 @@ $lang['CREATE_PROFILE'] = 'Створити акаунт'; $lang['TP_VERSION'] = 'Версія TorrentPier II'; $lang['TP_RELEASE_DATE'] = 'Дата випуску'; +$lang['ZF_VERSION'] = 'Версія Zend Framework'; $lang['PHP_INFO'] = 'Інформація про PHP'; $lang['CLICK_RETURN_ADMIN_INDEX'] = '%sВернуться на головну сторінку адміністраторського розділу%s'; diff --git a/upload/library/language/uk/search_stopwords.txt b/library/language/uk/search_stopwords.txt similarity index 100% rename from upload/library/language/uk/search_stopwords.txt rename to library/language/uk/search_stopwords.txt diff --git a/upload/library/language/uk/search_synonyms.txt b/library/language/uk/search_synonyms.txt similarity index 100% rename from upload/library/language/uk/search_synonyms.txt rename to library/language/uk/search_synonyms.txt diff --git a/upload/login.php b/login.php similarity index 93% rename from upload/login.php rename to login.php index e959e2eb5..9258210db 100644 --- a/upload/login.php +++ b/login.php @@ -57,15 +57,15 @@ if (isset($_REQUEST['admin']) && !IS_AM) bb_die($lang['NOT_ADMIN']); $mod_admin_login = (IS_AM && !$user->data['session_admin']); // login username & password -$login_username = ($mod_admin_login) ? $userdata['username'] : (string) @$_POST['login_username']; -$login_password = (string) @$_POST['login_password']; +$login_username = ($mod_admin_login) ? $userdata['username'] : (isset($_POST['login_username']) ? $_POST['login_username'] : ''); +$login_password = isset($_POST['login_password']) ? $_POST['login_password'] : ''; // Проверка на неверную комбинацию логин/пароль $need_captcha = false; -if(!$mod_admin_login) +if (!$mod_admin_login) { $need_captcha = CACHE('bb_login_err')->get('l_err_'. USER_IP); - if($need_captcha < $bb_cfg['invalid_logins']) $need_captcha = false; + if ($need_captcha < $bb_cfg['invalid_logins']) $need_captcha = false; } // login diff --git a/upload/memberlist.php b/memberlist.php similarity index 98% rename from upload/memberlist.php rename to memberlist.php index fc0ff96bd..7a55374d0 100644 --- a/upload/memberlist.php +++ b/memberlist.php @@ -108,7 +108,7 @@ $letters_range .= '-'; $letters_range .= iconv('windows-1251', 'UTF-8', chr(255)); $select_letter = $letter_sql = ''; -$by_letter_req = (@$_REQUEST['letter']) ? strtolower(trim($_REQUEST['letter'])) : false; +$by_letter_req = isset($_REQUEST['letter']) ? strtolower(trim($_REQUEST['letter'])) : false; if ($by_letter_req) { diff --git a/upload/modcp.php b/modcp.php similarity index 96% rename from upload/modcp.php rename to modcp.php index 0da5cffad..6467704ca 100644 --- a/upload/modcp.php +++ b/modcp.php @@ -58,10 +58,22 @@ function validate_topics ($forum_id, &$req_topics, &$topic_titles) $topic_titles = $valid_titles; } +/** + * @param $request_index + * @param $mod_action + * @return bool + */ +function validate_mode_condition($request_index, $mod_action='') { + if (!$mod_action) { + $mod_action = $request_index; + } + return (isset($_REQUEST[$request_index]) || (isset($_POST['mod_action']) && $_POST['mod_action'] === $mod_action)); +} + // Obtain initial vars -$forum_id = (int) @$_REQUEST['f']; -$topic_id = (int) @$_REQUEST['t']; -$post_id = (int) @$_REQUEST['p']; +$forum_id = isset($_REQUEST['f']) ? $_REQUEST['f'] : 0; +$topic_id = isset($_REQUEST['t']) ? $_REQUEST['t'] : 0; +$post_id = isset($_REQUEST['p']) ? $_REQUEST['p'] : 0; $start = isset($_REQUEST['start']) ? abs(intval($_REQUEST['start'])) : 0; $confirmed = isset($_POST['confirm']); @@ -74,27 +86,27 @@ if (isset($_REQUEST['mode'])) } else { - if (isset($_REQUEST['delete']) || @$_POST['mod_action'] === 'topic_delete') + if (validate_mode_condition('delete', 'topic_delete')) { $mode = 'delete'; } - elseif (isset($_REQUEST['move']) || @$_POST['mod_action'] === 'topic_move') + elseif (validate_mode_condition('move', 'topic_move')) { $mode = 'move'; } - elseif (isset($_REQUEST['lock']) || @$_POST['mod_action'] === 'topic_lock') + elseif (validate_mode_condition('lock', 'topic_lock')) { $mode = 'lock'; } - elseif (isset($_REQUEST['unlock']) || @$_POST['mod_action'] === 'topic_unlock') + elseif (validate_mode_condition('unlock', 'topic_unlock')) { $mode = 'unlock'; } - elseif (isset($_REQUEST['post_pin']) || @$_POST['mod_action'] === 'post_pin') + elseif (validate_mode_condition('post_pin')) { $mode = 'post_pin'; } - elseif (isset($_REQUEST['post_unpin']) || @$_POST['mod_action'] === 'post_unpin') + elseif (validate_mode_condition('post_unpin')) { $mode = 'post_unpin'; } diff --git a/upload/opensearch_desc.xml b/opensearch_desc.xml similarity index 100% rename from upload/opensearch_desc.xml rename to opensearch_desc.xml diff --git a/upload/opensearch_desc_bt.xml b/opensearch_desc_bt.xml similarity index 100% rename from upload/opensearch_desc_bt.xml rename to opensearch_desc_bt.xml diff --git a/upload/poll.php b/poll.php similarity index 100% rename from upload/poll.php rename to poll.php diff --git a/upload/posting.php b/posting.php similarity index 100% rename from upload/posting.php rename to posting.php diff --git a/upload/privmsg.php b/privmsg.php similarity index 100% rename from upload/privmsg.php rename to privmsg.php diff --git a/upload/profile.php b/profile.php similarity index 100% rename from upload/profile.php rename to profile.php diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 568d8cf97..000000000 --- a/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -Для получения информации об установке движка, откройте в своем браузере указаную ссылку: - -https://github.com/torrentpier/tracker/tree/master#torrentpier-ii \ No newline at end of file diff --git a/upload/robots.txt b/robots.txt similarity index 100% rename from upload/robots.txt rename to robots.txt diff --git a/upload/search.php b/search.php similarity index 100% rename from upload/search.php rename to search.php diff --git a/upload/styles/bootstrap/css/bootstrap-theme.css b/styles/bootstrap/css/bootstrap-theme.css similarity index 83% rename from upload/styles/bootstrap/css/bootstrap-theme.css rename to styles/bootstrap/css/bootstrap-theme.css index f860bbc06..c4cadf15e 100644 --- a/upload/styles/bootstrap/css/bootstrap-theme.css +++ b/styles/bootstrap/css/bootstrap-theme.css @@ -1,5 +1,5 @@ /*! - * Bootstrap v3.2.0 (http://getbootstrap.com) + * Bootstrap v3.3.1 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ @@ -29,6 +29,14 @@ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} .btn:active, .btn.active { background-image: none; @@ -61,28 +69,28 @@ background-image: none; } .btn-primary { - background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); - background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2)); - background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; - border-color: #2b669a; + border-color: #245580; } .btn-primary:hover, .btn-primary:focus { - background-color: #2d6ca2; + background-color: #265a88; background-position: 0 -15px; } .btn-primary:active, .btn-primary.active { - background-color: #2d6ca2; - border-color: #2b669a; + background-color: #265a88; + border-color: #245580; } .btn-primary:disabled, .btn-primary[disabled] { - background-color: #2d6ca2; + background-color: #265a88; background-image: none; } .btn-success { @@ -203,12 +211,12 @@ .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { - background-color: #357ebd; - background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); - background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); - background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); + background-color: #2e6da4; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } .navbar-default { @@ -223,12 +231,13 @@ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); } +.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .active > a { - background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); - background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3)); - background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); @@ -246,12 +255,13 @@ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; } +.navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .active > a { - background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); - background-image: -o-linear-gradient(top, #222 0%, #282828 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828)); - background-image: linear-gradient(to bottom, #222 0%, #282828 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); @@ -265,6 +275,19 @@ .navbar-fixed-bottom { border-radius: 0; } +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} .alert { text-shadow: 0 1px 0 rgba(255, 255, 255, .2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); @@ -315,11 +338,11 @@ background-repeat: repeat-x; } .progress-bar { - background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); - background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9)); - background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); background-repeat: repeat-x; } .progress-bar-success { @@ -367,14 +390,19 @@ .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { - text-shadow: 0 -1px 0 #3071a9; - background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); - background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3)); - background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); background-repeat: repeat-x; - border-color: #3278b3; + border-color: #2b669a; +} +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; } .panel { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); @@ -389,11 +417,11 @@ background-repeat: repeat-x; } .panel-primary > .panel-heading { - background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); - background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); - background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } .panel-success > .panel-heading { diff --git a/upload/styles/bootstrap/css/bootstrap.css b/styles/bootstrap/css/bootstrap.css similarity index 93% rename from upload/styles/bootstrap/css/bootstrap.css rename to styles/bootstrap/css/bootstrap.css index 037dd0561..c6f3d2106 100644 --- a/upload/styles/bootstrap/css/bootstrap.css +++ b/styles/bootstrap/css/bootstrap.css @@ -1,10 +1,10 @@ /*! - * Bootstrap v3.2.0 (http://getbootstrap.com) + * Bootstrap v3.3.1 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ -/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ html { font-family: sans-serif; -webkit-text-size-adjust: 100%; @@ -22,6 +22,7 @@ footer, header, hgroup, main, +menu, nav, section, summary { @@ -43,7 +44,7 @@ template { display: none; } a { - background: transparent; + background-color: transparent; } a:active, a:hover { @@ -187,8 +188,11 @@ td, th { padding: 0; } +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ @media print { - * { + *, + *:before, + *:after { color: #000 !important; text-shadow: none !important; background: transparent !important; @@ -205,8 +209,8 @@ th { abbr[title]:after { content: " (" attr(title) ")"; } - a[href^="javascript:"]:after, - a[href^="#"]:after { + a[href^="#"]:after, + a[href^="javascript:"]:after { content: ""; } pre, @@ -241,10 +245,6 @@ th { .navbar { display: none; } - .table td, - .table th { - background-color: #fff !important; - } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; @@ -255,6 +255,10 @@ th { .table { border-collapse: collapse !important; } + .table td, + .table th { + background-color: #fff !important; + } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; @@ -284,7 +288,8 @@ th { .glyphicon-plus:before { content: "\2b"; } -.glyphicon-euro:before { +.glyphicon-euro:before, +.glyphicon-eur:before { content: "\20ac"; } .glyphicon-minus:before { @@ -910,12 +915,12 @@ textarea { line-height: inherit; } a { - color: #428bca; + color: #337ab7; text-decoration: none; } a:hover, a:focus { - color: #2a6496; + color: #23527c; text-decoration: underline; } a:focus { @@ -935,7 +940,6 @@ img { .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; - width: 100% \9; max-width: 100%; height: auto; } @@ -944,7 +948,6 @@ img { } .img-thumbnail { display: inline-block; - width: 100% \9; max-width: 100%; height: auto; padding: 4px; @@ -1117,9 +1120,6 @@ small, .small { font-size: 85%; } -cite { - font-style: normal; -} mark, .mark { padding: .2em; @@ -1153,10 +1153,10 @@ mark, color: #777; } .text-primary { - color: #428bca; + color: #337ab7; } a.text-primary:hover { - color: #3071a9; + color: #286090; } .text-success { color: #3c763d; @@ -1184,10 +1184,10 @@ a.text-danger:hover { } .bg-primary { color: #fff; - background-color: #428bca; + background-color: #337ab7; } a.bg-primary:hover { - background-color: #3071a9; + background-color: #286090; } .bg-success { background-color: #dff0d8; @@ -1328,10 +1328,6 @@ blockquote.pull-right small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } -blockquote:before, -blockquote:after { - content: ""; -} address { margin-bottom: 20px; font-style: normal; @@ -1362,6 +1358,7 @@ kbd { kbd kbd { padding: 0; font-size: 100%; + font-weight: bold; -webkit-box-shadow: none; box-shadow: none; } @@ -2060,6 +2057,12 @@ pre code { table { background-color: transparent; } +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} th { text-align: left; } @@ -2120,12 +2123,10 @@ th { .table-bordered > thead > tr > td { border-bottom-width: 2px; } -.table-striped > tbody > tr:nth-child(odd) > td, -.table-striped > tbody > tr:nth-child(odd) > th { +.table-striped > tbody > tr:nth-child(odd) { background-color: #f9f9f9; } -.table-hover > tbody > tr:hover > td, -.table-hover > tbody > tr:hover > th { +.table-hover > tbody > tr:hover { background-color: #f5f5f5; } table col[class*="col-"] { @@ -2244,13 +2245,15 @@ table th[class*="col-"] { .table-hover > tbody > tr.danger:hover > th { background-color: #ebcccc; } +.table-responsive { + min-height: .01%; + overflow-x: auto; +} @media screen and (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 15px; - overflow-x: auto; overflow-y: hidden; - -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #ddd; } @@ -2375,14 +2378,14 @@ output { box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); } .form-control::-moz-placeholder { - color: #777; + color: #999; opacity: 1; } .form-control:-ms-input-placeholder { - color: #777; + color: #999; } .form-control::-webkit-input-placeholder { - color: #777; + color: #999; } .form-control[disabled], .form-control[readonly], @@ -2397,24 +2400,25 @@ textarea.form-control { input[type="search"] { -webkit-appearance: none; } -input[type="date"], -input[type="time"], -input[type="datetime-local"], -input[type="month"] { - line-height: 34px; - line-height: 1.42857143 \0; -} -input[type="date"].input-sm, -input[type="time"].input-sm, -input[type="datetime-local"].input-sm, -input[type="month"].input-sm { - line-height: 30px; -} -input[type="date"].input-lg, -input[type="time"].input-lg, -input[type="datetime-local"].input-lg, -input[type="month"].input-lg { - line-height: 46px; +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg { + line-height: 46px; + } } .form-group { margin-bottom: 15px; @@ -2423,12 +2427,12 @@ input[type="month"].input-lg { .checkbox { position: relative; display: block; - min-height: 20px; margin-top: 10px; margin-bottom: 10px; } .radio label, .checkbox label { + min-height: 20px; padding-left: 20px; margin-bottom: 0; font-weight: normal; @@ -2491,35 +2495,41 @@ fieldset[disabled] .checkbox label { padding-left: 0; } .input-sm, -.form-horizontal .form-group-sm .form-control { +.form-group-sm .form-control { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } -select.input-sm { +select.input-sm, +select.form-group-sm .form-control { height: 30px; line-height: 30px; } textarea.input-sm, -select[multiple].input-sm { +textarea.form-group-sm .form-control, +select[multiple].input-sm, +select[multiple].form-group-sm .form-control { height: auto; } .input-lg, -.form-horizontal .form-group-lg .form-control { +.form-group-lg .form-control { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } -select.input-lg { +select.input-lg, +select.form-group-lg .form-control { height: 46px; line-height: 46px; } textarea.input-lg, -select[multiple].input-lg { +textarea.form-group-lg .form-control, +select[multiple].input-lg, +select[multiple].form-group-lg .form-control { height: auto; } .has-feedback { @@ -2530,7 +2540,7 @@ select[multiple].input-lg { } .form-control-feedback { position: absolute; - top: 25px; + top: 0; right: 0; z-index: 2; display: block; @@ -2538,6 +2548,7 @@ select[multiple].input-lg { height: 34px; line-height: 34px; text-align: center; + pointer-events: none; } .input-lg + .form-control-feedback { width: 46px; @@ -2554,7 +2565,11 @@ select[multiple].input-lg { .has-success .radio, .has-success .checkbox, .has-success .radio-inline, -.has-success .checkbox-inline { +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { color: #3c763d; } .has-success .form-control { @@ -2580,7 +2595,11 @@ select[multiple].input-lg { .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, -.has-warning .checkbox-inline { +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { color: #8a6d3b; } .has-warning .form-control { @@ -2606,7 +2625,11 @@ select[multiple].input-lg { .has-error .radio, .has-error .checkbox, .has-error .radio-inline, -.has-error .checkbox-inline { +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { color: #a94442; } .has-error .form-control { @@ -2627,6 +2650,9 @@ select[multiple].input-lg { .has-error .form-control-feedback { color: #a94442; } +.has-feedback label ~ .form-control-feedback { + top: 25px; +} .has-feedback label.sr-only ~ .form-control-feedback { top: 0; } @@ -2647,6 +2673,9 @@ select[multiple].input-lg { width: auto; vertical-align: middle; } + .form-inline .form-control-static { + display: inline-block; + } .form-inline .input-group { display: inline-table; vertical-align: middle; @@ -2707,7 +2736,6 @@ select[multiple].input-lg { } } .form-horizontal .has-feedback .form-control-feedback { - top: 0; right: 15px; } @media (min-width: 768px) { @@ -2730,6 +2758,8 @@ select[multiple].input-lg { text-align: center; white-space: nowrap; vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; @@ -2741,13 +2771,17 @@ select[multiple].input-lg { } .btn:focus, .btn:active:focus, -.btn.active:focus { +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, -.btn:focus { +.btn:focus, +.btn.focus { color: #333; text-decoration: none; } @@ -2775,6 +2809,7 @@ fieldset[disabled] .btn { } .btn-default:hover, .btn-default:focus, +.btn-default.focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { @@ -2796,6 +2831,9 @@ fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, @@ -2811,17 +2849,18 @@ fieldset[disabled] .btn-default.active { } .btn-primary { color: #fff; - background-color: #428bca; - border-color: #357ebd; + background-color: #337ab7; + border-color: #2e6da4; } .btn-primary:hover, .btn-primary:focus, +.btn-primary.focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { color: #fff; - background-color: #3071a9; - border-color: #285e8e; + background-color: #286090; + border-color: #204d74; } .btn-primary:active, .btn-primary.active, @@ -2837,17 +2876,20 @@ fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { - background-color: #428bca; - border-color: #357ebd; + background-color: #337ab7; + border-color: #2e6da4; } .btn-primary .badge { - color: #428bca; + color: #337ab7; background-color: #fff; } .btn-success { @@ -2857,6 +2899,7 @@ fieldset[disabled] .btn-primary.active { } .btn-success:hover, .btn-success:focus, +.btn-success.focus, .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { @@ -2878,6 +2921,9 @@ fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, @@ -2898,6 +2944,7 @@ fieldset[disabled] .btn-success.active { } .btn-info:hover, .btn-info:focus, +.btn-info.focus, .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { @@ -2919,6 +2966,9 @@ fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, @@ -2939,6 +2989,7 @@ fieldset[disabled] .btn-info.active { } .btn-warning:hover, .btn-warning:focus, +.btn-warning.focus, .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { @@ -2960,6 +3011,9 @@ fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, @@ -2980,6 +3034,7 @@ fieldset[disabled] .btn-warning.active { } .btn-danger:hover, .btn-danger:focus, +.btn-danger.focus, .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { @@ -3001,6 +3056,9 @@ fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, @@ -3016,12 +3074,12 @@ fieldset[disabled] .btn-danger.active { } .btn-link { font-weight: normal; - color: #428bca; - cursor: pointer; + color: #337ab7; border-radius: 0; } .btn-link, .btn-link:active, +.btn-link.active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; @@ -3036,7 +3094,7 @@ fieldset[disabled] .btn-link { } .btn-link:hover, .btn-link:focus { - color: #2a6496; + color: #23527c; text-decoration: underline; background-color: transparent; } @@ -3091,9 +3149,11 @@ input[type="button"].btn-block { } .collapse { display: none; + visibility: hidden; } .collapse.in { display: block; + visibility: visible; } tr.collapse.in { display: table-row; @@ -3105,9 +3165,15 @@ tbody.collapse.in { position: relative; height: 0; overflow: hidden; - -webkit-transition: height .35s ease; - -o-transition: height .35s ease; - transition: height .35s ease; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; } .caret { display: inline-block; @@ -3177,7 +3243,7 @@ tbody.collapse.in { .dropdown-menu > .active > a:focus { color: #fff; text-decoration: none; - background-color: #428bca; + background-color: #337ab7; outline: 0; } .dropdown-menu > .disabled > a, @@ -3270,10 +3336,6 @@ tbody.collapse.in { .btn-group-vertical > .btn.active { z-index: 2; } -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus { - outline: 0; -} .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, @@ -3413,12 +3475,13 @@ tbody.collapse.in { .btn-group-justified > .btn-group .dropdown-menu { left: auto; } -[data-toggle="buttons"] > .btn > input[type="radio"], -[data-toggle="buttons"] > .btn > input[type="checkbox"] { +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { position: absolute; - z-index: -1; - filter: alpha(opacity=0); - opacity: 0; + clip: rect(0, 0, 0, 0); + pointer-events: none; } .input-group { position: relative; @@ -3607,7 +3670,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav .open > a:hover, .nav .open > a:focus { background-color: #eee; - border-color: #428bca; + border-color: #337ab7; } .nav .nav-divider { height: 1px; @@ -3700,7 +3763,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #fff; - background-color: #428bca; + background-color: #337ab7; } .nav-stacked > li { float: none; @@ -3757,9 +3820,11 @@ select[multiple].input-group-sm > .input-group-btn > .btn { } .tab-content > .tab-pane { display: none; + visibility: hidden; } .tab-content > .active { display: block; + visibility: visible; } .nav-tabs .dropdown-menu { margin-top: -1px; @@ -3806,6 +3871,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { height: auto !important; padding-bottom: 0; overflow: visible !important; + visibility: visible !important; } .navbar-collapse.in { overflow-y: visible; @@ -3821,7 +3887,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .navbar-fixed-bottom .navbar-collapse { max-height: 340px; } -@media (max-width: 480px) and (orientation: landscape) { +@media (max-device-width: 480px) and (orientation: landscape) { .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 200px; @@ -3858,9 +3924,6 @@ select[multiple].input-group-sm > .input-group-btn > .btn { right: 0; left: 0; z-index: 1030; - -webkit-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); } @media (min-width: 768px) { .navbar-fixed-top, @@ -3888,6 +3951,9 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .navbar-brand:focus { text-decoration: none; } +.navbar-brand > img { + display: block; +} @media (min-width: 768px) { .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { @@ -3966,17 +4032,6 @@ select[multiple].input-group-sm > .input-group-btn > .btn { padding-top: 15px; padding-bottom: 15px; } - .navbar-nav.navbar-right:last-child { - margin-right: -15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - } } .navbar-form { padding: 10px 15px; @@ -4000,6 +4055,9 @@ select[multiple].input-group-sm > .input-group-btn > .btn { width: auto; vertical-align: middle; } + .navbar-form .form-control-static { + display: inline-block; + } .navbar-form .input-group { display: inline-table; vertical-align: middle; @@ -4040,6 +4098,9 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .navbar-form .form-group { margin-bottom: 5px; } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } } @media (min-width: 768px) { .navbar-form { @@ -4052,9 +4113,6 @@ select[multiple].input-group-sm > .input-group-btn > .btn { -webkit-box-shadow: none; box-shadow: none; } - .navbar-form.navbar-right:last-child { - margin-right: -15px; - } } .navbar-nav > li > .dropdown-menu { margin-top: 0; @@ -4062,6 +4120,8 @@ select[multiple].input-group-sm > .input-group-btn > .btn { border-top-right-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-top-left-radius: 4px; + border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } @@ -4087,7 +4147,16 @@ select[multiple].input-group-sm > .input-group-btn > .btn { margin-right: 15px; margin-left: 15px; } - .navbar-text.navbar-right:last-child { +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { margin-right: 0; } } @@ -4192,7 +4261,7 @@ fieldset[disabled] .navbar-default .btn-link:focus { border-color: #080808; } .navbar-inverse .navbar-brand { - color: #777; + color: #9d9d9d; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { @@ -4200,10 +4269,10 @@ fieldset[disabled] .navbar-default .btn-link:focus { background-color: transparent; } .navbar-inverse .navbar-text { - color: #777; + color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a { - color: #777; + color: #9d9d9d; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { @@ -4250,7 +4319,7 @@ fieldset[disabled] .navbar-default .btn-link:focus { background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #777; + color: #9d9d9d; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { @@ -4271,13 +4340,13 @@ fieldset[disabled] .navbar-default .btn-link:focus { } } .navbar-inverse .navbar-link { - color: #777; + color: #9d9d9d; } .navbar-inverse .navbar-link:hover { color: #fff; } .navbar-inverse .btn-link { - color: #777; + color: #9d9d9d; } .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { @@ -4323,7 +4392,7 @@ fieldset[disabled] .navbar-inverse .btn-link:focus { padding: 6px 12px; margin-left: -1px; line-height: 1.42857143; - color: #428bca; + color: #337ab7; text-decoration: none; background-color: #fff; border: 1px solid #ddd; @@ -4343,7 +4412,7 @@ fieldset[disabled] .navbar-inverse .btn-link:focus { .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { - color: #2a6496; + color: #23527c; background-color: #eee; border-color: #ddd; } @@ -4356,8 +4425,8 @@ fieldset[disabled] .navbar-inverse .btn-link:focus { z-index: 2; color: #fff; cursor: default; - background-color: #428bca; - border-color: #428bca; + background-color: #337ab7; + border-color: #337ab7; } .pagination > .disabled > span, .pagination > .disabled > span:hover, @@ -4471,11 +4540,11 @@ a.label:focus { background-color: #5e5e5e; } .label-primary { - background-color: #428bca; + background-color: #337ab7; } .label-primary[href]:hover, .label-primary[href]:focus { - background-color: #3071a9; + background-color: #286090; } .label-success { background-color: #5cb85c; @@ -4536,16 +4605,22 @@ a.badge:focus { text-decoration: none; cursor: pointer; } -a.list-group-item.active > .badge, +.list-group-item.active > .badge, .nav-pills > .active > a > .badge { - color: #428bca; + color: #337ab7; background-color: #fff; } +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { - padding: 30px; + padding: 30px 15px; margin-bottom: 30px; color: inherit; background-color: #eee; @@ -4562,7 +4637,8 @@ a.list-group-item.active > .badge, .jumbotron > hr { border-top-color: #d5d5d5; } -.container .jumbotron { +.container .jumbotron, +.container-fluid .jumbotron { border-radius: 6px; } .jumbotron .container { @@ -4570,10 +4646,10 @@ a.list-group-item.active > .badge, } @media screen and (min-width: 768px) { .jumbotron { - padding-top: 48px; - padding-bottom: 48px; + padding: 48px 0; } - .container .jumbotron { + .container .jumbotron, + .container-fluid .jumbotron { padding-right: 60px; padding-left: 60px; } @@ -4590,9 +4666,9 @@ a.list-group-item.active > .badge, background-color: #fff; border: 1px solid #ddd; border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; } .thumbnail > img, .thumbnail a > img { @@ -4602,7 +4678,7 @@ a.list-group-item.active > .badge, a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { - border-color: #428bca; + border-color: #337ab7; } .thumbnail .caption { padding: 9px; @@ -4724,7 +4800,7 @@ a.thumbnail.active { line-height: 20px; color: #fff; text-align: center; - background-color: #428bca; + background-color: #337ab7; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); -webkit-transition: width .6s ease; @@ -4745,18 +4821,6 @@ a.thumbnail.active { -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } -.progress-bar[aria-valuenow="1"], -.progress-bar[aria-valuenow="2"] { - min-width: 30px; -} -.progress-bar[aria-valuenow="0"] { - min-width: 30px; - color: #777; - background-color: transparent; - background-image: none; - -webkit-box-shadow: none; - box-shadow: none; -} .progress-bar-success { background-color: #5cb85c; } @@ -4789,29 +4853,35 @@ a.thumbnail.active { background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media, -.media .media { +.media { margin-top: 15px; } .media:first-child { margin-top: 0; } -.media-object { - display: block; +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; } .media-heading { - margin: 0 0 5px; -} -.media > .pull-left { - margin-right: 10px; -} -.media > .pull-right { - margin-left: 10px; + margin-top: 0; + margin-bottom: 5px; } .media-list { padding-left: 0; @@ -4838,12 +4908,6 @@ a.thumbnail.active { border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} a.list-group-item { color: #555; } @@ -4860,6 +4924,7 @@ a.list-group-item:focus { .list-group-item.disabled:hover, .list-group-item.disabled:focus { color: #777; + cursor: not-allowed; background-color: #eee; } .list-group-item.disabled .list-group-item-heading, @@ -4877,8 +4942,8 @@ a.list-group-item:focus { .list-group-item.active:focus { z-index: 2; color: #fff; - background-color: #428bca; - border-color: #428bca; + background-color: #337ab7; + border-color: #337ab7; } .list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, @@ -4894,7 +4959,7 @@ a.list-group-item:focus { .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { - color: #e1edf7; + color: #c7ddef; } .list-group-item-success { color: #3c763d; @@ -5028,19 +5093,23 @@ a.list-group-item-danger.active:focus { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } -.panel > .list-group { +.panel > .list-group, +.panel > .panel-collapse > .list-group { margin-bottom: 0; } -.panel > .list-group .list-group-item { +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { border-width: 1px 0; border-radius: 0; } -.panel > .list-group:first-child .list-group-item:first-child { +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { border-top: 0; border-top-left-radius: 3px; border-top-right-radius: 3px; } -.panel > .list-group:last-child .list-group-item:last-child { +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { border-bottom: 0; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; @@ -5056,11 +5125,24 @@ a.list-group-item-danger.active:focus { .panel > .panel-collapse > .table { margin-bottom: 0; } +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} .panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { border-top-left-radius: 3px; border-top-right-radius: 3px; } +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, @@ -5086,6 +5168,13 @@ a.list-group-item-danger.active:focus { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, @@ -5107,7 +5196,9 @@ a.list-group-item-danger.active:focus { border-bottom-right-radius: 3px; } .panel > .panel-body + .table, -.panel > .panel-body + .table-responsive { +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { border-top: 1px solid #ddd; } .panel > .table > tbody:first-child > tr:first-child th, @@ -5183,7 +5274,8 @@ a.list-group-item-danger.active:focus { .panel-group .panel-heading { border-bottom: 0; } -.panel-group .panel-heading + .panel-collapse > .panel-body { +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { border-top: 1px solid #ddd; } .panel-group .panel-footer { @@ -5211,22 +5303,22 @@ a.list-group-item-danger.active:focus { border-bottom-color: #ddd; } .panel-primary { - border-color: #428bca; + border-color: #337ab7; } .panel-primary > .panel-heading { color: #fff; - background-color: #428bca; - border-color: #428bca; + background-color: #337ab7; + border-color: #337ab7; } .panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #428bca; + border-top-color: #337ab7; } .panel-primary > .panel-heading .badge { - color: #428bca; + color: #337ab7; background-color: #fff; } .panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #428bca; + border-bottom-color: #337ab7; } .panel-success { border-color: #d6e9c6; @@ -5310,7 +5402,8 @@ a.list-group-item-danger.active:focus { .embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, -.embed-responsive object { +.embed-responsive object, +.embed-responsive video { position: absolute; top: 0; bottom: 0; @@ -5381,7 +5474,7 @@ button.close { right: 0; bottom: 0; left: 0; - z-index: 1050; + z-index: 1040; display: none; overflow: hidden; -webkit-overflow-scrolling: touch; @@ -5391,14 +5484,16 @@ button.close { -webkit-transition: -webkit-transform .3s ease-out; -o-transition: -o-transform .3s ease-out; transition: transform .3s ease-out; - -webkit-transform: translate3d(0, -25%, 0); - -o-transform: translate3d(0, -25%, 0); - transform: translate3d(0, -25%, 0); + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); } .modal.in .modal-dialog { - -webkit-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); } .modal-open .modal { overflow-x: hidden; @@ -5422,12 +5517,10 @@ button.close { box-shadow: 0 3px 9px rgba(0, 0, 0, .5); } .modal-backdrop { - position: fixed; + position: absolute; top: 0; right: 0; - bottom: 0; left: 0; - z-index: 1040; background-color: #000; } .modal-backdrop.fade { @@ -5498,7 +5591,9 @@ button.close { position: absolute; z-index: 1070; display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; + font-weight: normal; line-height: 1.4; visibility: visible; filter: alpha(opacity=0); @@ -5548,14 +5643,16 @@ button.close { border-top-color: #000; } .tooltip.top-left .tooltip-arrow { + right: 5px; bottom: 0; - left: 5px; + margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.top-right .tooltip-arrow { - right: 5px; bottom: 0; + left: 5px; + margin-bottom: -5px; border-width: 5px 5px 0; border-top-color: #000; } @@ -5582,13 +5679,15 @@ button.close { } .tooltip.bottom-left .tooltip-arrow { top: 0; - left: 5px; + right: 5px; + margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .tooltip.bottom-right .tooltip-arrow { top: 0; - right: 5px; + left: 5px; + margin-top: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } @@ -5600,6 +5699,10 @@ button.close { display: none; max-width: 276px; padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; text-align: left; white-space: normal; background-color: #fff; @@ -5627,8 +5730,6 @@ button.close { padding: 8px 14px; margin: 0; font-size: 14px; - font-weight: normal; - line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; @@ -5731,6 +5832,37 @@ button.close { .carousel-inner > .item > a > img { line-height: 1; } +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000; + perspective: 1000; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { @@ -5986,9 +6118,6 @@ button.close { } .affix { position: fixed; - -webkit-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); } @-ms-viewport { width: device-width; diff --git a/upload/styles/bootstrap/fonts/glyphicons-halflings-regular.eot b/styles/bootstrap/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from upload/styles/bootstrap/fonts/glyphicons-halflings-regular.eot rename to styles/bootstrap/fonts/glyphicons-halflings-regular.eot diff --git a/upload/styles/bootstrap/fonts/glyphicons-halflings-regular.svg b/styles/bootstrap/fonts/glyphicons-halflings-regular.svg similarity index 99% rename from upload/styles/bootstrap/fonts/glyphicons-halflings-regular.svg rename to styles/bootstrap/fonts/glyphicons-halflings-regular.svg index e3e2dc739..25691af8f 100644 --- a/upload/styles/bootstrap/fonts/glyphicons-halflings-regular.svg +++ b/styles/bootstrap/fonts/glyphicons-halflings-regular.svg @@ -226,4 +226,4 @@
    - \ No newline at end of file + \ No newline at end of file diff --git a/upload/styles/bootstrap/fonts/glyphicons-halflings-regular.ttf b/styles/bootstrap/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from upload/styles/bootstrap/fonts/glyphicons-halflings-regular.ttf rename to styles/bootstrap/fonts/glyphicons-halflings-regular.ttf diff --git a/upload/styles/bootstrap/fonts/glyphicons-halflings-regular.woff b/styles/bootstrap/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from upload/styles/bootstrap/fonts/glyphicons-halflings-regular.woff rename to styles/bootstrap/fonts/glyphicons-halflings-regular.woff diff --git a/styles/bootstrap/js/bootstrap.min.js b/styles/bootstrap/js/bootstrap.min.js new file mode 100644 index 000000000..d83986590 --- /dev/null +++ b/styles/bootstrap/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.1",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.1",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.1",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c="prev"==a?-1:1,d=this.getItemIndex(b),e=(d+c)%this.$items.length;return this.$items.eq(e)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i="next"==b?"first":"last",j=this;if(!f.length){if(!this.options.wrap)return;f=this.$element.find(".item")[i]()}if(f.hasClass("active"))return this.sliding=!1;var k=f[0],l=a.Event("slide.bs.carousel",{relatedTarget:k,direction:h});if(this.$element.trigger(l),!l.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var m=a(this.$indicators.children()[this.getItemIndex(f)]);m&&m.addClass("active")}var n=a.Event("slid.bs.carousel",{relatedTarget:k,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),j.sliding=!1,setTimeout(function(){j.$element.trigger(n)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(n)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.1",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.find("> .panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.1",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('
    - + - + + + + +
    {L_VERSION_INFORMATION}
    {L_TP_VERSION}:{L_TP_VERSION}: {$bb_cfg['tp_version']} [{$bb_cfg['tp_release_state']}]
    {L_TP_RELEASE_DATE}:{L_TP_RELEASE_DATE}: {$bb_cfg['tp_release_date']}
    {L_ZF_VERSION}:{$bb_cfg['tp_zf_version']}

    diff --git a/upload/styles/templates/default/common.tpl b/styles/templates/default/common.tpl similarity index 100% rename from upload/styles/templates/default/common.tpl rename to styles/templates/default/common.tpl diff --git a/upload/styles/templates/default/css/admin.css b/styles/templates/default/css/admin.css similarity index 100% rename from upload/styles/templates/default/css/admin.css rename to styles/templates/default/css/admin.css diff --git a/upload/styles/templates/default/css/main.css b/styles/templates/default/css/main.css similarity index 100% rename from upload/styles/templates/default/css/main.css rename to styles/templates/default/css/main.css diff --git a/upload/styles/templates/default/group.tpl b/styles/templates/default/group.tpl similarity index 100% rename from upload/styles/templates/default/group.tpl rename to styles/templates/default/group.tpl diff --git a/upload/styles/templates/default/group_edit.tpl b/styles/templates/default/group_edit.tpl similarity index 92% rename from upload/styles/templates/default/group_edit.tpl rename to styles/templates/default/group_edit.tpl index afbf9f1c7..e66ce14ac 100644 --- a/upload/styles/templates/default/group_edit.tpl +++ b/styles/templates/default/group_edit.tpl @@ -68,19 +68,11 @@ function manage_group(mode, value) { - {L_AVATAR}: - -

    + {L_AVATAR}:

    -

    - avatar -

    -
    -

    - -

    +

    {AVATAR_IMG}


    +

    -
    {AVATAR_EXPLAIN}
    diff --git a/upload/styles/templates/default/images/aerobg.png b/styles/templates/default/images/aerobg.png similarity index 100% rename from upload/styles/templates/default/images/aerobg.png rename to styles/templates/default/images/aerobg.png diff --git a/upload/styles/templates/default/images/arrow1.gif b/styles/templates/default/images/arrow1.gif similarity index 100% rename from upload/styles/templates/default/images/arrow1.gif rename to styles/templates/default/images/arrow1.gif diff --git a/upload/styles/templates/default/images/button.gif b/styles/templates/default/images/button.gif similarity index 100% rename from upload/styles/templates/default/images/button.gif rename to styles/templates/default/images/button.gif diff --git a/upload/styles/templates/default/images/cellpic.gif b/styles/templates/default/images/cellpic.gif similarity index 100% rename from upload/styles/templates/default/images/cellpic.gif rename to styles/templates/default/images/cellpic.gif diff --git a/upload/styles/templates/default/images/cellpic1.gif b/styles/templates/default/images/cellpic1.gif similarity index 100% rename from upload/styles/templates/default/images/cellpic1.gif rename to styles/templates/default/images/cellpic1.gif diff --git a/upload/styles/templates/default/images/def_button.png b/styles/templates/default/images/def_button.png similarity index 100% rename from upload/styles/templates/default/images/def_button.png rename to styles/templates/default/images/def_button.png diff --git a/upload/styles/templates/default/images/def_button_light.png b/styles/templates/default/images/def_button_light.png similarity index 100% rename from upload/styles/templates/default/images/def_button_light.png rename to styles/templates/default/images/def_button_light.png diff --git a/upload/styles/templates/default/images/feed.png b/styles/templates/default/images/feed.png similarity index 100% rename from upload/styles/templates/default/images/feed.png rename to styles/templates/default/images/feed.png diff --git a/upload/styles/templates/default/images/folder.gif b/styles/templates/default/images/folder.gif similarity index 100% rename from upload/styles/templates/default/images/folder.gif rename to styles/templates/default/images/folder.gif diff --git a/upload/styles/templates/default/images/folder_announce.gif b/styles/templates/default/images/folder_announce.gif similarity index 100% rename from upload/styles/templates/default/images/folder_announce.gif rename to styles/templates/default/images/folder_announce.gif diff --git a/upload/styles/templates/default/images/folder_announce_new.gif b/styles/templates/default/images/folder_announce_new.gif similarity index 100% rename from upload/styles/templates/default/images/folder_announce_new.gif rename to styles/templates/default/images/folder_announce_new.gif diff --git a/upload/styles/templates/default/images/folder_big.gif b/styles/templates/default/images/folder_big.gif similarity index 100% rename from upload/styles/templates/default/images/folder_big.gif rename to styles/templates/default/images/folder_big.gif diff --git a/upload/styles/templates/default/images/folder_dl.gif b/styles/templates/default/images/folder_dl.gif similarity index 100% rename from upload/styles/templates/default/images/folder_dl.gif rename to styles/templates/default/images/folder_dl.gif diff --git a/upload/styles/templates/default/images/folder_dl_hot.gif b/styles/templates/default/images/folder_dl_hot.gif similarity index 100% rename from upload/styles/templates/default/images/folder_dl_hot.gif rename to styles/templates/default/images/folder_dl_hot.gif diff --git a/upload/styles/templates/default/images/folder_dl_hot_new.gif b/styles/templates/default/images/folder_dl_hot_new.gif similarity index 100% rename from upload/styles/templates/default/images/folder_dl_hot_new.gif rename to styles/templates/default/images/folder_dl_hot_new.gif diff --git a/upload/styles/templates/default/images/folder_dl_new.gif b/styles/templates/default/images/folder_dl_new.gif similarity index 100% rename from upload/styles/templates/default/images/folder_dl_new.gif rename to styles/templates/default/images/folder_dl_new.gif diff --git a/upload/styles/templates/default/images/folder_hot.gif b/styles/templates/default/images/folder_hot.gif similarity index 100% rename from upload/styles/templates/default/images/folder_hot.gif rename to styles/templates/default/images/folder_hot.gif diff --git a/upload/styles/templates/default/images/folder_lock.gif b/styles/templates/default/images/folder_lock.gif similarity index 100% rename from upload/styles/templates/default/images/folder_lock.gif rename to styles/templates/default/images/folder_lock.gif diff --git a/upload/styles/templates/default/images/folder_lock_new.gif b/styles/templates/default/images/folder_lock_new.gif similarity index 100% rename from upload/styles/templates/default/images/folder_lock_new.gif rename to styles/templates/default/images/folder_lock_new.gif diff --git a/upload/styles/templates/default/images/folder_locked_big.gif b/styles/templates/default/images/folder_locked_big.gif similarity index 100% rename from upload/styles/templates/default/images/folder_locked_big.gif rename to styles/templates/default/images/folder_locked_big.gif diff --git a/upload/styles/templates/default/images/folder_new.gif b/styles/templates/default/images/folder_new.gif similarity index 100% rename from upload/styles/templates/default/images/folder_new.gif rename to styles/templates/default/images/folder_new.gif diff --git a/upload/styles/templates/default/images/folder_new_big.gif b/styles/templates/default/images/folder_new_big.gif similarity index 100% rename from upload/styles/templates/default/images/folder_new_big.gif rename to styles/templates/default/images/folder_new_big.gif diff --git a/upload/styles/templates/default/images/folder_new_hot.gif b/styles/templates/default/images/folder_new_hot.gif similarity index 100% rename from upload/styles/templates/default/images/folder_new_hot.gif rename to styles/templates/default/images/folder_new_hot.gif diff --git a/upload/styles/templates/default/images/folder_sticky.gif b/styles/templates/default/images/folder_sticky.gif similarity index 100% rename from upload/styles/templates/default/images/folder_sticky.gif rename to styles/templates/default/images/folder_sticky.gif diff --git a/upload/styles/templates/default/images/folder_sticky_new.gif b/styles/templates/default/images/folder_sticky_new.gif similarity index 100% rename from upload/styles/templates/default/images/folder_sticky_new.gif rename to styles/templates/default/images/folder_sticky_new.gif diff --git a/upload/styles/templates/default/images/hr200_ltr_gradient.jpg b/styles/templates/default/images/hr200_ltr_gradient.jpg similarity index 100% rename from upload/styles/templates/default/images/hr200_ltr_gradient.jpg rename to styles/templates/default/images/hr200_ltr_gradient.jpg diff --git a/upload/styles/templates/default/images/hr400_ltr_gradient.jpg b/styles/templates/default/images/hr400_ltr_gradient.jpg similarity index 100% rename from upload/styles/templates/default/images/hr400_ltr_gradient.jpg rename to styles/templates/default/images/hr400_ltr_gradient.jpg diff --git a/upload/styles/templates/default/images/icon_birthday.gif b/styles/templates/default/images/icon_birthday.gif similarity index 100% rename from upload/styles/templates/default/images/icon_birthday.gif rename to styles/templates/default/images/icon_birthday.gif diff --git a/upload/styles/templates/default/images/icon_delete.gif b/styles/templates/default/images/icon_delete.gif similarity index 100% rename from upload/styles/templates/default/images/icon_delete.gif rename to styles/templates/default/images/icon_delete.gif diff --git a/upload/styles/templates/default/images/icon_female.gif b/styles/templates/default/images/icon_female.gif similarity index 100% rename from upload/styles/templates/default/images/icon_female.gif rename to styles/templates/default/images/icon_female.gif diff --git a/upload/styles/templates/default/images/icon_latest_reply.gif b/styles/templates/default/images/icon_latest_reply.gif similarity index 100% rename from upload/styles/templates/default/images/icon_latest_reply.gif rename to styles/templates/default/images/icon_latest_reply.gif diff --git a/upload/styles/templates/default/images/icon_male.gif b/styles/templates/default/images/icon_male.gif similarity index 100% rename from upload/styles/templates/default/images/icon_male.gif rename to styles/templates/default/images/icon_male.gif diff --git a/upload/styles/templates/default/images/icon_minipost.gif b/styles/templates/default/images/icon_minipost.gif similarity index 100% rename from upload/styles/templates/default/images/icon_minipost.gif rename to styles/templates/default/images/icon_minipost.gif diff --git a/upload/styles/templates/default/images/icon_minipost_new.gif b/styles/templates/default/images/icon_minipost_new.gif similarity index 100% rename from upload/styles/templates/default/images/icon_minipost_new.gif rename to styles/templates/default/images/icon_minipost_new.gif diff --git a/upload/styles/templates/default/images/icon_minus_1.gif b/styles/templates/default/images/icon_minus_1.gif similarity index 100% rename from upload/styles/templates/default/images/icon_minus_1.gif rename to styles/templates/default/images/icon_minus_1.gif diff --git a/upload/styles/templates/default/images/icon_minus_2.gif b/styles/templates/default/images/icon_minus_2.gif similarity index 100% rename from upload/styles/templates/default/images/icon_minus_2.gif rename to styles/templates/default/images/icon_minus_2.gif diff --git a/upload/styles/templates/default/images/icon_mod.gif b/styles/templates/default/images/icon_mod.gif similarity index 100% rename from upload/styles/templates/default/images/icon_mod.gif rename to styles/templates/default/images/icon_mod.gif diff --git a/upload/styles/templates/default/images/icon_newest_reply.gif b/styles/templates/default/images/icon_newest_reply.gif similarity index 100% rename from upload/styles/templates/default/images/icon_newest_reply.gif rename to styles/templates/default/images/icon_newest_reply.gif diff --git a/upload/styles/templates/default/images/icon_nogender.gif b/styles/templates/default/images/icon_nogender.gif similarity index 100% rename from upload/styles/templates/default/images/icon_nogender.gif rename to styles/templates/default/images/icon_nogender.gif diff --git a/upload/styles/templates/default/images/icon_plus_1.gif b/styles/templates/default/images/icon_plus_1.gif similarity index 100% rename from upload/styles/templates/default/images/icon_plus_1.gif rename to styles/templates/default/images/icon_plus_1.gif diff --git a/upload/styles/templates/default/images/icon_plus_2.gif b/styles/templates/default/images/icon_plus_2.gif similarity index 100% rename from upload/styles/templates/default/images/icon_plus_2.gif rename to styles/templates/default/images/icon_plus_2.gif diff --git a/upload/styles/templates/default/images/img_alert.gif b/styles/templates/default/images/img_alert.gif similarity index 100% rename from upload/styles/templates/default/images/img_alert.gif rename to styles/templates/default/images/img_alert.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_edit.gif b/styles/templates/default/images/lang/en/icon_edit.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_edit.gif rename to styles/templates/default/images/lang/en/icon_edit.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_email.gif b/styles/templates/default/images/lang/en/icon_email.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_email.gif rename to styles/templates/default/images/lang/en/icon_email.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_icq_add.gif b/styles/templates/default/images/lang/en/icon_icq_add.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_icq_add.gif rename to styles/templates/default/images/lang/en/icon_icq_add.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_ip.gif b/styles/templates/default/images/lang/en/icon_ip.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_ip.gif rename to styles/templates/default/images/lang/en/icon_ip.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_mc.gif b/styles/templates/default/images/lang/en/icon_mc.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_mc.gif rename to styles/templates/default/images/lang/en/icon_mc.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_pm.gif b/styles/templates/default/images/lang/en/icon_pm.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_pm.gif rename to styles/templates/default/images/lang/en/icon_pm.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_poll.gif b/styles/templates/default/images/lang/en/icon_poll.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_poll.gif rename to styles/templates/default/images/lang/en/icon_poll.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_profile.gif b/styles/templates/default/images/lang/en/icon_profile.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_profile.gif rename to styles/templates/default/images/lang/en/icon_profile.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_quote.gif b/styles/templates/default/images/lang/en/icon_quote.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_quote.gif rename to styles/templates/default/images/lang/en/icon_quote.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_search.gif b/styles/templates/default/images/lang/en/icon_search.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_search.gif rename to styles/templates/default/images/lang/en/icon_search.gif diff --git a/upload/styles/templates/default/images/lang/en/icon_www.gif b/styles/templates/default/images/lang/en/icon_www.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/icon_www.gif rename to styles/templates/default/images/lang/en/icon_www.gif diff --git a/upload/styles/templates/default/images/lang/en/msg_newpost.gif b/styles/templates/default/images/lang/en/msg_newpost.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/msg_newpost.gif rename to styles/templates/default/images/lang/en/msg_newpost.gif diff --git a/upload/styles/templates/default/images/lang/en/post.gif b/styles/templates/default/images/lang/en/post.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/post.gif rename to styles/templates/default/images/lang/en/post.gif diff --git a/upload/styles/templates/default/images/lang/en/release.gif b/styles/templates/default/images/lang/en/release.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/release.gif rename to styles/templates/default/images/lang/en/release.gif diff --git a/upload/styles/templates/default/images/lang/en/reply-locked.gif b/styles/templates/default/images/lang/en/reply-locked.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/reply-locked.gif rename to styles/templates/default/images/lang/en/reply-locked.gif diff --git a/upload/styles/templates/default/images/lang/en/reply.gif b/styles/templates/default/images/lang/en/reply.gif similarity index 100% rename from upload/styles/templates/default/images/lang/en/reply.gif rename to styles/templates/default/images/lang/en/reply.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_edit.gif b/styles/templates/default/images/lang/ru/icon_edit.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_edit.gif rename to styles/templates/default/images/lang/ru/icon_edit.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_email.gif b/styles/templates/default/images/lang/ru/icon_email.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_email.gif rename to styles/templates/default/images/lang/ru/icon_email.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_icq_add.gif b/styles/templates/default/images/lang/ru/icon_icq_add.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_icq_add.gif rename to styles/templates/default/images/lang/ru/icon_icq_add.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_ip.gif b/styles/templates/default/images/lang/ru/icon_ip.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_ip.gif rename to styles/templates/default/images/lang/ru/icon_ip.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_mc.gif b/styles/templates/default/images/lang/ru/icon_mc.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_mc.gif rename to styles/templates/default/images/lang/ru/icon_mc.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_pm.gif b/styles/templates/default/images/lang/ru/icon_pm.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_pm.gif rename to styles/templates/default/images/lang/ru/icon_pm.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_poll.gif b/styles/templates/default/images/lang/ru/icon_poll.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_poll.gif rename to styles/templates/default/images/lang/ru/icon_poll.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_profile.gif b/styles/templates/default/images/lang/ru/icon_profile.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_profile.gif rename to styles/templates/default/images/lang/ru/icon_profile.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_quote.gif b/styles/templates/default/images/lang/ru/icon_quote.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_quote.gif rename to styles/templates/default/images/lang/ru/icon_quote.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_search.gif b/styles/templates/default/images/lang/ru/icon_search.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_search.gif rename to styles/templates/default/images/lang/ru/icon_search.gif diff --git a/upload/styles/templates/default/images/lang/ru/icon_www.gif b/styles/templates/default/images/lang/ru/icon_www.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/icon_www.gif rename to styles/templates/default/images/lang/ru/icon_www.gif diff --git a/upload/styles/templates/default/images/lang/ru/msg_newpost.gif b/styles/templates/default/images/lang/ru/msg_newpost.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/msg_newpost.gif rename to styles/templates/default/images/lang/ru/msg_newpost.gif diff --git a/upload/styles/templates/default/images/lang/ru/post.gif b/styles/templates/default/images/lang/ru/post.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/post.gif rename to styles/templates/default/images/lang/ru/post.gif diff --git a/upload/styles/templates/default/images/lang/ru/release.gif b/styles/templates/default/images/lang/ru/release.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/release.gif rename to styles/templates/default/images/lang/ru/release.gif diff --git a/upload/styles/templates/default/images/lang/ru/reply-locked.gif b/styles/templates/default/images/lang/ru/reply-locked.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/reply-locked.gif rename to styles/templates/default/images/lang/ru/reply-locked.gif diff --git a/upload/styles/templates/default/images/lang/ru/reply.gif b/styles/templates/default/images/lang/ru/reply.gif similarity index 100% rename from upload/styles/templates/default/images/lang/ru/reply.gif rename to styles/templates/default/images/lang/ru/reply.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_edit.gif b/styles/templates/default/images/lang/uk/icon_edit.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_edit.gif rename to styles/templates/default/images/lang/uk/icon_edit.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_email.gif b/styles/templates/default/images/lang/uk/icon_email.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_email.gif rename to styles/templates/default/images/lang/uk/icon_email.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_icq_add.gif b/styles/templates/default/images/lang/uk/icon_icq_add.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_icq_add.gif rename to styles/templates/default/images/lang/uk/icon_icq_add.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_ip.gif b/styles/templates/default/images/lang/uk/icon_ip.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_ip.gif rename to styles/templates/default/images/lang/uk/icon_ip.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_mc.gif b/styles/templates/default/images/lang/uk/icon_mc.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_mc.gif rename to styles/templates/default/images/lang/uk/icon_mc.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_pm.gif b/styles/templates/default/images/lang/uk/icon_pm.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_pm.gif rename to styles/templates/default/images/lang/uk/icon_pm.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_poll.gif b/styles/templates/default/images/lang/uk/icon_poll.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_poll.gif rename to styles/templates/default/images/lang/uk/icon_poll.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_profile.gif b/styles/templates/default/images/lang/uk/icon_profile.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_profile.gif rename to styles/templates/default/images/lang/uk/icon_profile.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_quote.gif b/styles/templates/default/images/lang/uk/icon_quote.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_quote.gif rename to styles/templates/default/images/lang/uk/icon_quote.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_search.gif b/styles/templates/default/images/lang/uk/icon_search.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_search.gif rename to styles/templates/default/images/lang/uk/icon_search.gif diff --git a/upload/styles/templates/default/images/lang/uk/icon_www.gif b/styles/templates/default/images/lang/uk/icon_www.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/icon_www.gif rename to styles/templates/default/images/lang/uk/icon_www.gif diff --git a/upload/styles/templates/default/images/lang/uk/msg_newpost.gif b/styles/templates/default/images/lang/uk/msg_newpost.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/msg_newpost.gif rename to styles/templates/default/images/lang/uk/msg_newpost.gif diff --git a/upload/styles/templates/default/images/lang/uk/post.gif b/styles/templates/default/images/lang/uk/post.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/post.gif rename to styles/templates/default/images/lang/uk/post.gif diff --git a/upload/styles/templates/default/images/lang/uk/release.gif b/styles/templates/default/images/lang/uk/release.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/release.gif rename to styles/templates/default/images/lang/uk/release.gif diff --git a/upload/styles/templates/default/images/lang/uk/reply-locked.gif b/styles/templates/default/images/lang/uk/reply-locked.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/reply-locked.gif rename to styles/templates/default/images/lang/uk/reply-locked.gif diff --git a/upload/styles/templates/default/images/lang/uk/reply.gif b/styles/templates/default/images/lang/uk/reply.gif similarity index 100% rename from upload/styles/templates/default/images/lang/uk/reply.gif rename to styles/templates/default/images/lang/uk/reply.gif diff --git a/upload/styles/templates/default/images/link_help.cur b/styles/templates/default/images/link_help.cur similarity index 100% rename from upload/styles/templates/default/images/link_help.cur rename to styles/templates/default/images/link_help.cur diff --git a/upload/styles/templates/default/images/loading.gif b/styles/templates/default/images/loading.gif similarity index 100% rename from upload/styles/templates/default/images/loading.gif rename to styles/templates/default/images/loading.gif diff --git a/upload/styles/templates/default/images/loading_1.gif b/styles/templates/default/images/loading_1.gif similarity index 100% rename from upload/styles/templates/default/images/loading_1.gif rename to styles/templates/default/images/loading_1.gif diff --git a/upload/styles/templates/default/images/menu_open.gif b/styles/templates/default/images/menu_open.gif similarity index 100% rename from upload/styles/templates/default/images/menu_open.gif rename to styles/templates/default/images/menu_open.gif diff --git a/upload/styles/templates/default/images/menu_open_1.gif b/styles/templates/default/images/menu_open_1.gif similarity index 100% rename from upload/styles/templates/default/images/menu_open_1.gif rename to styles/templates/default/images/menu_open_1.gif diff --git a/upload/styles/templates/default/images/msg_inbox.gif b/styles/templates/default/images/msg_inbox.gif similarity index 100% rename from upload/styles/templates/default/images/msg_inbox.gif rename to styles/templates/default/images/msg_inbox.gif diff --git a/upload/styles/templates/default/images/msg_outbox.gif b/styles/templates/default/images/msg_outbox.gif similarity index 100% rename from upload/styles/templates/default/images/msg_outbox.gif rename to styles/templates/default/images/msg_outbox.gif diff --git a/upload/styles/templates/default/images/msg_savebox.gif b/styles/templates/default/images/msg_savebox.gif similarity index 100% rename from upload/styles/templates/default/images/msg_savebox.gif rename to styles/templates/default/images/msg_savebox.gif diff --git a/upload/styles/templates/default/images/msg_sentbox.gif b/styles/templates/default/images/msg_sentbox.gif similarity index 100% rename from upload/styles/templates/default/images/msg_sentbox.gif rename to styles/templates/default/images/msg_sentbox.gif diff --git a/upload/styles/templates/default/images/progress_bar.gif b/styles/templates/default/images/progress_bar.gif similarity index 100% rename from upload/styles/templates/default/images/progress_bar.gif rename to styles/templates/default/images/progress_bar.gif diff --git a/upload/styles/templates/default/images/progress_bar_full.gif b/styles/templates/default/images/progress_bar_full.gif similarity index 100% rename from upload/styles/templates/default/images/progress_bar_full.gif rename to styles/templates/default/images/progress_bar_full.gif diff --git a/upload/styles/templates/default/images/spacer.gif b/styles/templates/default/images/spacer.gif similarity index 100% rename from upload/styles/templates/default/images/spacer.gif rename to styles/templates/default/images/spacer.gif diff --git a/upload/styles/templates/default/images/tbl_sort_asc.gif b/styles/templates/default/images/tbl_sort_asc.gif similarity index 100% rename from upload/styles/templates/default/images/tbl_sort_asc.gif rename to styles/templates/default/images/tbl_sort_asc.gif diff --git a/upload/styles/templates/default/images/tbl_sort_bg.gif b/styles/templates/default/images/tbl_sort_bg.gif similarity index 100% rename from upload/styles/templates/default/images/tbl_sort_bg.gif rename to styles/templates/default/images/tbl_sort_bg.gif diff --git a/upload/styles/templates/default/images/tbl_sort_desc.gif b/styles/templates/default/images/tbl_sort_desc.gif similarity index 100% rename from upload/styles/templates/default/images/tbl_sort_desc.gif rename to styles/templates/default/images/tbl_sort_desc.gif diff --git a/upload/styles/templates/default/images/topic_delete.gif b/styles/templates/default/images/topic_delete.gif similarity index 100% rename from upload/styles/templates/default/images/topic_delete.gif rename to styles/templates/default/images/topic_delete.gif diff --git a/upload/styles/templates/default/images/topic_dl.gif b/styles/templates/default/images/topic_dl.gif similarity index 100% rename from upload/styles/templates/default/images/topic_dl.gif rename to styles/templates/default/images/topic_dl.gif diff --git a/upload/styles/templates/default/images/topic_lock.gif b/styles/templates/default/images/topic_lock.gif similarity index 100% rename from upload/styles/templates/default/images/topic_lock.gif rename to styles/templates/default/images/topic_lock.gif diff --git a/upload/styles/templates/default/images/topic_move.gif b/styles/templates/default/images/topic_move.gif similarity index 100% rename from upload/styles/templates/default/images/topic_move.gif rename to styles/templates/default/images/topic_move.gif diff --git a/upload/styles/templates/default/images/topic_normal.gif b/styles/templates/default/images/topic_normal.gif similarity index 100% rename from upload/styles/templates/default/images/topic_normal.gif rename to styles/templates/default/images/topic_normal.gif diff --git a/upload/styles/templates/default/images/topic_split.gif b/styles/templates/default/images/topic_split.gif similarity index 100% rename from upload/styles/templates/default/images/topic_split.gif rename to styles/templates/default/images/topic_split.gif diff --git a/upload/styles/templates/default/images/topic_unlock.gif b/styles/templates/default/images/topic_unlock.gif similarity index 100% rename from upload/styles/templates/default/images/topic_unlock.gif rename to styles/templates/default/images/topic_unlock.gif diff --git a/upload/styles/templates/default/images/treeview/treeview-default-line.gif b/styles/templates/default/images/treeview/treeview-default-line.gif similarity index 100% rename from upload/styles/templates/default/images/treeview/treeview-default-line.gif rename to styles/templates/default/images/treeview/treeview-default-line.gif diff --git a/upload/styles/templates/default/images/treeview/treeview-default.gif b/styles/templates/default/images/treeview/treeview-default.gif similarity index 100% rename from upload/styles/templates/default/images/treeview/treeview-default.gif rename to styles/templates/default/images/treeview/treeview-default.gif diff --git a/upload/styles/templates/default/images/vote_lcap.gif b/styles/templates/default/images/vote_lcap.gif similarity index 100% rename from upload/styles/templates/default/images/vote_lcap.gif rename to styles/templates/default/images/vote_lcap.gif diff --git a/upload/styles/templates/default/images/vote_rcap.gif b/styles/templates/default/images/vote_rcap.gif similarity index 100% rename from upload/styles/templates/default/images/vote_rcap.gif rename to styles/templates/default/images/vote_rcap.gif diff --git a/upload/styles/templates/default/images/voting_bar.gif b/styles/templates/default/images/voting_bar.gif similarity index 100% rename from upload/styles/templates/default/images/voting_bar.gif rename to styles/templates/default/images/voting_bar.gif diff --git a/upload/styles/templates/default/images/whosonline.gif b/styles/templates/default/images/whosonline.gif similarity index 100% rename from upload/styles/templates/default/images/whosonline.gif rename to styles/templates/default/images/whosonline.gif diff --git a/upload/styles/templates/default/index.tpl b/styles/templates/default/index.tpl similarity index 100% rename from upload/styles/templates/default/index.tpl rename to styles/templates/default/index.tpl diff --git a/upload/styles/templates/default/index_map.tpl b/styles/templates/default/index_map.tpl similarity index 100% rename from upload/styles/templates/default/index_map.tpl rename to styles/templates/default/index_map.tpl diff --git a/upload/styles/templates/default/login.tpl b/styles/templates/default/login.tpl similarity index 100% rename from upload/styles/templates/default/login.tpl rename to styles/templates/default/login.tpl diff --git a/upload/styles/templates/default/memberlist.tpl b/styles/templates/default/memberlist.tpl similarity index 100% rename from upload/styles/templates/default/memberlist.tpl rename to styles/templates/default/memberlist.tpl diff --git a/upload/styles/templates/default/modcp.tpl b/styles/templates/default/modcp.tpl similarity index 100% rename from upload/styles/templates/default/modcp.tpl rename to styles/templates/default/modcp.tpl diff --git a/upload/styles/templates/default/modcp_split.tpl b/styles/templates/default/modcp_split.tpl similarity index 100% rename from upload/styles/templates/default/modcp_split.tpl rename to styles/templates/default/modcp_split.tpl diff --git a/upload/styles/templates/default/page_footer.tpl b/styles/templates/default/page_footer.tpl similarity index 100% rename from upload/styles/templates/default/page_footer.tpl rename to styles/templates/default/page_footer.tpl diff --git a/upload/styles/templates/default/page_header.tpl b/styles/templates/default/page_header.tpl similarity index 98% rename from upload/styles/templates/default/page_header.tpl rename to styles/templates/default/page_header.tpl index 8016cec9d..8eb718770 100644 --- a/upload/styles/templates/default/page_header.tpl +++ b/styles/templates/default/page_header.tpl @@ -304,7 +304,7 @@ $(document).ready(function() {
    - + diff --git a/upload/styles/templates/default/posting.tpl b/styles/templates/default/posting.tpl similarity index 100% rename from upload/styles/templates/default/posting.tpl rename to styles/templates/default/posting.tpl diff --git a/upload/styles/templates/default/posting_attach.tpl b/styles/templates/default/posting_attach.tpl similarity index 100% rename from upload/styles/templates/default/posting_attach.tpl rename to styles/templates/default/posting_attach.tpl diff --git a/upload/styles/templates/default/posting_editor.tpl b/styles/templates/default/posting_editor.tpl similarity index 100% rename from upload/styles/templates/default/posting_editor.tpl rename to styles/templates/default/posting_editor.tpl diff --git a/upload/styles/templates/default/posting_smilies.tpl b/styles/templates/default/posting_smilies.tpl similarity index 100% rename from upload/styles/templates/default/posting_smilies.tpl rename to styles/templates/default/posting_smilies.tpl diff --git a/upload/styles/templates/default/privmsgs.tpl b/styles/templates/default/privmsgs.tpl similarity index 100% rename from upload/styles/templates/default/privmsgs.tpl rename to styles/templates/default/privmsgs.tpl diff --git a/upload/styles/templates/default/privmsgs_read.tpl b/styles/templates/default/privmsgs_read.tpl similarity index 100% rename from upload/styles/templates/default/privmsgs_read.tpl rename to styles/templates/default/privmsgs_read.tpl diff --git a/upload/styles/templates/default/search.tpl b/styles/templates/default/search.tpl similarity index 100% rename from upload/styles/templates/default/search.tpl rename to styles/templates/default/search.tpl diff --git a/upload/styles/templates/default/search_results.tpl b/styles/templates/default/search_results.tpl similarity index 100% rename from upload/styles/templates/default/search_results.tpl rename to styles/templates/default/search_results.tpl diff --git a/upload/styles/templates/default/terms.tpl b/styles/templates/default/terms.tpl similarity index 100% rename from upload/styles/templates/default/terms.tpl rename to styles/templates/default/terms.tpl diff --git a/upload/styles/templates/default/torhelp.tpl b/styles/templates/default/torhelp.tpl similarity index 100% rename from upload/styles/templates/default/torhelp.tpl rename to styles/templates/default/torhelp.tpl diff --git a/upload/styles/templates/default/tpl_config.php b/styles/templates/default/tpl_config.php similarity index 100% rename from upload/styles/templates/default/tpl_config.php rename to styles/templates/default/tpl_config.php diff --git a/upload/styles/templates/default/tracker.tpl b/styles/templates/default/tracker.tpl similarity index 100% rename from upload/styles/templates/default/tracker.tpl rename to styles/templates/default/tracker.tpl diff --git a/upload/styles/templates/default/usercp_bonus.tpl b/styles/templates/default/usercp_bonus.tpl similarity index 100% rename from upload/styles/templates/default/usercp_bonus.tpl rename to styles/templates/default/usercp_bonus.tpl diff --git a/upload/styles/templates/default/usercp_email.tpl b/styles/templates/default/usercp_email.tpl similarity index 100% rename from upload/styles/templates/default/usercp_email.tpl rename to styles/templates/default/usercp_email.tpl diff --git a/upload/styles/templates/default/usercp_register.tpl b/styles/templates/default/usercp_register.tpl similarity index 97% rename from upload/styles/templates/default/usercp_register.tpl rename to styles/templates/default/usercp_register.tpl index aac351baa..cbc27c538 100644 --- a/upload/styles/templates/default/usercp_register.tpl +++ b/styles/templates/default/usercp_register.tpl @@ -288,8 +288,8 @@ ajax.callback.posts = function(data){ -

    avatar--

    -

    +

    {AVATAR_IMG}

    +

    diff --git a/upload/styles/templates/default/usercp_sendpasswd.tpl b/styles/templates/default/usercp_sendpasswd.tpl similarity index 100% rename from upload/styles/templates/default/usercp_sendpasswd.tpl rename to styles/templates/default/usercp_sendpasswd.tpl diff --git a/upload/styles/templates/default/usercp_topic_watch.tpl b/styles/templates/default/usercp_topic_watch.tpl similarity index 100% rename from upload/styles/templates/default/usercp_topic_watch.tpl rename to styles/templates/default/usercp_topic_watch.tpl diff --git a/upload/styles/templates/default/usercp_viewprofile.tpl b/styles/templates/default/usercp_viewprofile.tpl similarity index 100% rename from upload/styles/templates/default/usercp_viewprofile.tpl rename to styles/templates/default/usercp_viewprofile.tpl diff --git a/upload/styles/templates/default/viewforum.tpl b/styles/templates/default/viewforum.tpl similarity index 100% rename from upload/styles/templates/default/viewforum.tpl rename to styles/templates/default/viewforum.tpl diff --git a/upload/styles/templates/default/viewtopic.tpl b/styles/templates/default/viewtopic.tpl similarity index 100% rename from upload/styles/templates/default/viewtopic.tpl rename to styles/templates/default/viewtopic.tpl diff --git a/upload/styles/templates/default/viewtopic_attach.tpl b/styles/templates/default/viewtopic_attach.tpl similarity index 100% rename from upload/styles/templates/default/viewtopic_attach.tpl rename to styles/templates/default/viewtopic_attach.tpl diff --git a/upload/styles/templates/default/viewtopic_attach_guest.tpl b/styles/templates/default/viewtopic_attach_guest.tpl similarity index 100% rename from upload/styles/templates/default/viewtopic_attach_guest.tpl rename to styles/templates/default/viewtopic_attach_guest.tpl diff --git a/upload/styles/templates/default/viewtopic_poll.tpl b/styles/templates/default/viewtopic_poll.tpl similarity index 100% rename from upload/styles/templates/default/viewtopic_poll.tpl rename to styles/templates/default/viewtopic_poll.tpl diff --git a/upload/styles/templates/default/viewtopic_torrent.tpl b/styles/templates/default/viewtopic_torrent.tpl similarity index 100% rename from upload/styles/templates/default/viewtopic_torrent.tpl rename to styles/templates/default/viewtopic_torrent.tpl diff --git a/upload/styles/templates/posting_tpl.tpl b/styles/templates/posting_tpl.tpl similarity index 100% rename from upload/styles/templates/posting_tpl.tpl rename to styles/templates/posting_tpl.tpl diff --git a/upload/terms.php b/terms.php similarity index 100% rename from upload/terms.php rename to terms.php diff --git a/upload/tracker.php b/tracker.php similarity index 100% rename from upload/tracker.php rename to tracker.php diff --git a/upload/.htaccess b/upload/.htaccess deleted file mode 100644 index 1c8daf65c..000000000 --- a/upload/.htaccess +++ /dev/null @@ -1,13 +0,0 @@ -## Set charset server -AddDefaultCharset UTF-8 - -## Access control -Options All -Indexes - - -deny from all - - -RewriteEngine On -RewriteRule ^sitemap.xml$ internal_data/sitemap/sitemap.xml [L] -RewriteRule ^/internal_data/atom/(.*) /atom$1 [L] \ No newline at end of file diff --git a/upload/common.php b/upload/common.php deleted file mode 100644 index 0062edf4e..000000000 --- a/upload/common.php +++ /dev/null @@ -1,2129 +0,0 @@ - $srv_cfg - var $srv = array(); // $srv_name => $db_obj - var $alias = array(); // $srv_alias => $srv_name - - var $log_file = 'sql_queries'; - var $log_counter = 0; - var $num_queries = 0; - var $sql_inittime = 0; - var $sql_timetotal = 0; - - function DBS ($cfg) - { - $this->cfg = $cfg['db']; - $this->alias = $cfg['db_alias']; - - foreach ($this->cfg as $srv_name => $srv_cfg) - { - $this->srv[$srv_name] = null; - } - } - - // получение/инициализация класса для сервера $srv_name - function get_db_obj ($srv_name_or_alias = 'db1') - { - $srv_name = $this->get_srv_name($srv_name_or_alias); - - if (!is_object($this->srv[$srv_name])) - { - $this->srv[$srv_name] = new sql_db($this->cfg[$srv_name]); - $this->srv[$srv_name]->db_server = $srv_name; - } - return $this->srv[$srv_name]; - } - - // определение имени сервера - function get_srv_name ($name) - { - if (isset($this->alias[$name])) - { - $srv_name = $this->alias[$name]; - } - else if (isset($this->cfg[$name])) - { - $srv_name = $name; - } - else - { - $srv_name = 'db1'; - } - return $srv_name; - } -} - -$DBS = new DBS($bb_cfg); - -function DB ($db_alias = 'db1') -{ - global $DBS; - return $DBS->get_db_obj($db_alias); -} - -// Cache -define('PEER_HASH_PREFIX', 'peer_'); -define('PEERS_LIST_PREFIX', 'peers_list_'); - -define('PEER_HASH_EXPIRE', round($bb_cfg['announce_interval'] * (0.85*$tr_cfg['expire_factor']))); // sec -define('PEERS_LIST_EXPIRE', round($bb_cfg['announce_interval'] * 0.7)); // sec - -if (!function_exists('sqlite_escape_string')) -{ - function sqlite_escape_string($string) - { - return SQLite3::escapeString($string); - } -} - -class CACHES -{ - var $cfg = array(); // конфиг - var $obj = array(); // кеш-объекты - var $ref = array(); // ссылки на $obj (имя_кеша => кеш_объект) - - function CACHES ($cfg) - { - $this->cfg = $cfg['cache']; - $this->obj['__stub'] = new cache_common(); - } - - function get_cache_obj ($cache_name) - { - if (!isset($this->ref[$cache_name])) - { - if (!$engine_cfg =& $this->cfg['engines'][$cache_name]) - { - $this->ref[$cache_name] =& $this->obj['__stub']; - } - else - { - $cache_type =& $engine_cfg[0]; - $cache_cfg =& $engine_cfg[1]; - - switch ($cache_type) - { - case 'memcache': - if (!isset($this->obj[$cache_name])) - { - $this->obj[$cache_name] = new cache_memcache($this->cfg['memcache'], $this->cfg['prefix']); - } - $this->ref[$cache_name] =& $this->obj[$cache_name]; - break; - - case 'sqlite': - if (!isset($this->obj[$cache_name])) - { - $cache_cfg['pconnect'] = $this->cfg['pconnect']; - $cache_cfg['db_file_path'] = $this->get_db_path($cache_name, $cache_cfg, '.sqlite.db'); - - $this->obj[$cache_name] = new cache_sqlite($cache_cfg, $this->cfg['prefix']); - } - $this->ref[$cache_name] =& $this->obj[$cache_name]; - break; - - case 'db_sqlite': - if (!isset($this->obj[$cache_name])) - { - $cache_cfg['pconnect'] = $this->cfg['pconnect']; - $cache_cfg['db_file_path'] = $this->get_db_path($cache_name, $cache_cfg, '.sqlite.db'); - $cache_cfg['table_name'] = $cache_name; - $cache_cfg['table_schema'] = $this->get_table_schema($cache_cfg); - - $this->obj[$cache_name] = new sqlite_common($cache_cfg); - } - $this->ref[$cache_name] =& $this->obj[$cache_name]; - break; - - case 'redis': - if (!isset($this->obj[$cache_name])) - { - $this->obj[$cache_name] = new cache_redis($this->cfg['redis'], $this->cfg['prefix']); - } - $this->ref[$cache_name] =& $this->obj[$cache_name]; - break; - - case 'apc': - if (!isset($this->obj[$cache_name])) - { - $this->obj[$cache_name] = new cache_apc($this->cfg['prefix']); - } - $this->ref[$cache_name] =& $this->obj[$cache_name]; - break; - - case 'xcache': - if (!isset($this->obj[$cache_name])) - { - $this->obj[$cache_name] = new cache_xcache($this->cfg['prefix']); - } - $this->ref[$cache_name] =& $this->obj[$cache_name]; - break; - - default: //filecache - if (!isset($this->obj[$cache_name])) - { - $this->obj[$cache_name] = new cache_file($this->cfg['db_dir'] . $cache_name .'/', $this->cfg['prefix']); - } - $this->ref[$cache_name] =& $this->obj[$cache_name]; - break; - } - } - } - - return $this->ref[$cache_name]; - } - - function get_db_path ($name, $cfg, $ext) - { - if (!empty($cfg['shard_type']) && $cfg['shard_type'] != 'none') - { - return $this->cfg['db_dir'] . $name .'_*'. $ext; - } - else - { - return $this->cfg['db_dir'] . $name . $ext; - } - } - - function get_table_schema ($cfg) - { - return "CREATE TABLE {$cfg['table_name']} ( {$cfg['columns']} )"; - } -} - -$CACHES = new CACHES($bb_cfg); - -function CACHE ($cache_name) -{ - global $CACHES; - return $CACHES->get_cache_obj($cache_name); -} - -class cache_common -{ - var $used = false; - /** - * Returns value of variable - */ - function get ($name, $get_miss_key_callback = '', $ttl = 604800) - { - if ($get_miss_key_callback) return $get_miss_key_callback($name); - return is_array($name) ? array() : false; - } - /** - * Store value of variable - */ - function set ($name, $value, $ttl = 604800) - { - return false; - } - /** - * Remove variable - */ - function rm ($name = '') - { - return false; - } - - var $num_queries = 0; - var $sql_starttime = 0; - var $sql_inittime = 0; - var $sql_timetotal = 0; - var $cur_query_time = 0; - - var $dbg = array(); - var $dbg_id = 0; - var $dbg_enabled = false; - var $cur_query = null; - - function debug ($mode, $cur_query = null) - { - if (!$this->dbg_enabled) return; - - $id =& $this->dbg_id; - $dbg =& $this->dbg[$id]; - - if ($mode == 'start') - { - $this->sql_starttime = utime(); - - $dbg['sql'] = isset($cur_query) ? short_query($cur_query) : short_query($this->cur_query); - $dbg['src'] = $this->debug_find_source(); - $dbg['file'] = $this->debug_find_source('file'); - $dbg['line'] = $this->debug_find_source('line'); - $dbg['time'] = ''; - } - else if ($mode == 'stop') - { - $this->cur_query_time = utime() - $this->sql_starttime; - $this->sql_timetotal += $this->cur_query_time; - $dbg['time'] = $this->cur_query_time; - $id++; - } - } - - function debug_find_source ($mode = '') - { - foreach (debug_backtrace() as $trace) - { - if ($trace['file'] !== __FILE__) - { - switch ($mode) - { - case 'file': return $trace['file']; - case 'line': return $trace['line']; - default: return hide_bb_path($trace['file']) .'('. $trace['line'] .')'; - } - } - } - return 'src not found'; - } -} - -class cache_memcache extends cache_common -{ - var $used = true; - var $engine = 'Memcache'; - var $cfg = null; - var $prefix = null; - var $memcache = null; - var $connected = false; - - function cache_memcache ($cfg, $prefix = null) - { - if (!$this->is_installed()) - { - die('Error: Memcached extension not installed'); - } - - $this->cfg = $cfg; - $this->prefix = $prefix; - $this->memcache = new Memcache; - $this->dbg_enabled = sql_dbg_enabled(); - } - - function connect () - { - $connect_type = ($this->cfg['pconnect']) ? 'pconnect' : 'connect'; - - $this->cur_query = $connect_type .' '. $this->cfg['host'] .':'. $this->cfg['port']; - $this->debug('start'); - - if (@$this->memcache->$connect_type($this->cfg['host'], $this->cfg['port'])) - { - $this->connected = true; - } - - if (DBG_LOG) dbg_log(' ', 'CACHE-connect'. ($this->connected ? '' : '-FAIL')); - - if (!$this->connected && $this->cfg['con_required']) - { - die('Could not connect to memcached server'); - } - - $this->debug('stop'); - $this->cur_query = null; - } - - function get ($name, $get_miss_key_callback = '', $ttl = 0) - { - if (!$this->connected) $this->connect(); - - $this->cur_query = "cache->get('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return ($this->connected) ? $this->memcache->get($this->prefix . $name) : false; - } - - function set ($name, $value, $ttl = 0) - { - if (!$this->connected) $this->connect(); - - $this->cur_query = "cache->set('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return ($this->connected) ? $this->memcache->set($this->prefix . $name, $value, false, $ttl) : false; - } - - function rm ($name = '') - { - if (!$this->connected) $this->connect(); - - if ($name) - { - $this->cur_query = "cache->rm('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return ($this->connected) ? $this->memcache->delete($this->prefix . $name, 0) : false; - } - else - { - return ($this->connected) ? $this->memcache->flush() : false; - } - } - - function is_installed () - { - return class_exists('Memcache'); - } -} - -class cache_sqlite extends cache_common -{ - var $used = true; - var $db = null; - var $prefix = null; - var $cfg = array( - 'db_file_path' => '/path/to/cache.db.sqlite', - 'table_name' => 'cache', - 'table_schema' => 'CREATE TABLE cache ( - cache_name VARCHAR(255), - cache_expire_time INT, - cache_value TEXT, - PRIMARY KEY (cache_name) - )', - 'pconnect' => true, - 'con_required' => true, - 'log_name' => 'CACHE', - ); - - function cache_sqlite ($cfg, $prefix = null) - { - $this->cfg = array_merge($this->cfg, $cfg); - $this->db = new sqlite_common($this->cfg); - $this->prefix = $prefix; - } - - function get ($name, $get_miss_key_callback = '', $ttl = 604800) - { - if (empty($name)) - { - return is_array($name) ? array() : false; - } - $this->db->shard($name); - $cached_items = array(); - $this->prefix_len = strlen($this->prefix); - $this->prefix_sql = sqlite_escape_string($this->prefix); - - $name_ary = $name_sql = (array) $name; - array_deep($name_sql, 'sqlite_escape_string'); - - // get available items - $rowset = $this->db->fetch_rowset(" - SELECT cache_name, cache_value - FROM ". $this->cfg['table_name'] ." - WHERE cache_name IN('$this->prefix_sql". join("','$this->prefix_sql", $name_sql) ."') AND cache_expire_time > ". TIMENOW ." - LIMIT ". count($name) ." - "); - - $this->db->debug('start', 'unserialize()'); - foreach ($rowset as $row) - { - $cached_items[substr($row['cache_name'], $this->prefix_len)] = unserialize($row['cache_value']); - } - $this->db->debug('stop'); - - // get miss items - if ($get_miss_key_callback AND $miss_key = array_diff($name_ary, array_keys($cached_items))) - { - foreach ($get_miss_key_callback($miss_key) as $k => $v) - { - $this->set($this->prefix . $k, $v, $ttl); - $cached_items[$k] = $v; - } - } - // return - if (is_array($this->prefix . $name)) - { - return $cached_items; - } - else - { - return isset($cached_items[$name]) ? $cached_items[$name] : false; - } - } - - function set ($name, $value, $ttl = 604800) - { - $this->db->shard($this->prefix . $name); - $name_sql = sqlite_escape_string($this->prefix . $name); - $expire = TIMENOW + $ttl; - $value_sql = sqlite_escape_string(serialize($value)); - - $result = $this->db->query("REPLACE INTO ". $this->cfg['table_name'] ." (cache_name, cache_expire_time, cache_value) VALUES ('$name_sql', $expire, '$value_sql')"); - return (bool) $result; - } - - function rm ($name = '') - { - if ($name) - { - $this->db->shard($this->prefix . $name); - $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_name = '". sqlite_escape_string($this->prefix . $name) ."'"); - } - else - { - $result = $this->db->query("DELETE FROM ". $this->cfg['table_name']); - } - return (bool) $result; - } - - function gc ($expire_time = TIMENOW) - { - $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_expire_time < $expire_time"); - return ($result) ? $this->db->changes() : 0; - } -} - -class sqlite_common extends cache_common -{ - var $cfg = array( - 'db_file_path' => 'sqlite.db', - 'table_name' => 'table_name', - 'table_schema' => 'CREATE TABLE table_name (...)', - 'pconnect' => true, - 'con_required' => true, - 'log_name' => 'SQLite', - 'shard_type' => 'none', # none, string, int (тип перевичного ключа для шардинга) - 'shard_val' => 0, # для string - кол. начальных символов, для int - делитель (будет использован остаток от деления) - ); - var $engine = 'SQLite'; - var $dbh = null; - var $connected = false; - var $shard_val = false; - - var $table_create_attempts = 0; - - function sqlite_common ($cfg) - { - $this->cfg = array_merge($this->cfg, $cfg); - $this->dbg_enabled = sql_dbg_enabled(); - } - - function connect () - { - $this->cur_query = ($this->dbg_enabled) ? 'connect to: '. $this->cfg['db_file_path'] : 'connect'; - $this->debug('start'); - - if (@$this->dbh = new SQLite3($this->cfg['db_file_path'])) - { - $this->connected = true; - } - - if (DBG_LOG) dbg_log(' ', $this->cfg['log_name'] .'-connect'. ($this->connected ? '' : '-FAIL')); - - if (!$this->connected && $this->cfg['con_required']) - { - trigger_error('SQLite not connected', E_USER_ERROR); - } - - $this->debug('stop'); - $this->cur_query = null; - } - - function create_table () - { - $this->table_create_attempts++; - return $this->dbh->query($this->cfg['table_schema']); - } - - function shard ($name) - { - $type = $this->cfg['shard_type']; - - if ($type == 'none') return; - if (is_array($name)) trigger_error('cannot shard: $name is array', E_USER_ERROR); - - // define shard_val - if ($type == 'string') - { - $shard_val = substr($name, 0, $this->cfg['shard_val']); - } - else - { - $shard_val = $name % $this->cfg['shard_val']; - } - // все запросы должны быть к одному и тому же шарду - if ($this->shard_val !== false) - { - if ($shard_val != $this->shard_val) - { - trigger_error("shard cannot be reassigned. [{$this->shard_val}, $shard_val, $name]", E_USER_ERROR); - } - else - { - return; - } - } - $this->shard_val = $shard_val; - $this->cfg['db_file_path'] = str_replace('*', $shard_val, $this->cfg['db_file_path']); - } - - function query ($query) - { - if (!$this->connected) $this->connect(); - - $this->cur_query = $query; - $this->debug('start'); - - if (!$result = @$this->dbh->query($query)) - { - $rowsresult = $this->dbh->query("PRAGMA table_info({$this->cfg['table_name']})"); - $rowscount = 0; - while ($row = $rowsresult->fetchArray(SQLITE3_ASSOC)) - { - $rowscount++; - } - if (!$this->table_create_attempts && !$rowscount) - { - if ($this->create_table()) - { - $result = $this->dbh->query($query); - } - } - if (!$result) - { - $this->trigger_error($this->get_error_msg()); - } - } - - $this->debug('stop'); - $this->cur_query = null; - - $this->num_queries++; - - return $result; - } - - function fetch_row ($query) - { - $result = $this->query($query); - return is_resource($result) ? $result->fetchArray(SQLITE3_ASSOC) : false; - } - - function fetch_rowset ($query) - { - $result = $this->query($query); - $rowset = array(); - while ($row = $result->fetchArray(SQLITE3_ASSOC)) - { - $rowset[] = $row; - } - return $rowset; - } - - function changes () - { - return is_resource($this->dbh) ? $this->dbh->changes() : 0; - } - - function escape ($str) - { - return sqlite_escape_string($str); - } - - function get_error_msg () - { - return 'SQLite error #'. ($err_code = $this->dbh->lastErrorCode()) .': '. $this->dbh->lastErrorMsg(); - } - - function rm ($name = '') - { - if ($name) - { - $this->db->shard($this->prefix . $name); - $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_name = '". sqlite_escape_string($this->prefix . $name) ."'"); - } - else - { - $result = $this->db->query("DELETE FROM ". $this->cfg['table_name']); - } - return (bool) $result; - } - - function gc ($expire_time = TIMENOW) - { - $result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_expire_time < $expire_time"); - return ($result) ? sqlite_changes($this->db->dbh) : 0; - } - - function trigger_error ($msg = 'DB Error') - { - if (error_reporting()) trigger_error($msg, E_USER_ERROR); - } -} - -class cache_redis extends cache_common -{ - var $used = true; - var $engine = 'Redis'; - var $cfg = null; - var $redis = null; - var $prefix = null; - var $connected = false; - - function cache_redis ($cfg, $prefix = null) - { - if (!$this->is_installed()) - { - die('Error: Redis extension not installed'); - } - - $this->cfg = $cfg; - $this->prefix = $prefix; - $this->redis = new Redis(); - $this->dbg_enabled = sql_dbg_enabled(); - } - - function connect () - { - $this->cur_query = 'connect '. $this->cfg['host'] .':'. $this->cfg['port']; - $this->debug('start'); - - if (@$this->redis->connect($this->cfg['host'], $this->cfg['port'])) - { - $this->connected = true; - } - - if (!$this->connected && $this->cfg['con_required']) - { - die('Could not connect to redis server'); - } - - $this->debug('stop'); - $this->cur_query = null; - } - - function get ($name, $get_miss_key_callback = '', $ttl = 0) - { - if (!$this->connected) $this->connect(); - - $this->cur_query = "cache->get('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return ($this->connected) ? unserialize($this->redis->get($this->prefix . $name)) : false; - } - - function set ($name, $value, $ttl = 0) - { - if (!$this->connected) $this->connect(); - - $this->cur_query = "cache->set('$name')"; - $this->debug('start'); - - if ($this->redis->set($this->prefix . $name, serialize($value))) - { - if ($ttl > 0) - { - $this->redis->expire($this->prefix . $name, $ttl); - } - - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return true; - } - else - { - return false; - } - } - - function rm ($name = '') - { - if (!$this->connected) $this->connect(); - - if ($name) - { - $this->cur_query = "cache->rm('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return ($this->connected) ? $this->redis->del($this->prefix . $name) : false; - } - else - { - return ($this->connected) ? $this->redis->flushdb() : false; - } - } - - function is_installed () - { - return class_exists('Redis'); - } -} - -class cache_apc extends cache_common -{ - var $used = true; - var $engine = 'APC'; - var $prefix = null; - - function cache_apc ($prefix = null) - { - if (!$this->is_installed()) - { - die('Error: APC extension not installed'); - } - $this->dbg_enabled = sql_dbg_enabled(); - $this->prefix = $prefix; - } - - function get ($name, $get_miss_key_callback = '', $ttl = 0) - { - $this->cur_query = "cache->get('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return apc_fetch($this->prefix . $name); - } - - function set ($name, $value, $ttl = 0) - { - $this->cur_query = "cache->set('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return apc_store($this->prefix . $name, $value, $ttl); - } - - function rm ($name = '') - { - if ($name) - { - $this->cur_query = "cache->rm('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return apc_delete($this->prefix . $name); - } - else - { - return apc_clear_cache(); - } - } - - function is_installed () - { - return function_exists('apc_fetch'); - } -} - -class cache_xcache extends cache_common -{ - var $used = true; - var $engine = 'XCache'; - var $prefix = null; - - function cache_xcache ($prefix = null) - { - if (!$this->is_installed()) - { - die('Error: XCache extension not installed'); - } - $this->dbg_enabled = sql_dbg_enabled(); - $this->prefix = $prefix; - } - - function get ($name, $get_miss_key_callback = '', $ttl = 0) - { - $this->cur_query = "cache->get('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return xcache_get($this->prefix . $name); - } - - function set ($name, $value, $ttl = 0) - { - $this->cur_query = "cache->set('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return xcache_set($this->prefix . $name, $value, $ttl); - } - - function rm ($name = '') - { - if ($name) - { - $this->cur_query = "cache->rm('$name')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return xcache_unset($this->prefix . $name); - } - else - { - xcache_clear_cache(XC_TYPE_PHP, 0); - xcache_clear_cache(XC_TYPE_VAR, 0); - return; - } - } - - function is_installed () - { - return function_exists('xcache_get'); - } -} - -class cache_file extends cache_common -{ - var $used = true; - var $engine = 'Filecache'; - var $dir = null; - var $prefix = null; - - function cache_file ($dir, $prefix = null) - { - $this->dir = $dir; - $this->prefix = $prefix; - $this->dbg_enabled = sql_dbg_enabled(); - } - - function get ($name, $get_miss_key_callback = '', $ttl = 0) - { - $filename = $this->dir . clean_filename($this->prefix . $name) . '.php'; - - $this->cur_query = "cache->set('$name')"; - $this->debug('start'); - - if (file_exists($filename)) - { - require($filename); - } - - $this->debug('stop'); - $this->cur_query = null; - - return (!empty($filecache['value'])) ? $filecache['value'] : false; - } - - function set ($name, $value, $ttl = 86400) - { - if (!function_exists('var_export')) - { - return false; - } - - $this->cur_query = "cache->set('$name')"; - $this->debug('start'); - - $filename = $this->dir . clean_filename($this->prefix . $name) . '.php'; - $expire = TIMENOW + $ttl; - $cache_data = array( - 'expire' => $expire, - 'value' => $value, - ); - - $filecache = "'; - - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return (bool) file_write($filecache, $filename, false, true, true); - } - - function rm ($name = '') - { - $clear = false; - if ($name) - { - $this->cur_query = "cache->rm('$name')"; - $this->debug('start'); - - $filename = $this->dir . clean_filename($this->prefix . $name) . '.php'; - if (file_exists($filename)) - { - $clear = (bool) unlink($filename); - } - - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - } - else - { - if (is_dir($this->dir)) - { - if ($dh = opendir($this->dir)) - { - while (($file = readdir($dh)) !== false) - { - if ($file != "." && $file != "..") - { - $filename = $this->dir . $file; - - unlink($filename); - $clear = true; - } - } - closedir($dh); - } - } - } - return $clear; - } - - function gc ($expire_time = TIMENOW) - { - $clear = false; - - if (is_dir($this->dir)) - { - if ($dh = opendir($this->dir)) - { - while (($file = readdir($dh)) !== false) - { - if ($file != "." && $file != "..") - { - $filename = $this->dir . $file; - - require($filename); - - if(!empty($filecache['expire']) && ($filecache['expire'] < $expire_time)) - { - unlink($filename); - $clear = true; - } - } - } - closedir($dh); - } - } - - return $clear; - } -} - -/** -* Datastore -*/ -class datastore_common -{ - /** - * Директория с builder-скриптами (внутри INC_DIR) - */ - var $ds_dir = 'datastore/'; - /** - * Готовая к употреблению data - * array('title' => data) - */ - var $data = array(); - /** - * Список элементов, которые будут извлечены из хранилища при первом же запросе get() - * до этого момента они ставятся в очередь $queued_items для дальнейшего извлечения _fetch()'ем - * всех элементов одним запросом - * array('title1', 'title2'...) - */ - var $queued_items = array(); - - /** - * 'title' => 'builder script name' inside "includes/datastore" dir - */ - var $known_items = array( - 'cat_forums' => 'build_cat_forums.php', - 'jumpbox' => 'build_cat_forums.php', - 'viewtopic_forum_select' => 'build_cat_forums.php', - 'latest_news' => 'build_cat_forums.php', - 'network_news' => 'build_cat_forums.php', - 'ads' => 'build_cat_forums.php', - 'moderators' => 'build_moderators.php', - 'stats' => 'build_stats.php', - 'ranks' => 'build_ranks.php', - 'attach_extensions' => 'build_attach_extensions.php', - 'smile_replacements' => 'build_smilies.php', - ); - - /** - * Constructor - */ - function datastore_common () {} - - /** - * @param array(item1_title, item2_title...) or single item's title - */ - function enqueue ($items) - { - foreach ((array) $items as $item) - { - // игнор уже поставленного в очередь либо уже извлеченного - if (!in_array($item, $this->queued_items) && !isset($this->data[$item])) - { - $this->queued_items[] = $item; - } - } - } - - function &get ($title) - { - if (!isset($this->data[$title])) - { - $this->enqueue($title); - $this->_fetch(); - } - return $this->data[$title]; - } - - function store ($item_name, $item_data) {} - - function rm ($items) - { - foreach ((array) $items as $item) - { - unset($this->data[$item]); - } - } - - function update ($items) - { - if ($items == 'all') - { - $items = array_keys(array_unique($this->known_items)); - } - foreach ((array) $items as $item) - { - $this->_build_item($item); - } - } - - function _fetch () - { - $this->_fetch_from_store(); - - foreach ($this->queued_items as $title) - { - if (!isset($this->data[$title]) || $this->data[$title] === false) - { - $this->_build_item($title); - } - } - - $this->queued_items = array(); - } - - function _fetch_from_store () {} - - function _build_item ($title) - { - if (!empty($this->known_items[$title])) - { - require(INC_DIR . $this->ds_dir . $this->known_items[$title]); - } - else - { - trigger_error("Unknown datastore item: $title", E_USER_ERROR); - } - } - - var $num_queries = 0; - var $sql_starttime = 0; - var $sql_inittime = 0; - var $sql_timetotal = 0; - var $cur_query_time = 0; - - var $dbg = array(); - var $dbg_id = 0; - var $dbg_enabled = false; - var $cur_query = null; - - function debug ($mode, $cur_query = null) - { - if (!$this->dbg_enabled) return; - - $id =& $this->dbg_id; - $dbg =& $this->dbg[$id]; - - if ($mode == 'start') - { - $this->sql_starttime = utime(); - - $dbg['sql'] = isset($cur_query) ? short_query($cur_query) : short_query($this->cur_query); - $dbg['src'] = $this->debug_find_source(); - $dbg['file'] = $this->debug_find_source('file'); - $dbg['line'] = $this->debug_find_source('line'); - $dbg['time'] = ''; - } - else if ($mode == 'stop') - { - $this->cur_query_time = utime() - $this->sql_starttime; - $this->sql_timetotal += $this->cur_query_time; - $dbg['time'] = $this->cur_query_time; - $id++; - } - } - - function debug_find_source ($mode = '') - { - foreach (debug_backtrace() as $trace) - { - if ($trace['file'] !== __FILE__) - { - switch ($mode) - { - case 'file': return $trace['file']; - case 'line': return $trace['line']; - default: return hide_bb_path($trace['file']) .'('. $trace['line'] .')'; - } - } - } - return 'src not found'; - } -} - -class datastore_memcache extends datastore_common -{ - var $cfg = null; - var $memcache = null; - var $connected = false; - var $engine = 'Memcache'; - var $prefix = null; - - function datastore_memcache ($cfg, $prefix = null) - { - if (!$this->is_installed()) - { - die('Error: Memcached extension not installed'); - } - - $this->cfg = $cfg; - $this->prefix = $prefix; - $this->memcache = new Memcache; - $this->dbg_enabled = sql_dbg_enabled(); - } - - function connect () - { - $connect_type = ($this->cfg['pconnect']) ? 'pconnect' : 'connect'; - - $this->cur_query = $connect_type .' '. $this->cfg['host'] .':'. $this->cfg['port']; - $this->debug('start'); - - if (@$this->memcache->$connect_type($this->cfg['host'], $this->cfg['port'])) - { - $this->connected = true; - } - - if (DBG_LOG) dbg_log(' ', 'CACHE-connect'. ($this->connected ? '' : '-FAIL')); - - if (!$this->connected && $this->cfg['con_required']) - { - die('Could not connect to memcached server'); - } - - $this->debug('stop'); - $this->cur_query = null; - } - - function store ($title, $var) - { - if (!$this->connected) $this->connect(); - $this->data[$title] = $var; - - $this->cur_query = "cache->set('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return (bool) $this->memcache->set($this->prefix . $title, $var); - } - - function clean () - { - if (!$this->connected) $this->connect(); - foreach ($this->known_items as $title => $script_name) - { - $this->cur_query = "cache->rm('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - $this->memcache->delete($this->prefix . $title, 0); - } - } - - function _fetch_from_store () - { - if (!$items = $this->queued_items) - { - $src = $this->_debug_find_caller('enqueue'); - trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); - } - - if (!$this->connected) $this->connect(); - foreach ($items as $item) - { - $this->cur_query = "cache->get('$item')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - $this->data[$item] = $this->memcache->get($this->prefix . $item); - } - } - - function is_installed () - { - return class_exists('Memcache'); - } -} - -class datastore_sqlite extends datastore_common -{ - var $engine = 'SQLite'; - var $db = null; - var $prefix = null; - var $cfg = array( - 'db_file_path' => '/path/to/datastore.db.sqlite', - 'table_name' => 'datastore', - 'table_schema' => 'CREATE TABLE datastore ( - ds_title VARCHAR(255), - ds_data TEXT, - PRIMARY KEY (ds_title) - )', - 'pconnect' => true, - 'con_required' => true, - 'log_name' => 'DATASTORE', - ); - - function datastore_sqlite ($cfg, $prefix = null) - { - $this->cfg = array_merge($this->cfg, $cfg); - $this->db = new sqlite_common($this->cfg); - $this->prefix = $prefix; - } - - function store ($item_name, $item_data) - { - $this->data[$item_name] = $item_data; - - $ds_title = sqlite_escape_string($this->prefix . $item_name); - $ds_data = sqlite_escape_string(serialize($item_data)); - - $result = $this->db->query("REPLACE INTO ". $this->cfg['table_name'] ." (ds_title, ds_data) VALUES ('$ds_title', '$ds_data')"); - - return (bool) $result; - } - - function clean () - { - $this->db->query("DELETE FROM ". $this->cfg['table_name']); - } - - function _fetch_from_store () - { - if (!$items = $this->queued_items) return; - - $prefix_len = strlen($this->prefix); - $prefix_sql = sqlite_escape_string($this->prefix); - - array_deep($items, 'sqlite_escape_string'); - $items_list = $prefix_sql . join("','$prefix_sql", $items); - - $rowset = $this->db->fetch_rowset("SELECT ds_title, ds_data FROM ". $this->cfg['table_name'] ." WHERE ds_title IN ('$items_list')"); - - $this->db->debug('start', "unserialize()"); - foreach ($rowset as $row) - { - $this->data[substr($row['ds_title'], $prefix_len)] = unserialize($row['ds_data']); - } - $this->db->debug('stop'); - } -} - -class datastore_redis extends datastore_common -{ - var $cfg = null; - var $redis = null; - var $prefix = null; - var $connected = false; - var $engine = 'Redis'; - - function datastore_redis ($cfg, $prefix = null) - { - if (!$this->is_installed()) - { - die('Error: Redis extension not installed'); - } - - $this->cfg = $cfg; - $this->redis = new Redis(); - $this->dbg_enabled = sql_dbg_enabled(); - $this->prefix = $prefix; - } - - function connect () - { - $this->cur_query = 'connect '. $this->cfg['host'] .':'. $this->cfg['port']; - $this->debug('start'); - - if (@$this->redis->connect($this->cfg['host'],$this->cfg['port'])) - { - $this->connected = true; - } - - if (!$this->connected && $this->cfg['con_required']) - { - die('Could not connect to redis server'); - } - - $this->debug('stop'); - $this->cur_query = null; - } - - function store ($title, $var) - { - if (!$this->connected) $this->connect(); - $this->data[$title] = $var; - - $this->cur_query = "cache->set('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return (bool) $this->redis->set($this->prefix . $title, serialize($var)); - } - - function clean () - { - if (!$this->connected) $this->connect(); - foreach ($this->known_items as $title => $script_name) - { - $this->cur_query = "cache->rm('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - $this->redis->del($this->prefix . $title); - } - } - - function _fetch_from_store () - { - if (!$items = $this->queued_items) - { - $src = $this->_debug_find_caller('enqueue'); - trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); - } - - if (!$this->connected) $this->connect(); - foreach ($items as $item) - { - $this->cur_query = "cache->get('$item')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - $this->data[$item] = unserialize($this->redis->get($this->prefix . $item)); - } - } - - function is_installed () - { - return class_exists('Redis'); - } -} - -class datastore_xcache extends datastore_common -{ - var $prefix = null; - var $engine = 'XCache'; - - function datastore_xcache ($prefix = null) - { - if (!$this->is_installed()) - { - die('Error: XCache extension not installed'); - } - - $this->dbg_enabled = sql_dbg_enabled(); - $this->prefix = $prefix; - } - - function store ($title, $var) - { - $this->data[$title] = $var; - - $this->cur_query = "cache->set('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return (bool) xcache_set($this->prefix . $title, $var); - } - - function clean () - { - foreach ($this->known_items as $title => $script_name) - { - $this->cur_query = "cache->rm('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - xcache_unset($this->prefix . $title); - } - } - - function _fetch_from_store () - { - if (!$items = $this->queued_items) - { - $src = $this->_debug_find_caller('enqueue'); - trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); - } - - foreach ($items as $item) - { - $this->cur_query = "cache->set('$item')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - $this->data[$item] = xcache_get($this->prefix . $item); - } - } - - function is_installed () - { - return function_exists('xcache_get'); - } -} - -class datastore_apc extends datastore_common -{ - var $engine = 'APC'; - var $prefix = null; - - function datastore_apc ($prefix = null) - { - if (!$this->is_installed()) - { - die('Error: APC extension not installed'); - } - $this->dbg_enabled = sql_dbg_enabled(); - $this->prefix = $prefix; - } - - function store ($title, $var) - { - $this->data[$title] = $var; - - $this->cur_query = "cache->set('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return (bool) apc_store($this->prefix . $title, $var); - } - - function clean () - { - foreach ($this->known_items as $title => $script_name) - { - $this->cur_query = "cache->rm('$title')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - apc_delete($this->prefix . $title); - } - } - - function _fetch_from_store () - { - if (!$items = $this->queued_items) - { - $src = $this->_debug_find_caller('enqueue'); - trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); - } - - foreach ($items as $item) - { - $this->cur_query = "cache->get('$item')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - $this->data[$item] = apc_fetch($this->prefix . $item); - } - } - - function is_installed () - { - return function_exists('apc_fetch'); - } -} - -class datastore_file extends datastore_common -{ - var $dir = null; - var $prefix = null; - var $engine = 'Filecache'; - - function datastore_file ($dir, $prefix = null) - { - $this->prefix = $prefix; - $this->dir = $dir; - $this->dbg_enabled = sql_dbg_enabled(); - } - - function store ($title, $var) - { - $this->cur_query = "cache->set('$title')"; - $this->debug('start'); - - $this->data[$title] = $var; - - $filename = $this->dir . clean_filename($this->prefix . $title) . '.php'; - - $filecache = "'; - - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - return (bool) file_write($filecache, $filename, false, true, true); - } - - function clean () - { - $dir = $this->dir; - - if (is_dir($dir)) - { - if ($dh = opendir($dir)) - { - while (($file = readdir($dh)) !== false) - { - if ($file != "." && $file != "..") - { - $filename = $dir . $file; - - unlink($filename); - } - } - closedir($dh); - } - } - } - - function _fetch_from_store () - { - if (!$items = $this->queued_items) - { - $src = $this->_debug_find_caller('enqueue'); - trigger_error("Datastore: item '$item' already enqueued [$src]", E_USER_ERROR); - } - - foreach($items as $item) - { - $filename = $this->dir . $this->prefix . $item . '.php'; - - $this->cur_query = "cache->get('$item')"; - $this->debug('start'); - $this->debug('stop'); - $this->cur_query = null; - $this->num_queries++; - - if(file_exists($filename)) - { - require($filename); - - $this->data[$item] = $filecache; - } - } - } -} - -// Initialize Datastore -switch ($bb_cfg['datastore_type']) -{ - case 'memcache': - $datastore = new datastore_memcache($bb_cfg['cache']['memcache'], $bb_cfg['cache']['prefix']); - break; - - case 'sqlite': - $default_cfg = array( - 'db_file_path' => $bb_cfg['cache']['db_dir'] .'datastore.sqlite.db', - 'pconnect' => true, - 'con_required' => true, - ); - $datastore = new datastore_sqlite($default_cfg, $bb_cfg['cache']['prefix']); - break; - - case 'redis': - $datastore = new datastore_redis($bb_cfg['cache']['redis'], $bb_cfg['cache']['prefix']); - break; - - case 'xcache': - $datastore = new datastore_xcache($bb_cfg['cache']['prefix']); - break; - - case 'apc': - $datastore = new datastore_apc($bb_cfg['cache']['prefix']); - break; - - case 'filecache': - default: $datastore = new datastore_file($bb_cfg['cache']['db_dir'] . 'datastore/', $bb_cfg['cache']['prefix']); -} - -function sql_dbg_enabled () -{ - return (SQL_DEBUG && DBG_USER && !empty($_COOKIE['sql_log'])); -} - -function short_query ($sql, $esc_html = false) -{ - $max_len = 100; - $sql = str_compact($sql); - - if (!empty($_COOKIE['sql_log_full'])) - { - if (mb_strlen($sql, 'UTF-8') > $max_len) - { - $sql = mb_substr($sql, 0, 50) .' [...cut...] '. mb_substr($sql, -50); - } - } - - return ($esc_html) ? htmlCHR($sql, true) : $sql; -} - -// Functions -function utime () -{ - return array_sum(explode(' ', microtime())); -} - -function bb_log ($msg, $file_name) -{ - if (is_array($msg)) - { - $msg = join(LOG_LF, $msg); - } - $file_name .= (LOG_EXT) ? '.'. LOG_EXT : ''; - return file_write($msg, LOG_DIR . $file_name); -} - -function file_write ($str, $file, $max_size = LOG_MAX_SIZE, $lock = true, $replace_content = false) -{ - $bytes_written = false; - - if ($max_size && @filesize($file) >= $max_size) - { - $old_name = $file; $ext = ''; - if (preg_match('#^(.+)(\.[^\\/]+)$#', $file, $matches)) - { - $old_name = $matches[1]; $ext = $matches[2]; - } - $new_name = $old_name .'_[old]_'. date('Y-m-d_H-i-s_') . getmypid() . $ext; - clearstatcache(); - if (@file_exists($file) && @filesize($file) >= $max_size && !@file_exists($new_name)) - { - @rename($file, $new_name); - } - } - if (!$fp = @fopen($file, 'ab')) - { - if ($dir_created = bb_mkdir(dirname($file))) - { - $fp = @fopen($file, 'ab'); - } - } - if ($fp) - { - if ($lock) - { - @flock($fp, LOCK_EX); - } - if ($replace_content) - { - @ftruncate($fp, 0); - @fseek($fp, 0, SEEK_SET); - } - $bytes_written = @fwrite($fp, $str); - @fclose($fp); - } - - return $bytes_written; -} - -function bb_mkdir ($path, $mode = 0777) -{ - $old_um = umask(0); - $dir = mkdir_rec($path, $mode); - umask($old_um); - return $dir; -} - -function mkdir_rec ($path, $mode) -{ - if (is_dir($path)) - { - return ($path !== '.' && $path !== '..') ? is_writable($path) : false; - } - else - { - return (mkdir_rec(dirname($path), $mode)) ? @mkdir($path, $mode) : false; - } -} - -function verify_id ($id, $length) -{ - return (is_string($id) && preg_match('#^[a-zA-Z0-9]{'. $length .'}$#', $id)); -} - -function clean_filename ($fname) -{ - static $s = array('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' '); - return str_replace($s, '_', str_compact($fname)); -} - -function encode_ip ($ip) -{ - $d = explode('.', $ip); - return sprintf('%02x%02x%02x%02x', $d[0], $d[1], $d[2], $d[3]); -} - -function decode_ip ($ip) -{ - return long2ip("0x{$ip}"); -} - -function ip2int ($ip) -{ - return (float) sprintf('%u', ip2long($ip)); // для совместимости с 32 битными системами -} - -// long2ip( mask_ip_int(ip2int('1.2.3.4'), 24) ) = '1.2.3.255' -function mask_ip_int ($ip, $mask) -{ - $ip_int = is_numeric($ip) ? $ip : ip2int($ip); - $ip_masked = $ip_int | ((1 << (32 - $mask)) - 1); - return (float) sprintf('%u', $ip_masked); -} - -function bb_crc32 ($str) -{ - return (float) sprintf('%u', crc32($str)); -} - -function hexhex ($value) -{ - return dechex(hexdec($value)); -} - -function verify_ip ($ip) -{ - return preg_match('#^(\d{1,3}\.){3}\d{1,3}$#', $ip); -} - -function str_compact ($str) -{ - return preg_replace('#\s+#u', ' ', trim($str)); -} - -function make_rand_str ($len = 10) -{ - $str = ''; - while (strlen($str) < $len) - { - $str .= str_shuffle(preg_replace('#[^0-9a-zA-Z]#', '', crypt(uniqid(mt_rand(), true)))); - } - return substr($str, 0, $len); -} - -// bencode: based on OpenTracker -function bencode ($var) -{ - if (is_string($var)) - { - return strlen($var) .':'. $var; - } - else if (is_int($var)) - { - return 'i'. $var .'e'; - } - else if (is_float($var)) - { - return 'i'. sprintf('%.0f', $var) .'e'; - } - else if (is_array($var)) - { - if (count($var) == 0) - { - return 'de'; - } - else - { - $assoc = false; - - foreach ($var as $key => $val) - { - if (!is_int($key)) - { - $assoc = true; - break; - } - } - - if ($assoc) - { - ksort($var, SORT_REGULAR); - $ret = 'd'; - - foreach ($var as $key => $val) - { - $ret .= bencode($key) . bencode($val); - } - return $ret .'e'; - } - else - { - $ret = 'l'; - - foreach ($var as $val) - { - $ret .= bencode($val); - } - return $ret .'e'; - } - } - } - else - { - trigger_error('bencode error: wrong data type', E_USER_ERROR); - } -} - -function array_deep (&$var, $fn, $one_dimensional = false, $array_only = false) -{ - if (is_array($var)) - { - foreach ($var as $k => $v) - { - if (is_array($v)) - { - if ($one_dimensional) - { - unset($var[$k]); - } - else if ($array_only) - { - $var[$k] = $fn($v); - } - else - { - array_deep($var[$k], $fn); - } - } - else if (!$array_only) - { - $var[$k] = $fn($v); - } - } - } - else if (!$array_only) - { - $var = $fn($var); - } -} - -function hide_bb_path ($path) -{ - return ltrim(str_replace(BB_PATH, '', $path), '/\\'); -} - -function tr_drop_request ($drop_type) -{ - if (DBG_LOG) dbg_log(' ', "request-dropped-$drop_type"); - dummy_exit(mt_rand(60, 600)); -} - -function sys ($param) -{ - switch ($param) - { - case 'la': - return function_exists('sys_getloadavg') ? join(' ', sys_getloadavg()) : 0; - break; - case 'mem': - return function_exists('memory_get_usage') ? memory_get_usage() : 0; - break; - case 'mem_peak': - return function_exists('memory_get_peak_usage') ? memory_get_peak_usage() : 0; - break; - default: - trigger_error("invalid param: $param", E_USER_ERROR); - } -} - -function ver_compare ($version1, $operator, $version2) -{ - return version_compare($version1, $version2, $operator); -} - -function dbg_log ($str, $file) -{ - $dir = LOG_DIR . (defined('IN_TRACKER') ? 'dbg_tr/' : 'dbg_bb/') . date('m-d_H') .'/'; - return file_write($str, $dir . $file, false, false); -} - -function log_get ($file = '', $prepend_str = false) -{ - log_request($file, $prepend_str, false); -} - -function log_post ($file = '', $prepend_str = false) -{ - log_request($file, $prepend_str, true); -} - -function log_request ($file = '', $prepend_str = false, $add_post = true) -{ - global $user; - - $file = ($file) ? $file : 'req/'. date('m-d'); - $str = array(); - $str[] = date('m-d H:i:s'); - if ($prepend_str !== false) $str[] = $prepend_str; - if (!empty($user->data)) $str[] = $user->id ."\t". html_entity_decode($user->name); - $str[] = sprintf('%-15s', $_SERVER['REMOTE_ADDR']); - $str[] = @$_SERVER['REQUEST_URI']; - $str[] = @$_SERVER['HTTP_USER_AGENT']; - $str[] = @$_SERVER['HTTP_REFERER']; - if (!empty($_POST) && $add_post) $str[] = "post: ". str_compact(urldecode(http_build_query($_POST))); - $str = join("\t", $str) . "\n"; - bb_log($str, $file); -} - -// Board init -if (defined('IN_FORUM')) -{ - require(INC_DIR .'init_bb.php'); -} -// Tracker init -else if (defined('IN_TRACKER')) -{ - define('DUMMY_PEER', pack('Nn', ip2long($_SERVER['REMOTE_ADDR']), !empty($_GET['port']) ? intval($_GET['port']) : mt_rand(1000, 65000))); - - function dummy_exit ($interval = 1800) - { - $output = bencode(array( - 'interval' => (int) $interval, - 'min interval' => (int) $interval, - 'peers' => (string) DUMMY_PEER, - )); - - die($output); - } - - header('Content-Type: text/plain'); - header('Pragma: no-cache'); - - if (!defined('IN_ADMIN')) - { - // Exit if tracker is disabled via ON/OFF trigger - if (file_exists(BB_DISABLED)) - { - dummy_exit(mt_rand(60, 2400)); # die('d14:failure reason20:temporarily disablede'); - } - } -} \ No newline at end of file diff --git a/upload/internal_data/ajax_html/jumpbox_guest.html b/upload/internal_data/ajax_html/jumpbox_guest.html deleted file mode 100644 index e25cf03e0..000000000 --- a/upload/internal_data/ajax_html/jumpbox_guest.html +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/upload/internal_data/ajax_html/jumpbox_user.html b/upload/internal_data/ajax_html/jumpbox_user.html deleted file mode 100644 index e25cf03e0..000000000 --- a/upload/internal_data/ajax_html/jumpbox_user.html +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/upload/library/includes/sql_parse.php b/upload/library/includes/sql_parse.php deleted file mode 100644 index 65e3f48c9..000000000 --- a/upload/library/includes/sql_parse.php +++ /dev/null @@ -1,164 +0,0 @@ - 0)) - { - if ($lines[$i][0] != "#") - { - $output .= $lines[$i] . "\n"; - } - else - { - $output .= "\n"; - } - // Trading a bit of speed for lower mem. use here. - $lines[$i] = ""; - } - } - - return $output; - -} - -// -// split_sql_file will split an uploaded sql file into single sql statements. -// Note: expects trim() to have already been run on $sql. -// -function split_sql_file($sql, $delimiter) -{ - // Split up our string into "possible" SQL statements. - $tokens = explode($delimiter, $sql); - - // try to save mem. - $sql = ""; - $output = array(); - - // we don't actually care about the matches preg gives us. - $matches = array(); - - // this is faster than calling count($oktens) every time thru the loop. - $token_count = count($tokens); - for ($i = 0; $i < $token_count; $i++) - { - // Don't wanna add an empty string as the last thing in the array. - if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0))) - { - // This is the total number of single quotes in the token. - $total_quotes = preg_match_all("/'/", $tokens[$i], $matches); - // Counts single quotes that are preceded by an odd number of backslashes, - // which means they're escaped quotes. - $escaped_quotes = preg_match_all("/(? -

    BitTorrent клиенты

    - -
    - -
    - - - -
    - - \ No newline at end of file diff --git a/upload/library/language/uk/html/sidebar1.html b/upload/library/language/uk/html/sidebar1.html deleted file mode 100644 index f0dcac149..000000000 --- a/upload/library/language/uk/html/sidebar1.html +++ /dev/null @@ -1,23 +0,0 @@ -
    -

    BitTorrent клієнти

    -
      -
    • uTorrent (рекомендований: 3.4)
    • -
    • BitTorrent (рекомендований: 7.9)
    • -
    • Transmission (рекомендований: 2.82)
    • -
    -
    - -
    - - - -
    - - \ No newline at end of file diff --git a/upload/styles/bootstrap/css/bootstrap-theme.css.map b/upload/styles/bootstrap/css/bootstrap-theme.css.map deleted file mode 100644 index 4cc41ab00..000000000 --- a/upload/styles/bootstrap/css/bootstrap-theme.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bootstrap-theme.css","sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"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","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n}\n\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",null,"// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/upload/styles/bootstrap/css/bootstrap-theme.min.css b/upload/styles/bootstrap/css/bootstrap-theme.min.css deleted file mode 100644 index 2e97597c8..000000000 --- a/upload/styles/bootstrap/css/bootstrap-theme.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.2.0 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-o-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#2d6ca2));background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary:disabled,.btn-primary[disabled]{background-color:#2d6ca2;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f3f3f3));background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-o-linear-gradient(top,#222 0,#282828 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#222),to(#282828));background-image:linear-gradient(to bottom,#222 0,#282828 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-o-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3071a9));background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-o-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3278b3));background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} \ No newline at end of file diff --git a/upload/styles/bootstrap/css/bootstrap.css.map b/upload/styles/bootstrap/css/bootstrap.css.map deleted file mode 100644 index bfb561689..000000000 --- a/upload/styles/bootstrap/css/bootstrap.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bootstrap.css","sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA,6DAA4D;ACQ5D;EACE,yBAAA;EACA,4BAAA;EACA,gCAAA;EDND;ACaD;EACE,WAAA;EDXD;ACuBD;;;;;;;;;;;;EAYE,gBAAA;EDrBD;AC6BD;;;;EAIE,uBAAA;EACA,0BAAA;ED3BD;ACmCD;EACE,eAAA;EACA,WAAA;EDjCD;ACyCD;;EAEE,eAAA;EDvCD;ACiDD;EACE,yBAAA;ED/CD;ACsDD;;EAEE,YAAA;EDpDD;AC8DD;EACE,2BAAA;ED5DD;ACmED;;EAEE,mBAAA;EDjED;ACwED;EACE,oBAAA;EDtED;AC8ED;EACE,gBAAA;EACA,kBAAA;ED5ED;ACmFD;EACE,kBAAA;EACA,aAAA;EDjFD;ACwFD;EACE,gBAAA;EDtFD;AC6FD;;EAEE,gBAAA;EACA,gBAAA;EACA,oBAAA;EACA,0BAAA;ED3FD;AC8FD;EACE,aAAA;ED5FD;AC+FD;EACE,iBAAA;ED7FD;ACuGD;EACE,WAAA;EDrGD;AC4GD;EACE,kBAAA;ED1GD;ACoHD;EACE,kBAAA;EDlHD;ACyHD;EACE,8BAAA;EACA,iCAAA;EAAA,yBAAA;EACA,WAAA;EDvHD;AC8HD;EACE,gBAAA;ED5HD;ACmID;;;;EAIE,mCAAA;EACA,gBAAA;EDjID;ACmJD;;;;;EAKE,gBAAA;EACA,eAAA;EACA,WAAA;EDjJD;ACwJD;EACE,mBAAA;EDtJD;ACgKD;;EAEE,sBAAA;ED9JD;ACyKD;;;;EAIE,4BAAA;EACA,iBAAA;EDvKD;AC8KD;;EAEE,iBAAA;ED5KD;ACmLD;;EAEE,WAAA;EACA,YAAA;EDjLD;ACyLD;EACE,qBAAA;EDvLD;ACkMD;;EAEE,gCAAA;EAAA,6BAAA;EAAA,wBAAA;EACA,YAAA;EDhMD;ACyMD;;EAEE,cAAA;EDvMD;ACgND;EACE,+BAAA;EACA,8BAAA;EACA,iCAAA;EACA,yBAAA;ED9MD;ACuND;;EAEE,0BAAA;EDrND;AC4ND;EACE,2BAAA;EACA,eAAA;EACA,gCAAA;ED1ND;ACkOD;EACE,WAAA;EACA,YAAA;EDhOD;ACuOD;EACE,gBAAA;EDrOD;AC6OD;EACE,mBAAA;ED3OD;ACqPD;EACE,2BAAA;EACA,mBAAA;EDnPD;ACsPD;;EAEE,YAAA;EDpPD;AE9ED;EA9FE;IACE,8BAAA;IACA,wBAAA;IACA,oCAAA;IACA,qCAAA;IAAA,6BAAA;IF+KD;EE5KD;;IAEE,4BAAA;IF8KD;EE3KD;IACE,8BAAA;IF6KD;EE1KD;IACE,+BAAA;IF4KD;EExKD;;IAEE,aAAA;IF0KD;EEvKD;;IAEE,wBAAA;IACA,0BAAA;IFyKD;EEtKD;IACE,6BAAA;IFwKD;EErKD;;IAEE,0BAAA;IFuKD;EEpKD;IACE,4BAAA;IFsKD;EEnKD;;;IAGE,YAAA;IACA,WAAA;IFqKD;EElKD;;IAEE,yBAAA;IFoKD;EE/JD;IACE,6BAAA;IFiKD;EE7JD;IACE,eAAA;IF+JD;EE7JD;;IAGI,mCAAA;IF8JH;EE3JD;;IAGI,mCAAA;IF4JH;EEzJD;IACE,wBAAA;IF2JD;EExJD;IACE,sCAAA;IF0JD;EExJD;;IAGI,mCAAA;IFyJH;EACF;AGhPD;EACE,qCAAA;EACA,uDAAA;EACA,6TAAA;EHkPD;AG3OD;EACE,oBAAA;EACA,UAAA;EACA,uBAAA;EACA,qCAAA;EACA,oBAAA;EACA,qBAAA;EACA,gBAAA;EACA,qCAAA;EACA,oCAAA;EH6OD;AGzOmC;EAAW,gBAAA;EH4O9C;AG3OmC;EAAW,gBAAA;EH8O9C;AG7OmC;EAAW,kBAAA;EHgP9C;AG/OmC;EAAW,kBAAA;EHkP9C;AGjPmC;EAAW,kBAAA;EHoP9C;AGnPmC;EAAW,kBAAA;EHsP9C;AGrPmC;EAAW,kBAAA;EHwP9C;AGvPmC;EAAW,kBAAA;EH0P9C;AGzPmC;EAAW,kBAAA;EH4P9C;AG3PmC;EAAW,kBAAA;EH8P9C;AG7PmC;EAAW,kBAAA;EHgQ9C;AG/PmC;EAAW,kBAAA;EHkQ9C;AGjQmC;EAAW,kBAAA;EHoQ9C;AGnQmC;EAAW,kBAAA;EHsQ9C;AGrQmC;EAAW,kBAAA;EHwQ9C;AGvQmC;EAAW,kBAAA;EH0Q9C;AGzQmC;EAAW,kBAAA;EH4Q9C;AG3QmC;EAAW,kBAAA;EH8Q9C;AG7QmC;EAAW,kBAAA;EHgR9C;AG/QmC;EAAW,kBAAA;EHkR9C;AGjRmC;EAAW,kBAAA;EHoR9C;AGnRmC;EAAW,kBAAA;EHsR9C;AGrRmC;EAAW,kBAAA;EHwR9C;AGvRmC;EAAW,kBAAA;EH0R9C;AGzRmC;EAAW,kBAAA;EH4R9C;AG3RmC;EAAW,kBAAA;EH8R9C;AG7RmC;EAAW,kBAAA;EHgS9C;AG/RmC;EAAW,kBAAA;EHkS9C;AGjSmC;EAAW,kBAAA;EHoS9C;AGnSmC;EAAW,kBAAA;EHsS9C;AGrSmC;EAAW,kBAAA;EHwS9C;AGvSmC;EAAW,kBAAA;EH0S9C;AGzSmC;EAAW,kBAAA;EH4S9C;AG3SmC;EAAW,kBAAA;EH8S9C;AG7SmC;EAAW,kBAAA;EHgT9C;AG/SmC;EAAW,kBAAA;EHkT9C;AGjTmC;EAAW,kBAAA;EHoT9C;AGnTmC;EAAW,kBAAA;EHsT9C;AGrTmC;EAAW,kBAAA;EHwT9C;AGvTmC;EAAW,kBAAA;EH0T9C;AGzTmC;EAAW,kBAAA;EH4T9C;AG3TmC;EAAW,kBAAA;EH8T9C;AG7TmC;EAAW,kBAAA;EHgU9C;AG/TmC;EAAW,kBAAA;EHkU9C;AGjUmC;EAAW,kBAAA;EHoU9C;AGnUmC;EAAW,kBAAA;EHsU9C;AGrUmC;EAAW,kBAAA;EHwU9C;AGvUmC;EAAW,kBAAA;EH0U9C;AGzUmC;EAAW,kBAAA;EH4U9C;AG3UmC;EAAW,kBAAA;EH8U9C;AG7UmC;EAAW,kBAAA;EHgV9C;AG/UmC;EAAW,kBAAA;EHkV9C;AGjVmC;EAAW,kBAAA;EHoV9C;AGnVmC;EAAW,kBAAA;EHsV9C;AGrVmC;EAAW,kBAAA;EHwV9C;AGvVmC;EAAW,kBAAA;EH0V9C;AGzVmC;EAAW,kBAAA;EH4V9C;AG3VmC;EAAW,kBAAA;EH8V9C;AG7VmC;EAAW,kBAAA;EHgW9C;AG/VmC;EAAW,kBAAA;EHkW9C;AGjWmC;EAAW,kBAAA;EHoW9C;AGnWmC;EAAW,kBAAA;EHsW9C;AGrWmC;EAAW,kBAAA;EHwW9C;AGvWmC;EAAW,kBAAA;EH0W9C;AGzWmC;EAAW,kBAAA;EH4W9C;AG3WmC;EAAW,kBAAA;EH8W9C;AG7WmC;EAAW,kBAAA;EHgX9C;AG/WmC;EAAW,kBAAA;EHkX9C;AGjXmC;EAAW,kBAAA;EHoX9C;AGnXmC;EAAW,kBAAA;EHsX9C;AGrXmC;EAAW,kBAAA;EHwX9C;AGvXmC;EAAW,kBAAA;EH0X9C;AGzXmC;EAAW,kBAAA;EH4X9C;AG3XmC;EAAW,kBAAA;EH8X9C;AG7XmC;EAAW,kBAAA;EHgY9C;AG/XmC;EAAW,kBAAA;EHkY9C;AGjYmC;EAAW,kBAAA;EHoY9C;AGnYmC;EAAW,kBAAA;EHsY9C;AGrYmC;EAAW,kBAAA;EHwY9C;AGvYmC;EAAW,kBAAA;EH0Y9C;AGzYmC;EAAW,kBAAA;EH4Y9C;AG3YmC;EAAW,kBAAA;EH8Y9C;AG7YmC;EAAW,kBAAA;EHgZ9C;AG/YmC;EAAW,kBAAA;EHkZ9C;AGjZmC;EAAW,kBAAA;EHoZ9C;AGnZmC;EAAW,kBAAA;EHsZ9C;AGrZmC;EAAW,kBAAA;EHwZ9C;AGvZmC;EAAW,kBAAA;EH0Z9C;AGzZmC;EAAW,kBAAA;EH4Z9C;AG3ZmC;EAAW,kBAAA;EH8Z9C;AG7ZmC;EAAW,kBAAA;EHga9C;AG/ZmC;EAAW,kBAAA;EHka9C;AGjamC;EAAW,kBAAA;EHoa9C;AGnamC;EAAW,kBAAA;EHsa9C;AGramC;EAAW,kBAAA;EHwa9C;AGvamC;EAAW,kBAAA;EH0a9C;AGzamC;EAAW,kBAAA;EH4a9C;AG3amC;EAAW,kBAAA;EH8a9C;AG7amC;EAAW,kBAAA;EHgb9C;AG/amC;EAAW,kBAAA;EHkb9C;AGjbmC;EAAW,kBAAA;EHob9C;AGnbmC;EAAW,kBAAA;EHsb9C;AGrbmC;EAAW,kBAAA;EHwb9C;AGvbmC;EAAW,kBAAA;EH0b9C;AGzbmC;EAAW,kBAAA;EH4b9C;AG3bmC;EAAW,kBAAA;EH8b9C;AG7bmC;EAAW,kBAAA;EHgc9C;AG/bmC;EAAW,kBAAA;EHkc9C;AGjcmC;EAAW,kBAAA;EHoc9C;AGncmC;EAAW,kBAAA;EHsc9C;AGrcmC;EAAW,kBAAA;EHwc9C;AGvcmC;EAAW,kBAAA;EH0c9C;AGzcmC;EAAW,kBAAA;EH4c9C;AG3cmC;EAAW,kBAAA;EH8c9C;AG7cmC;EAAW,kBAAA;EHgd9C;AG/cmC;EAAW,kBAAA;EHkd9C;AGjdmC;EAAW,kBAAA;EHod9C;AGndmC;EAAW,kBAAA;EHsd9C;AGrdmC;EAAW,kBAAA;EHwd9C;AGvdmC;EAAW,kBAAA;EH0d9C;AGzdmC;EAAW,kBAAA;EH4d9C;AG3dmC;EAAW,kBAAA;EH8d9C;AG7dmC;EAAW,kBAAA;EHge9C;AG/dmC;EAAW,kBAAA;EHke9C;AGjemC;EAAW,kBAAA;EHoe9C;AGnemC;EAAW,kBAAA;EHse9C;AGremC;EAAW,kBAAA;EHwe9C;AGvemC;EAAW,kBAAA;EH0e9C;AGzemC;EAAW,kBAAA;EH4e9C;AG3emC;EAAW,kBAAA;EH8e9C;AG7emC;EAAW,kBAAA;EHgf9C;AG/emC;EAAW,kBAAA;EHkf9C;AGjfmC;EAAW,kBAAA;EHof9C;AGnfmC;EAAW,kBAAA;EHsf9C;AGrfmC;EAAW,kBAAA;EHwf9C;AGvfmC;EAAW,kBAAA;EH0f9C;AGzfmC;EAAW,kBAAA;EH4f9C;AG3fmC;EAAW,kBAAA;EH8f9C;AG7fmC;EAAW,kBAAA;EHggB9C;AG/fmC;EAAW,kBAAA;EHkgB9C;AGjgBmC;EAAW,kBAAA;EHogB9C;AGngBmC;EAAW,kBAAA;EHsgB9C;AGrgBmC;EAAW,kBAAA;EHwgB9C;AGvgBmC;EAAW,kBAAA;EH0gB9C;AGzgBmC;EAAW,kBAAA;EH4gB9C;AG3gBmC;EAAW,kBAAA;EH8gB9C;AG7gBmC;EAAW,kBAAA;EHghB9C;AG/gBmC;EAAW,kBAAA;EHkhB9C;AGjhBmC;EAAW,kBAAA;EHohB9C;AGnhBmC;EAAW,kBAAA;EHshB9C;AGrhBmC;EAAW,kBAAA;EHwhB9C;AGvhBmC;EAAW,kBAAA;EH0hB9C;AGzhBmC;EAAW,kBAAA;EH4hB9C;AG3hBmC;EAAW,kBAAA;EH8hB9C;AG7hBmC;EAAW,kBAAA;EHgiB9C;AG/hBmC;EAAW,kBAAA;EHkiB9C;AGjiBmC;EAAW,kBAAA;EHoiB9C;AGniBmC;EAAW,kBAAA;EHsiB9C;AGriBmC;EAAW,kBAAA;EHwiB9C;AGviBmC;EAAW,kBAAA;EH0iB9C;AGziBmC;EAAW,kBAAA;EH4iB9C;AG3iBmC;EAAW,kBAAA;EH8iB9C;AG7iBmC;EA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A;Efk8CH;Ae97CC;EACE,YAAA;Efg8CH;Ael7CC;EACE,mBAAA;Efo7CH;Aer7CC;EACE,2BAAA;Efu7CH;Aex7CC;EACE,2BAAA;Ef07CH;Ae37CC;EACE,kBAAA;Ef67CH;Ae97CC;EACE,2BAAA;Efg8CH;Aej8CC;EACE,2BAAA;Efm8CH;Aep8CC;EACE,kBAAA;Efs8CH;Aev8CC;EACE,2BAAA;Efy8CH;Ae18CC;EACE,2BAAA;Ef48CH;Ae78CC;EACE,kBAAA;Ef+8CH;Aeh9CC;EACE,2BAAA;Efk9CH;Aen9CC;EACE,0BAAA;Efq9CH;Aet9CC;EACE,iBAAA;Efw9CH;Aa59CD;EE9BI;IACE,aAAA;If6/CH;Eet/CD;IACE,aAAA;Ifw/CD;Eez/CD;IACE,qBAAA;If2/CD;Ee5/CD;IACE,qBAAA;If8/CD;Ee//CD;IACE,YAAA;IfigDD;EelgDD;IACE,qBAAA;IfogDD;EergDD;IACE,qBAAA;IfugDD;EexgDD;IACE,YAAA;If0gDD;Ee3gDD;IACE,qBAAA;If6gDD;Ee9gDD;IACE,qBAAA;IfghDD;EejhDD;IACE,YAAA;IfmhDD;EephDD;IACE,qBAAA;IfshDD;EevhDD;IACE,oBAAA;IfyhDD;Ee3gDD;IACE,aAAA;If6gDD;Ee9gDD;IACE,qBAAA;IfghDD;EejhDD;IACE,qBAAA;IfmhDD;EephDD;IACE,YAAA;IfshDD;EevhDD;IACE,qBAAA;IfyhDD;Ee1hDD;IACE,qBAAA;If4hDD;Ee7hDD;IACE,YAAA;If+hDD;EehiDD;IACE,qBAAA;IfkiDD;EeniDD;IACE,qBAAA;IfqiDD;EetiDD;IACE,YAAA;IfwiDD;EeziDD;IACE,qBAAA;If2iDD;Ee5iDD;IACE,oBAAA;If8iDD;Ee1iDD;IACE,aAAA;If4iDD;Ee5jDD;IACE,YAAA;If8jDD;Ee/jDD;IACE,oBAAA;IfikDD;EelkDD;IACE,oBAAA;IfokDD;EerkDD;IACE,WAAA;IfukDD;EexkDD;IACE,oBAAA;If0kDD;Ee3kDD;IACE,oBAAA;If6kDD;Ee9kDD;IACE,WAAA;IfglDD;EejlDD;IACE,oBAAA;IfmlDD;EeplDD;IACE,oBAAA;IfslDD;EevlDD;IACE,WAAA;IfylDD;Ee1lDD;IACE,oBAAA;If4lDD;Ee7lDD;IACE,mBAAA;If+lDD;Ee3lDD;IACE,YAAA;If6lDD;Ee/kDD;IACE,mBAAA;IfilDD;EellDD;IACE,2BAAA;IfolDD;EerlDD;IACE,2BAAA;IfulDD;EexlDD;IACE,kBAAA;If0lDD;Ee3lDD;IACE,2BAAA;If6lDD;Ee9lDD;IACE,2BAAA;IfgmDD;EejmDD;IACE,kBAAA;IfmmDD;EepmDD;IACE,2BAAA;IfsmDD;EevmDD;IACE,2BAAA;IfymDD;Ee1mDD;IACE,kBAAA;If4mDD;Ee7mDD;IACE,2BAAA;If+mDD;EehnDD;IACE,0BAAA;IfknDD;EennDD;IACE,iBAAA;IfqnDD;EACF;AajnDD;EEvCI;IACE,aAAA;If2pDH;EeppDD;IACE,aAAA;IfspDD;EevpDD;IACE,qBAAA;IfypDD;Ee1pDD;IACE,qBAAA;If4pDD;Ee7pDD;IACE,YAAA;If+pDD;EehqDD;IACE,qBAAA;IfkqDD;EenqDD;IACE,qBAAA;IfqqDD;EetqDD;IACE,YAAA;IfwqDD;EezqDD;IACE,qBAAA;If2qDD;Ee5qDD;IACE,qBAAA;If8qDD;Ee/qDD;IACE,YAAA;IfirDD;EelrDD;IACE,qBAAA;IforDD;EerrDD;IACE,oBAAA;IfurDD;EezqDD;IACE,aAAA;If2qDD;Ee5qDD;IACE,qBAAA;If8qDD;Ee/qDD;IACE,qBAAA;IfirDD;EelrDD;IACE,YAAA;IforDD;EerrDD;IACE,qBAAA;IfurDD;EexrDD;IACE,qBAAA;If0rDD;Ee3rDD;IACE,YAAA;If6rDD;Ee9rDD;IACE,qBAAA;IfgsDD;EejsDD;IACE,qBAAA;IfmsDD;EepsDD;IACE,YAAA;IfssDD;EevsDD;IACE,qBAAA;IfysDD;Ee1sDD;IACE,oBAAA;If4sDD;EexsDD;IACE,aAAA;If0sDD;Ee1tDD;IACE,YAAA;If4tDD;Ee7tDD;IACE,oBAAA;If+tDD;EehuDD;IACE,oBAAA;IfkuDD;EenuDD;IACE,WAAA;IfquDD;EetuDD;IACE,oBAAA;IfwuDD;EezuDD;IACE,oBAAA;If2uDD;Ee5uDD;IACE,WAAA;If8uDD;Ee/uDD;IACE,oBAAA;IfivDD;EelvDD;IACE,oBAAA;IfovDD;EervDD;IACE,WAAA;IfuvDD;EexvDD;IACE,oBAAA;If0vDD;Ee3vDD;IACE,mBAAA;If6vDD;EezvDD;IACE,YAAA;If2vDD;Ee7uDD;IACE,mBAAA;If+uDD;EehvDD;IACE,2BAAA;IfkvDD;EenvDD;IACE,2BAAA;IfqvDD;EetvDD;IACE,kBAAA;IfwvDD;EezvDD;IACE,2BAAA;If2vDD;Ee5vDD;IACE,2BAAA;If8vDD;Ee/vDD;IACE,kBAAA;IfiwDD;EelwDD;IACE,2BAAA;IfowDD;EerwDD;IACE,2BAAA;IfuwDD;EexwDD;IACE,kBAAA;If0wDD;Ee3wDD;IACE,2BAAA;If6wDD;Ee9wDD;IACE,0BAAA;IfgxDD;EejxDD;IACE,iBAAA;IfmxDD;EACF;AaxwDD;EE9CI;IACE,aAAA;IfyzDH;EelzDD;IACE,aAAA;IfozDD;EerzDD;IACE,qBAAA;IfuzDD;EexzDD;IACE,qBAAA;If0zDD;Ee3zDD;IACE,YAAA;If6zDD;Ee9zDD;IACE,qBAAA;Ifg0DD;Eej0DD;IACE,qBAAA;Ifm0DD;Eep0DD;IACE,YAAA;Ifs0DD;Eev0DD;IACE,qBAAA;Ify0DD;Ee10DD;IACE,qBAAA;If40DD;Ee70DD;IACE,YAAA;If+0DD;Eeh1DD;IACE,qBAAA;Ifk1DD;Een1DD;IACE,oBAAA;Ifq1DD;Eev0DD;IACE,aAAA;Ify0DD;Ee10DD;IACE,qBAAA;If40DD;Ee70DD;IACE,qBAAA;If+0DD;Eeh1DD;IACE,YAAA;Ifk1DD;Een1DD;IACE,qBAAA;Ifq1DD;Eet1DD;IACE,qBAAA;Ifw1DD;Eez1DD;IACE,YAAA;If21DD;Ee51DD;IACE,qBAAA;If81DD;Ee/1DD;IACE,qBAAA;Ifi2DD;Eel2DD;IACE,YAAA;Ifo2DD;Eer2DD;IACE,qBAAA;Ifu2DD;Eex2DD;IACE,oBAAA;If02DD;Eet2DD;IACE,aAAA;Ifw2DD;Eex3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AAA;EACI,uBAAA;ErBqsFT;AoB9qFD;EClBI,gBAAA;EACA,2BAAA;ErBmsFH;AoB/qFD;ECrDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBuuFD;AqBruFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBuuFP;AqBruFC;;;EAGE,wBAAA;ErBuuFH;AqBluFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErB8uFT;AoBptFD;ECrBI,gBAAA;EACA,2BAAA;ErB4uFH;AoBptFD;ECzDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBgxFD;AqB9wFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBgxFP;AqB9wFC;;;EAGE,wBAAA;ErBgxFH;AqB3wFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBuxFT;AoBzvFD;ECzBI,gBAAA;EACA,2BAAA;ErBqxFH;AoBzvFD;EC7DE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErByzFD;AqBvzFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErByzFP;AqBvzFC;;;EAGE,wBAAA;ErByzFH;AqBpzFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBg0FT;AoB9xFD;EC7BI,gBAAA;EACA,2BAAA;ErB8zFH;AoB9xFD;ECjEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBk2FD;AqBh2FC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBk2FP;AqBh2FC;;;EAGE,wBAAA;ErBk2FH;AqB71FG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBy2FT;AoBn0FD;ECjCI,gBAAA;EACA,2BAAA;ErBu2FH;AoBn0FD;ECrEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErB24FD;AqBz4FC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErB24FP;AqBz4FC;;;EAGE,wBAAA;ErB24FH;AqBt4FG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBk5FT;AoBx2FD;ECrCI,gBAAA;EACA,2BAAA;ErBg5FH;AoBn2FD;EACE,gBAAA;EACA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","sourcesContent":[null,"/*! normalize.css v3.0.1 | MIT License | git.io/normalize */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS text size adjust after orientation change, without disabling\n// user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background: transparent;\n}\n\n//\n// Improve readability when focused and also mouse hovered in all browsers.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n -moz-box-sizing: content-box;\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome\n// (include `-moz` to future-proof).\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n -moz-box-sizing: content-box;\n -webkit-box-sizing: content-box; // 2\n box-sizing: content-box;\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","//\n// Basic print styles\n// --------------------------------------------------\n// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css\n\n@media print {\n\n * {\n text-shadow: none !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n background: transparent !important;\n box-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links for images, or javascript/internal links\n a[href^=\"javascript:\"]:after,\n a[href^=\"#\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245\n // Once fixed, we can just straight up remove this.\n select {\n background: #fff !important;\n }\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .table {\n td,\n th {\n background-color: #fff !important;\n }\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: underline;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n width: 100% \\9; // Force IE10 and below to size SVG images correctly\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\n// Undo browser default styling\ncite {\n font-style: normal;\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Quotes\nblockquote:before,\nblockquote:after {\n content: \"\";\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: (@gutter / -2);\n margin-right: (@gutter / -2);\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) when (@index = 1) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) when (@index = 1) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-child(odd) {\n > td,\n > th {\n background-color: @table-bg-accent;\n }\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n > td,\n > th {\n background-color: @table-bg-hover;\n }\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n overflow-x: auto;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n -webkit-overflow-scrolling: touch;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\n// Set the height of file controls to match text inputs\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius;\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n cursor: not-allowed;\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned.\n// As a workaround, we set a pixel line-height that matches the\n// given height of the input. Since this fucks up everything else, we have to\n// appropriately reset it for Internet Explorer and the size variations.\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n line-height: @input-height-base;\n // IE8+ misaligns the text within date inputs, so we reset\n line-height: @line-height-base ~\"\\0\";\n\n &.input-sm {\n line-height: @input-height-small;\n }\n &.input-lg {\n line-height: @input-height-large;\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: 15px;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n min-height: @line-height-computed; // clear the floating input if there is no label text\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because