- New "admin dashboard" /admin page + login, #494
- New ESI scope for admin access - New admin.log file for admin actions (kick, ban,..) - New login status for characters - improved cronJob exec time for systemData import (jump/kill data) - Added PHP 64-bit check to /setup
This commit is contained in:
@@ -16,6 +16,9 @@ tenMinutes = */10 * * * *
|
||||
; 2 times per hour (each 30min)
|
||||
halfHour = */30 * * * *
|
||||
|
||||
; 1 times per hour (12:30, 13:30, 14:30,...)
|
||||
halfPastHour = 30 * * * *
|
||||
|
||||
; run on EVE downtime 11:00 GMT/UTC
|
||||
downtime = 0 11 * * *
|
||||
|
||||
@@ -36,7 +39,7 @@ deleteLogData = Cron\CharacterUpdate->deleteLogData, @in
|
||||
deleteSignatures = Cron\MapUpdate->deleteSignatures, @halfHour
|
||||
|
||||
; import system data (jump, kill,..) from CCP API
|
||||
importSystemData = Cron\CcpSystemsUpdate->importSystemData, @hourly
|
||||
importSystemData = Cron\CcpSystemsUpdate->importSystemData, @halfPastHour
|
||||
|
||||
; disable outdated maps
|
||||
deactivateMapData = Cron\MapUpdate->deactivateMapData, @hourly
|
||||
|
||||
@@ -35,6 +35,7 @@ CCP_SSO_SECRET_KEY =
|
||||
CCP_ESI_URL = https://esi.tech.ccp.is
|
||||
CCP_ESI_DATASOURCE = singularity
|
||||
CCP_ESI_SCOPES = esi-location.read_location.v1,esi-location.read_ship_type.v1,esi-ui.write_waypoint.v1,esi-ui.open_window.v1
|
||||
CCP_ESI_SCOPES_ADMIN = esi-corporations.read_corporation_membership.v1
|
||||
|
||||
; SMTP settings (optional)
|
||||
SMTP_HOST = localhost
|
||||
@@ -80,6 +81,7 @@ CCP_SSO_SECRET_KEY =
|
||||
CCP_ESI_URL = https://esi.tech.ccp.is
|
||||
CCP_ESI_DATASOURCE = tranquility
|
||||
CCP_ESI_SCOPES = esi-location.read_location.v1,esi-location.read_ship_type.v1,esi-ui.write_waypoint.v1,esi-ui.open_window.v1
|
||||
CCP_ESI_SCOPES_ADMIN = esi-corporations.read_corporation_membership.v1
|
||||
|
||||
; SMTP settings (optional)
|
||||
SMTP_HOST = localhost
|
||||
|
||||
@@ -17,13 +17,14 @@ class AccessController extends Controller {
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
parent::beforeroute($f3);
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
parent::beforeroute($f3, $params);
|
||||
|
||||
// Any route/endpoint of a child class of this one,
|
||||
// requires a valid logged in user!
|
||||
$loginCheck = $this->checkLogTimer($f3);
|
||||
$loginCheck = $this->isLoggedIn($f3);
|
||||
|
||||
if( !$loginCheck ){
|
||||
// no user found or login timer expired
|
||||
@@ -34,7 +35,7 @@ class AccessController extends Controller {
|
||||
$f3->status(403);
|
||||
}else{
|
||||
// redirect to landing page
|
||||
$f3->reroute('@login');
|
||||
$f3->reroute(['login']);
|
||||
}
|
||||
|
||||
// die() triggers unload() function
|
||||
@@ -42,6 +43,55 @@ class AccessController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get current character and check if it is a valid character
|
||||
* @param \Base $f3
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLoggedIn(\Base $f3){
|
||||
$loginCheck = false;
|
||||
if( $character = $this->getCharacter() ){
|
||||
if($this->checkLogTimer($f3, $character)){
|
||||
if($character->isAuthorized() === 'OK'){
|
||||
$loginCheck = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $loginCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether a user/character is currently logged in
|
||||
* @param \Base $f3
|
||||
* @param Model\CharacterModel $character
|
||||
* @return bool
|
||||
*/
|
||||
private function checkLogTimer(\Base $f3, Model\CharacterModel $character){
|
||||
$loginCheck = false;
|
||||
|
||||
if(
|
||||
!$character->dry() &&
|
||||
$character->lastLogin
|
||||
){
|
||||
// check logIn time
|
||||
$logInTime = new \DateTime($character->lastLogin);
|
||||
$now = new \DateTime();
|
||||
|
||||
$timeDiff = $now->diff($logInTime);
|
||||
|
||||
$minutes = $timeDiff->days * 60 * 24 * 60;
|
||||
$minutes += $timeDiff->h * 60;
|
||||
$minutes += $timeDiff->i;
|
||||
|
||||
if($minutes <= $f3->get('PATHFINDER.TIMER.LOGGED')){
|
||||
$loginCheck = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $loginCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* broadcast map data to clients
|
||||
* -> send over TCP Socket
|
||||
|
||||
246
app/main/controller/admin.php
Normal file
246
app/main/controller/admin.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 12.05.2017
|
||||
* Time: 20:30
|
||||
*/
|
||||
|
||||
namespace Controller;
|
||||
|
||||
|
||||
use Controller\Ccp\Sso;
|
||||
use Model\BasicModel;
|
||||
use Model\CharacterModel;
|
||||
use Model\CorporationModel;
|
||||
|
||||
class Admin extends Controller{
|
||||
|
||||
const ERROR_SSO_CHARACTER_EXISTS = 'No character found. Please login first.';
|
||||
const ERROR_SSO_CHARACTER_ROLES = 'Insufficient in-game roles. "%s" requires at least one of these corp roles: %s.';
|
||||
|
||||
const LOG_TEXT_KICK_BAN = '%s "%s" from corporation "%s", by "%s"';
|
||||
|
||||
const KICK_OPTIONS = [
|
||||
5 => '5m',
|
||||
60 => '1h',
|
||||
1440 => '24h'
|
||||
];
|
||||
|
||||
/**
|
||||
* event handler for all "views"
|
||||
* some global template variables are set in here
|
||||
* @param \Base $f3
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
parent::beforeroute($f3, $params);
|
||||
|
||||
$f3->set('tplPage', 'login');
|
||||
|
||||
if($character = $this->getAdminCharacter($f3)){
|
||||
$f3->set('tplLogged', true);
|
||||
$f3->set('character', $character);
|
||||
$this->dispatch($f3, $params, $character);
|
||||
}
|
||||
|
||||
$f3->set('tplAuthType', $f3->alias( 'sso', ['action' => 'requestAdminAuthorization']));
|
||||
|
||||
// page title
|
||||
$f3->set('pageTitle', 'Admin');
|
||||
|
||||
// main page content
|
||||
$f3->set('pageContent', $f3->get('PATHFINDER.VIEW.ADMIN'));
|
||||
|
||||
// body element class
|
||||
$f3->set('bodyClass', 'pf-body pf-landing');
|
||||
|
||||
// js path (build/minified or raw uncompressed files)
|
||||
$f3->set('pathJs', 'public/js/' . $f3->get('PATHFINDER.VERSION') );
|
||||
}
|
||||
|
||||
/**
|
||||
* event handler after routing
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function afterroute(\Base $f3) {
|
||||
// js view (file)
|
||||
$f3->set('jsView', 'login');
|
||||
|
||||
// render view
|
||||
echo \Template::instance()->render( $f3->get('PATHFINDER.VIEW.INDEX') );
|
||||
|
||||
// clear all SSO related temp data
|
||||
if( $f3->exists(Sso::SESSION_KEY_SSO) ){
|
||||
$f3->clear('SESSION.SSO.ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns valid admin $characterModel for current user
|
||||
* @param \Base $f3
|
||||
* @return CharacterModel|null
|
||||
*/
|
||||
protected function getAdminCharacter(\Base $f3){
|
||||
$adminCharacter = null;
|
||||
if( !$f3->exists(Sso::SESSION_KEY_SSO_ERROR) ){
|
||||
if( $character = $this->getCharacter() ){
|
||||
if($character->roleId == 1){
|
||||
// current character is admin
|
||||
$adminCharacter = $character;
|
||||
}else{
|
||||
$f3->set(Sso::SESSION_KEY_SSO_ERROR,
|
||||
sprintf(
|
||||
self::ERROR_SSO_CHARACTER_ROLES,
|
||||
$character->name,
|
||||
implode(', ', CorporationModel::ADMIN_ROLES
|
||||
)));
|
||||
}
|
||||
}else{
|
||||
$f3->set(Sso::SESSION_KEY_SSO_ERROR, self::ERROR_SSO_CHARACTER_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
return $adminCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatch page events by URL $params
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
* @param null $character
|
||||
*/
|
||||
public function dispatch(\Base $f3, $params, $character = null){
|
||||
if($character instanceof CharacterModel){
|
||||
// user logged in
|
||||
|
||||
$parts = array_values(array_filter(array_map('strtolower', explode('/', $params['*']))));
|
||||
$f3->set('tplPage', $parts[0]);
|
||||
|
||||
switch($parts[0]){
|
||||
case 'settings':
|
||||
break;
|
||||
case 'members':
|
||||
switch($parts[1]){
|
||||
case 'kick':
|
||||
$objectId = (int)$parts[2];
|
||||
$value = (int)$parts[3];
|
||||
$this->kickCharacter($character, $objectId, $value);
|
||||
|
||||
$f3->reroute('@admin(@*=/' . $parts[0] . ')');
|
||||
break;
|
||||
case 'ban':
|
||||
$objectId = (int)$parts[2];
|
||||
$value = (int)$parts[3];
|
||||
$this->banCharacter($character, $objectId, $value);
|
||||
break;
|
||||
}
|
||||
$f3->set('tplPage', 'members');
|
||||
$f3->set('tplKickOptions', self::KICK_OPTIONS);
|
||||
|
||||
$this->initMembers($f3, $character);
|
||||
break;
|
||||
case 'login':
|
||||
default:
|
||||
$f3->set('tplPage', 'login');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kick or revoke a character
|
||||
* @param CharacterModel $character
|
||||
* @param int $kickCharacterId
|
||||
* @param int $minutes
|
||||
*/
|
||||
protected function kickCharacter(CharacterModel $character, $kickCharacterId, $minutes){
|
||||
$kickOptions = self::KICK_OPTIONS;
|
||||
$minKickTime = key($kickOptions) ;
|
||||
end($kickOptions);
|
||||
$maxKickTime = key($kickOptions);
|
||||
$minutes = in_array($minutes, range($minKickTime, $maxKickTime)) ? $minutes : 0;
|
||||
|
||||
$kickCharacters = $this->filterValidCharacters($character, $kickCharacterId);
|
||||
foreach($kickCharacters as $kickCharacter){
|
||||
$kickCharacter->kick($minutes);
|
||||
$kickCharacter->save();
|
||||
|
||||
self::getLogger()->write(
|
||||
sprintf(
|
||||
self::LOG_TEXT_KICK_BAN,
|
||||
$minutes ? 'KICK' : 'KICK REVOKE',
|
||||
$kickCharacter->name,
|
||||
$kickCharacter->getCorporation()->name,
|
||||
$character->name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CharacterModel $character
|
||||
* @param int $banCharacterId
|
||||
* @param int $value
|
||||
*/
|
||||
protected function banCharacter(CharacterModel $character, $banCharacterId, $value){
|
||||
$banCharacters = $this->filterValidCharacters($character, $banCharacterId);
|
||||
|
||||
foreach($banCharacters as $banCharacter){
|
||||
$banCharacter->ban($value);
|
||||
$banCharacter->save();
|
||||
|
||||
self::getLogger()->write(
|
||||
sprintf(
|
||||
self::LOG_TEXT_KICK_BAN,
|
||||
$value ? 'BAN' : 'BAN REVOKE',
|
||||
$banCharacter->name,
|
||||
$banCharacter->getCorporation()->name,
|
||||
$character->name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether a $character has admin access rights for $charcterId
|
||||
* -> must be in same corporation
|
||||
* @param CharacterModel $character
|
||||
* @param int $characterId
|
||||
* @return array|CharacterModel[]
|
||||
*/
|
||||
protected function filterValidCharacters(CharacterModel $character, $characterId){
|
||||
$characters = [];
|
||||
// check if kickCharacters belong to same Corp as admin character
|
||||
// -> remove admin char from kickCharacters...
|
||||
if( !empty($characterIds = array_diff( [$characterId], [$character->_id])) ){
|
||||
$characters = $character->getCorporation()->getCharacters($characterIds);
|
||||
}
|
||||
return $characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* get log file for "admin" logs
|
||||
* @param string $type
|
||||
* @return \Log
|
||||
*/
|
||||
static function getLogger($type = 'ADMIN'){
|
||||
return parent::getLogger('ADMIN');
|
||||
}
|
||||
|
||||
/**
|
||||
* init /member page data
|
||||
* @param \Base $f3
|
||||
* @param CharacterModel $character
|
||||
*/
|
||||
protected function initMembers(\Base $f3, CharacterModel $character){
|
||||
$data = (object) [];;
|
||||
|
||||
$test = BasicModel::getNew('CharacterModel');
|
||||
$test->getById( $character->_id, 0);
|
||||
|
||||
$data->members = $test->getCorporation()->getCharacters();
|
||||
|
||||
$f3->set('tplMembers', $data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,11 +15,12 @@ class Access extends Controller\AccessController {
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3);
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,11 +14,12 @@ class Connection extends Controller\AccessController {
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3);
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,11 +27,12 @@ class Map extends Controller\AccessController {
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3);
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,11 +16,12 @@ class Signature extends Controller\AccessController {
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3);
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,9 +67,10 @@ class System extends Controller\AccessController {
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
parent::beforeroute($f3);
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
parent::beforeroute($f3, $params);
|
||||
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
|
||||
@@ -80,6 +80,7 @@ class User extends Controller\Controller{
|
||||
$this->f3->set(self::SESSION_KEY_CHARACTERS, $sessionCharacters);
|
||||
|
||||
// save user login information --------------------------------------------------------
|
||||
$characterModel->roleId = $characterModel->requestRoleId();
|
||||
$characterModel->touch('lastLogin');
|
||||
$characterModel->save();
|
||||
|
||||
@@ -113,7 +114,8 @@ class User extends Controller\Controller{
|
||||
if( !empty($data['cookie']) ){
|
||||
if( !empty($cookieData = $this->getCookieByName($data['cookie']) )){
|
||||
// cookie data is valid -> validate data against DB (security check!)
|
||||
if( !empty($characters = $this->getCookieCharacters(array_slice($cookieData, 0, 1, true))) ){
|
||||
// -> add characters WITHOUT permission to log in too!
|
||||
if( !empty($characters = $this->getCookieCharacters(array_slice($cookieData, 0, 1, true), false)) ){
|
||||
// character is valid and allowed to login
|
||||
$return->character = reset($characters)->getData();
|
||||
}else{
|
||||
|
||||
@@ -42,6 +42,9 @@ class AppController extends Controller {
|
||||
// JS main file
|
||||
$f3->set('jsView', 'login');
|
||||
|
||||
// href for SSO Auth
|
||||
$f3->set('tplAuthType', $f3->alias( 'sso', ['action' => 'requestAuthorization'] ));
|
||||
|
||||
// characters from cookies
|
||||
$f3->set('cookieCharacters', $this->getCookieByName(self::COOKIE_PREFIX_CHARACTER, true));
|
||||
$f3->set('getCharacterGrid', function($characters){
|
||||
|
||||
@@ -33,7 +33,7 @@ class Sso extends Api\User{
|
||||
const SESSION_KEY_SSO = 'SESSION.SSO';
|
||||
const SESSION_KEY_SSO_ERROR = 'SESSION.SSO.ERROR';
|
||||
const SESSION_KEY_SSO_STATE = 'SESSION.SSO.STATE';
|
||||
const SESSION_KEY_SSO_FROM_MAP = 'SESSION.SSO.FROM_MAP';
|
||||
const SESSION_KEY_SSO_FROM = 'SESSION.SSO.FROM';
|
||||
|
||||
// error messages
|
||||
const ERROR_CCP_SSO_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].CCP_SSO_URL" url. %s';
|
||||
@@ -42,76 +42,98 @@ class Sso extends Api\User{
|
||||
const ERROR_VERIFY_CHARACTER = 'Unable to verify character data. %s';
|
||||
const ERROR_LOGIN_FAILED = 'Failed authentication due to technical problems: %s';
|
||||
const ERROR_CHARACTER_VERIFICATION = 'Character verification failed by SSP SSO';
|
||||
const ERROR_CHARACTER_FORBIDDEN = 'Character "%s" is not authorized to log in';
|
||||
const ERROR_CHARACTER_FORBIDDEN = 'Character "%s" is not authorized to log in. Reason: %s';
|
||||
const ERROR_SERVICE_TIMEOUT = 'CCP SSO service timeout (%ss). Try again later';
|
||||
const ERROR_COOKIE_LOGIN = 'Login from Cookie failed. Please retry by CCP SSO';
|
||||
|
||||
|
||||
/**
|
||||
* redirect user to CCP SSO page and request authorization
|
||||
* -> cf. Controller->getCookieCharacters() ( equivalent cookie based login)
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function requestAdminAuthorization($f3){
|
||||
$f3->set(self::SESSION_KEY_SSO_FROM, 'admin');
|
||||
|
||||
$scopes = $this->getScopesByAuthType('admin');
|
||||
$this->rerouteAuthorization($f3, $scopes, 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* redirect user to CCP SSO page and request authorization
|
||||
* -> cf. Controller->getCookieCharacters() ( equivalent cookie based login)
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function requestAuthorization($f3){
|
||||
$params = $f3->get('GET');
|
||||
|
||||
if( !empty($ssoCcpClientId = Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID')) ){
|
||||
$params = $f3->get('GET');
|
||||
if(
|
||||
isset($params['characterId']) &&
|
||||
( $activeCharacter = $this->getCharacter(0) )
|
||||
){
|
||||
// authentication restricted to a characterId -----------------------------------------------
|
||||
// restrict login to this characterId e.g. for character switch on map page
|
||||
$characterId = (int)trim($params['characterId']);
|
||||
|
||||
/**
|
||||
* @var Model\CharacterModel $character
|
||||
*/
|
||||
$character = Model\BasicModel::getNew('CharacterModel');
|
||||
$character->getById($characterId, 0);
|
||||
|
||||
// check if character is valid and exists
|
||||
if(
|
||||
isset($params['characterId']) &&
|
||||
( $activeCharacter = $this->getCharacter(0) )
|
||||
!$character->dry() &&
|
||||
$character->hasUserCharacter() &&
|
||||
($activeCharacter->getUser()->_id === $character->getUser()->_id)
|
||||
){
|
||||
// authentication restricted to a characterId -----------------------------------------------
|
||||
// restrict login to this characterId e.g. for character switch on map page
|
||||
$characterId = (int)trim($params['characterId']);
|
||||
// requested character belongs to current user
|
||||
// -> update character vom ESI (e.g. corp changed,..)
|
||||
$updateStatus = $character->updateFromESI();
|
||||
|
||||
/**
|
||||
* @var Model\CharacterModel $character
|
||||
*/
|
||||
$character = Model\BasicModel::getNew('CharacterModel');
|
||||
$character->getById($characterId, 0);
|
||||
if( empty($updateStatus) ){
|
||||
|
||||
// check if character is valid and exists
|
||||
if(
|
||||
!$character->dry() &&
|
||||
$character->hasUserCharacter() &&
|
||||
($activeCharacter->getUser()->_id === $character->getUser()->_id)
|
||||
){
|
||||
// requested character belongs to current user
|
||||
// -> update character vom ESI (e.g. corp changed,..)
|
||||
$updateStatus = $character->updateFromESI();
|
||||
// make sure character data is up2date!
|
||||
// -> this is not the case if e.g. userCharacters was removed "ownerHash" changed...
|
||||
$character->getById($character->_id);
|
||||
|
||||
if( empty($updateStatus) ){
|
||||
if(
|
||||
$character->hasUserCharacter() &&
|
||||
($character->isAuthorized() === 'OK')
|
||||
){
|
||||
$loginCheck = $this->loginByCharacter($character);
|
||||
|
||||
// make sure character data is up2date!
|
||||
// -> this is not the case if e.g. userCharacters was removed "ownerHash" changed...
|
||||
$character->getById($character->_id);
|
||||
if($loginCheck){
|
||||
// set "login" cookie
|
||||
$this->setLoginCookie($character, $this->generateHashFromScopes($this->getScopesByAuthType()) );
|
||||
|
||||
if(
|
||||
$character->hasUserCharacter() &&
|
||||
$character->isAuthorized()
|
||||
){
|
||||
$loginCheck = $this->loginByCharacter($character);
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
|
||||
|
||||
if($loginCheck){
|
||||
// set "login" cookie
|
||||
$this->setLoginCookie($character, $this->getRequestedScopeHash());
|
||||
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
|
||||
|
||||
// route to "map"
|
||||
$f3->reroute('@map');
|
||||
}
|
||||
// route to "map"
|
||||
$f3->reroute(['map']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// redirect to map map page on successful login
|
||||
$f3->set(self::SESSION_KEY_SSO_FROM_MAP, true);
|
||||
}
|
||||
|
||||
// redirect to CCP SSO ----------------------------------------------------------------------
|
||||
// redirect to map map page on successful login
|
||||
$f3->set(self::SESSION_KEY_SSO_FROM, 'map');
|
||||
}
|
||||
|
||||
// redirect to CCP SSO ----------------------------------------------------------------------
|
||||
$scopes = $this->getScopesByAuthType();
|
||||
$this->rerouteAuthorization($f3, $scopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* redirect user to CCPs SSO page
|
||||
* @param \Base $f3
|
||||
* @param array $scopes
|
||||
* @param string $rootAlias
|
||||
*/
|
||||
private function rerouteAuthorization(\Base $f3, $scopes = [], $rootAlias = 'login'){
|
||||
if( !empty( Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID') ) ){
|
||||
// used for "state" check between request and callback
|
||||
$state = bin2hex( openssl_random_pseudo_bytes(12) );
|
||||
$f3->set(self::SESSION_KEY_SSO_STATE, $state);
|
||||
@@ -120,7 +142,7 @@ class Sso extends Api\User{
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => Controller\Controller::getEnvironmentData('URL') . Controller\Controller::getEnvironmentData('BASE') . $f3->build('/sso/callbackAuthorization'),
|
||||
'client_id' => Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID'),
|
||||
'scope' => implode(' ', Controller\Controller::getEnvironmentData('CCP_ESI_SCOPES')),
|
||||
'scope' => implode(' ', $scopes),
|
||||
'state' => $state
|
||||
];
|
||||
|
||||
@@ -128,12 +150,11 @@ class Sso extends Api\User{
|
||||
|
||||
$f3->status(302);
|
||||
$f3->reroute($ssoAuthUrl);
|
||||
|
||||
}else{
|
||||
// SSO clientId missing
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, self::ERROR_CCP_CLIENT_ID);
|
||||
self::getSSOLogger()->write(self::ERROR_CCP_CLIENT_ID);
|
||||
$f3->reroute('@login');
|
||||
$f3->reroute([$rootAlias, ['*' => '']]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,8 +167,12 @@ class Sso extends Api\User{
|
||||
$getParams = (array)$f3->get('GET');
|
||||
|
||||
// users can log in either from @login (new user) or @map (existing user) root alias
|
||||
// -> or from /admin page
|
||||
// -> in case login fails, users should be redirected differently
|
||||
$authFromMapAlias = false;
|
||||
$rootAlias = 'login';
|
||||
if( !empty($f3->get(self::SESSION_KEY_SSO_FROM)) ){
|
||||
$rootAlias = $f3->get(self::SESSION_KEY_SSO_FROM);
|
||||
}
|
||||
|
||||
if($f3->exists(self::SESSION_KEY_SSO_STATE)){
|
||||
// check response and validate 'state'
|
||||
@@ -158,14 +183,9 @@ class Sso extends Api\User{
|
||||
!empty($getParams['state']) &&
|
||||
$f3->get(self::SESSION_KEY_SSO_STATE) === $getParams['state']
|
||||
){
|
||||
// check if user came from map (for redirect)
|
||||
if( $f3->get(self::SESSION_KEY_SSO_FROM_MAP) ){
|
||||
$authFromMapAlias = true;
|
||||
}
|
||||
|
||||
// clear 'state' for new next login request
|
||||
$f3->clear(self::SESSION_KEY_SSO_STATE);
|
||||
$f3->clear(self::SESSION_KEY_SSO_FROM_MAP);
|
||||
$f3->clear(self::SESSION_KEY_SSO_FROM);
|
||||
|
||||
$accessData = $this->getSsoAccessData($getParams['code']);
|
||||
|
||||
@@ -196,8 +216,7 @@ class Sso extends Api\User{
|
||||
|
||||
if( !is_null($characterModel) ){
|
||||
// check if character is authorized to log in
|
||||
if($characterModel->isAuthorized()){
|
||||
|
||||
if( ($authStatus = $characterModel->isAuthorized()) === 'OK'){
|
||||
// character is authorized to log in
|
||||
// -> update character log (current location,...)
|
||||
$characterModel = $characterModel->updateLog();
|
||||
@@ -236,19 +255,25 @@ class Sso extends Api\User{
|
||||
|
||||
if($loginCheck){
|
||||
// set "login" cookie
|
||||
$this->setLoginCookie($characterModel, $this->getRequestedScopeHash());
|
||||
$this->setLoginCookie($characterModel, $this->generateHashFromScopes( explode(' ', $verificationCharacterData->Scopes) ));
|
||||
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $characterModel->_id);
|
||||
|
||||
// route to "map"
|
||||
$f3->reroute('@map');
|
||||
if($rootAlias == 'admin'){
|
||||
$f3->reroute([$rootAlias, ['*' => '']]);
|
||||
}else{
|
||||
$f3->reroute(['map']);
|
||||
}
|
||||
}else{
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_LOGIN_FAILED, $characterModel->name));
|
||||
}
|
||||
}else{
|
||||
// character is not authorized to log in
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_CHARACTER_FORBIDDEN, $characterModel->name));
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR,
|
||||
sprintf(self::ERROR_CHARACTER_FORBIDDEN, $characterModel->name, Model\CharacterModel::AUTHORIZATION_STATUS[$authStatus])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,13 +291,7 @@ class Sso extends Api\User{
|
||||
}
|
||||
}
|
||||
|
||||
if($authFromMapAlias){
|
||||
// on error -> route back to map
|
||||
$f3->reroute('@map');
|
||||
}else{
|
||||
// on error -> route back to login form
|
||||
$f3->reroute('@login');
|
||||
}
|
||||
$f3->reroute([$rootAlias, ['*' => '']]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,13 +322,13 @@ class Sso extends Api\User{
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
|
||||
|
||||
// route to "map"
|
||||
$f3->reroute('@map');
|
||||
$f3->reroute(['map']);
|
||||
}
|
||||
}
|
||||
|
||||
// on error -> route back to login form
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, self::ERROR_COOKIE_LOGIN);
|
||||
$f3->reroute('@login');
|
||||
$f3->reroute(['login']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,8 +70,9 @@ class Controller {
|
||||
* event handler for all "views"
|
||||
* some global template variables are set in here
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
$this->setF3($f3);
|
||||
|
||||
// initiate DB connection
|
||||
@@ -242,11 +243,12 @@ class Controller {
|
||||
* -> validate cookie data
|
||||
* -> validate characters
|
||||
* -> cf. Sso->requestAuthorization() ( equivalent DB based login)
|
||||
*
|
||||
* @param array $cookieData
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
* @param bool $checkAuthorization
|
||||
* @return Model\CharacterModel[]
|
||||
*/
|
||||
protected function getCookieCharacters($cookieData = []){
|
||||
protected function getCookieCharacters($cookieData = [], $checkAuthorization = true){
|
||||
$characters = [];
|
||||
|
||||
if(
|
||||
@@ -268,12 +270,17 @@ class Controller {
|
||||
$data = explode(':', $value);
|
||||
if(count($data) === 2){
|
||||
// cookie data is well formatted
|
||||
$characterAuth->getByForeignKey('selector', $data[0], ['limit' => 1]);
|
||||
$characterAuth->getByForeignKey('selector', $data[0], ['limit' => 1], 0);
|
||||
|
||||
// validate "scope hash", "expire data" and "validate token"
|
||||
// validate "scope hash"
|
||||
// -> either "normal" scopes OR "admin" scopes
|
||||
// "expire data" and "validate token"
|
||||
if( !$characterAuth->dry() ){
|
||||
if(
|
||||
$characterAuth->scopeHash === $this->getRequestedScopeHash() &&
|
||||
(
|
||||
$characterAuth->scopeHash === $this->generateHashFromScopes($this->getScopesByAuthType()) ||
|
||||
$characterAuth->scopeHash === $this->generateHashFromScopes($this->getScopesByAuthType('admin'))
|
||||
) &&
|
||||
strtotime($characterAuth->expires) >= $currentTime->getTimestamp() &&
|
||||
hash_equals($characterAuth->token, hash('sha256', $data[1]))
|
||||
){
|
||||
@@ -294,10 +301,16 @@ class Controller {
|
||||
// check if character still has user (is not the case of "ownerHash" changed
|
||||
// check if character is still authorized to log in (e.g. corp/ally or config has changed
|
||||
// -> do NOT remove cookie on failure. This can be a temporary problem (e.g. ESI is down,..)
|
||||
if(
|
||||
$character->hasUserCharacter() &&
|
||||
$character->isAuthorized()
|
||||
){
|
||||
if( $character->hasUserCharacter() ){
|
||||
$authStatus = $character->isAuthorized();
|
||||
|
||||
if(
|
||||
$authStatus == 'OK' ||
|
||||
!$checkAuthorization
|
||||
){
|
||||
$character->virtual( 'authStatus', $authStatus);
|
||||
}
|
||||
|
||||
$characters[$name] = $character;
|
||||
}
|
||||
}else{
|
||||
@@ -365,35 +378,6 @@ class Controller {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether a user/character is currently logged in
|
||||
* @param \Base $f3
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkLogTimer($f3){
|
||||
$loginCheck = false;
|
||||
$characterData = $this->getSessionCharacterData();
|
||||
|
||||
if( !empty($characterData) ){
|
||||
// check logIn time
|
||||
$logInTime = new \DateTime();
|
||||
$logInTime->setTimestamp( (int)$characterData['TIME'] );
|
||||
$now = new \DateTime();
|
||||
|
||||
$timeDiff = $now->diff($logInTime);
|
||||
|
||||
$minutes = $timeDiff->days * 60 * 24 * 60;
|
||||
$minutes += $timeDiff->h * 60;
|
||||
$minutes += $timeDiff->i;
|
||||
|
||||
if($minutes <= $f3->get('PATHFINDER.TIMER.LOGGED')){
|
||||
$loginCheck = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $loginCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current character
|
||||
* @param int $ttl
|
||||
@@ -452,12 +436,32 @@ class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* get a hash over all requested ESI scopes
|
||||
* -> this helps to invalidate "authentication data" after scope change
|
||||
* get scope array by a "role"
|
||||
* @param string $authType
|
||||
* @return array
|
||||
*/
|
||||
protected function getScopesByAuthType($authType = ''){
|
||||
$scopes = (array)self::getEnvironmentData('CCP_ESI_SCOPES');
|
||||
|
||||
switch($authType){
|
||||
case 'admin':
|
||||
$scopesAdmin = (array)self::getEnvironmentData('CCP_ESI_SCOPES_ADMIN');
|
||||
$scopes = array_merge($scopes, $scopesAdmin);
|
||||
break;
|
||||
}
|
||||
sort($scopes, SORT_NUMERIC);
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* get hash from an array of ESI scopes
|
||||
* @param array $scopes
|
||||
* @return string
|
||||
*/
|
||||
protected function getRequestedScopeHash(){
|
||||
return md5(serialize( self::getEnvironmentData('CCP_ESI_SCOPES') ));
|
||||
protected function generateHashFromScopes($scopes){
|
||||
$scopes = (array)$scopes;
|
||||
sort($scopes);
|
||||
return md5(serialize( $scopes ));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -121,8 +121,9 @@ class Setup extends Controller {
|
||||
* event handler for all "views"
|
||||
* some global template variables are set in here
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3) {
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// page title
|
||||
$f3->set('pageTitle', 'Setup');
|
||||
|
||||
@@ -405,6 +406,7 @@ class Setup extends Controller {
|
||||
// server type ------------------------------------------------------------------
|
||||
$serverData = self::getServerData(0);
|
||||
|
||||
|
||||
$checkRequirements = [
|
||||
'serverType' => [
|
||||
'label' => 'Server type',
|
||||
@@ -429,6 +431,12 @@ class Setup extends Controller {
|
||||
'version' => phpversion(),
|
||||
'check' => version_compare( phpversion(), $f3->get('REQUIREMENTS.PHP.VERSION'), '>=')
|
||||
],
|
||||
'php_bit' => [
|
||||
'label' => 'php_int_size',
|
||||
'required' => ($f3->get('REQUIREMENTS.PHP.PHP_INT_SIZE') * 8 ) . '-bit',
|
||||
'version' => (PHP_INT_SIZE * 8) . '-bit',
|
||||
'check' => $f3->get('REQUIREMENTS.PHP.PHP_INT_SIZE') == PHP_INT_SIZE
|
||||
],
|
||||
'pcre' => [
|
||||
'label' => 'PCRE',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.PCRE_VERSION'),
|
||||
|
||||
@@ -20,7 +20,7 @@ class Config extends \Prefab {
|
||||
* environment config keys that should be parsed as array
|
||||
* -> use "," as delimiter in config files/data
|
||||
*/
|
||||
const ARRAY_KEYS = ['CCP_ESI_SCOPES'];
|
||||
const ARRAY_KEYS = ['CCP_ESI_SCOPES', 'CCP_ESI_SCOPES_ADMIN'];
|
||||
|
||||
/**
|
||||
* all environment data
|
||||
|
||||
@@ -139,28 +139,26 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* @throws Exception\ValidationException
|
||||
*/
|
||||
public function set($key, $val){
|
||||
|
||||
if(
|
||||
!$this->dry() &&
|
||||
$key != 'updated'
|
||||
){
|
||||
if( $this->exists($key) ){
|
||||
$currentVal = $this->get($key);
|
||||
// get raw column data (no objects)
|
||||
$currentVal = $this->get($key, true);
|
||||
|
||||
// if current value is not a relational object
|
||||
// and value has changed -> update table col
|
||||
if(is_object($currentVal)){
|
||||
if(is_object($val)){
|
||||
if(
|
||||
is_numeric($val) &&
|
||||
is_subclass_of($currentVal, 'Model\BasicModel') &&
|
||||
$currentVal->_id !== (int)$val
|
||||
is_subclass_of($val, 'Model\BasicModel') &&
|
||||
$val->_id != $currentVal
|
||||
){
|
||||
// relational object changed
|
||||
$this->touch('updated');
|
||||
}
|
||||
}elseif($currentVal != $val){
|
||||
}elseif($val != $currentVal){
|
||||
// non object value
|
||||
$this->touch('updated');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,6 +598,16 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* format dateTime column
|
||||
* @param $column
|
||||
* @param string $format
|
||||
* @return false|null|string
|
||||
*/
|
||||
public function getFormattedColumn($column, $format = 'Y-m-d H:i'){
|
||||
return $this->get($column) ? date($format, strtotime( $this->get($column) )) : null;;
|
||||
}
|
||||
|
||||
/**
|
||||
* export and download table data as *.csv
|
||||
* this is primarily used for static tables
|
||||
@@ -773,8 +781,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* @param string $text
|
||||
* @param string $type
|
||||
*/
|
||||
public static function log($text, $type = null){
|
||||
$type = isset($type) ? $type : 'DEBUG';
|
||||
public static function log($text, $type = 'DEBUG'){
|
||||
Controller\LogController::getLogger($type)->write($text);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,34 @@ class CharacterModel extends BasicModel {
|
||||
*/
|
||||
const DATA_CACHE_KEY_LOG = 'LOG';
|
||||
|
||||
/**
|
||||
* character authorization status
|
||||
* @var array
|
||||
*/
|
||||
const AUTHORIZATION_STATUS = [
|
||||
'OK' => true, // success
|
||||
'UNKNOWN' => 'error', // general authorization error
|
||||
'CORPORATION' => 'failed to match corporation whitelist',
|
||||
'ALLIANCE' => 'failed to match alliance whitelist',
|
||||
'KICKED' => 'character is kicked',
|
||||
'BANNED' => 'character is banned'
|
||||
];
|
||||
|
||||
/**
|
||||
* enables change for "kicked" column
|
||||
* -> see kick();
|
||||
* @var bool
|
||||
*/
|
||||
private $allowKickChange = false;
|
||||
|
||||
/**
|
||||
* enables change for "banned" column
|
||||
* -> see ban();
|
||||
* @var bool
|
||||
*/
|
||||
private $allowBanChange = false;
|
||||
|
||||
|
||||
protected $fieldConf = [
|
||||
'lastLogin' => [
|
||||
'type' => Schema::DT_TIMESTAMP,
|
||||
@@ -75,6 +103,20 @@ class CharacterModel extends BasicModel {
|
||||
]
|
||||
]
|
||||
],
|
||||
'roleId' => [
|
||||
'type' => Schema::DT_TINYINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'index' => true
|
||||
],
|
||||
'kicked' => [
|
||||
'type' => Schema::DT_TIMESTAMP,
|
||||
'index' => true
|
||||
],
|
||||
'banned' => [
|
||||
'type' => Schema::DT_TIMESTAMP,
|
||||
'index' => true
|
||||
],
|
||||
'shared' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
@@ -125,9 +167,14 @@ class CharacterModel extends BasicModel {
|
||||
$characterData = (object) [];
|
||||
$characterData->id = $this->id;
|
||||
$characterData->name = $this->name;
|
||||
$characterData->roleId = $this->roleId;
|
||||
$characterData->shared = $this->shared;
|
||||
$characterData->logLocation = $this->logLocation;
|
||||
|
||||
if( $this->authStatus ){
|
||||
$characterData->authStatus = $this->authStatus;
|
||||
}
|
||||
|
||||
if($addCharacterLogData){
|
||||
if($logModel = $this->getLog()){
|
||||
$characterData->log = $logModel->getData();
|
||||
@@ -187,6 +234,58 @@ class CharacterModel extends BasicModel {
|
||||
return $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for "kicked" until time
|
||||
* @param bool|int $minutes
|
||||
* @return mixed
|
||||
*/
|
||||
public function set_kicked($minutes){
|
||||
if($this->allowKickChange){
|
||||
// allowed to set/change -> reset "allowed" property
|
||||
$this->allowKickChange = false;
|
||||
$kicked = null;
|
||||
|
||||
if($minutes){
|
||||
$seconds = $minutes * 60;
|
||||
$timezone = new \DateTimeZone( self::getF3()->get('TZ') );
|
||||
$kickedUntil = new \DateTime('now', $timezone);
|
||||
|
||||
// add cookie expire time
|
||||
$kickedUntil->add(new \DateInterval('PT' . $seconds . 'S'));
|
||||
$kicked = $kickedUntil->format('Y-m-d H:i:s');
|
||||
}
|
||||
}else{
|
||||
// not allowed to set/change -> keep current status
|
||||
$kicked = $this->kicked;
|
||||
}
|
||||
|
||||
return $kicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for "banned" status
|
||||
* @param bool|int $status
|
||||
* @return mixed
|
||||
*/
|
||||
public function set_banned($status){
|
||||
if($this->allowBanChange){
|
||||
// allowed to set/change -> reset "allowed" property
|
||||
$this->allowBanChange = false;
|
||||
$banned = null;
|
||||
|
||||
if($status){
|
||||
$timezone = new \DateTimeZone( self::getF3()->get('TZ') );
|
||||
$bannedSince = new \DateTime('now', $timezone);
|
||||
$banned = $bannedSince->format('Y-m-d H:i:s');
|
||||
}
|
||||
}else{
|
||||
// not allowed to set/change -> keep current status
|
||||
$banned = $this->banned;
|
||||
}
|
||||
|
||||
return $banned;
|
||||
}
|
||||
|
||||
/**
|
||||
* logLocation specifies whether the current system should be tracked or not
|
||||
* @param $logLocation
|
||||
@@ -205,6 +304,30 @@ class CharacterModel extends BasicModel {
|
||||
return $logLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* kick character for $minutes
|
||||
* -> do NOT use $this->kicked!
|
||||
* -> this will not work (prevent abuse)
|
||||
* @param bool|int $minutes
|
||||
*/
|
||||
public function kick($minutes = false){
|
||||
// enables "kicked" change for this model
|
||||
$this->allowKickChange = true;
|
||||
$this->kicked = $minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* ban character
|
||||
* -> do NOT use $this->banned!
|
||||
* -> this will not work (prevent abuse)
|
||||
* @param bool|int $status
|
||||
*/
|
||||
public function ban($status = false){
|
||||
// enables "banned" change for this model
|
||||
$this->allowBanChange = true;
|
||||
$this->banned = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* @param self $self
|
||||
@@ -317,7 +440,7 @@ class CharacterModel extends BasicModel {
|
||||
!empty($this->crestAccessToken) &&
|
||||
!empty($this->crestAccessTokenUpdated)
|
||||
){
|
||||
$timezone = new \DateTimeZone( $this->getF3()->get('TZ') );
|
||||
$timezone = new \DateTimeZone( self::getF3()->get('TZ') );
|
||||
$tokenTime = \DateTime::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->crestAccessTokenUpdated,
|
||||
@@ -357,46 +480,112 @@ class CharacterModel extends BasicModel {
|
||||
return $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if character is currently kicked
|
||||
* @return bool
|
||||
*/
|
||||
public function isKicked(){
|
||||
$kicked = false;
|
||||
if( !is_null($this->kicked) ){
|
||||
$kickedUntil = new \DateTime();
|
||||
$kickedUntil->setTimestamp( (int)strtotime($this->kicked) );
|
||||
$now = new \DateTime();
|
||||
$kicked = ($kickedUntil > $now);
|
||||
}
|
||||
|
||||
return $kicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether this character is authorized to log in
|
||||
* -> check corp/ally whitelist config (pathfinder.ini)
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuthorized(){
|
||||
$isAuthorized = false;
|
||||
$f3 = self::getF3();
|
||||
$authStatus = 'UNKNOWN';
|
||||
|
||||
$whitelistCorporations = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.CORPORATION') ) );
|
||||
$whitelistAlliance = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.ALLIANCE') ) );
|
||||
// check whether character is banned or temp kicked
|
||||
if(is_null($this->banned)){
|
||||
if( !$this->isKicked() ){
|
||||
$f3 = self::getF3();
|
||||
$whitelistCorporations = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.CORPORATION') ) );
|
||||
$whitelistAlliance = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.ALLIANCE') ) );
|
||||
|
||||
if(
|
||||
empty($whitelistCorporations) &&
|
||||
empty($whitelistAlliance)
|
||||
){
|
||||
// no corp/ally restrictions set -> any character is allowed to login
|
||||
$isAuthorized = true;
|
||||
}else{
|
||||
// check if character corporation is set in whitelist
|
||||
if(
|
||||
!empty($whitelistCorporations) &&
|
||||
$this->hasCorporation() &&
|
||||
in_array((int)$this->get('corporationId', true), $whitelistCorporations)
|
||||
){
|
||||
$isAuthorized = true;
|
||||
if(
|
||||
empty($whitelistCorporations) &&
|
||||
empty($whitelistAlliance)
|
||||
){
|
||||
// no corp/ally restrictions set -> any character is allowed to login
|
||||
$authStatus = 'OK';
|
||||
}else{
|
||||
// check if character corporation is set in whitelist
|
||||
if(
|
||||
!empty($whitelistCorporations) &&
|
||||
$this->hasCorporation() &&
|
||||
in_array((int)$this->get('corporationId', true), $whitelistCorporations)
|
||||
){
|
||||
$authStatus = 'OK';
|
||||
}else{
|
||||
$authStatus = 'CORPORATION';
|
||||
}
|
||||
|
||||
// check if character alliance is set in whitelist
|
||||
if(
|
||||
!$authStatus &&
|
||||
!empty($whitelistAlliance) &&
|
||||
$this->hasAlliance() &&
|
||||
in_array((int)$this->get('allianceId', true), $whitelistAlliance)
|
||||
){
|
||||
$authStatus = 'OK';
|
||||
}else{
|
||||
$authStatus = 'ALLIANCE';
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$authStatus = 'KICKED';
|
||||
}
|
||||
}else{
|
||||
$authStatus = 'BANNED';
|
||||
}
|
||||
|
||||
// check if character alliance is set in whitelist
|
||||
if(
|
||||
!$isAuthorized &&
|
||||
!empty($whitelistAlliance) &&
|
||||
$this->hasAlliance() &&
|
||||
in_array((int)$this->get('allianceId', true), $whitelistAlliance)
|
||||
){
|
||||
$isAuthorized = true;
|
||||
return $authStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* get pathfinder roleId
|
||||
* @return int
|
||||
*/
|
||||
public function requestRoleId(){
|
||||
$roleId = 0;
|
||||
$rolesData = $this->requestRoles();
|
||||
if( !empty($rolesData) ){
|
||||
// roles that grant admin access for this character
|
||||
$adminRoles = array_intersect(CorporationModel::ADMIN_ROLES, $rolesData);
|
||||
if( !empty($adminRoles) ){
|
||||
$roleId = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $isAuthorized;
|
||||
return $roleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* request all corporation roles granted to this character
|
||||
* @return array
|
||||
*/
|
||||
protected function requestRoles(){
|
||||
$rolesData = [];
|
||||
if( $accessToken = $this->getAccessToken() ){
|
||||
// check if corporation exists (should never fail)
|
||||
if( $corporation = $this->getCorporation() ){
|
||||
$characterRolesData = $corporation->getCharactersRoles($accessToken);
|
||||
if( !empty($characterRolesData[$this->_id]) ){
|
||||
$rolesData = $characterRolesData[$this->_id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rolesData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,72 @@ class CorporationModel extends BasicModel {
|
||||
|
||||
protected $table = 'corporation';
|
||||
|
||||
/**
|
||||
* all available corp roles EVE has
|
||||
* -> a corp member has granted roles 0 up to all roles
|
||||
*/
|
||||
const CCP_ROLES = [
|
||||
'director',
|
||||
'personnel_manager',
|
||||
'accountant',
|
||||
'security_officer',
|
||||
'factory_manager',
|
||||
'station_manager',
|
||||
'auditor',
|
||||
'hangar_take_1',
|
||||
'hangar_take_2',
|
||||
'hangar_take_3',
|
||||
'hangar_take_4',
|
||||
'hangar_take_5',
|
||||
'hangar_take_6',
|
||||
'hangar_take_7',
|
||||
'hangar_query_1',
|
||||
'hangar_query_2',
|
||||
'hangar_query_3',
|
||||
'hangar_query_4',
|
||||
'hangar_query_5',
|
||||
'hangar_query_6',
|
||||
'hangar_query_7',
|
||||
'account_take_1',
|
||||
'account_take_2',
|
||||
'account_take_3',
|
||||
'account_take_4',
|
||||
'account_take_5',
|
||||
'account_take_6',
|
||||
'account_take_7',
|
||||
'diplomat',
|
||||
'config_equipment',
|
||||
'container_take_1',
|
||||
'container_take_2',
|
||||
'container_take_3',
|
||||
'container_take_4',
|
||||
'container_take_5',
|
||||
'container_take_6',
|
||||
'container_take_7',
|
||||
'rent_office',
|
||||
'rent_factory_facility',
|
||||
'rent_research_facility',
|
||||
'junior_accountant',
|
||||
'config_starbase_equipment',
|
||||
'trader',
|
||||
'communications_officer',
|
||||
'contract_manager',
|
||||
'starbase_defense_operator',
|
||||
'starbase_fuel_technician',
|
||||
'fitting_manager',
|
||||
'terrestrial_combat_officer',
|
||||
'terrestrial_logistics_officer'
|
||||
];
|
||||
|
||||
/**
|
||||
* corp roles that give admin access for a corp
|
||||
*/
|
||||
const ADMIN_ROLES = [
|
||||
'director',
|
||||
'personnel_manager',
|
||||
'security_officer'
|
||||
];
|
||||
|
||||
protected $fieldConf = [
|
||||
'active' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
@@ -60,7 +126,7 @@ class CorporationModel extends BasicModel {
|
||||
|
||||
/**
|
||||
* get all maps for this corporation
|
||||
* @return array|mixed
|
||||
* @return MapModel[]
|
||||
*/
|
||||
public function getMaps(){
|
||||
$maps = [];
|
||||
@@ -90,12 +156,19 @@ class CorporationModel extends BasicModel {
|
||||
|
||||
/**
|
||||
* get all characters in this corporation
|
||||
* @return array
|
||||
* @param array $characterIds
|
||||
* @return CharacterModel[]
|
||||
*/
|
||||
public function getCharacters(){
|
||||
public function getCharacters( $characterIds = []){
|
||||
$characters = [];
|
||||
$filter = ['active = ?', 1];
|
||||
|
||||
$this->filter('corporationCharacters', ['active = ?', 1]);
|
||||
if( !empty($characterIds) ){
|
||||
$filter[0] .= ' AND id IN (?)';
|
||||
$filter[] = $characterIds;
|
||||
}
|
||||
|
||||
$this->filter('corporationCharacters', $filter);
|
||||
|
||||
if($this->corporationCharacters){
|
||||
foreach($this->corporationCharacters as $character){
|
||||
@@ -105,4 +178,22 @@ class CorporationModel extends BasicModel {
|
||||
|
||||
return $characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* get roles for each character in this corp
|
||||
* -> CCP API call
|
||||
* @param string $accessToken
|
||||
* @return array
|
||||
*/
|
||||
public function getCharactersRoles($accessToken){
|
||||
$characterRolesData = [];
|
||||
if(
|
||||
!empty($accessToken) &&
|
||||
!$this->isNPC
|
||||
){
|
||||
$characterRolesData = self::getF3()->ccpClient->getCorporationRoles($this->_id, $accessToken);
|
||||
}
|
||||
|
||||
return $characterRolesData;
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ ALLIANCE =
|
||||
INDEX = templates/view/index.html
|
||||
SETUP = templates/view/setup.html
|
||||
LOGIN = templates/view/login.html
|
||||
ADMIN = templates/view/admin.html
|
||||
|
||||
; HTTP status pages ===============================================================================
|
||||
[PATHFINDER.STATUS]
|
||||
@@ -156,6 +157,8 @@ SESSION_SUSPECT = session_suspect
|
||||
DELETE_ACCOUNT = account_delete
|
||||
; unauthorized request (HTTP 401)
|
||||
UNAUTHORIZED = unauthorized
|
||||
; admin action (e.g. kick, bann) log
|
||||
ADMIN = admin
|
||||
; TCP socket errors
|
||||
SOCKET_ERROR = socket_error
|
||||
; debug log for development
|
||||
|
||||
@@ -12,6 +12,9 @@ NGINX.VERSION = 1.9
|
||||
; recommended is >= 5.6
|
||||
VERSION = 7.0
|
||||
|
||||
; 64-bit version of PHP (4 = 32-bit, 8 = 64-bit)
|
||||
PHP_INT_SIZE = 8
|
||||
|
||||
; "Perl-Compatible Regular Expressions"
|
||||
; usually shipped with PHP package,
|
||||
; but needs to be additionally updated on CentOS or Red Hat systems
|
||||
|
||||
@@ -10,6 +10,8 @@ GET @login: / [sync] = Controller\AppContro
|
||||
GET @sso: /sso/@action [sync] = Controller\Ccp\Sso->@action
|
||||
; map page
|
||||
GET @map: /map [sync] = Controller\MapController->init
|
||||
; admin panel
|
||||
GET @admin: /admin* [sync] = Controller\Admin->dispatch
|
||||
|
||||
; ajax wildcard APIs (throttled)
|
||||
GET|POST /api/@controller/@action [ajax] = Controller\Api\@controller->@action, 0, 512
|
||||
|
||||
@@ -53,8 +53,8 @@ define(['jquery'], function($) {
|
||||
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
|
||||
},
|
||||
url: {
|
||||
ccpImageServer: 'https://image.eveonline.com/', // CCP image Server
|
||||
zKillboard: 'https://zkillboard.com/api/' // killboard api
|
||||
ccpImageServer: '//image.eveonline.com/', // CCP image Server
|
||||
zKillboard: '//zkillboard.com/api/' // killboard api
|
||||
},
|
||||
breakpoints: [
|
||||
{ name: 'desktop', width: Infinity },
|
||||
|
||||
@@ -65,8 +65,9 @@ define([
|
||||
// notification panel
|
||||
notificationPanelId: 'pf-notification-panel', // id for "notification panel" (e.g. last update information)
|
||||
|
||||
// server panel
|
||||
serverPanelId: 'pf-server-panel', // id for EVE Online server status panel
|
||||
// sticky panel
|
||||
stickyPanelClass: 'pf-landing-sticky-panel', // class for sticky panels
|
||||
stickyPanelServerId: 'pf-landing-server-panel', // id for EVE Online server status panel
|
||||
|
||||
// animation
|
||||
animateElementClass: 'pf-animate-on-visible', // class for elements that will be animated to show
|
||||
@@ -376,14 +377,14 @@ define([
|
||||
|
||||
$('.youtube').each(function() {
|
||||
// Based on the YouTube ID, we can easily find the thumbnail image
|
||||
$(this).css('background-image', 'url(https://i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
|
||||
$(this).css('background-image', 'url(//i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
|
||||
|
||||
// Overlay the Play icon to make it look like a video player
|
||||
$(this).append($('<div/>', {'class': 'play'}));
|
||||
|
||||
$(document).delegate('#' + this.id, 'click', function() {
|
||||
// Create an iFrame with autoplay set to true
|
||||
let iFrameUrl = 'https://www.youtube.com/embed/' + this.id + '?autoplay=1&autohide=1';
|
||||
let iFrameUrl = '//www.youtube.com/embed/' + this.id + '?autoplay=1&autohide=1';
|
||||
if ( $(this).data('params') ){
|
||||
iFrameUrl += '&'+$(this).data('params');
|
||||
}
|
||||
@@ -467,7 +468,8 @@ define([
|
||||
|
||||
if(responseData.hasOwnProperty('status')){
|
||||
let data = responseData.status;
|
||||
data.serverPanelId = config.serverPanelId;
|
||||
data.stickyPanelServerId = config.stickyPanelServerId;
|
||||
data.stickyPanelClass = config.stickyPanelClass;
|
||||
|
||||
let statusClass = '';
|
||||
switch(data.serviceStatus.toLowerCase()){
|
||||
@@ -483,7 +485,7 @@ define([
|
||||
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache) {
|
||||
let content = Mustache.render(template, data);
|
||||
$('#' + config.headerId).prepend(content);
|
||||
$('#' + config.serverPanelId).velocity('transition.slideLeftBigIn', {
|
||||
$('#' + config.stickyPanelServerId).velocity('transition.slideLeftBigIn', {
|
||||
duration: 240
|
||||
});
|
||||
});
|
||||
@@ -612,6 +614,25 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
let getCharacterAuthLabel = (authStatus) => {
|
||||
let label = '';
|
||||
switch(authStatus){
|
||||
case 'UNKNOWN':
|
||||
label = 'ERROR';
|
||||
break;
|
||||
case 'CORPORATION':
|
||||
case 'ALLIANCE':
|
||||
label = 'INVALID';
|
||||
break;
|
||||
default:
|
||||
label = authStatus;
|
||||
break;
|
||||
}
|
||||
return label;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// request character data for each character panel
|
||||
requirejs(['text!templates/ui/character_panel.html', 'mustache'], function(template, Mustache){
|
||||
@@ -653,7 +674,9 @@ define([
|
||||
let data = {
|
||||
link: this.characterElement.data('href'),
|
||||
cookieName: this.cookieName,
|
||||
character: responseData.character
|
||||
character: responseData.character,
|
||||
authLabel: getCharacterAuthLabel(responseData.character.authStatus),
|
||||
authOK: responseData.character.authStatus === 'OK'
|
||||
};
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
@@ -801,6 +824,38 @@ define([
|
||||
});
|
||||
}, false);
|
||||
|
||||
|
||||
require([
|
||||
'datatables.net',
|
||||
'datatables.net-buttons',
|
||||
'datatables.net-buttons-html',
|
||||
'datatables.net-responsive',
|
||||
'datatables.net-select'
|
||||
], function (startup) {
|
||||
let systemsDataTable = $('.dataTable').dataTable( {
|
||||
pageLength: 100,
|
||||
paging: true,
|
||||
// lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
|
||||
ordering: true,
|
||||
// order: [[ 9, 'desc' ], [ 3, 'asc' ]],
|
||||
autoWidth: false,
|
||||
// responsive: {
|
||||
// breakpoints: Init.breakpoints,
|
||||
// details: false
|
||||
// },
|
||||
hover: false,
|
||||
//data: systemsData,
|
||||
columnDefs: [],
|
||||
language: {
|
||||
emptyTable: 'No members',
|
||||
zeroRecords: 'No members found',
|
||||
lengthMenu: 'Show _MENU_ members',
|
||||
info: 'Showing _START_ to _END_ of _TOTAL_ members'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -9,22 +9,22 @@ define([
|
||||
], function($) {
|
||||
'use strict';
|
||||
|
||||
var config = {
|
||||
let config = {
|
||||
previewElementClass: 'pf-header-preview-element' // class for "preview" elements
|
||||
};
|
||||
|
||||
|
||||
var width, height, largeHeader, canvas, ctx, points, target, animateHeader = true;
|
||||
let width, height, largeHeader, canvas, ctx, points, target, animateHeader = true;
|
||||
|
||||
var canvasHeight = 450;
|
||||
var colorRGB = '108, 174, 173';
|
||||
var connectionCount = 4;
|
||||
let canvasHeight = 355;
|
||||
let colorRGB = '108, 174, 173';
|
||||
let connectionCount = 4;
|
||||
|
||||
|
||||
var initHeader = function() {
|
||||
let initHeader = function() {
|
||||
width = window.innerWidth;
|
||||
height = canvasHeight;
|
||||
target = {x: width * 0.8, y: 230};
|
||||
target = {x: width * 1, y: 230};
|
||||
|
||||
largeHeader.style.height = height+'px';
|
||||
|
||||
@@ -34,24 +34,24 @@ define([
|
||||
|
||||
// create points
|
||||
points = [];
|
||||
for(var x = 0; x < width; x = x + width/20) {
|
||||
for(var y = 0; y < height; y = y + height/15) {
|
||||
var px = x + Math.random()*width/15;
|
||||
var py = y + Math.random()*height/15;
|
||||
var p = {x: px, originX: px, y: py, originY: py };
|
||||
for(let x = 0; x < width; x = x + width/20) {
|
||||
for(let y = 0; y < height; y = y + height/15) {
|
||||
let px = x + Math.random()*width/15;
|
||||
let py = y + Math.random()*height/15;
|
||||
let p = {x: px, originX: px, y: py, originY: py };
|
||||
points.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
// for each point find the 5 closest points
|
||||
for(var i = 0; i < points.length; i++) {
|
||||
var closest = [];
|
||||
var p1 = points[i];
|
||||
for(var j = 0; j < points.length; j++) {
|
||||
var p2 = points[j];
|
||||
for(let i = 0; i < points.length; i++) {
|
||||
let closest = [];
|
||||
let p1 = points[i];
|
||||
for(let j = 0; j < points.length; j++) {
|
||||
let p2 = points[j];
|
||||
if(p1 !== p2) {
|
||||
var placed = false;
|
||||
for(var k = 0; k < connectionCount; k++) {
|
||||
let placed = false;
|
||||
for(let k = 0; k < connectionCount; k++) {
|
||||
if(!placed) {
|
||||
if(closest[k] === undefined) {
|
||||
closest[k] = p2;
|
||||
@@ -60,7 +60,7 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
for(var m = 0; m < connectionCount; m++) {
|
||||
for(let m = 0; m < connectionCount; m++) {
|
||||
if(!placed) {
|
||||
if(getDistance(p1, p2) < getDistance(p1, closest[m])) {
|
||||
closest[m] = p2;
|
||||
@@ -74,14 +74,14 @@ define([
|
||||
}
|
||||
|
||||
// assign a circle to each point
|
||||
for(var n in points) {
|
||||
var c = new Circle(points[n], 2+Math.random()*2, 'rgba(255,255,255,0.3)');
|
||||
for(let n in points) {
|
||||
let c = new Circle(points[n], 2+Math.random()*2, 'rgba(255,255,255,0.3)');
|
||||
points[n].circle = c;
|
||||
}
|
||||
};
|
||||
|
||||
// Event handling
|
||||
var addListeners = function() {
|
||||
let addListeners = function() {
|
||||
if(!('ontouchstart' in window)) {
|
||||
window.addEventListener('mousemove', mouseMove);
|
||||
}
|
||||
@@ -89,9 +89,9 @@ define([
|
||||
window.addEventListener('resize', resize);
|
||||
};
|
||||
|
||||
var mouseMove = function(e) {
|
||||
var posx = 0;
|
||||
var posy = 0;
|
||||
let mouseMove = function(e) {
|
||||
let posx = 0;
|
||||
let posy = 0;
|
||||
if (e.pageX || e.pageY) {
|
||||
posx = e.pageX;
|
||||
posy = e.pageY;
|
||||
@@ -103,7 +103,7 @@ define([
|
||||
target.y = posy;
|
||||
};
|
||||
|
||||
var scrollCheck = function() {
|
||||
let scrollCheck = function() {
|
||||
if(document.body.scrollTop > height){
|
||||
animateHeader = false;
|
||||
}else{
|
||||
@@ -111,7 +111,7 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var resize = function() {
|
||||
let resize = function() {
|
||||
width = window.innerWidth;
|
||||
height = canvasHeight;
|
||||
largeHeader.style.height = height+'px';
|
||||
@@ -120,17 +120,17 @@ define([
|
||||
};
|
||||
|
||||
// animation
|
||||
var initAnimation = function() {
|
||||
let initAnimation = function() {
|
||||
animate();
|
||||
for(var i in points) {
|
||||
for(let i in points) {
|
||||
shiftPoint(points[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var animate = function animate() {
|
||||
let animate = function animate() {
|
||||
if(animateHeader) {
|
||||
ctx.clearRect(0,0,width,height);
|
||||
for(var i in points) {
|
||||
for(let i in points) {
|
||||
// detect points in range
|
||||
if(Math.abs(getDistance(target, points[i])) < 4000) {
|
||||
points[i].active = 0.25;
|
||||
@@ -153,7 +153,7 @@ define([
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
var shiftPoint = function (p) {
|
||||
let shiftPoint = function (p) {
|
||||
TweenLite.to(p, 1 + 1 * Math.random(), {x: p.originX - 50 + Math.random() * 100,
|
||||
y: p.originY - 50 + Math.random() * 100, ease: Circ.easeInOut,
|
||||
onComplete: function () {
|
||||
@@ -162,9 +162,9 @@ define([
|
||||
};
|
||||
|
||||
// Canvas manipulation
|
||||
var drawLines = function (p) {
|
||||
let drawLines = function (p) {
|
||||
if(!p.active) return;
|
||||
for(var i in p.closest) {
|
||||
for(let i in p.closest) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(p.x, p.y);
|
||||
ctx.lineTo(p.closest[i].x, p.closest[i].y);
|
||||
@@ -173,8 +173,8 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var Circle = function(pos,rad,color) {
|
||||
var _this = this;
|
||||
let Circle = function(pos,rad,color) {
|
||||
let _this = this;
|
||||
|
||||
// constructor
|
||||
(function() {
|
||||
@@ -193,7 +193,7 @@ define([
|
||||
};
|
||||
|
||||
// Util
|
||||
var getDistance = function(p1, p2) {
|
||||
let getDistance = function(p1, p2) {
|
||||
return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ define([
|
||||
], function($, Init, Util, Morris) {
|
||||
'use strict';
|
||||
|
||||
var config = {
|
||||
let config = {
|
||||
// module info
|
||||
moduleClass: 'pf-module', // class for each module
|
||||
|
||||
@@ -23,10 +23,9 @@ define([
|
||||
systemKillboardListImgShip: 'pf-system-killboard-img-ship', // class for all ship images
|
||||
systemKillboardListImgAlly: 'pf-system-killboard-img-ally', // class for all alliance logos
|
||||
systemKillboardListImgCorp: 'pf-system-killboard-img-corp' // class for all corp logos
|
||||
|
||||
};
|
||||
|
||||
var cache = {
|
||||
let cache = {
|
||||
systemKillsGraphData: {} // data for system kills info graph
|
||||
};
|
||||
|
||||
@@ -35,8 +34,8 @@ define([
|
||||
* @param text
|
||||
* @returns {*|XMLList}
|
||||
*/
|
||||
var getLabel = function(text, options){
|
||||
var label = $('<span>', {
|
||||
let getLabel = function(text, options){
|
||||
let label = $('<span>', {
|
||||
class: ['label', options.type, options.align].join(' ')
|
||||
}).text( text );
|
||||
|
||||
@@ -44,16 +43,16 @@ define([
|
||||
};
|
||||
|
||||
|
||||
var showKillmails = function(moduleElement, killboardData){
|
||||
let showKillmails = function(moduleElement, killboardData){
|
||||
|
||||
// show number of killMails
|
||||
var killMailCounterMax = 20;
|
||||
var killMailCounter = 0;
|
||||
let killMailCounterMax = 20;
|
||||
let killMailCounter = 0;
|
||||
|
||||
// change order (show right to left)
|
||||
killboardData.tableData.reverse();
|
||||
|
||||
for(var i = 0; i < killboardData.tableData.length; i++){
|
||||
for(let i = 0; i < killboardData.tableData.length; i++){
|
||||
|
||||
// check if killMails exist in this hour
|
||||
if(killboardData.tableData[i].killmails){
|
||||
@@ -64,43 +63,43 @@ define([
|
||||
|
||||
moduleElement.append( $('<h5>').text(i + 'h ago'));
|
||||
|
||||
var killMailData = killboardData.tableData[i].killmails;
|
||||
let killMailData = killboardData.tableData[i].killmails;
|
||||
|
||||
var listeElement = $('<ul>', {
|
||||
let listeElement = $('<ul>', {
|
||||
class: ['media-list', config.systemKillboardListClass].join(' ')
|
||||
});
|
||||
|
||||
for(var j = 0; j < killMailData.length; j++){
|
||||
for(let j = 0; j < killMailData.length; j++){
|
||||
killMailCounter++;
|
||||
if(killMailCounter >= killMailCounterMax){
|
||||
break;
|
||||
}
|
||||
|
||||
var killData = killMailData[j];
|
||||
let killData = killMailData[j];
|
||||
|
||||
var linkUrl = 'https://zkillboard.com/kill/' + killData.killID + '/';
|
||||
var victimImageUrl = 'https://image.eveonline.com/Type/' + killData.victim.shipTypeID + '_64.png';
|
||||
var killDate = getDateObjectByTimeString(killData.killTime);
|
||||
var killDateString = Util.convertDateToString(killDate);
|
||||
var killLossValue = Util.formatPrice( killData.zkb.totalValue );
|
||||
let linkUrl = '//zkillboard.com/kill/' + killData.killID + '/';
|
||||
let victimImageUrl = Init.url.ccpImageServer + 'Type/' + killData.victim.shipTypeID + '_64.png';
|
||||
let killDate = getDateObjectByTimeString(killData.killTime);
|
||||
let killDateString = Util.convertDateToString(killDate);
|
||||
let killLossValue = Util.formatPrice( killData.zkb.totalValue );
|
||||
|
||||
// check for ally
|
||||
var victimAllyLogoUrl = '';
|
||||
var displayAlly = 'none';
|
||||
let victimAllyLogoUrl = '';
|
||||
let displayAlly = 'none';
|
||||
if(killData.victim.allianceID > 0){
|
||||
victimAllyLogoUrl = 'https://image.eveonline.com/Alliance/' + killData.victim.allianceID + '_32.png';
|
||||
victimAllyLogoUrl = Init.url.ccpImageServer + 'Alliance/' + killData.victim.allianceID + '_32.png';
|
||||
displayAlly = 'block';
|
||||
}
|
||||
|
||||
// check for corp
|
||||
var victimCorpLogoUrl = '';
|
||||
var displayCorp = 'none';
|
||||
let victimCorpLogoUrl = '';
|
||||
let displayCorp = 'none';
|
||||
if(killData.victim.corporationID > 0){
|
||||
victimCorpLogoUrl = 'https://image.eveonline.com/Corporation/' + killData.victim.corporationID + '_32.png';
|
||||
victimCorpLogoUrl = Init.url.ccpImageServer + 'Corporation/' + killData.victim.corporationID + '_32.png';
|
||||
displayCorp = 'inline';
|
||||
}
|
||||
|
||||
var liElement = $('<li>', {
|
||||
let liElement = $('<li>', {
|
||||
class: ['media', config.systemKillboardListEntryClass].join(' ')
|
||||
}).append(
|
||||
$('<a>', {
|
||||
@@ -180,11 +179,11 @@ define([
|
||||
*/
|
||||
$.fn.updateSystemInfoGraphs = function(systemData){
|
||||
|
||||
var moduleElement = $(this);
|
||||
let moduleElement = $(this);
|
||||
|
||||
|
||||
// headline toolbar icons
|
||||
var headlineToolbar = $('<h5>', {
|
||||
let headlineToolbar = $('<h5>', {
|
||||
class: 'pull-right'
|
||||
}).append(
|
||||
$('<i>', {
|
||||
@@ -192,7 +191,7 @@ define([
|
||||
title: 'zkillboard.com'
|
||||
}).on('click', function(e){
|
||||
window.open(
|
||||
'https://zkillboard.com/system/' + systemData.systemId,
|
||||
'//zkillboard.com/system/' + systemData.systemId,
|
||||
'_blank'
|
||||
);
|
||||
}).attr('data-toggle', 'tooltip')
|
||||
@@ -201,30 +200,30 @@ define([
|
||||
moduleElement.append(headlineToolbar);
|
||||
|
||||
// headline
|
||||
var headline = $('<h5>', {
|
||||
let headline = $('<h5>', {
|
||||
text: 'Killboard'
|
||||
});
|
||||
|
||||
moduleElement.append(headline);
|
||||
|
||||
var killboardGraphElement = $('<div>', {
|
||||
let killboardGraphElement = $('<div>', {
|
||||
class: config.systemKillboardGraphKillsClass
|
||||
});
|
||||
|
||||
moduleElement.append(killboardGraphElement);
|
||||
|
||||
var showHours = 24;
|
||||
var maxKillmailCount = 200; // limited by API
|
||||
let showHours = 24;
|
||||
let maxKillmailCount = 200; // limited by API
|
||||
|
||||
var labelOptions = {
|
||||
let labelOptions = {
|
||||
align: 'center-block'
|
||||
};
|
||||
var label = '';
|
||||
let label = '';
|
||||
|
||||
// private function draws a "system kills" graph
|
||||
var drawGraph = function(data){
|
||||
let drawGraph = function(data){
|
||||
|
||||
var tableData = data.tableData;
|
||||
let tableData = data.tableData;
|
||||
|
||||
// change order (show right to left)
|
||||
tableData.reverse();
|
||||
@@ -240,7 +239,7 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
var labelYFormat = function(y){
|
||||
let labelYFormat = function(y){
|
||||
return Math.round(y);
|
||||
};
|
||||
|
||||
@@ -287,10 +286,10 @@ define([
|
||||
};
|
||||
|
||||
// get recent KB stats (last 24h))
|
||||
var localDate = new Date();
|
||||
let localDate = new Date();
|
||||
|
||||
// cache result for 5min
|
||||
var cacheKey = systemData.systemId + '_' + localDate.getHours() + '_' + ( Math.ceil( localDate.getMinutes() / 5 ) * 5);
|
||||
let cacheKey = systemData.systemId + '_' + localDate.getHours() + '_' + ( Math.ceil( localDate.getMinutes() / 5 ) * 5);
|
||||
|
||||
if(cache.systemKillsGraphData.hasOwnProperty(cacheKey) ){
|
||||
// cached results
|
||||
@@ -302,10 +301,10 @@ define([
|
||||
}else{
|
||||
|
||||
// chart data
|
||||
var chartData = [];
|
||||
let chartData = [];
|
||||
|
||||
for(var i = 0; i < showHours; i++){
|
||||
var tempData = {
|
||||
for(let i = 0; i < showHours; i++){
|
||||
let tempData = {
|
||||
label: i + 'h',
|
||||
kills: 0
|
||||
};
|
||||
@@ -314,18 +313,18 @@ define([
|
||||
}
|
||||
|
||||
// get kills within the last 24h
|
||||
var timeFrameInSeconds = 60 * 60 * 24;
|
||||
let timeFrameInSeconds = 60 * 60 * 24;
|
||||
|
||||
// get current server time
|
||||
var serverDate= Util.getServerTime();
|
||||
let serverDate= Util.getServerTime();
|
||||
|
||||
// if system is w-space system -> add link modifier
|
||||
var wSpaceLinkModifier = '';
|
||||
let wSpaceLinkModifier = '';
|
||||
if(systemData.type.id === 1){
|
||||
wSpaceLinkModifier = 'w-space/';
|
||||
}
|
||||
|
||||
var url = Init.url.zKillboard;
|
||||
let url = Init.url.zKillboard;
|
||||
url += 'no-items/' + wSpaceLinkModifier + 'no-attackers/solarSystemID/' + systemData.systemId + '/pastSeconds/' + timeFrameInSeconds + '/';
|
||||
|
||||
killboardGraphElement.showLoadingAnimation();
|
||||
@@ -337,18 +336,18 @@ define([
|
||||
}).done(function(kbData) {
|
||||
|
||||
// the API wont return more than 200KMs ! - remember last bar block with complete KM information
|
||||
var lastCompleteDiffHourData = 0;
|
||||
let lastCompleteDiffHourData = 0;
|
||||
|
||||
|
||||
// loop kills and count kills by hour
|
||||
for (var i = 0; i < kbData.length; i++) {
|
||||
var killmailData = kbData[i];
|
||||
for (let i = 0; i < kbData.length; i++) {
|
||||
let killmailData = kbData[i];
|
||||
|
||||
var killDate = getDateObjectByTimeString(killmailData.killTime);
|
||||
let killDate = getDateObjectByTimeString(killmailData.killTime);
|
||||
|
||||
// get time diff
|
||||
var timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60);
|
||||
var timeDiffHour = Math.round(timeDiffMin / 60);
|
||||
let timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60);
|
||||
let timeDiffHour = Math.round(timeDiffMin / 60);
|
||||
|
||||
// update chart data
|
||||
if (chartData[timeDiffHour]) {
|
||||
@@ -401,7 +400,7 @@ define([
|
||||
|
||||
|
||||
// init tooltips
|
||||
var tooltipElements = moduleElement.find('[data-toggle="tooltip"]');
|
||||
let tooltipElements = moduleElement.find('[data-toggle="tooltip"]');
|
||||
tooltipElements.tooltip({
|
||||
container: 'body'
|
||||
});
|
||||
@@ -412,7 +411,7 @@ define([
|
||||
* minify the killboard graph element e.g. if no kills where found, or on error
|
||||
* @param killboardGraphElement
|
||||
*/
|
||||
var minifyKillboardGraphElement = function(killboardGraphElement){
|
||||
let minifyKillboardGraphElement = function(killboardGraphElement){
|
||||
killboardGraphElement.velocity({
|
||||
height: '20px',
|
||||
marginBottom: '0px'
|
||||
@@ -426,9 +425,9 @@ define([
|
||||
* @param timeString
|
||||
* @returns {Date}
|
||||
*/
|
||||
var getDateObjectByTimeString = function(timeString){
|
||||
var match = timeString.match(/^(\d+)-(\d+)-(\d+) (\d+)\:(\d+)\:(\d+)$/);
|
||||
var date = new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6]);
|
||||
let getDateObjectByTimeString = function(timeString){
|
||||
let match = timeString.match(/^(\d+)-(\d+)-(\d+) (\d+)\:(\d+)\:(\d+)$/);
|
||||
let date = new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6]);
|
||||
|
||||
return date;
|
||||
};
|
||||
@@ -438,10 +437,10 @@ define([
|
||||
* @param systemData
|
||||
* @returns {*|HTMLElement}
|
||||
*/
|
||||
var getModule = function(parentElement, systemData){
|
||||
let getModule = function(parentElement, systemData){
|
||||
|
||||
// create new module container
|
||||
var moduleElement = $('<div>', {
|
||||
let moduleElement = $('<div>', {
|
||||
class: [config.moduleClass, config.systemKillboardModuleClass].join(' '),
|
||||
css: {opacity: 0}
|
||||
});
|
||||
@@ -461,10 +460,10 @@ define([
|
||||
*/
|
||||
$.fn.drawSystemKillboardModule = function(systemData){
|
||||
|
||||
var parentElement = $(this);
|
||||
let parentElement = $(this);
|
||||
|
||||
// show route module
|
||||
var showModule = function(moduleElement){
|
||||
let showModule = function(moduleElement){
|
||||
if(moduleElement){
|
||||
moduleElement.velocity('transition.slideDownIn', {
|
||||
duration: Init.animationSpeed.mapModule,
|
||||
@@ -474,7 +473,7 @@ define([
|
||||
};
|
||||
|
||||
// check if module already exists
|
||||
var moduleElement = parentElement.find('.' + config.systemKillboardModuleClass);
|
||||
let moduleElement = parentElement.find('.' + config.systemKillboardModuleClass);
|
||||
|
||||
if(moduleElement.length > 0){
|
||||
moduleElement.velocity('transition.slideDownOut', {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -53,8 +53,8 @@ define(['jquery'], function($) {
|
||||
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
|
||||
},
|
||||
url: {
|
||||
ccpImageServer: 'https://image.eveonline.com/', // CCP image Server
|
||||
zKillboard: 'https://zkillboard.com/api/' // killboard api
|
||||
ccpImageServer: '//image.eveonline.com/', // CCP image Server
|
||||
zKillboard: '//zkillboard.com/api/' // killboard api
|
||||
},
|
||||
breakpoints: [
|
||||
{ name: 'desktop', width: Infinity },
|
||||
|
||||
@@ -65,8 +65,9 @@ define([
|
||||
// notification panel
|
||||
notificationPanelId: 'pf-notification-panel', // id for "notification panel" (e.g. last update information)
|
||||
|
||||
// server panel
|
||||
serverPanelId: 'pf-server-panel', // id for EVE Online server status panel
|
||||
// sticky panel
|
||||
stickyPanelClass: 'pf-landing-sticky-panel', // class for sticky panels
|
||||
stickyPanelServerId: 'pf-landing-server-panel', // id for EVE Online server status panel
|
||||
|
||||
// animation
|
||||
animateElementClass: 'pf-animate-on-visible', // class for elements that will be animated to show
|
||||
@@ -376,14 +377,14 @@ define([
|
||||
|
||||
$('.youtube').each(function() {
|
||||
// Based on the YouTube ID, we can easily find the thumbnail image
|
||||
$(this).css('background-image', 'url(https://i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
|
||||
$(this).css('background-image', 'url(//i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
|
||||
|
||||
// Overlay the Play icon to make it look like a video player
|
||||
$(this).append($('<div/>', {'class': 'play'}));
|
||||
|
||||
$(document).delegate('#' + this.id, 'click', function() {
|
||||
// Create an iFrame with autoplay set to true
|
||||
let iFrameUrl = 'https://www.youtube.com/embed/' + this.id + '?autoplay=1&autohide=1';
|
||||
let iFrameUrl = '//www.youtube.com/embed/' + this.id + '?autoplay=1&autohide=1';
|
||||
if ( $(this).data('params') ){
|
||||
iFrameUrl += '&'+$(this).data('params');
|
||||
}
|
||||
@@ -467,7 +468,8 @@ define([
|
||||
|
||||
if(responseData.hasOwnProperty('status')){
|
||||
let data = responseData.status;
|
||||
data.serverPanelId = config.serverPanelId;
|
||||
data.stickyPanelServerId = config.stickyPanelServerId;
|
||||
data.stickyPanelClass = config.stickyPanelClass;
|
||||
|
||||
let statusClass = '';
|
||||
switch(data.serviceStatus.toLowerCase()){
|
||||
@@ -483,7 +485,7 @@ define([
|
||||
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache) {
|
||||
let content = Mustache.render(template, data);
|
||||
$('#' + config.headerId).prepend(content);
|
||||
$('#' + config.serverPanelId).velocity('transition.slideLeftBigIn', {
|
||||
$('#' + config.stickyPanelServerId).velocity('transition.slideLeftBigIn', {
|
||||
duration: 240
|
||||
});
|
||||
});
|
||||
@@ -612,6 +614,25 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
let getCharacterAuthLabel = (authStatus) => {
|
||||
let label = '';
|
||||
switch(authStatus){
|
||||
case 'UNKNOWN':
|
||||
label = 'ERROR';
|
||||
break;
|
||||
case 'CORPORATION':
|
||||
case 'ALLIANCE':
|
||||
label = 'INVALID';
|
||||
break;
|
||||
default:
|
||||
label = authStatus;
|
||||
break;
|
||||
}
|
||||
return label;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// request character data for each character panel
|
||||
requirejs(['text!templates/ui/character_panel.html', 'mustache'], function(template, Mustache){
|
||||
@@ -653,7 +674,9 @@ define([
|
||||
let data = {
|
||||
link: this.characterElement.data('href'),
|
||||
cookieName: this.cookieName,
|
||||
character: responseData.character
|
||||
character: responseData.character,
|
||||
authLabel: getCharacterAuthLabel(responseData.character.authStatus),
|
||||
authOK: responseData.character.authStatus === 'OK'
|
||||
};
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
@@ -801,6 +824,38 @@ define([
|
||||
});
|
||||
}, false);
|
||||
|
||||
|
||||
require([
|
||||
'datatables.net',
|
||||
'datatables.net-buttons',
|
||||
'datatables.net-buttons-html',
|
||||
'datatables.net-responsive',
|
||||
'datatables.net-select'
|
||||
], function (startup) {
|
||||
let systemsDataTable = $('.dataTable').dataTable( {
|
||||
pageLength: 100,
|
||||
paging: true,
|
||||
// lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
|
||||
ordering: true,
|
||||
// order: [[ 9, 'desc' ], [ 3, 'asc' ]],
|
||||
autoWidth: false,
|
||||
// responsive: {
|
||||
// breakpoints: Init.breakpoints,
|
||||
// details: false
|
||||
// },
|
||||
hover: false,
|
||||
//data: systemsData,
|
||||
columnDefs: [],
|
||||
language: {
|
||||
emptyTable: 'No members',
|
||||
zeroRecords: 'No members found',
|
||||
lengthMenu: 'Show _MENU_ members',
|
||||
info: 'Showing _START_ to _END_ of _TOTAL_ members'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -9,22 +9,22 @@ define([
|
||||
], function($) {
|
||||
'use strict';
|
||||
|
||||
var config = {
|
||||
let config = {
|
||||
previewElementClass: 'pf-header-preview-element' // class for "preview" elements
|
||||
};
|
||||
|
||||
|
||||
var width, height, largeHeader, canvas, ctx, points, target, animateHeader = true;
|
||||
let width, height, largeHeader, canvas, ctx, points, target, animateHeader = true;
|
||||
|
||||
var canvasHeight = 450;
|
||||
var colorRGB = '108, 174, 173';
|
||||
var connectionCount = 4;
|
||||
let canvasHeight = 355;
|
||||
let colorRGB = '108, 174, 173';
|
||||
let connectionCount = 4;
|
||||
|
||||
|
||||
var initHeader = function() {
|
||||
let initHeader = function() {
|
||||
width = window.innerWidth;
|
||||
height = canvasHeight;
|
||||
target = {x: width * 0.8, y: 230};
|
||||
target = {x: width * 1, y: 230};
|
||||
|
||||
largeHeader.style.height = height+'px';
|
||||
|
||||
@@ -34,24 +34,24 @@ define([
|
||||
|
||||
// create points
|
||||
points = [];
|
||||
for(var x = 0; x < width; x = x + width/20) {
|
||||
for(var y = 0; y < height; y = y + height/15) {
|
||||
var px = x + Math.random()*width/15;
|
||||
var py = y + Math.random()*height/15;
|
||||
var p = {x: px, originX: px, y: py, originY: py };
|
||||
for(let x = 0; x < width; x = x + width/20) {
|
||||
for(let y = 0; y < height; y = y + height/15) {
|
||||
let px = x + Math.random()*width/15;
|
||||
let py = y + Math.random()*height/15;
|
||||
let p = {x: px, originX: px, y: py, originY: py };
|
||||
points.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
// for each point find the 5 closest points
|
||||
for(var i = 0; i < points.length; i++) {
|
||||
var closest = [];
|
||||
var p1 = points[i];
|
||||
for(var j = 0; j < points.length; j++) {
|
||||
var p2 = points[j];
|
||||
for(let i = 0; i < points.length; i++) {
|
||||
let closest = [];
|
||||
let p1 = points[i];
|
||||
for(let j = 0; j < points.length; j++) {
|
||||
let p2 = points[j];
|
||||
if(p1 !== p2) {
|
||||
var placed = false;
|
||||
for(var k = 0; k < connectionCount; k++) {
|
||||
let placed = false;
|
||||
for(let k = 0; k < connectionCount; k++) {
|
||||
if(!placed) {
|
||||
if(closest[k] === undefined) {
|
||||
closest[k] = p2;
|
||||
@@ -60,7 +60,7 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
for(var m = 0; m < connectionCount; m++) {
|
||||
for(let m = 0; m < connectionCount; m++) {
|
||||
if(!placed) {
|
||||
if(getDistance(p1, p2) < getDistance(p1, closest[m])) {
|
||||
closest[m] = p2;
|
||||
@@ -74,14 +74,14 @@ define([
|
||||
}
|
||||
|
||||
// assign a circle to each point
|
||||
for(var n in points) {
|
||||
var c = new Circle(points[n], 2+Math.random()*2, 'rgba(255,255,255,0.3)');
|
||||
for(let n in points) {
|
||||
let c = new Circle(points[n], 2+Math.random()*2, 'rgba(255,255,255,0.3)');
|
||||
points[n].circle = c;
|
||||
}
|
||||
};
|
||||
|
||||
// Event handling
|
||||
var addListeners = function() {
|
||||
let addListeners = function() {
|
||||
if(!('ontouchstart' in window)) {
|
||||
window.addEventListener('mousemove', mouseMove);
|
||||
}
|
||||
@@ -89,9 +89,9 @@ define([
|
||||
window.addEventListener('resize', resize);
|
||||
};
|
||||
|
||||
var mouseMove = function(e) {
|
||||
var posx = 0;
|
||||
var posy = 0;
|
||||
let mouseMove = function(e) {
|
||||
let posx = 0;
|
||||
let posy = 0;
|
||||
if (e.pageX || e.pageY) {
|
||||
posx = e.pageX;
|
||||
posy = e.pageY;
|
||||
@@ -103,7 +103,7 @@ define([
|
||||
target.y = posy;
|
||||
};
|
||||
|
||||
var scrollCheck = function() {
|
||||
let scrollCheck = function() {
|
||||
if(document.body.scrollTop > height){
|
||||
animateHeader = false;
|
||||
}else{
|
||||
@@ -111,7 +111,7 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var resize = function() {
|
||||
let resize = function() {
|
||||
width = window.innerWidth;
|
||||
height = canvasHeight;
|
||||
largeHeader.style.height = height+'px';
|
||||
@@ -120,17 +120,17 @@ define([
|
||||
};
|
||||
|
||||
// animation
|
||||
var initAnimation = function() {
|
||||
let initAnimation = function() {
|
||||
animate();
|
||||
for(var i in points) {
|
||||
for(let i in points) {
|
||||
shiftPoint(points[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var animate = function animate() {
|
||||
let animate = function animate() {
|
||||
if(animateHeader) {
|
||||
ctx.clearRect(0,0,width,height);
|
||||
for(var i in points) {
|
||||
for(let i in points) {
|
||||
// detect points in range
|
||||
if(Math.abs(getDistance(target, points[i])) < 4000) {
|
||||
points[i].active = 0.25;
|
||||
@@ -153,7 +153,7 @@ define([
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
var shiftPoint = function (p) {
|
||||
let shiftPoint = function (p) {
|
||||
TweenLite.to(p, 1 + 1 * Math.random(), {x: p.originX - 50 + Math.random() * 100,
|
||||
y: p.originY - 50 + Math.random() * 100, ease: Circ.easeInOut,
|
||||
onComplete: function () {
|
||||
@@ -162,9 +162,9 @@ define([
|
||||
};
|
||||
|
||||
// Canvas manipulation
|
||||
var drawLines = function (p) {
|
||||
let drawLines = function (p) {
|
||||
if(!p.active) return;
|
||||
for(var i in p.closest) {
|
||||
for(let i in p.closest) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(p.x, p.y);
|
||||
ctx.lineTo(p.closest[i].x, p.closest[i].y);
|
||||
@@ -173,8 +173,8 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var Circle = function(pos,rad,color) {
|
||||
var _this = this;
|
||||
let Circle = function(pos,rad,color) {
|
||||
let _this = this;
|
||||
|
||||
// constructor
|
||||
(function() {
|
||||
@@ -193,7 +193,7 @@ define([
|
||||
};
|
||||
|
||||
// Util
|
||||
var getDistance = function(p1, p2) {
|
||||
let getDistance = function(p1, p2) {
|
||||
return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ define([
|
||||
], function($, Init, Util, Morris) {
|
||||
'use strict';
|
||||
|
||||
var config = {
|
||||
let config = {
|
||||
// module info
|
||||
moduleClass: 'pf-module', // class for each module
|
||||
|
||||
@@ -23,10 +23,9 @@ define([
|
||||
systemKillboardListImgShip: 'pf-system-killboard-img-ship', // class for all ship images
|
||||
systemKillboardListImgAlly: 'pf-system-killboard-img-ally', // class for all alliance logos
|
||||
systemKillboardListImgCorp: 'pf-system-killboard-img-corp' // class for all corp logos
|
||||
|
||||
};
|
||||
|
||||
var cache = {
|
||||
let cache = {
|
||||
systemKillsGraphData: {} // data for system kills info graph
|
||||
};
|
||||
|
||||
@@ -35,8 +34,8 @@ define([
|
||||
* @param text
|
||||
* @returns {*|XMLList}
|
||||
*/
|
||||
var getLabel = function(text, options){
|
||||
var label = $('<span>', {
|
||||
let getLabel = function(text, options){
|
||||
let label = $('<span>', {
|
||||
class: ['label', options.type, options.align].join(' ')
|
||||
}).text( text );
|
||||
|
||||
@@ -44,16 +43,16 @@ define([
|
||||
};
|
||||
|
||||
|
||||
var showKillmails = function(moduleElement, killboardData){
|
||||
let showKillmails = function(moduleElement, killboardData){
|
||||
|
||||
// show number of killMails
|
||||
var killMailCounterMax = 20;
|
||||
var killMailCounter = 0;
|
||||
let killMailCounterMax = 20;
|
||||
let killMailCounter = 0;
|
||||
|
||||
// change order (show right to left)
|
||||
killboardData.tableData.reverse();
|
||||
|
||||
for(var i = 0; i < killboardData.tableData.length; i++){
|
||||
for(let i = 0; i < killboardData.tableData.length; i++){
|
||||
|
||||
// check if killMails exist in this hour
|
||||
if(killboardData.tableData[i].killmails){
|
||||
@@ -64,43 +63,43 @@ define([
|
||||
|
||||
moduleElement.append( $('<h5>').text(i + 'h ago'));
|
||||
|
||||
var killMailData = killboardData.tableData[i].killmails;
|
||||
let killMailData = killboardData.tableData[i].killmails;
|
||||
|
||||
var listeElement = $('<ul>', {
|
||||
let listeElement = $('<ul>', {
|
||||
class: ['media-list', config.systemKillboardListClass].join(' ')
|
||||
});
|
||||
|
||||
for(var j = 0; j < killMailData.length; j++){
|
||||
for(let j = 0; j < killMailData.length; j++){
|
||||
killMailCounter++;
|
||||
if(killMailCounter >= killMailCounterMax){
|
||||
break;
|
||||
}
|
||||
|
||||
var killData = killMailData[j];
|
||||
let killData = killMailData[j];
|
||||
|
||||
var linkUrl = 'https://zkillboard.com/kill/' + killData.killID + '/';
|
||||
var victimImageUrl = 'https://image.eveonline.com/Type/' + killData.victim.shipTypeID + '_64.png';
|
||||
var killDate = getDateObjectByTimeString(killData.killTime);
|
||||
var killDateString = Util.convertDateToString(killDate);
|
||||
var killLossValue = Util.formatPrice( killData.zkb.totalValue );
|
||||
let linkUrl = '//zkillboard.com/kill/' + killData.killID + '/';
|
||||
let victimImageUrl = Init.url.ccpImageServer + 'Type/' + killData.victim.shipTypeID + '_64.png';
|
||||
let killDate = getDateObjectByTimeString(killData.killTime);
|
||||
let killDateString = Util.convertDateToString(killDate);
|
||||
let killLossValue = Util.formatPrice( killData.zkb.totalValue );
|
||||
|
||||
// check for ally
|
||||
var victimAllyLogoUrl = '';
|
||||
var displayAlly = 'none';
|
||||
let victimAllyLogoUrl = '';
|
||||
let displayAlly = 'none';
|
||||
if(killData.victim.allianceID > 0){
|
||||
victimAllyLogoUrl = 'https://image.eveonline.com/Alliance/' + killData.victim.allianceID + '_32.png';
|
||||
victimAllyLogoUrl = Init.url.ccpImageServer + 'Alliance/' + killData.victim.allianceID + '_32.png';
|
||||
displayAlly = 'block';
|
||||
}
|
||||
|
||||
// check for corp
|
||||
var victimCorpLogoUrl = '';
|
||||
var displayCorp = 'none';
|
||||
let victimCorpLogoUrl = '';
|
||||
let displayCorp = 'none';
|
||||
if(killData.victim.corporationID > 0){
|
||||
victimCorpLogoUrl = 'https://image.eveonline.com/Corporation/' + killData.victim.corporationID + '_32.png';
|
||||
victimCorpLogoUrl = Init.url.ccpImageServer + 'Corporation/' + killData.victim.corporationID + '_32.png';
|
||||
displayCorp = 'inline';
|
||||
}
|
||||
|
||||
var liElement = $('<li>', {
|
||||
let liElement = $('<li>', {
|
||||
class: ['media', config.systemKillboardListEntryClass].join(' ')
|
||||
}).append(
|
||||
$('<a>', {
|
||||
@@ -180,11 +179,11 @@ define([
|
||||
*/
|
||||
$.fn.updateSystemInfoGraphs = function(systemData){
|
||||
|
||||
var moduleElement = $(this);
|
||||
let moduleElement = $(this);
|
||||
|
||||
|
||||
// headline toolbar icons
|
||||
var headlineToolbar = $('<h5>', {
|
||||
let headlineToolbar = $('<h5>', {
|
||||
class: 'pull-right'
|
||||
}).append(
|
||||
$('<i>', {
|
||||
@@ -192,7 +191,7 @@ define([
|
||||
title: 'zkillboard.com'
|
||||
}).on('click', function(e){
|
||||
window.open(
|
||||
'https://zkillboard.com/system/' + systemData.systemId,
|
||||
'//zkillboard.com/system/' + systemData.systemId,
|
||||
'_blank'
|
||||
);
|
||||
}).attr('data-toggle', 'tooltip')
|
||||
@@ -201,30 +200,30 @@ define([
|
||||
moduleElement.append(headlineToolbar);
|
||||
|
||||
// headline
|
||||
var headline = $('<h5>', {
|
||||
let headline = $('<h5>', {
|
||||
text: 'Killboard'
|
||||
});
|
||||
|
||||
moduleElement.append(headline);
|
||||
|
||||
var killboardGraphElement = $('<div>', {
|
||||
let killboardGraphElement = $('<div>', {
|
||||
class: config.systemKillboardGraphKillsClass
|
||||
});
|
||||
|
||||
moduleElement.append(killboardGraphElement);
|
||||
|
||||
var showHours = 24;
|
||||
var maxKillmailCount = 200; // limited by API
|
||||
let showHours = 24;
|
||||
let maxKillmailCount = 200; // limited by API
|
||||
|
||||
var labelOptions = {
|
||||
let labelOptions = {
|
||||
align: 'center-block'
|
||||
};
|
||||
var label = '';
|
||||
let label = '';
|
||||
|
||||
// private function draws a "system kills" graph
|
||||
var drawGraph = function(data){
|
||||
let drawGraph = function(data){
|
||||
|
||||
var tableData = data.tableData;
|
||||
let tableData = data.tableData;
|
||||
|
||||
// change order (show right to left)
|
||||
tableData.reverse();
|
||||
@@ -240,7 +239,7 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
var labelYFormat = function(y){
|
||||
let labelYFormat = function(y){
|
||||
return Math.round(y);
|
||||
};
|
||||
|
||||
@@ -287,10 +286,10 @@ define([
|
||||
};
|
||||
|
||||
// get recent KB stats (last 24h))
|
||||
var localDate = new Date();
|
||||
let localDate = new Date();
|
||||
|
||||
// cache result for 5min
|
||||
var cacheKey = systemData.systemId + '_' + localDate.getHours() + '_' + ( Math.ceil( localDate.getMinutes() / 5 ) * 5);
|
||||
let cacheKey = systemData.systemId + '_' + localDate.getHours() + '_' + ( Math.ceil( localDate.getMinutes() / 5 ) * 5);
|
||||
|
||||
if(cache.systemKillsGraphData.hasOwnProperty(cacheKey) ){
|
||||
// cached results
|
||||
@@ -302,10 +301,10 @@ define([
|
||||
}else{
|
||||
|
||||
// chart data
|
||||
var chartData = [];
|
||||
let chartData = [];
|
||||
|
||||
for(var i = 0; i < showHours; i++){
|
||||
var tempData = {
|
||||
for(let i = 0; i < showHours; i++){
|
||||
let tempData = {
|
||||
label: i + 'h',
|
||||
kills: 0
|
||||
};
|
||||
@@ -314,18 +313,18 @@ define([
|
||||
}
|
||||
|
||||
// get kills within the last 24h
|
||||
var timeFrameInSeconds = 60 * 60 * 24;
|
||||
let timeFrameInSeconds = 60 * 60 * 24;
|
||||
|
||||
// get current server time
|
||||
var serverDate= Util.getServerTime();
|
||||
let serverDate= Util.getServerTime();
|
||||
|
||||
// if system is w-space system -> add link modifier
|
||||
var wSpaceLinkModifier = '';
|
||||
let wSpaceLinkModifier = '';
|
||||
if(systemData.type.id === 1){
|
||||
wSpaceLinkModifier = 'w-space/';
|
||||
}
|
||||
|
||||
var url = Init.url.zKillboard;
|
||||
let url = Init.url.zKillboard;
|
||||
url += 'no-items/' + wSpaceLinkModifier + 'no-attackers/solarSystemID/' + systemData.systemId + '/pastSeconds/' + timeFrameInSeconds + '/';
|
||||
|
||||
killboardGraphElement.showLoadingAnimation();
|
||||
@@ -337,18 +336,18 @@ define([
|
||||
}).done(function(kbData) {
|
||||
|
||||
// the API wont return more than 200KMs ! - remember last bar block with complete KM information
|
||||
var lastCompleteDiffHourData = 0;
|
||||
let lastCompleteDiffHourData = 0;
|
||||
|
||||
|
||||
// loop kills and count kills by hour
|
||||
for (var i = 0; i < kbData.length; i++) {
|
||||
var killmailData = kbData[i];
|
||||
for (let i = 0; i < kbData.length; i++) {
|
||||
let killmailData = kbData[i];
|
||||
|
||||
var killDate = getDateObjectByTimeString(killmailData.killTime);
|
||||
let killDate = getDateObjectByTimeString(killmailData.killTime);
|
||||
|
||||
// get time diff
|
||||
var timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60);
|
||||
var timeDiffHour = Math.round(timeDiffMin / 60);
|
||||
let timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60);
|
||||
let timeDiffHour = Math.round(timeDiffMin / 60);
|
||||
|
||||
// update chart data
|
||||
if (chartData[timeDiffHour]) {
|
||||
@@ -401,7 +400,7 @@ define([
|
||||
|
||||
|
||||
// init tooltips
|
||||
var tooltipElements = moduleElement.find('[data-toggle="tooltip"]');
|
||||
let tooltipElements = moduleElement.find('[data-toggle="tooltip"]');
|
||||
tooltipElements.tooltip({
|
||||
container: 'body'
|
||||
});
|
||||
@@ -412,7 +411,7 @@ define([
|
||||
* minify the killboard graph element e.g. if no kills where found, or on error
|
||||
* @param killboardGraphElement
|
||||
*/
|
||||
var minifyKillboardGraphElement = function(killboardGraphElement){
|
||||
let minifyKillboardGraphElement = function(killboardGraphElement){
|
||||
killboardGraphElement.velocity({
|
||||
height: '20px',
|
||||
marginBottom: '0px'
|
||||
@@ -426,9 +425,9 @@ define([
|
||||
* @param timeString
|
||||
* @returns {Date}
|
||||
*/
|
||||
var getDateObjectByTimeString = function(timeString){
|
||||
var match = timeString.match(/^(\d+)-(\d+)-(\d+) (\d+)\:(\d+)\:(\d+)$/);
|
||||
var date = new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6]);
|
||||
let getDateObjectByTimeString = function(timeString){
|
||||
let match = timeString.match(/^(\d+)-(\d+)-(\d+) (\d+)\:(\d+)\:(\d+)$/);
|
||||
let date = new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6]);
|
||||
|
||||
return date;
|
||||
};
|
||||
@@ -438,10 +437,10 @@ define([
|
||||
* @param systemData
|
||||
* @returns {*|HTMLElement}
|
||||
*/
|
||||
var getModule = function(parentElement, systemData){
|
||||
let getModule = function(parentElement, systemData){
|
||||
|
||||
// create new module container
|
||||
var moduleElement = $('<div>', {
|
||||
let moduleElement = $('<div>', {
|
||||
class: [config.moduleClass, config.systemKillboardModuleClass].join(' '),
|
||||
css: {opacity: 0}
|
||||
});
|
||||
@@ -461,10 +460,10 @@ define([
|
||||
*/
|
||||
$.fn.drawSystemKillboardModule = function(systemData){
|
||||
|
||||
var parentElement = $(this);
|
||||
let parentElement = $(this);
|
||||
|
||||
// show route module
|
||||
var showModule = function(moduleElement){
|
||||
let showModule = function(moduleElement){
|
||||
if(moduleElement){
|
||||
moduleElement.velocity('transition.slideDownIn', {
|
||||
duration: Init.animationSpeed.mapModule,
|
||||
@@ -474,7 +473,7 @@ define([
|
||||
};
|
||||
|
||||
// check if module already exists
|
||||
var moduleElement = parentElement.find('.' + config.systemKillboardModuleClass);
|
||||
let moduleElement = parentElement.find('.' + config.systemKillboardModuleClass);
|
||||
|
||||
if(moduleElement.length > 0){
|
||||
moduleElement.velocity('transition.slideDownOut', {
|
||||
|
||||
32
public/templates/admin/login.html
Normal file
32
public/templates/admin/login.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<section id="pf-landing-login">
|
||||
<div class="container col-xs-12">
|
||||
|
||||
<div class="row text-center">
|
||||
<div class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-6 col-lg-offset-3 pf-landing-pricing-panel">
|
||||
<div class="panel panel-default pricing-big pf-animate-on-visible pf-animate">
|
||||
<div class="ribbon-wrapper"><div class="ribbon ribbon-orange">BETA</div></div>
|
||||
<div class="panel-heading text-left">
|
||||
<h3 class="panel-title">Admin access</h3>
|
||||
</div>
|
||||
<div class="panel-body no-padding">
|
||||
<div class="price-features price-features-fluid">
|
||||
<ul class="list-unstyled text-left">
|
||||
<li><i class="fa fa-fw fa-caret-right"></i> Click the SSO button below for <em>PATHFINDER</em> access with admin roles</li>
|
||||
<li><i class="fa fa-fw fa-caret-right"></i> Admin roles require at least on of these in-game corporation roles granted to your character</li>
|
||||
<li>
|
||||
<ul>
|
||||
<li><i class="fa fa-fw fa-angle-right"></i> <kbd>director</kbd>, <kbd>personnel_manager</kbd> or <kbd>security_officer</kbd></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><i class="fa fa-fw fa-caret-right"></i> The admin SSO requires additional <em>ESI</em> scopes to be accepted, in order to get in-game roles from <em>EVE</em></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<include href="templates/modules/sso.html"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container"></div>
|
||||
</section>
|
||||
102
public/templates/admin/members.html
Normal file
102
public/templates/admin/members.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<section>
|
||||
<div class="container col-xs-12">
|
||||
<div class="row"></div>
|
||||
|
||||
<h4><i class="fa fa-fw fa-users"></i> {{ ucfirst(@tplPage) }}</h4>
|
||||
|
||||
<div class="row text-center">
|
||||
<div class="col-xs-12 pf-landing-pricing-panel">
|
||||
<div class="panel panel-default pricing-big">
|
||||
<div class="panel-heading text-left">
|
||||
<h3 class="panel-title">Corporation</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table id="table_id" class="stripe order-column row-border dataTable" data-order="[1, "asc"]" data-length-change="0" data-paging-type="full_numbers">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-width="50">id</th>
|
||||
<th>name</th>
|
||||
<th data-width="50">role</th>
|
||||
<th data-width="50">share</th>
|
||||
<th data-width="50">tracking</th>
|
||||
<th data-width="50">security</th>
|
||||
<th data-width="90">last login</th>
|
||||
<th data-width="90">kicked until</th>
|
||||
<th data-width="90" data-orderable="false">kick</th>
|
||||
<th data-width="90">banned since</th>
|
||||
<th data-width="30" data-orderable="false">ban</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<repeat group="{{ @tplMembers->members }}" value="{{ @member }}" counter="{{ @ctr }}">
|
||||
<set disableRow = "{{@member->_id}} == {{@character->_id}}" />
|
||||
|
||||
<tr>
|
||||
<td class="text-right">{{ @member->_id }}</td>
|
||||
<td class="pf-table-button-sm-cell" data-order="{{ @member->name }}" data-search="{{ @member->name }}">
|
||||
<img src="//image.eveonline.com/Character/{{ @member->_id }}_32.jpg" > {{ @member->name }}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<check if="{{ @member->roleId }}">
|
||||
<span class="label label-success">admin</span>
|
||||
</check>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<check if="{{ @member->shared }}">
|
||||
<true>
|
||||
<span class="label label-info">active</span>
|
||||
</true>
|
||||
<false>
|
||||
<span class="label label-default">disabled</span>
|
||||
</false>
|
||||
</check>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<check if="{{ @member->logLocation }}">
|
||||
<true>
|
||||
<span class="label label-success">active</span>
|
||||
</true>
|
||||
<false>
|
||||
<span class="label label-warning">disabled</span>
|
||||
</false>
|
||||
</check>
|
||||
</td>
|
||||
<td class="text-right txt-color {{ @member->securityStatus >= 0 ? 'txt-color-green' : 'txt-color-orange' }}">{{ number_format(round(@member->securityStatus, 2), 2) }}</td>
|
||||
<td class="text-right">{{ @member->getFormattedColumn('lastLogin') }}</td>
|
||||
<td class="text-right txt-color txt-color-orange">{{ @member->getFormattedColumn('kicked') }}</td>
|
||||
<td class="text-center pf-table-button-sm-cell">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<check if="{{ @member->kicked }}">
|
||||
<true>
|
||||
<a class="btn btn-primary {{ @disableRow ? 'disabled' : '' }}" href="/admin/{{ @tplPage}}/kick/{{ @member->_id }}">revoke</a>
|
||||
</true>
|
||||
<false>
|
||||
<repeat group="{{ @tplKickOptions }}" key="{{ @key }}" value="{{ @label }}">
|
||||
<a class="btn btn-default {{ @disableRow ? 'disabled' : '' }}" href="/admin/{{ @tplPage}}/kick/{{ @member->_id }}/{{ @key }}">{{ @label }}</a>
|
||||
</repeat>
|
||||
</false>
|
||||
</check>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right txt-color txt-color-danger">{{ @member->getFormattedColumn('banned') }}</td>
|
||||
<td class="text-center pf-table-button-sm-cell">
|
||||
<check if="{{ @member->banned }}">
|
||||
<true>
|
||||
<a class="btn btn-sm btn-primary {{ @disableRow ? 'disabled' : '' }}" href="/admin/{{ @tplPage}}/ban/{{ @member->_id }}">revoke</a>
|
||||
</true>
|
||||
<false>
|
||||
<a class="btn btn-sm btn-danger {{ @disableRow ? 'disabled' : '' }}" href="/admin/{{ @tplPage}}/ban/{{ @member->_id }}/1">ban</a>
|
||||
</false>
|
||||
</check>
|
||||
</td>
|
||||
</tr>
|
||||
</repeat>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
12
public/templates/admin/settings.html
Normal file
12
public/templates/admin/settings.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="row text-center">
|
||||
<div class="col-xs-12 pf-landing-pricing-panel">
|
||||
<div class="panel panel-default pricing-big">
|
||||
<div class="panel-heading text-left">
|
||||
<h3 class="panel-title">Settings</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
25
public/templates/modules/sso.html
Normal file
25
public/templates/modules/sso.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<check if="{{ @SESSION.SSO.ERROR }}">
|
||||
<div class="container-fluid">
|
||||
<div class="row ">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<div class="alert alert-danger" >
|
||||
<span class="txt-color txt-color-danger">Access denied</span>
|
||||
<small>{{ @SESSION.SSO.ERROR }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</check>
|
||||
|
||||
{* SSO login *}
|
||||
<div class="container-fluid">
|
||||
<div class="row text-center">
|
||||
<div class="col-xs-12">
|
||||
<a class="pf-sso-login-button {{@registrationStatusButton}}" target="_self" href="{{ @tplAuthType }}" type="button" tabindex="3" title="{{@registrationStatusTitle}}"> </a><br>
|
||||
<a class="font-lg" target="_blank" href="http://community.eveonline.com/news/dev-blogs/eve-online-sso-and-what-you-need-to-know">What is this?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* splash page -> shown by Javascript *}
|
||||
<div id="pf-notification-panel"></div>
|
||||
6
public/templates/ui/admin_panel.html
Normal file
6
public/templates/ui/admin_panel.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<div id="pf-landing-admin-panel" class="hidden-xs pf-landing-sticky-panel">
|
||||
<h4 class="text-center">Admin</h4>
|
||||
<ul class="fa-ul">
|
||||
<li><i class="fa-li fa fa-sign-in"></i><a href="{{ 'admin', ['*' => ''] | alias }}">login</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -1,4 +1,15 @@
|
||||
{{ character.authorizationStatus}}
|
||||
|
||||
<a href="{{ link }}?cookie={{ cookieName }}">
|
||||
{{#authOK}}
|
||||
{{#character.roleId}}
|
||||
<div class="ribbon-wrapper fade in"><div class="ribbon ribbon-blue">ADMIN</div></div>
|
||||
{{/character.roleId}}
|
||||
{{/authOK}}
|
||||
{{^authOK}}
|
||||
<div class="ribbon-wrapper fade in"><div class="ribbon ribbon-red">{{authLabel}}</div></div>
|
||||
{{/authOK}}
|
||||
|
||||
<div class="pf-character-image-wrapper">
|
||||
<div class="pf-character-select-image">
|
||||
<img class="pf-character-image" src="https://image.eveonline.com/Character/{{ character.id }}_128.jpg" alt="{{ character.name }}"/>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<div id="{{ serverPanelId }}" class="hidden-xs">
|
||||
<div id="{{ stickyPanelServerId }}" class="hidden-xs {{ stickyPanelClass }}">
|
||||
<h4 class="text-center">{{ serverName }}</h4>
|
||||
<ul class="fa-ul">
|
||||
{{#serviceStatus}}
|
||||
<li><i class="fa-li fa fa-server " aria-hidden="true"></i><span class="txt-color {{ style }}">{{ eve }}</span></li>
|
||||
<li><i class="fa-li fa fa-server"></i><span class="txt-color {{ style }}">{{ eve }}</span></li>
|
||||
{{/serviceStatus}}
|
||||
{{#playerCount}}
|
||||
<li><i class="fa-li fa fa-users" aria-hidden="true"></i>{{ playerCount }}</li>
|
||||
<li><i class="fa-li fa fa-users"></i>{{ playerCount }}</li>
|
||||
{{/playerCount}}
|
||||
{{#startTime}}
|
||||
<li><i class="fa-li fa fa-clock-o" aria-hidden="true"></i>up {{ startTime }}</li>
|
||||
<li><i class="fa-li fa fa-clock-o"></i>up {{ startTime }}</li>
|
||||
{{/startTime}}
|
||||
{{#serverVersion}}
|
||||
<li><i class="fa-li fa fa-certificate" aria-hidden="true"></i>v. {{ serverVersion }}</li>
|
||||
<li><i class="fa-li fa fa-certificate"></i>v. {{ serverVersion }}</li>
|
||||
{{/serverVersion}}
|
||||
</ul>
|
||||
</div>
|
||||
32
public/templates/view/admin.html
Normal file
32
public/templates/view/admin.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{* splash page *}
|
||||
<include href="templates/ui/splash.html"/>
|
||||
|
||||
<nav id="pf-navbar" class="navbar navbar-default navbar-fixed-top pf-head">
|
||||
<div class="container col-sm-12">
|
||||
{* Logged in *}
|
||||
<check if="{{ @tplLogged }}">
|
||||
<div id="pf-head" class="navbar-header">
|
||||
<span class="navbar-brand">
|
||||
<img class="pull-left" style="width: 16px" src="https://image.eveonline.com/Corporation/{{ @character->corporationId->id }}_64.png"/> {{ @character->corporationId->name }}
|
||||
</span>
|
||||
|
||||
<p class="navbar-text">
|
||||
{{ @character->name }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right" role="tablist">
|
||||
<li class="{{ @tplPage == 'settings' ? 'active' : '' }}"><a href="/admin/settings">Settings</a></li>
|
||||
<li class="{{ @tplPage == 'members' ? 'active' : '' }}"><a href="/admin/members"><i class="fa fa-fw fa-users"></i> Members</a></li>
|
||||
<li class="{{ @tplPage == 'maps' ? 'active' : '' }}"><a href="/admin/maps">Maps</a></li>
|
||||
<li class="{{ @tplPage == 'activity' ? 'active' : '' }}"><a href="/admin/activity">Activity</a></li>
|
||||
<li class="{{ @tplPage == 'login' ? 'active' : '' }}"><a href="/admin/login"><i class="fa fa-fw fa-sign-in"></i> SSO</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</check>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<include if="{{ @tplPage }}" href="{{ 'templates/admin/' . @tplPage . '.html' }}" />
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right" role="tablist">
|
||||
<li><a class="page-scroll" data-anchor="#pf-landing-top" href="#">Home</a></li>
|
||||
<li><a class="page-scroll" data-anchor="#pf-landing-top" href="#">Login</a></li>
|
||||
<li> <a class="page-scroll" data-anchor="#pf-landing-gallery" href="#">Features</a></li>
|
||||
<li> <a class="page-scroll" data-anchor="#pf-landing-maps" href="#">Maps</a></li>
|
||||
<li> <a class="page-scroll" data-anchor="#pf-landing-install" href="#">Install</a></li>
|
||||
@@ -41,6 +41,10 @@
|
||||
|
||||
{* header *}
|
||||
<header id="pf-landing-top">
|
||||
|
||||
{* Admin login panel *}
|
||||
<include href="templates/ui/admin_panel.html"/>
|
||||
|
||||
<div id="pf-header-container">
|
||||
<canvas id="pf-header-canvas" class="hidden-xs" width="500" height="480"></canvas>
|
||||
<div class="container">
|
||||
@@ -113,29 +117,8 @@
|
||||
</false>
|
||||
</check>
|
||||
|
||||
{* login message container *}
|
||||
<check if="{{ @SESSION.SSO.ERROR }}">
|
||||
<div class="container-fluid">
|
||||
<div class="row ">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<div class="alert alert-danger" >
|
||||
<span class="txt-color txt-color-danger">Access denied</span>
|
||||
<small>{{ @SESSION.SSO.ERROR }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</check>
|
||||
|
||||
{* SSO login *}
|
||||
<div class="container-fluid">
|
||||
<div class="row text-center">
|
||||
<div class="col-xs-12">
|
||||
<a class="pf-sso-login-button {{@registrationStatusButton}}" target="_self" href="{{ 'sso','action=requestAuthorization' | alias }}" type="button" tabindex="3" title="{{@registrationStatusTitle}}"> </a><br>
|
||||
<a class="font-lg" target="_blank" href="http://community.eveonline.com/news/dev-blogs/eve-online-sso-and-what-you-need-to-know">What is this?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<include href="templates/modules/sso.html"/>
|
||||
|
||||
{* splash page -> shown by Javascript *}
|
||||
<div id="pf-notification-panel"></div>
|
||||
@@ -858,7 +841,7 @@
|
||||
Feel free to contact me with your problem, either by submitting a <a target="_blank" href="https://github.com/exodus4d/pathfinder/issues">bug report</a> or contact me in game.
|
||||
I´ll give my best to find a solution for your problem or path <em>Pathfinder</em>.
|
||||
</p>
|
||||
<h3>I don´t trust you, can I host <em>Pathfinder</em> on my own webserver?</h3>
|
||||
<h3>I don´t trust you, can I host <em>Pathfinder</em> on my own?</h3>
|
||||
<p>
|
||||
Yes you can! I developed this application for the great community of <em>EVE Online</em>.
|
||||
The program code is open source and can be used by anyone who have the required software skills.
|
||||
@@ -870,10 +853,10 @@
|
||||
</p>
|
||||
<ul class="fa-ul pf-landing-list">
|
||||
<li><i></i> A webserver with a <em><a target="_blank" href="https://en.wikipedia.org/wiki/LAMP_(software_bundle)">LAMP</a></em> environment</li>
|
||||
<li><i></i> PHP 5.6+</li>
|
||||
<li><i></i> PHP framework <a target="_blank" href="http://fatfreeframework.com/system-requirements">requirements</a> </li>
|
||||
<li><i></i> MySQL 5.6+ </li>
|
||||
<li><i></i> Some kind of server side caching technology (Memcache,APC,xCache) file caching will also work</li>
|
||||
<li><i></i> PHP 7.0+</li>
|
||||
<li><i></i> PHP framework <a target="_blank" href="http://fatfreeframework.com/system-requirements">requirements</a></li>
|
||||
<li><i></i> MySQL 5.7+ </li>
|
||||
<li><i></i> Some kind of server side caching. <em><a target="_blank" href="https://redis.io/">Redis</a></em> is preferred, file cache will also work</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
@import "_timeline";
|
||||
@import "_ribbon";
|
||||
@import "_loading-bar";
|
||||
@import "_server-panel";
|
||||
@import "sticky-panel";
|
||||
@import "_youtube";
|
||||
@@ -167,7 +167,7 @@
|
||||
}
|
||||
// header --------------------------------------------------------------------
|
||||
#pf-landing-top{
|
||||
height: 450px;
|
||||
height: 355px;
|
||||
border-bottom: 1px solid $gray-dark;
|
||||
position: relative;
|
||||
|
||||
@@ -182,6 +182,10 @@
|
||||
@include filter(brightness(0.9))
|
||||
}
|
||||
|
||||
#pf-logo-container{
|
||||
@include transform( scale3d(0.8, 0.8, 1) ); // downscales logo
|
||||
}
|
||||
|
||||
#pf-header-container{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@@ -203,7 +207,7 @@
|
||||
left: 400px;
|
||||
width: 590px;
|
||||
height: 350px;
|
||||
top: 92px;
|
||||
top: 37px;
|
||||
|
||||
.pf-header-preview-element{
|
||||
position: relative;
|
||||
@@ -241,7 +245,7 @@
|
||||
|
||||
.container{
|
||||
position: relative;
|
||||
margin-top: 50px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,9 +305,14 @@
|
||||
padding: 10px 10px 5px 10px;
|
||||
min-width: 155px;
|
||||
min-height: 184px;
|
||||
overflow: visible; // overwrite default
|
||||
@include border-radius(10px);
|
||||
@include box-shadow(0 4px 10px rgba(0,0,0, 0.4));
|
||||
|
||||
.ribbon-wrapper{
|
||||
z-index: 5 ;
|
||||
}
|
||||
|
||||
// character images
|
||||
.pf-character-image-wrapper{
|
||||
opacity: 0;
|
||||
@@ -538,9 +547,12 @@
|
||||
background: $gray;
|
||||
color: $gray-lighter;
|
||||
padding: 20px 15px;
|
||||
min-height: 205px;
|
||||
line-height: 22px;
|
||||
|
||||
&:not(.price-features-fluid){
|
||||
min-height: 205px;
|
||||
}
|
||||
|
||||
.list-unstyled.text-left li{
|
||||
text-indent: -1em;
|
||||
padding-left: 1.5em;
|
||||
|
||||
@@ -252,6 +252,10 @@ select:active, select:hover {
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-table-button-sm-cell{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.pf-table-counter-cell{
|
||||
color: $gray-light;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -3px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ribbon {
|
||||
@@ -54,9 +55,28 @@
|
||||
@include background-image(linear-gradient(top, darken($orange, 3%), darken($orange-dark, 3%)));
|
||||
|
||||
&:before, &:after{
|
||||
border-top: 3px solid darken($orange-dark, 18%);
|
||||
border-top: 3px solid darken($orange-dark, 18%) ;
|
||||
}
|
||||
}
|
||||
|
||||
&.ribbon-red{
|
||||
background-color: $red;
|
||||
@include background-image(linear-gradient(top, darken($red, 10%), darken($red, 18%)));
|
||||
|
||||
&:before, &:after{
|
||||
border-top: 3px solid darken($red, 38%);
|
||||
}
|
||||
}
|
||||
|
||||
&.ribbon-blue{
|
||||
background-color: $blue;
|
||||
@include background-image(linear-gradient(top, darken($blue, 3%), darken($blue-dark, 3%)));
|
||||
|
||||
&:before, &:after{
|
||||
border-top: 3px solid darken($blue-dark, 18%);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ribbon:before {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pf-server-panel{
|
||||
.pf-landing-sticky-panel{
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
min-width: 100px;
|
||||
left: 10px;
|
||||
border-radius: 5px;
|
||||
padding: 7px;
|
||||
box-shadow: 0 4px 10px rgba(0,0,0,0.4);
|
||||
@@ -21,5 +19,14 @@
|
||||
text-transform: lowercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pf-landing-server-panel{
|
||||
top: 50px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
#pf-landing-admin-panel{
|
||||
bottom: 45px;
|
||||
right: 10px;
|
||||
}
|
||||
Reference in New Issue
Block a user