diff --git a/classes/account.php b/classes/account.php index 64171fc..1172a1c 100644 --- a/classes/account.php +++ b/classes/account.php @@ -12,6 +12,10 @@ class Account extends ObjectData public static $fields = array('id', 'name', 'password', 'premium_ends_at', 'email', 'key', 'create_ip', 'creation', 'premium_points', 'page_access', 'location', 'rlname', 'email_new', 'email_new_time', 'email_code', 'next_email', 'last_post', 'flag'); public $players; public $bans; + /** + * @var DatabaseList|PlayerTrade[] + */ + private $playerTrades; public function __construct($search_text = null, $search_by = self::LOADTYPE_ID) { @@ -122,6 +126,17 @@ public function getBanTime($forceReload = false) return $lastExpires; } + public function getPlayerTrades($forceReload = false) + { + if (!isset($this->playerTrades) || $forceReload) { + $this->playerTrades = new DatabaseList('PlayerTrade'); + $filter = new SQL_Filter(new SQL_Field('account_seller_id'), SQL_Filter::EQUAL, $this->getID()); + $this->playerTrades->setFilter($filter); + $this->playerTrades->addOrder(new SQL_Order(new SQL_Field('status'))); + } + return $this->playerTrades; + } + public function delete() { $this->getDatabaseHandler()->query('DELETE FROM ' . $this->getDatabaseHandler()->tableName(self::$table) . ' WHERE ' . $this->getDatabaseHandler()->fieldName('id') . ' = ' . $this->getDatabaseHandler()->quote($this->data['id'])); diff --git a/classes/playertrade.php b/classes/playertrade.php new file mode 100644 index 0000000..1fbfe38 --- /dev/null +++ b/classes/playertrade.php @@ -0,0 +1,236 @@ + 'Active', + self::STATUS_SOLD => 'Sold', + self::STATUS_CANCELED => 'Canceled', + ]; + + public static $table = 'player_trades'; + public $data = array( + 'account_seller_id' => null, + 'account_buyer_id' => null, + 'player_id' => null, + 'status' => null, + 'type' => null, + 'create_date' => null, + 'price_seller' => null, + 'price_buyer' => null + ); + public static $fields = array( + 'id', + 'account_seller_id', + 'account_buyer_id', + 'player_id', + 'status', + 'type', + 'create_date', + 'price_seller', + 'price_buyer' + ); + + public static function getIdFromSecretCode($secretCode) + { + $data = explode('_', $secretCode); + if (isset($data[0])) { + return intval(($data[0])); + } else { + return -1; + } + } + + public function getSecretCode() + { + return $this->getId() . '_' . substr(md5(serialize([ + (int)$this->getPlayerId(), + (int)$this->getCreateDate(), + (int)$this->getAccountSellerId(), + (int)$this->getPriceSeller(), + (int)$this->getPriceBuyer() + ])), 2, 10); + } + + public function isValidSecretCode($secretCode) + { + return strtolower($this->getSecretCode()) == strtolower($secretCode); + } + + public function isActive() + { + return $this->getStatus() == self::STATUS_ACTIVE; + } + + public function isPublic() + { + return $this->getType() == self::TYPE_PUBLIC; + } + + public function isPrivate() + { + return $this->getType() == self::TYPE_PRIVATE; + } + + public function getStatusName() + { + return self::$statusNames[$this->getStatus()]; + } + + public function __construct($search_text = null, $search_by = self::LOADTYPE_ID) + { + if ($search_text != null) { + $this->load($search_text, $search_by); + } + } + + public function load($search_text, $search_by = self::LOADTYPE_ID) + { + if (in_array($search_by, self::$fields)) { + $search_string = $this->getDatabaseHandler()->fieldName($search_by) . ' = ' . $this->getDatabaseHandler()->quote($search_text); + } else { + throw new RuntimeException('Wrong Account search_by type.'); + } + $fieldsArray = array(); + foreach (self::$fields as $fieldName) { + $fieldsArray[$fieldName] = $this->getDatabaseHandler()->fieldName($fieldName); + } + $this->data = $this->getDatabaseHandler()->query('SELECT ' . implode(', ', + $fieldsArray) . ' FROM ' . $this->getDatabaseHandler()->tableName(self::$table) . ' WHERE ' . $search_string)->fetch(); + } + + public function save($forceInsert = false) + { + if (!isset($this->data['id']) || $forceInsert) { + $keys = array(); + $values = array(); + foreach (self::$fields as $key) { + if ($key != 'id') { + $keys[] = $this->getDatabaseHandler()->fieldName($key); + $values[] = $this->getDatabaseHandler()->quote($this->data[$key]); + } + } + $this->getDatabaseHandler()->query('INSERT INTO ' . $this->getDatabaseHandler()->tableName(self::$table) . ' (' . implode(', ', + $keys) . ') VALUES (' . implode(', ', $values) . ')'); + $this->setId($this->getDatabaseHandler()->lastInsertId()); + } else { + $updates = array(); + foreach (self::$fields as $key) { + if ($key != 'id') { + $updates[] = $this->getDatabaseHandler()->fieldName($key) . ' = ' . $this->getDatabaseHandler()->quote($this->data[$key]); + } + } + $this->getDatabaseHandler()->query('UPDATE ' . $this->getDatabaseHandler()->tableName(self::$table) . ' SET ' . implode(', ', + $updates) . ' WHERE ' . $this->getDatabaseHandler()->fieldName('id') . ' = ' . $this->getDatabaseHandler()->quote($this->data['id'])); + } + } + + public function setId($value) + { + $this->data['id'] = $value; + } + + public function getAccountSellerId() + { + return $this->data['account_seller_id']; + } + + public function setAccountSellerId($value) + { + $this->data['account_seller_id'] = $value; + } + + public function getAccountBuyerId() + { + return $this->data['account_buyer_id']; + } + + public function setAccountBuyerId($value) + { + $this->data['account_buyer_id'] = $value; + } + + public function getPlayerId() + { + return $this->data['player_id']; + } + + public function setPlayerId($value) + { + $this->data['player_id'] = $value; + } + + public function getStatus() + { + return $this->data['status']; + } + + public function setStatus($value) + { + $this->data['status'] = $value; + } + + public function getType() + { + return $this->data['type']; + } + + public function setType($value) + { + $this->data['type'] = $value; + } + + public function getCreateDate() + { + return $this->data['create_date']; + } + + public function setCreateDate($value) + { + $this->data['create_date'] = $value; + } + + public function getPriceSeller() + { + return $this->data['price_seller']; + } + + public function setPriceSeller($value) + { + $this->data['price_seller'] = $value; + } + + public function getPriceBuyer() + { + return $this->data['price_buyer']; + } + + public function setPriceBuyer($value) + { + $this->data['price_buyer'] = $value; + } +} diff --git a/config/config.php b/config/config.php index b83be82..94dc6ec 100755 --- a/config/config.php +++ b/config/config.php @@ -95,3 +95,12 @@ $config['site']['lightborder'] = '#F1E0C6'; $config['site']['download_page'] = false; $config['site']['serverinfo_page'] = true; + +# character trade +$config['site']['trade_player_create_require_recovery_key'] = false; +$config['site']['trade_player_minimum_price'] = 100; +$config['site']['trade_player_minimum_level'] = 30; +$config['site']['trade_player_public_commission_fixed'] = 10; +$config['site']['trade_player_public_commission_percent'] = 10; +$config['site']['trade_player_private_commission_fixed'] = 0; +$config['site']['trade_player_private_commission_percent'] = 20; diff --git a/css/bootstrap.min.css b/css/bootstrap.min.css new file mode 100644 index 0000000..331b567 --- /dev/null +++ b/css/bootstrap.min.css @@ -0,0 +1,1704 @@ +/*! + * Generated using the Bootstrap Customizer (https://getbootstrap.com/docs/3.4/customize/) + *//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +a { + background-color: transparent +} + +a:active, a:hover { + outline: 0 +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted +} + +b, strong { + font-weight: bold +} + +dfn { + font-style: italic +} + +h1 { + font-size: 2em; + margin: 0.67em 0 +} + +mark { + background: #ff0; + color: #000 +} + +small { + font-size: 80% +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sup { + top: -0.5em +} + +sub { + bottom: -0.25em +} + +img { + border: 0 +} + +svg:not(:root) { + overflow: hidden +} + +figure { + margin: 1em 40px +} + +hr { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0 +} + +pre { + overflow: auto +} + +code, kbd, pre, samp { + font-family: monospace, monospace; + font-size: 1em +} + +button, input, optgroup, select, textarea { + color: inherit; + font: inherit; + margin: 0 +} + +button { + overflow: visible +} + +button, select { + text-transform: none +} + +button, html input[type="button"], input[type="reset"], input[type="submit"] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], html input[disabled] { + cursor: default +} + +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0 +} + +input { + line-height: normal +} + +input[type="checkbox"], input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0 +} + +input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { + height: auto +} + +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box +} + +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none +} + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em +} + +legend { + border: 0; + padding: 0 +} + +textarea { + overflow: auto +} + +optgroup { + font-weight: bold +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +td, th { + padding: 0 +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +input, button, select, textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit +} + +a { + color: #337ab7; + text-decoration: none +} + +a:hover, a:focus { + color: #23527c; + text-decoration: underline +} + +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +figure { + margin: 0 +} + +img { + vertical-align: middle +} + +.img-responsive, .thumbnail > img, .thumbnail a > img { + display: block; + max-width: 100%; + height: auto +} + +.img-rounded { + border-radius: 6px +} + +.img-thumbnail { + padding: 4px; + line-height: 1.42857143; + 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; + display: inline-block; + max-width: 100%; + height: auto +} + +.img-circle { + border-radius: 50% +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0 +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto +} + +[role="button"] { + cursor: pointer +} + +table { + background-color: transparent +} + +table col[class*="col-"] { + position: static; + display: table-column; + float: none +} + +table td[class*="col-"], table th[class*="col-"] { + position: static; + display: table-cell; + float: none +} + +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left +} + +th { + text-align: left +} + +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px +} + +.table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd +} + +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd +} + +.table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { + border-top: 0 +} + +.table > tbody + tbody { + border-top: 2px solid #ddd +} + +.table .table { + background-color: #fff +} + +.table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { + padding: 5px +} + +.table-bordered { + border: 1px solid #ddd +} + +.table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { + border: 1px solid #ddd +} + +.table-bordered > thead > tr > th, .table-bordered > thead > tr > td { + border-bottom-width: 2px +} + +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9 +} + +.table-hover > tbody > tr:hover { + background-color: #f5f5f5 +} + +.table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { + background-color: #f5f5f5 +} + +.table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8 +} + +.table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { + background-color: #dff0d8 +} + +.table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6 +} + +.table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { + background-color: #d9edf7 +} + +.table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3 +} + +.table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { + background-color: #fcf8e3 +} + +.table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc +} + +.table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { + background-color: #f2dede +} + +.table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .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-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd + } + + .table-responsive > .table { + margin-bottom: 0 + } + + .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { + white-space: nowrap + } + + .table-responsive > .table-bordered { + border: 0 + } + + .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0 + } + + .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0 + } + + .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0 + } +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0 +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5 +} + +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: 700 +} + +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-appearance: none; + appearance: none +} + +input[type="radio"], input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal +} + +input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed +} + +input[type="file"] { + display: block +} + +input[type="range"] { + display: block; + width: 100% +} + +select[multiple], select[size] { + height: auto +} + +input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555 +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s +} + +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6) +} + +.form-control::-moz-placeholder { + color: #999; + opacity: 1 +} + +.form-control:-ms-input-placeholder { + color: #999 +} + +.form-control::-webkit-input-placeholder { + color: #999 +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0 +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1 +} + +.form-control[disabled], fieldset[disabled] .form-control { + cursor: not-allowed +} + +textarea.form-control { + height: auto +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control { + line-height: 34px + } + + input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm, .input-group-sm input[type="date"], .input-group-sm input[type="time"], .input-group-sm input[type="datetime-local"], .input-group-sm input[type="month"] { + line-height: 30px + } + + input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg, .input-group-lg input[type="date"], .input-group-lg input[type="time"], .input-group-lg input[type="datetime-local"], .input-group-lg input[type="month"] { + line-height: 46px + } +} + +.form-group { + margin-bottom: 15px +} + +.radio, .checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px +} + +.radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { + cursor: not-allowed +} + +.radio label, .checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + cursor: pointer +} + +.radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px +} + +.radio + .radio, .checkbox + .checkbox { + margin-top: -5px +} + +.radio-inline, .checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + vertical-align: middle; + cursor: pointer +} + +.radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { + cursor: not-allowed +} + +.radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px +} + +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0 +} + +.form-control-static.input-lg, .form-control-static.input-sm { + padding-right: 0; + padding-left: 0 +} + +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-sm { + height: 30px; + line-height: 30px +} + +textarea.input-sm, select[multiple].input-sm { + height: auto +} + +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.form-group-sm select.form-control { + height: 30px; + line-height: 30px +} + +.form-group-sm textarea.form-control, .form-group-sm select[multiple].form-control { + height: auto +} + +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5 +} + +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +select.input-lg { + height: 46px; + line-height: 46px +} + +textarea.input-lg, select[multiple].input-lg { + height: auto +} + +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +.form-group-lg select.form-control { + height: 46px; + line-height: 46px +} + +.form-group-lg textarea.form-control, .form-group-lg select[multiple].form-control { + height: auto +} + +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333 +} + +.has-feedback { + position: relative +} + +.has-feedback .form-control { + padding-right: 42.5px +} + +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none +} + +.input-lg + .form-control-feedback, .input-group-lg + .form-control-feedback, .form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px +} + +.input-sm + .form-control-feedback, .input-group-sm + .form-control-feedback, .form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px +} + +.has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-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 { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) +} + +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168 +} + +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d +} + +.has-success .form-control-feedback { + color: #3c763d +} + +.has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-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 { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) +} + +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b +} + +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b +} + +.has-warning .form-control-feedback { + color: #8a6d3b +} + +.has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-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 { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) +} + +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483 +} + +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442 +} + +.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 +} + +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373 +} + +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .form-inline .form-control-static { + display: inline-block + } + + .form-inline .input-group { + display: inline-table; + vertical-align: middle + } + + .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { + width: auto + } + + .form-inline .input-group > .form-control { + width: 100% + } + + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .radio, .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .radio label, .form-inline .checkbox label { + padding-left: 0 + } + + .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0 + } + + .form-inline .has-feedback .form-control-feedback { + top: 0 + } +} + +.form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0 +} + +.form-horizontal .radio, .form-horizontal .checkbox { + min-height: 27px +} + +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px +} + +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right + } +} + +.form-horizontal .has-feedback .form-control-feedback { + right: 15px +} + +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px + } +} + +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px + } +} + +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none +} + +.btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +.btn:hover, .btn:focus, .btn.focus { + color: #333; + text-decoration: none +} + +.btn:active, .btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125) +} + +.btn.disabled, .btn[disabled], fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + opacity: .65; + -webkit-box-shadow: none; + box-shadow: none +} + +a.btn.disabled, fieldset[disabled] a.btn { + pointer-events: none +} + +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc +} + +.btn-default:focus, .btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c +} + +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad +} + +.btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + background-image: none; + border-color: #adadad +} + +.btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c +} + +.btn-default.disabled:hover, .btn-default[disabled]:hover, 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 { + background-color: #fff; + border-color: #ccc +} + +.btn-default .badge { + color: #fff; + background-color: #333 +} + +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4 +} + +.btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40 +} + +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74 +} + +.btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + background-image: none; + border-color: #204d74 +} + +.btn-primary:active:hover, .btn-primary.active:hover, .open > .dropdown-toggle.btn-primary:hover, .btn-primary:active:focus, .btn-primary.active:focus, .open > .dropdown-toggle.btn-primary:focus, .btn-primary:active.focus, .btn-primary.active.focus, .open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40 +} + +.btn-primary.disabled:hover, .btn-primary[disabled]:hover, 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 { + background-color: #337ab7; + border-color: #2e6da4 +} + +.btn-primary .badge { + color: #337ab7; + background-color: #fff +} + +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c +} + +.btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625 +} + +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439 +} + +.btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + background-image: none; + border-color: #398439 +} + +.btn-success:active:hover, .btn-success.active:hover, .open > .dropdown-toggle.btn-success:hover, .btn-success:active:focus, .btn-success.active:focus, .open > .dropdown-toggle.btn-success:focus, .btn-success:active.focus, .btn-success.active.focus, .open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625 +} + +.btn-success.disabled:hover, .btn-success[disabled]:hover, 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 { + background-color: #5cb85c; + border-color: #4cae4c +} + +.btn-success .badge { + color: #5cb85c; + background-color: #fff +} + +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da +} + +.btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85 +} + +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc +} + +.btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + background-image: none; + border-color: #269abc +} + +.btn-info:active:hover, .btn-info.active:hover, .open > .dropdown-toggle.btn-info:hover, .btn-info:active:focus, .btn-info.active:focus, .open > .dropdown-toggle.btn-info:focus, .btn-info:active.focus, .btn-info.active.focus, .open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85 +} + +.btn-info.disabled:hover, .btn-info[disabled]:hover, 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 { + background-color: #5bc0de; + border-color: #46b8da +} + +.btn-info .badge { + color: #5bc0de; + background-color: #fff +} + +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236 +} + +.btn-warning:focus, .btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d +} + +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512 +} + +.btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + background-image: none; + border-color: #d58512 +} + +.btn-warning:active:hover, .btn-warning.active:hover, .open > .dropdown-toggle.btn-warning:hover, .btn-warning:active:focus, .btn-warning.active:focus, .open > .dropdown-toggle.btn-warning:focus, .btn-warning:active.focus, .btn-warning.active.focus, .open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d +} + +.btn-warning.disabled:hover, .btn-warning[disabled]:hover, 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 { + background-color: #f0ad4e; + border-color: #eea236 +} + +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff +} + +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a +} + +.btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19 +} + +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925 +} + +.btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + background-image: none; + border-color: #ac2925 +} + +.btn-danger:active:hover, .btn-danger.active:hover, .open > .dropdown-toggle.btn-danger:hover, .btn-danger:active:focus, .btn-danger.active:focus, .open > .dropdown-toggle.btn-danger:focus, .btn-danger:active.focus, .btn-danger.active.focus, .open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19 +} + +.btn-danger.disabled:hover, .btn-danger[disabled]:hover, 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 { + background-color: #d9534f; + border-color: #d43f3a +} + +.btn-danger .badge { + color: #d9534f; + background-color: #fff +} + +.btn-link { + font-weight: 400; + color: #337ab7; + border-radius: 0 +} + +.btn-link, .btn-link:active, .btn-link.active, .btn-link[disabled], fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none +} + +.btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { + border-color: transparent +} + +.btn-link:hover, .btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent +} + +.btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none +} + +.btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-block { + display: block; + width: 100% +} + +.btn-block + .btn-block { + margin-top: 5px +} + +input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { + width: 100% +} + +.input-group { + position: relative; + display: table; + border-collapse: separate +} + +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0 +} + +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0 +} + +.input-group .form-control:focus { + z-index: 3 +} + +.input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px +} + +textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn, select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto +} + +.input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px +} + +textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn, select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto +} + +.input-group-addon, .input-group-btn, .input-group .form-control { + display: table-cell +} + +.input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0 +} + +.input-group-addon, .input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px +} + +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px +} + +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px +} + +.input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { + margin-top: 0 +} + +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group-addon:first-child { + border-right: 0 +} + +.input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group-addon:last-child { + border-left: 0 +} + +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap +} + +.input-group-btn > .btn { + position: relative +} + +.input-group-btn > .btn + .btn { + margin-left: -1px +} + +.input-group-btn > .btn:hover, .input-group-btn > .btn:focus, .input-group-btn > .btn:active { + z-index: 2 +} + +.input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { + margin-right: -1px +} + +.input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px +} + +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px +} + +.breadcrumb > li { + display: inline-block +} + +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0" +} + +.breadcrumb > .active { + color: #777 +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em +} + +a.label:hover, a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer +} + +.label:empty { + display: none +} + +.btn .label { + position: relative; + top: -1px +} + +.label-default { + background-color: #777 +} + +.label-default[href]:hover, .label-default[href]:focus { + background-color: #5e5e5e +} + +.label-primary { + background-color: #337ab7 +} + +.label-primary[href]:hover, .label-primary[href]:focus { + background-color: #286090 +} + +.label-success { + background-color: #5cb85c +} + +.label-success[href]:hover, .label-success[href]:focus { + background-color: #449d44 +} + +.label-info { + background-color: #5bc0de +} + +.label-info[href]:hover, .label-info[href]:focus { + background-color: #31b0d5 +} + +.label-warning { + background-color: #f0ad4e +} + +.label-warning[href]:hover, .label-warning[href]:focus { + background-color: #ec971f +} + +.label-danger { + background-color: #d9534f +} + +.label-danger[href]:hover, .label-danger[href]:focus { + background-color: #c9302c +} + +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -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 { + margin-right: auto; + margin-left: auto +} + +a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { + border-color: #337ab7 +} + +.thumbnail .caption { + padding: 9px; + color: #333 +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px +} + +.alert h4 { + margin-top: 0; + color: inherit +} + +.alert .alert-link { + font-weight: bold +} + +.alert > p, .alert > ul { + margin-bottom: 0 +} + +.alert > p + p { + margin-top: 5px +} + +.alert-dismissable, .alert-dismissible { + padding-right: 35px +} + +.alert-dismissable .close, .alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit +} + +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6 +} + +.alert-success hr { + border-top-color: #c9e2b3 +} + +.alert-success .alert-link { + color: #2b542c +} + +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1 +} + +.alert-info hr { + border-top-color: #a6e1ec +} + +.alert-info .alert-link { + color: #245269 +} + +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc +} + +.alert-warning hr { + border-top-color: #f7e1b5 +} + +.alert-warning .alert-link { + color: #66512c +} + +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1 +} + +.alert-danger hr { + border-top-color: #e4b9c0 +} + +.alert-danger .alert-link { + color: #843534 +} + +.clearfix:before, .clearfix:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after { + display: table; + content: " " +} + +.clearfix:after, .form-horizontal .form-group:after { + clear: both +} + +.center-block { + display: block; + margin-right: auto; + margin-left: auto +} + +.pull-right { + float: right !important +} + +.pull-left { + float: left !important +} + +.hide { + display: none !important +} + +.show { + display: block !important +} + +.invisible { + visibility: hidden +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0 +} + +.hidden { + display: none !important +} + +.affix { + position: fixed +} diff --git a/pages/charactertrade.php b/pages/charactertrade.php new file mode 100644 index 0000000..124c4eb --- /dev/null +++ b/pages/charactertrade.php @@ -0,0 +1,494 @@ +' . $text . ''; +} + +function trade_messageWarning($text) { + return '
' . $text . '
'; +} + +function trade_messageError($text) { + return '
' . $text . '
'; +} + +function trade_messageSuccess($text) { + return '
' . $text . '
'; +} + +function trade_getConfig($name) { + global $config; + return $config['site'][$name]; +} + +function trade_getUrl($action = '', $params = []) { + return '?subtopic=charactertrade&action=' . $action . '&' . http_build_query($params); +} +echo ''; + +if (empty($action)) { + echo '

Player trade offers

'; + + echo trade_messageInfo('On this site you can sell and buy characters safely.
' . + 'Purchased character will be transferred to your account.'); + + if (Visitor::isLogged()) { + $accountPlayerTrades = Visitor::getAccount()->getPlayerTrades(); + echo '

Your player trade offers

'; + if ($accountPlayerTrades->count() > 0) { + echo ''; + echo ''; + echo ''; + foreach ($accountPlayerTrades as $accountPlayerTrade) { + $player = new Player($accountPlayerTrade->getPlayerId()); + if ($player->isLoaded()) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + } + echo ''; + echo '
Character nameCreate datePriceStatus
' . htmlspecialchars($player->getName()) . '' . date(TRADE_DATE_FORMAT, $accountPlayerTrade->getCreateDate()) . '' . $accountPlayerTrade->getPriceBuyer() . ' (' . $accountPlayerTrade->getPriceSeller() . ') PP' . $accountPlayerTrade->getStatusName() . '
'; + echo ($accountPlayerTrade->isPublic() ? '(public)' : '(private)'); + echo '
VIEW OFFER
'; + } else { + echo 'You do not have any offer.'; + } + } + + echo '
Create offer
'; + + /** @var DatabaseList|PlayerTrade[] $playerTrades */ + $playerTrades = new DatabaseList('PlayerTrade'); + $filterPublic = new SQL_Filter(new SQL_Field('type'), SQL_Filter::EQUAL, PlayerTrade::TYPE_PUBLIC); + $filterActiveOffer = new SQL_Filter(new SQL_Field('status'), SQL_Filter::EQUAL, PlayerTrade::STATUS_ACTIVE); + $filter = new SQL_Filter($filterPublic, SQL_Filter::CRITERIUM_AND, $filterActiveOffer); + $playerTrades->setFilter($filter); + $playerTrades->addOrder(new SQL_Order(new SQL_Field('id'), SQL_Order::DESC)); + + echo '

Public player trade offers

'; + if ($playerTrades->count() > 0) { + echo ''; + echo ''; + echo ''; + foreach ($playerTrades as $playerTrade) { + $player = new Player($playerTrade->getPlayerId()); + if ($player->isLoaded()) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + } + echo ''; + echo '
Character nameCreate datePriceStatus
' . htmlspecialchars($player->getName()) . '' . date(TRADE_DATE_FORMAT, $playerTrade->getCreateDate()) . '' . $playerTrade->getPriceBuyer() . ' (' . $playerTrade->getPriceSeller() . ') PP' . $playerTrade->getStatusName() . 'VIEW OFFER
'; + } else { + echo 'There are no public player trade offers'; + } +} elseif ($action === 'offer_buy') { + if (!Visitor::isLogged()) { + trade_loginRequiredError(); + return; + } + + $secretCode = (string) $_REQUEST['secretCode']; + $playerTradeId = PlayerTrade::getIdFromSecretCode($secretCode); + $playerTrade = new PlayerTrade($playerTradeId); + + if (!$playerTrade->isLoaded()) { + echo trade_messageError('Offer with this ID does not exist.'); + return; + } + + if (!$playerTrade->isValidSecretCode($secretCode)) { + echo trade_messageError('Invalid code to offer.'); + return; + } + + echo '
Back
'; + + if (!$playerTrade->isActive()) { + echo trade_messageError('This offer is not active. It was sold or canceled.'); + return; + } + + $player = new Player($playerTrade->getPlayerId()); + if (!$player->isLoaded()) { + echo trade_messageError('Player from that offer does not exist.'); + return; + } + + if ($player->getAccountId() == Visitor::getAccount()->getId()) { + echo trade_messageError('You cannot buy your own character.'); + return; + } + + $csrf = md5(Visitor::getAccount()->getId() . Visitor::getAccount()->getPassword() . Visitor::getIP()); + + if (Visitor::getAccount()->getPremiumPoints() < $playerTrade->getPriceBuyer()) { + echo trade_messageError('This player costs ' . $playerTrade->getPriceBuyer() . ' premium points. You do not have enough.'); + return; + } + + if (isset($_POST['csrf'])) { + if ($_POST['csrf'] === $csrf) { + $removePointsQuery = Website::getDBHandle()->prepare( + 'UPDATE `accounts`' . + ' SET `premium_points` = `premium_points` - ' . $playerTrade->getPriceBuyer() . + ' WHERE `id` = ' . Visitor::getAccount()->getId() . + ' AND `premium_points` >= ' . $playerTrade->getPriceBuyer() + ); + // zabezpiecza przed odjeciem punktow kiedy konto nie ma dosc punktow (klonowaniem) + if ($removePointsQuery->execute() && $removePointsQuery->rowCount() > 0) { + Website::getDBHandle()->query( + 'UPDATE `accounts`' . + ' SET `premium_points` = `premium_points` + ' . $playerTrade->getPriceSeller() . + ' WHERE `id` = ' . $playerTrade->getAccountSellerId() + ); + $player->setAccountID(Visitor::getAccount()->getId()); + $player->save(); + $playerTrade->setAccountBuyerId(Visitor::getAccount()->getId()); + $playerTrade->setStatus(PlayerTrade::STATUS_SOLD); + $playerTrade->save(); + + echo trade_messageSuccess( + 'You bought player ' . htmlspecialchars($player->getName()) . ' for ' . + $playerTrade->getPriceBuyer() . ' premium points.
' . + 'Player was transferred to your account.' + ); + + } else { + echo trade_messageError('Could not remove premium points from your account. Try again or contact with administrator.'); + } + + return; + } else { + echo trade_messageError('Invalid CSRF code. Try again.'); + } + } + + echo '

Buy player

'; + + echo trade_messageInfo('Do you really want to buy player ' . htmlspecialchars($player->getName()) . ' for ' . $playerTrade->getPriceBuyer() . ' premium points?'); + + echo '
+ +
+
+ +
+
+
+ '; +} elseif ($action === 'offer_cancel') { + if (!Visitor::isLogged()) { + trade_loginRequiredError(); + return; + } + + $secretCode = (string) $_REQUEST['secretCode']; + $playerTradeId = PlayerTrade::getIdFromSecretCode($secretCode); + $playerTrade = new PlayerTrade($playerTradeId); + + if (!$playerTrade->isLoaded()) { + echo trade_messageError('Offer with this ID does not exist.'); + return; + } + + if (!$playerTrade->isValidSecretCode($secretCode)) { + echo trade_messageError('Invalid code to offer.'); + return; + } + + if (!$playerTrade->isActive()) { + echo trade_messageError('This offer is not active. It was sold or canceled.'); + return; + } + + $player = new Player($playerTrade->getPlayerId()); + if (!$player->isLoaded()) { + echo trade_messageError('Player from that offer does not exist.'); + return; + } + + echo '
Back
'; + + $csrf = md5(Visitor::getAccount()->getId() . Visitor::getAccount()->getPassword() . Visitor::getIP()); + + if (Visitor::getAccount()->getId() != $playerTrade->getAccountSellerId()) { + echo trade_messageError('This is not your player trade offer. You cannot cancel it.'); + return; + } + + if (isset($_POST['csrf'])) { + if ($_POST['csrf'] === $csrf) { + $playerTrade->setStatus(PlayerTrade::STATUS_CANCELED); + $playerTrade->save(); + + echo trade_messageSuccess( + 'Your player ' . htmlspecialchars($player->getName()) . ' sell offer for ' . + $playerTrade->getPriceSeller() . ' premium points has been canceled.
' + ); + + return; + } else { + echo trade_messageError('Invalid CSRF code. Try again.'); + } + } + + echo '

Cancel trade offer

'; + + echo trade_messageInfo('Do you really want to cancel player ' . htmlspecialchars($player->getName()) . ' sell offer?'); + + echo '
+ +
+
+ +
+
+
+ '; +} elseif ($action === 'offer_create') { + if (!Visitor::isLogged()) { + trade_loginRequiredError(); + return; + } + + $configCreateRequireRecoveryKey = trade_getConfig('trade_player_create_require_recovery_key'); + $configMinimumPrice = trade_getConfig('trade_player_minimum_price'); + $configMinimumLevel = trade_getConfig('trade_player_minimum_level'); + + $configPublicCommissionFixed = trade_getConfig('trade_player_public_commission_fixed'); + $configPublicCommissionPercent = trade_getConfig('trade_player_public_commission_percent'); + $configPrivateCommissionFixed = trade_getConfig('trade_player_private_commission_fixed'); + $configPrivateCommissionPercent = trade_getConfig('trade_player_private_commission_percent'); + + echo '
Back
'; + + $csrf = md5(Visitor::getAccount()->getId() . Visitor::getAccount()->getPassword() . Visitor::getIP()); + + $type = PlayerTrade::TYPE_PUBLIC; + $price = 100; + $playerId = 0; + + if ($configCreateRequireRecoveryKey && empty(Visitor::getAccount()->getKey())) { + echo trade_messageWarning('Only accounts with Recovery Key may create trade offers.'); + return; + } + + if ($configCreateRequireRecoveryKey && isset($_POST['recovery_key'])) { + $recoveryKey = (string) $_POST['recovery_key']; + } + + if (isset($_POST['csrf']) && isset($_POST['player_id']) && isset($_POST['type']) && isset($_POST['price'])) { + $type = intval($_POST['type']); + $price = intval($_POST['price']); + $playerId = intval($_POST['player_id']); + if ($_POST['csrf'] === $csrf) { + $player = new Player($_POST['player_id']); + if ($configCreateRequireRecoveryKey) { + if ($recoveryKey !== Visitor::getAccount()->getKey()) { + echo trade_messageError('Invalid Recovery Key to account.'); + return; + } + } + if (!$player->isLoaded()) { + echo trade_messageError('Player does not exist.'); + return; + } + if ($player->getAccountId() != Visitor::getAccount()->getId()) { + echo trade_messageError('Player is not on your account.'); + return; + } + if ($player->getLevel() < $configMinimumLevel) { + echo trade_messageError('Minimum player level is ' . $configMinimumLevel . '.'); + return; + } + if ($player->getGroup() != 1) { + echo trade_messageError('Support team members cannot sell their characters!'); + return; + } + if (!in_array($type, [PlayerTrade::TYPE_PUBLIC, PlayerTrade::TYPE_PRIVATE])) { + echo trade_messageError('Invalid offer type.'); + return; + } + + $activePlayerTrades = new DatabaseList('PlayerTrade'); + $filterPlayer = new SQL_Filter(new SQL_Field('player_id'), SQL_Filter::EQUAL, $player->getId()); + $filterActiveOffer = new SQL_Filter(new SQL_Field('status'), SQL_Filter::EQUAL, PlayerTrade::STATUS_ACTIVE); + $filter = new SQL_Filter($filterPlayer, SQL_Filter::CRITERIUM_AND, $filterActiveOffer); + $activePlayerTrades->setFilter($filter); + + if ($activePlayerTrades->count() == 0) { + if ($price >= $configMinimumPrice) { + $buyerPrice = $price; + if ($type == PlayerTrade::TYPE_PUBLIC) { + $buyerPrice = $price * (1 + $configPublicCommissionPercent / 100) + $configPublicCommissionFixed; + } elseif ($type == PlayerTrade::TYPE_PRIVATE) { + $buyerPrice = $price * (1 + $configPrivateCommissionPercent / 100) + $configPrivateCommissionFixed; + } + + $playerTrade = new PlayerTrade(); + $playerTrade->setAccountSellerId(Visitor::getAccount()->getId()); + $playerTrade->setPlayerId($player->getId()); + $playerTrade->setType($type); + $playerTrade->setPriceSeller($price); + $playerTrade->setPriceBuyer($buyerPrice); + $playerTrade->setStatus(PlayerTrade::STATUS_ACTIVE); + $playerTrade->setCreateDate(time()); + $playerTrade->save(); + + echo trade_messageSuccess( + 'You created player trade offer.
' . + 'Player name: ' . htmlspecialchars($player->getName()) . '
' . + 'You will receive: ' . $playerTrade->getPriceSeller() . ' premium points
' . + 'Buyer will pay: ' . $playerTrade->getPriceBuyer() . ' premium points
' . + 'Link to offer: ' . + trade_getUrl('offer_view', ['secretCode' => $playerTrade->getSecretCode()]) . '
' . + '
' . + 'You can play on that player until someone buy it.' + ); + + return; + } else { + echo trade_messageError('Minimum price for offer is ' . $configMinimumPrice . '.'); + } + } else { + echo trade_messageError('There is another trade offer for this player. You must cancel it first to create new offer.'); + } + } else { + echo trade_messageError('Invalid CSRF code. Try again.'); + } + } + + echo '

Create trade player offer

'; + + echo trade_messageInfo('Minimum level of traded player is ' . $configMinimumLevel . '.
' . + 'Minimum price of trade offer is ' . $configMinimumPrice . ' premium points.
'); + + echo '
+ +
+
Type
+
+
+ +
+
+
+
Price
+
+ Premium Points
+ This is value you will receive for player. Buyer will have to pay a little more, because of trade commission. +
+
+
+
+
'; + echo ' +
+
'; + + if ($configCreateRequireRecoveryKey) { + echo '
+
Account Recovery Key
+
+ + Type your Account Recovery Key, to confirm, that you are owner of account. +
+
+ '; + } + + echo '
+
+ +
+
+
+ '; +} elseif ($action === 'offer_view') { + $secretCode = (string) $_REQUEST['secretCode']; + + $playerTradeId = PlayerTrade::getIdFromSecretCode($secretCode); + $playerTrade = new PlayerTrade($playerTradeId); + + echo '
Back
'; + + if ($playerTrade->isLoaded()) { + if ($playerTrade->isValidSecretCode($secretCode)) { + $player = new Player($playerTrade->getPlayerId()); + if ($player->isLoaded()) { + echo '

Player trade offer

'; + if ($playerTrade->isPrivate()) { + echo trade_messageInfo('This is private trade offer. Only users who has link to that site can buy this player. Link to share:
' . + ''); + } + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo '
Name' . htmlspecialchars($player->getName()) . '
Status' . $playerTrade->getStatusName() . '
PRICE' . $playerTrade->getPriceBuyer() . ' premium points
'; + echo '(seller will receive ' . $playerTrade->getPriceSeller() . ' premium points)
Level' . $player->getLevel() . '
Magic Level' . $player->getMagLevel() . '
Vocation' . htmlspecialchars(Website::getVocationName($player->getVocation(), $player->getPromotion())) . '
'; + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo '
Skill Fist' . $player->getSkill(Highscores::SKILL_FIST) . '
Skill Club' . $player->getSkill(Highscores::SKILL_CLUB) . '
Skill Sword' . $player->getSkill(Highscores::SKILL_SWORD) . '
Skill Axe' . $player->getSkill(Highscores::SKILL_AXE) . '
Skill Distance' . $player->getSkill(Highscores::SKILL_DISTANCE) . '
Skill Shielding' . $player->getSkill(Highscores::SKILL_SHIELD) . '
Skill Fishing' . $player->getSkill(Highscores::SKILL_FISHING) . '
'; + + echo '
'; + echo trade_messageInfo('Player items, depot items and house will be transferred.
' . + 'You should NOT consider them as part of offer, ' . + 'as seller may remove them,
before you finalize payment!'); + echo '
'; + if ($playerTrade->isActive()) { + echo 'Buy'; + } + if (Visitor::isLogged() && $playerTrade->isActive() && $playerTrade->getAccountSellerId() == Visitor::getAccount()->getId()) { + echo 'Cancel Offer'; + } + echo '
'; + } else { + echo trade_messageError('Player from that offer does not exist.'); + } + } else { + echo trade_messageError('Invalid code to offer.'); + } + } else { + echo trade_messageError('Offer with this ID does not exist.'); + } +}