- ESI API switch, closed #473
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -50,3 +50,5 @@ Temporary Items
|
||||
.sass-cache
|
||||
.usage
|
||||
*.gz
|
||||
/composer-dev.lock
|
||||
/composer.lock
|
||||
|
||||
@@ -27,10 +27,12 @@ DB_CCP_USER = root
|
||||
DB_CCP_PASS =
|
||||
|
||||
; CCP SSO settings (OAuth2) - visit: https://developers.eveonline.com/applications
|
||||
CCP_CREST_URL = https://api-sisi.testeveonline.com
|
||||
SSO_CCP_URL = https://sisilogin.testeveonline.com
|
||||
SSO_CCP_CLIENT_ID =
|
||||
SSO_CCP_SECRET_KEY =
|
||||
CCP_SSO_URL = https://sisilogin.testeveonline.com
|
||||
CCP_SSO_CLIENT_ID =
|
||||
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 XML APIv2
|
||||
CCP_XML = https://api.testeveonline.com
|
||||
@@ -71,10 +73,12 @@ DB_CCP_USER =
|
||||
DB_CCP_PASS =
|
||||
|
||||
; CCP SSO settings
|
||||
CCP_CREST_URL = https://crest-tq.eveonline.com
|
||||
SSO_CCP_URL = https://login.eveonline.com
|
||||
SSO_CCP_CLIENT_ID =
|
||||
SSO_CCP_SECRET_KEY =
|
||||
CCP_SSO_URL = https://login.eveonline.com
|
||||
CCP_SSO_CLIENT_ID =
|
||||
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 XML APIv2
|
||||
CCP_XML = https://api.eveonline.com
|
||||
|
||||
@@ -385,20 +385,25 @@ class System extends Controller\AccessController {
|
||||
$return->clearOtherWaypoints = (bool)$postData['clearOtherWaypoints'];
|
||||
$return->first = (bool)$postData['first'];
|
||||
|
||||
/**
|
||||
* @var Sso $ssoController
|
||||
*/
|
||||
$ssoController = self::getController('Sso');
|
||||
foreach($postData['systemData'] as $systemData){
|
||||
$waypointData = $ssoController->setWaypoint($activeCharacter, $systemData['systemId'], [
|
||||
if( $accessToken = $activeCharacter->getAccessToken() ){
|
||||
$options = [
|
||||
'clearOtherWaypoints' => $return->clearOtherWaypoints,
|
||||
'first' => $return->first,
|
||||
]);
|
||||
if($waypointData['systemId']){
|
||||
$return->systemData[] = $systemData;
|
||||
}elseif( isset($waypointData['error']) ){
|
||||
$return->error[] = $waypointData['error'];
|
||||
'addToBeginning' => $return->first,
|
||||
];
|
||||
|
||||
foreach($postData['systemData'] as $systemData){
|
||||
$response = $f3->ccpClient->setWaypoint($systemData['systemId'], $accessToken, $options);
|
||||
|
||||
if(empty($response)){
|
||||
$return->systemData[] = $systemData;
|
||||
}else{
|
||||
$error = (object) [];
|
||||
$error->type = 'error';
|
||||
$error->message = $response['error'];
|
||||
$return->error[] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ class User extends Controller\Controller{
|
||||
}else{
|
||||
$characterError = (object) [];
|
||||
$characterError->type = 'warning';
|
||||
$characterError->message = 'This can happen through "invalid cookie data", "login restrictions", "CREST problems".';
|
||||
$characterError->message = 'This can happen through "invalid cookies(SSO)", "login restrictions", "ESI problems".';
|
||||
$return->error[] = $characterError;
|
||||
}
|
||||
}
|
||||
@@ -196,10 +196,39 @@ class User extends Controller\Controller{
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* remote open ingame information window (character, corporation or alliance) Id
|
||||
* -> the type is auto-recognized by CCP
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function openIngameWindow(\Base $f3){
|
||||
$data = $f3->get('POST');
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
if( $targetId = (int)$data['targetId']){
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
$response = $f3->ccpClient->openWindow($targetId, $activeCharacter->getAccessToken());
|
||||
|
||||
if(empty($response)){
|
||||
$return->targetId = $targetId;
|
||||
}else{
|
||||
$error = (object) [];
|
||||
$error->type = 'error';
|
||||
$error->message = $response['error'];
|
||||
$return->error[] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* update user account data
|
||||
* -> a fresh user automatically generated on first login with a new character
|
||||
* -> see CREST SSO login
|
||||
* -> see SSO login
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function saveAccount(\Base $f3){
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Date: 23.01.2016
|
||||
* Time: 17:18
|
||||
*
|
||||
* Handles access to EVE-Online "CREST API" and "SSO" auth functions
|
||||
* Handles access to EVE-Online "ESI API" and "SSO" auth functions
|
||||
* - Add your API credentials in "environment.ini"
|
||||
* - Check "PATHFINDER.API" in "pathfinder.ini" for correct API URLs
|
||||
* Hint: \Web::instance()->request automatically caches responses by their response "Cache-Control" header!
|
||||
@@ -14,7 +14,6 @@
|
||||
namespace Controller\Ccp;
|
||||
use Controller;
|
||||
use Controller\Api as Api;
|
||||
use Data\Mapper as Mapper;
|
||||
use Model;
|
||||
use Lib;
|
||||
|
||||
@@ -23,7 +22,7 @@ class Sso extends Api\User{
|
||||
/**
|
||||
* @var int timeout (seconds) for API calls
|
||||
*/
|
||||
const CREST_TIMEOUT = 4;
|
||||
const SSO_TIMEOUT = 4;
|
||||
|
||||
/**
|
||||
* @var int expire time (seconds) for an valid "accessToken"
|
||||
@@ -37,32 +36,16 @@ class Sso extends Api\User{
|
||||
const SESSION_KEY_SSO_FROM_MAP = 'SESSION.SSO.FROM_MAP';
|
||||
|
||||
// error messages
|
||||
const ERROR_CCP_SSO_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].SSO_CCP_URL" url. %s';
|
||||
const ERROR_CCP_CREST_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].CCP_CREST_URL" url. %s';
|
||||
const ERROR_CCP_CLIENT_ID = 'Missing "ENVIRONMENT.[ENVIRONMENT].SSO_CCP_CLIENT_ID".';
|
||||
const ERROR_RESOURCE_DEPRECATED = 'Resource: %s has been marked as deprecated. %s';
|
||||
const ERROR_CCP_SSO_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].CCP_SSO_URL" url. %s';
|
||||
const ERROR_CCP_CLIENT_ID = 'Missing "ENVIRONMENT.[ENVIRONMENT].CCP_SSO_CLIENT_ID".';
|
||||
const ERROR_ACCESS_TOKEN = 'Unable to get a valid "access_token. %s';
|
||||
const ERROR_VERIFY_CHARACTER = 'Unable to verify character data. %s';
|
||||
const ERROR_GET_ENDPOINT = 'Unable to get endpoint data. $s';
|
||||
const ERROR_FIND_ENDPOINT = 'Unable to find endpoint: %s';
|
||||
const ERROR_LOGIN_FAILED = 'Failed authentication due to technical problems: %s';
|
||||
const ERROR_CHARACTER_VERIFICATION = 'Character verification failed from CREST';
|
||||
const ERROR_CHARACTER_VERIFICATION = 'Character verification failed by SSP SSO';
|
||||
const ERROR_CHARACTER_FORBIDDEN = 'Character "%s" is not authorized to log in';
|
||||
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';
|
||||
|
||||
/**
|
||||
* CREST "Scopes" are used by pathfinder
|
||||
* -> Enable scopes: https://developers.eveonline.com
|
||||
* @var array
|
||||
*/
|
||||
private $requestScopes = [
|
||||
// 'characterFittingsRead',
|
||||
// 'characterFittingsWrite',
|
||||
'characterLocationRead',
|
||||
'characterNavigationWrite'
|
||||
];
|
||||
|
||||
/**
|
||||
* redirect user to CCP SSO page and request authorization
|
||||
* -> cf. Controller->getCookieCharacters() ( equivalent cookie based login)
|
||||
@@ -70,7 +53,7 @@ class Sso extends Api\User{
|
||||
*/
|
||||
public function requestAuthorization($f3){
|
||||
|
||||
if( !empty($ssoCcpClientId = Controller\Controller::getEnvironmentData('SSO_CCP_CLIENT_ID')) ){
|
||||
if( !empty($ssoCcpClientId = Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID')) ){
|
||||
$params = $f3->get('GET');
|
||||
|
||||
if(
|
||||
@@ -94,8 +77,8 @@ class Sso extends Api\User{
|
||||
($activeCharacter->getUser()->_id === $character->getUser()->_id)
|
||||
){
|
||||
// requested character belongs to current user
|
||||
// -> update character vom CREST (e.g. corp changed,..)
|
||||
$updateStatus = $character->updateFromCrest();
|
||||
// -> update character vom ESI (e.g. corp changed,..)
|
||||
$updateStatus = $character->updateFromESI();
|
||||
|
||||
if( empty($updateStatus) ){
|
||||
|
||||
@@ -111,7 +94,7 @@ class Sso extends Api\User{
|
||||
|
||||
if($loginCheck){
|
||||
// set "login" cookie
|
||||
$this->setLoginCookie($character);
|
||||
$this->setLoginCookie($character, $this->getRequestedScopeHash());
|
||||
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
|
||||
@@ -136,8 +119,8 @@ class Sso extends Api\User{
|
||||
$urlParams = [
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => Controller\Controller::getEnvironmentData('URL') . $f3->build('/sso/callbackAuthorization'),
|
||||
'client_id' => Controller\Controller::getEnvironmentData('SSO_CCP_CLIENT_ID'),
|
||||
'scope' => implode(' ', $this->requestScopes),
|
||||
'client_id' => Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID'),
|
||||
'scope' => implode(' ', Controller\Controller::getEnvironmentData('CCP_ESI_SCOPES')),
|
||||
'state' => $state
|
||||
];
|
||||
|
||||
@@ -149,7 +132,7 @@ class Sso extends Api\User{
|
||||
}else{
|
||||
// SSO clientId missing
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, self::ERROR_CCP_CLIENT_ID);
|
||||
self::getCrestLogger()->write(self::ERROR_CCP_CLIENT_ID);
|
||||
self::getSSOLogger()->write(self::ERROR_CCP_CLIENT_ID);
|
||||
$f3->reroute('@login');
|
||||
}
|
||||
}
|
||||
@@ -199,11 +182,11 @@ class Sso extends Api\User{
|
||||
|
||||
// verification available data. Data is needed for "ownerHash" check
|
||||
|
||||
// get character data from CREST
|
||||
$characterData = $this->getCharacterData($accessData->accessToken);
|
||||
// get character data from ESI
|
||||
$characterData = $this->getCharacterData($verificationCharacterData->CharacterID);
|
||||
|
||||
if( isset($characterData->character) ){
|
||||
// add "ownerHash" and CREST tokens
|
||||
// add "ownerHash" and SSO tokens
|
||||
$characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash;
|
||||
$characterData->character['crestAccessToken'] = $accessData->accessToken;
|
||||
$characterData->character['crestRefreshToken'] = $accessData->refreshToken;
|
||||
@@ -253,7 +236,7 @@ class Sso extends Api\User{
|
||||
|
||||
if($loginCheck){
|
||||
// set "login" cookie
|
||||
$this->setLoginCookie($characterModel);
|
||||
$this->setLoginCookie($characterModel, $this->getRequestedScopeHash());
|
||||
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $characterModel->_id);
|
||||
@@ -270,15 +253,15 @@ class Sso extends Api\User{
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// failed to verify character by CREST
|
||||
// failed to verify character by CCP SSO
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, self::ERROR_CHARACTER_VERIFICATION);
|
||||
}
|
||||
}else{
|
||||
// CREST "accessData" missing (e.g. timeout)
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_SERVICE_TIMEOUT, self::CREST_TIMEOUT));
|
||||
// SSO "accessData" missing (e.g. timeout)
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_SERVICE_TIMEOUT, self::SSO_TIMEOUT));
|
||||
}
|
||||
}else{
|
||||
// invalid CREST response
|
||||
// invalid SSO response
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_LOGIN_FAILED, 'Invalid response'));
|
||||
}
|
||||
}
|
||||
@@ -345,7 +328,7 @@ class Sso extends Api\User{
|
||||
$accessData = $this->verifyAuthorizationCode($authCode);
|
||||
}else{
|
||||
// Unable to get Token -> trigger error
|
||||
self::getCrestLogger()->write(sprintf(self::ERROR_ACCESS_TOKEN, $authCode));
|
||||
self::getSSOLogger()->write(sprintf(self::ERROR_ACCESS_TOKEN, $authCode));
|
||||
}
|
||||
|
||||
return $accessData;
|
||||
@@ -398,7 +381,7 @@ class Sso extends Api\User{
|
||||
if($verifyAuthCodeUrlParts){
|
||||
$contentType = 'application/x-www-form-urlencoded';
|
||||
$requestOptions = [
|
||||
'timeout' => self::CREST_TIMEOUT,
|
||||
'timeout' => self::SSO_TIMEOUT,
|
||||
'method' => 'POST',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'header' => [
|
||||
@@ -428,7 +411,7 @@ class Sso extends Api\User{
|
||||
}
|
||||
}
|
||||
}else{
|
||||
self::getCrestLogger()->write(
|
||||
self::getSSOLogger()->write(
|
||||
sprintf(
|
||||
self::ERROR_ACCESS_TOKEN,
|
||||
print_r($requestParams, true)
|
||||
@@ -436,7 +419,7 @@ class Sso extends Api\User{
|
||||
);
|
||||
}
|
||||
}else{
|
||||
self::getCrestLogger()->write(
|
||||
self::getSSOLogger()->write(
|
||||
sprintf(self::ERROR_CCP_SSO_URL, __METHOD__)
|
||||
);
|
||||
}
|
||||
@@ -447,7 +430,7 @@ class Sso extends Api\User{
|
||||
/**
|
||||
* verify character data by "access_token"
|
||||
* -> get some basic information (like character id)
|
||||
* -> if more character information is required, use CREST endpoints request instead
|
||||
* -> if more character information is required, use ESI "characters" endpoints request instead
|
||||
* @param $accessToken
|
||||
* @return mixed|null
|
||||
*/
|
||||
@@ -458,7 +441,7 @@ class Sso extends Api\User{
|
||||
|
||||
if($verifyUrlParts){
|
||||
$requestOptions = [
|
||||
'timeout' => self::CREST_TIMEOUT,
|
||||
'timeout' => self::SSO_TIMEOUT,
|
||||
'method' => 'GET',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'header' => [
|
||||
@@ -472,271 +455,83 @@ class Sso extends Api\User{
|
||||
if($apiResponse['body']){
|
||||
$characterData = json_decode($apiResponse['body']);
|
||||
}else{
|
||||
self::getCrestLogger()->write(sprintf(self::ERROR_VERIFY_CHARACTER, __METHOD__));
|
||||
self::getSSOLogger()->write(sprintf(self::ERROR_VERIFY_CHARACTER, __METHOD__));
|
||||
}
|
||||
}else{
|
||||
self::getCrestLogger()->write(sprintf(self::ERROR_CCP_SSO_URL, __METHOD__));
|
||||
self::getSSOLogger()->write(sprintf(self::ERROR_CCP_SSO_URL, __METHOD__));
|
||||
}
|
||||
|
||||
return $characterData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all available Endpoints
|
||||
* @param $accessToken
|
||||
* @param array $additionalOptions
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function getEndpoints($accessToken = '', $additionalOptions = []){
|
||||
$crestUrl = self::getCrestEndpoint();
|
||||
$additionalOptions['accept'] = 'application/vnd.ccp.eve.Api-v5+json';
|
||||
$endpoint = $this->getEndpoint($crestUrl, $accessToken, $additionalOptions);
|
||||
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a specific endpoint by its $resourceUrl
|
||||
* @param string $resourceUrl endpoint API url
|
||||
* @param string $accessToken CREST access token
|
||||
* @param array $additionalOptions optional request options (pathfinder specific)
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function getEndpoint($resourceUrl, $accessToken = '', $additionalOptions = []){
|
||||
$resourceUrlParts = parse_url($resourceUrl);
|
||||
$endpoint = null;
|
||||
|
||||
if($resourceUrlParts){
|
||||
$requestOptions = [
|
||||
'timeout' => self::CREST_TIMEOUT,
|
||||
'method' => 'GET',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'header' => [
|
||||
'Host: login.eveonline.com',
|
||||
'Host: ' . $resourceUrlParts['host']
|
||||
]
|
||||
];
|
||||
|
||||
// some endpoints don´t require an "access_token" (e.g. public crest data)
|
||||
if( !empty($accessToken) ){
|
||||
$requestOptions['header'][] = 'Authorization: Bearer ' . $accessToken;
|
||||
}
|
||||
|
||||
// if specific contentType is required -> add it to request header
|
||||
// CREST versioning can be done by calling different "Accept:" Headers
|
||||
if( isset($additionalOptions['accept']) ){
|
||||
$requestOptions['header'][] = 'Accept: ' . $additionalOptions['accept'];
|
||||
}
|
||||
|
||||
$apiResponse = Lib\Web::instance()->request($resourceUrl, $requestOptions, $additionalOptions);
|
||||
|
||||
if(
|
||||
$apiResponse['timeout'] === false &&
|
||||
$apiResponse['headers']
|
||||
){
|
||||
// check headers for error
|
||||
$this->checkResponseHeaders($apiResponse['headers'], $requestOptions);
|
||||
|
||||
if($apiResponse['body']){
|
||||
$endpoint = json_decode($apiResponse['body'], true);
|
||||
}else{
|
||||
self::getCrestLogger()->write(sprintf(self::ERROR_GET_ENDPOINT, __METHOD__));
|
||||
}
|
||||
}
|
||||
}else{
|
||||
self::getCrestLogger()->write(sprintf(self::ERROR_CCP_CREST_URL, __METHOD__));
|
||||
}
|
||||
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* recursively walk down the CREST API tree by a given $path array
|
||||
* -> return "leaf" endpoint
|
||||
* @param $endpoint
|
||||
* @param $accessToken
|
||||
* @param array $path
|
||||
* @param array $additionalOptions
|
||||
* @return null
|
||||
*/
|
||||
protected function walkEndpoint($endpoint, $accessToken, $path = [], $additionalOptions = []){
|
||||
$targetEndpoint = null;
|
||||
|
||||
if( !empty($path) ){
|
||||
$newNode = array_shift($path);
|
||||
if(isset($endpoint[$newNode])){
|
||||
$currentEndpoint = $endpoint[$newNode];
|
||||
if(isset($currentEndpoint['href'])){
|
||||
$newEndpoint = $this->getEndpoint($currentEndpoint['href'], $accessToken, $additionalOptions);
|
||||
$targetEndpoint = $this->walkEndpoint($newEndpoint, $accessToken, $path, $additionalOptions);
|
||||
}else{
|
||||
// leaf found
|
||||
$targetEndpoint = $currentEndpoint;
|
||||
}
|
||||
}else{
|
||||
// endpoint not found
|
||||
self::getCrestLogger()->write(sprintf(self::ERROR_FIND_ENDPOINT, $newNode));
|
||||
}
|
||||
}else{
|
||||
$targetEndpoint = $endpoint;
|
||||
}
|
||||
|
||||
return $targetEndpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* get character data
|
||||
* @param $accessToken
|
||||
* @param array $additionalOptions
|
||||
* @param int $characterId
|
||||
* @return object
|
||||
*/
|
||||
public function getCharacterData($accessToken, $additionalOptions = []){
|
||||
$endpoints = $this->getEndpoints($accessToken, $additionalOptions);
|
||||
public function getCharacterData($characterId){
|
||||
$characterData = (object) [];
|
||||
|
||||
$endpoint = $this->walkEndpoint($endpoints, $accessToken, [
|
||||
'decode',
|
||||
'character'
|
||||
], $additionalOptions);
|
||||
$characterDataBasic = $this->getF3()->ccpClient->getCharacterData($characterId);
|
||||
|
||||
if( !empty($endpoint) ){
|
||||
$crestCharacterData = (new Mapper\CrestCharacter($endpoint))->getData();
|
||||
$characterData->character = $crestCharacterData
|
||||
;
|
||||
if(isset($endpoint['corporation'])){
|
||||
$characterData->corporation = (new Mapper\CrestCorporation($endpoint['corporation']))->getData();
|
||||
}
|
||||
if( !empty($characterDataBasic) ){
|
||||
// remove some "unwanted" data -> not relevant for Pathfinder
|
||||
$characterData->character = array_filter($characterDataBasic, function($key){
|
||||
return in_array($key, ['id', 'name', 'securityStatus']);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
// IMPORTANT: alliance data is not yet available over CREST!
|
||||
// -> we need to request them over the XML api
|
||||
/*
|
||||
if(isset($endpoint['alliance'])){
|
||||
$characterData->alliance = (new Mapper\CrestAlliance($endpoint['alliance']))->getData();
|
||||
}
|
||||
*/
|
||||
$characterData->corporation = null;
|
||||
$characterData->alliance = null;
|
||||
|
||||
$xmlCharacterData = (new Xml())->getPublicCharacterData( (int)$crestCharacterData['id'] );
|
||||
if(isset($xmlCharacterData['alli'])){
|
||||
$characterData->alliance = $xmlCharacterData['alli'];
|
||||
}
|
||||
}
|
||||
if(isset($characterDataBasic['corporation'])){
|
||||
$corporationId = (int)$characterDataBasic['corporation']['id'];
|
||||
|
||||
return $characterData;
|
||||
}
|
||||
/**
|
||||
* @var Model\CorporationModel $corporationModel
|
||||
*/
|
||||
$corporationModel = Model\BasicModel::getNew('CorporationModel');
|
||||
$corporationModel->getById($corporationId, 0);
|
||||
|
||||
/**
|
||||
* get current character location data (result is cached!)
|
||||
* -> solarSystem data where character is currently active
|
||||
* @param $accessToken
|
||||
* @param array $additionalOptions
|
||||
* @return array
|
||||
*/
|
||||
public function getCharacterLocationData($accessToken, $additionalOptions = []){
|
||||
// null == CREST call failed (e.g. timeout)
|
||||
$locationData = [
|
||||
'timeout' => false
|
||||
];
|
||||
if($corporationModel->dry()){
|
||||
// request corporation data
|
||||
$corporationData = $this->getF3()->ccpClient->getCorporationData($corporationId);
|
||||
|
||||
$endpoints = $this->getEndpoints($accessToken, $additionalOptions);
|
||||
if( !empty($corporationData) ){
|
||||
// check for NPC corporation
|
||||
$corporationData['isNPC'] = $this->getF3()->ccpClient->isNpcCorporation($corporationId);
|
||||
|
||||
$additionalOptions['accept'] = 'application/vnd.ccp.eve.CharacterLocation-v1+json';
|
||||
$endpoint = $this->walkEndpoint($endpoints, $accessToken, [
|
||||
'decode',
|
||||
'character',
|
||||
'location'
|
||||
], $additionalOptions);
|
||||
|
||||
if( !is_null($endpoint) ){
|
||||
// request succeeded (e.g. no timeout)
|
||||
if(isset($endpoint['solarSystem'])){
|
||||
$locationData['system'] = (new Mapper\CrestSystem($endpoint['solarSystem']))->getData();
|
||||
}
|
||||
|
||||
if(isset($endpoint['station'])){
|
||||
$locationData['station'] = (new Mapper\CrestStation($endpoint['station']))->getData();
|
||||
}
|
||||
}else{
|
||||
// timeout
|
||||
$locationData['timeout'] = true;
|
||||
}
|
||||
|
||||
return $locationData;
|
||||
}
|
||||
|
||||
/**
|
||||
* set new ingame waypoint
|
||||
* @param Model\CharacterModel $character
|
||||
* @param int $systemId
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function setWaypoint( Model\CharacterModel $character, $systemId, $options = []){
|
||||
$crestUrlParts = parse_url( self::getCrestEndpoint() );
|
||||
$waypointData = [];
|
||||
|
||||
if( $crestUrlParts ){
|
||||
$accessToken = $character->getAccessToken();
|
||||
$endpoints = $this->getEndpoints($accessToken);
|
||||
|
||||
// get endpoint list for "ui" endpoints
|
||||
$uiEndpoints = $endpoint = $this->walkEndpoint($endpoints, $accessToken, [
|
||||
'decode',
|
||||
'character',
|
||||
'ui'
|
||||
]);
|
||||
|
||||
if(
|
||||
isset($uiEndpoints['setWaypoints']) &&
|
||||
isset($uiEndpoints['setWaypoints']['href'])
|
||||
){
|
||||
$endpointUrl = $uiEndpoints['setWaypoints']['href'];
|
||||
$systemEndpoint = self::getCrestEndpoint() . '/solarsystems/' . $systemId . '/';
|
||||
|
||||
// request body
|
||||
$content = [
|
||||
'clearOtherWaypoints' => (bool)$options['clearOtherWaypoints'],
|
||||
'first' => (bool)$options['first'],
|
||||
'solarSystem' => [
|
||||
'href' => $systemEndpoint,
|
||||
'id' => (int)$systemId
|
||||
]
|
||||
];
|
||||
|
||||
$requestOptions = [
|
||||
'timeout' => self::CREST_TIMEOUT,
|
||||
'method' => 'POST',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'header' => [
|
||||
'Scope: characterNavigationWrite',
|
||||
'Authorization: Bearer ' . $character->getAccessToken(),
|
||||
'Host: ' . $crestUrlParts['host'],
|
||||
'Content-Type: application/vnd.ccp.eve.PostWaypoint-v1+json;charset=utf-8',
|
||||
],
|
||||
'content' => json_encode($content, JSON_UNESCAPED_SLASHES)
|
||||
];
|
||||
|
||||
$apiResponse = Lib\Web::instance()->request($endpointUrl, $requestOptions);
|
||||
|
||||
if( isset($apiResponse['body']) ){
|
||||
$responseData = json_decode($apiResponse['body']);
|
||||
|
||||
if( empty($responseData) ){
|
||||
$waypointData['systemId'] = (int)$systemId;
|
||||
}elseif(
|
||||
isset($responseData->message) &&
|
||||
isset($responseData->key)
|
||||
){
|
||||
// waypoint could not be set...
|
||||
$error = (object) [];
|
||||
$error->type = 'error';
|
||||
$error->message = $responseData->key;
|
||||
$waypointData['error'] = $error;
|
||||
$corporationModel->copyfrom($corporationData, ['id', 'name', 'isNPC']);
|
||||
$characterData->corporation = $corporationModel->save();
|
||||
}
|
||||
}else{
|
||||
$characterData->corporation = $corporationModel;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($characterDataBasic['alliance'])){
|
||||
$allianceId = (int)$characterDataBasic['alliance']['id'];
|
||||
|
||||
/**
|
||||
* @var Model\AllianceModel $allianceModel
|
||||
*/
|
||||
$allianceModel = Model\BasicModel::getNew('AllianceModel');
|
||||
$allianceModel->getById($allianceId, 0);
|
||||
|
||||
if($allianceModel->dry()){
|
||||
// request alliance data
|
||||
$allianceData = $this->getF3()->ccpClient->getAllianceData($allianceId);
|
||||
|
||||
if( !empty($allianceData) ){
|
||||
$allianceModel->copyfrom($allianceData, ['id', 'name']);
|
||||
$characterData->alliance = $allianceModel->save();
|
||||
}
|
||||
}else{
|
||||
$characterData->alliance = $allianceModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $waypointData;
|
||||
return $characterData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -746,96 +541,24 @@ class Sso extends Api\User{
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function updateCharacter($characterData){
|
||||
|
||||
$characterModel = null;
|
||||
$corporationModel = null;
|
||||
$allianceModel = null;
|
||||
|
||||
if( isset($characterData->corporation) ){
|
||||
/**
|
||||
* @var Model\CorporationModel $corporationModel
|
||||
*/
|
||||
$corporationModel = Model\BasicModel::getNew('CorporationModel');
|
||||
$corporationModel->getById((int)$characterData->corporation['id'], 0);
|
||||
$corporationModel->copyfrom($characterData->corporation);
|
||||
$corporationModel->save();
|
||||
}
|
||||
if( !empty($characterData->character) ){
|
||||
|
||||
if( isset($characterData->alliance) ){
|
||||
/**
|
||||
* @var Model\AllianceModel $allianceModel
|
||||
*/
|
||||
$allianceModel = Model\BasicModel::getNew('AllianceModel');
|
||||
$allianceModel->getById((int)$characterData->alliance['id'], 0);
|
||||
$allianceModel->copyfrom($characterData->alliance);
|
||||
$allianceModel->save();
|
||||
}
|
||||
|
||||
if( isset($characterData->character) ){
|
||||
/**
|
||||
* @var Model\CharacterModel $characterModel
|
||||
*/
|
||||
$characterModel = Model\BasicModel::getNew('CharacterModel');
|
||||
$characterModel->getById((int)$characterData->character['id'], 0);
|
||||
$characterModel->copyfrom($characterData->character);
|
||||
$characterModel->corporationId = $corporationModel;
|
||||
$characterModel->allianceId = $allianceModel;
|
||||
$characterModel->copyfrom($characterData->character, ['id', 'name', 'ownerHash', 'crestAccessToken', 'crestRefreshToken', 'securityStatus']);
|
||||
$characterModel->corporationId = $characterData->corporation;
|
||||
$characterModel->allianceId = $characterData->alliance;
|
||||
$characterModel = $characterModel->save();
|
||||
}
|
||||
|
||||
return $characterModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* get CREST server status (online/offline)
|
||||
* @return \stdClass object
|
||||
*/
|
||||
public function getCrestServerStatus(){
|
||||
$endpoints = $this->getEndpoints();
|
||||
|
||||
// set default status e.g. Endpoints don´t work
|
||||
$data = (object) [];
|
||||
$data->crestOffline = true;
|
||||
$data->serverName = 'EVE ONLINE';
|
||||
$data->serviceStatus = [
|
||||
'eve' => 'offline',
|
||||
'server' => 'offline',
|
||||
];
|
||||
$data->userCounts = [
|
||||
'eve' => 0
|
||||
];
|
||||
|
||||
$endpoint = $this->walkEndpoint($endpoints, '', ['serverName']);
|
||||
if( !empty($endpoint) ){
|
||||
$data->crestOffline = false;
|
||||
$data->serverName = (string) $endpoint;
|
||||
}
|
||||
$endpoint = $this->walkEndpoint($endpoints, '', ['serviceStatus']);
|
||||
if( !empty($endpoint) ){
|
||||
$data->crestOffline = false;
|
||||
$data->serviceStatus = (string) $endpoint;
|
||||
}
|
||||
$endpoint = $this->walkEndpoint($endpoints, '', ['userCount_str']);
|
||||
if( !empty($endpoint) ){
|
||||
$data->crestOffline = false;
|
||||
$data->userCounts = (string) $endpoint;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* check response "Header" data for errors
|
||||
* @param $headers
|
||||
* @param string $requestUrl
|
||||
* @param string $contentType
|
||||
*/
|
||||
protected function checkResponseHeaders($headers, $requestUrl = '', $contentType = ''){
|
||||
$headers = (array)$headers;
|
||||
if( preg_grep('/^X-Deprecated/i', $headers) ){
|
||||
self::getCrestLogger()->write(sprintf(self::ERROR_RESOURCE_DEPRECATED, $requestUrl, $contentType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get "Authorization:" Header data
|
||||
* -> This header is required for any Auth-required endpoints!
|
||||
@@ -843,29 +566,11 @@ class Sso extends Api\User{
|
||||
*/
|
||||
protected function getAuthorizationHeader(){
|
||||
return base64_encode(
|
||||
Controller\Controller::getEnvironmentData('SSO_CCP_CLIENT_ID') . ':'
|
||||
. Controller\Controller::getEnvironmentData('SSO_CCP_SECRET_KEY')
|
||||
Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID') . ':'
|
||||
. Controller\Controller::getEnvironmentData('CCP_SSO_SECRET_KEY')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get CCP CREST url from configuration file
|
||||
* -> throw error if url is broken/missing
|
||||
* @return string
|
||||
*/
|
||||
static function getCrestEndpoint(){
|
||||
$url = '';
|
||||
if( \Audit::instance()->url(self::getEnvironmentData('CCP_CREST_URL')) ){
|
||||
$url = self::getEnvironmentData('CCP_CREST_URL');
|
||||
}else{
|
||||
$error = sprintf(self::ERROR_CCP_CREST_URL, __METHOD__);
|
||||
self::getCrestLogger()->write($error);
|
||||
\Base::instance()->error(502, $error);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* get CCP SSO url from configuration file
|
||||
* -> throw error if url is broken/missing
|
||||
@@ -873,11 +578,11 @@ class Sso extends Api\User{
|
||||
*/
|
||||
static function getSsoUrlRoot(){
|
||||
$url = '';
|
||||
if( \Audit::instance()->url(self::getEnvironmentData('SSO_CCP_URL')) ){
|
||||
$url = self::getEnvironmentData('SSO_CCP_URL');
|
||||
if( \Audit::instance()->url(self::getEnvironmentData('CCP_SSO_URL')) ){
|
||||
$url = self::getEnvironmentData('CCP_SSO_URL');
|
||||
}else{
|
||||
$error = sprintf(self::ERROR_CCP_SSO_URL, __METHOD__);
|
||||
self::getCrestLogger()->write($error);
|
||||
self::getSSOLogger()->write($error);
|
||||
\Base::instance()->error(502, $error);
|
||||
}
|
||||
|
||||
@@ -897,10 +602,10 @@ class Sso extends Api\User{
|
||||
}
|
||||
|
||||
/**
|
||||
* get logger for CREST logging
|
||||
* get logger for SSO logging
|
||||
* @return \Log
|
||||
*/
|
||||
static function getCrestLogger(){
|
||||
return parent::getLogger('CREST');
|
||||
static function getSSOLogger(){
|
||||
return parent::getLogger('SSO');
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 23.05.2016
|
||||
* Time: 18:25
|
||||
*/
|
||||
|
||||
namespace controller\ccp;
|
||||
use Data\Mapper as Mapper;
|
||||
use Controller;
|
||||
use Lib;
|
||||
|
||||
|
||||
class Xml extends Controller\Controller{
|
||||
|
||||
/**
|
||||
* get HTTP request options for API (curl) request
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequestOptions(){
|
||||
$requestOptions = [
|
||||
'timeout' => 4,
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
|
||||
];
|
||||
|
||||
return $requestOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* request character data from CCP API
|
||||
* @param int $characterId
|
||||
* @return array
|
||||
*/
|
||||
public function getPublicCharacterData($characterId){
|
||||
$characterData = [];
|
||||
$apiPath = self::getEnvironmentData('CCP_XML') . '/eve/CharacterInfo.xml.aspx';
|
||||
|
||||
$baseOptions = $this->getRequestOptions();
|
||||
$requestOptions = [
|
||||
'method' => 'GET',
|
||||
'content' => [
|
||||
'characterID' => (int)$characterId
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
$requestOptions = array_merge($baseOptions, $requestOptions);
|
||||
$apiResponse = Lib\Web::instance()->request($apiPath, $requestOptions );
|
||||
if(
|
||||
$apiResponse['body'] &&
|
||||
($xml = simplexml_load_string($apiResponse['body']))
|
||||
){
|
||||
|
||||
if(
|
||||
isset($xml->result) &&
|
||||
is_object($rowApiData = $xml->result->children())
|
||||
){
|
||||
foreach($rowApiData as $item){
|
||||
// map attributes to array
|
||||
if(count($item->children()) == 0){
|
||||
$characterData[$item->getName()] = strval($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = (new Mapper\CcpCharacterMapper($characterData))->getData();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -189,8 +189,9 @@ class Controller {
|
||||
* set/update logged in cookie by character model
|
||||
* -> store validation data in DB
|
||||
* @param Model\CharacterModel $character
|
||||
* @param string $scopeHash
|
||||
*/
|
||||
protected function setLoginCookie(Model\CharacterModel $character){
|
||||
protected function setLoginCookie(Model\CharacterModel $character, $scopeHash = ''){
|
||||
|
||||
if( $this->getCookieState() ){
|
||||
$expireSeconds = (int) $this->getF3()->get('PATHFINDER.LOGIN.COOKIE_EXPIRE');
|
||||
@@ -222,7 +223,8 @@ class Controller {
|
||||
'characterId' => $character,
|
||||
'selector' => $selector,
|
||||
'token' => $token,
|
||||
'expires' => $expireTime->format('Y-m-d H:i:s')
|
||||
'expires' => $expireTime->format('Y-m-d H:i:s'),
|
||||
'scopeHash' => $scopeHash
|
||||
];
|
||||
|
||||
$authenticationModel = $character->rel('characterAuthentications');
|
||||
@@ -270,20 +272,20 @@ class Controller {
|
||||
// cookie data is well formatted
|
||||
$characterAuth->getByForeignKey('selector', $data[0], ['limit' => 1]);
|
||||
|
||||
// validate expire data
|
||||
// validate token
|
||||
// validate "scope hash", "expire data" and "validate token"
|
||||
if( !$characterAuth->dry() ){
|
||||
if(
|
||||
$characterAuth->scopeHash === $this->getRequestedScopeHash() &&
|
||||
strtotime($characterAuth->expires) >= $currentTime->getTimestamp() &&
|
||||
hash_equals($characterAuth->token, hash('sha256', $data[1]))
|
||||
){
|
||||
// cookie information is valid
|
||||
// -> try to update character information from CREST
|
||||
// -> try to update character information from ESI
|
||||
// e.g. Corp has changed, this also ensures valid "access_token"
|
||||
/**
|
||||
* @var $character Model\CharacterModel
|
||||
*/
|
||||
$updateStatus = $characterAuth->characterId->updateFromCrest();
|
||||
$updateStatus = $characterAuth->characterId->updateFromESI();
|
||||
|
||||
if( empty($updateStatus) ){
|
||||
// make sure character data is up2date!
|
||||
@@ -293,13 +295,15 @@ 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. CREST is down,..)
|
||||
// -> do NOT remove cookie on failure. This can be a temporary problem (e.g. ESI is down,..)
|
||||
if(
|
||||
$character->hasUserCharacter() &&
|
||||
$character->isAuthorized()
|
||||
){
|
||||
$characters[$name] = $character;
|
||||
}
|
||||
}else{
|
||||
$invalidCookie = true;
|
||||
}
|
||||
}else{
|
||||
// clear existing authentication data from DB
|
||||
@@ -449,6 +453,15 @@ class Controller {
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a hash over all requested ESI scopes
|
||||
* -> this helps to invalidate "authentication data" after scope change
|
||||
* @return string
|
||||
*/
|
||||
protected function getRequestedScopeHash(){
|
||||
return md5(serialize( self::getEnvironmentData('CCP_ESI_SCOPES') ));
|
||||
}
|
||||
|
||||
/**
|
||||
* log out current character
|
||||
* @param \Base $f3
|
||||
@@ -477,16 +490,33 @@ class Controller {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function getEveServerStatus(\Base $f3){
|
||||
// server status can be cached for some seconds
|
||||
$cacheKey = 'eve_server_status';
|
||||
if( !$f3->exists($cacheKey, $return) ){
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->status = [
|
||||
'serverName' => strtoupper( self::getEnvironmentData('CCP_ESI_DATASOURCE') ),
|
||||
'serviceStatus' => 'offline'
|
||||
];
|
||||
|
||||
$sso = new Sso();
|
||||
$return->status = $sso->getCrestServerStatus();
|
||||
$response = $f3->ccpClient->getServerStatus();
|
||||
|
||||
if( !empty($response) ){
|
||||
// calculate time diff since last server restart
|
||||
$timezone = new \DateTimeZone( $f3->get('TZ') );
|
||||
$dateNow = new \DateTime('now', $timezone);
|
||||
$dateServerStart = new \DateTime($response['startTime']);
|
||||
$interval = $dateNow->diff($dateServerStart);
|
||||
$startTimestampFormat = $interval->format('%hh %im');
|
||||
if($interval->days > 0){
|
||||
$startTimestampFormat = $interval->days . 'd ' . $startTimestampFormat;
|
||||
}
|
||||
|
||||
$response['serverName'] = strtoupper( self::getEnvironmentData('CCP_ESI_DATASOURCE') );
|
||||
$response['serviceStatus'] = 'online';
|
||||
$response['startTime'] = $startTimestampFormat;
|
||||
$return->status = $response;
|
||||
|
||||
if( !$return->status->crestOffline ){
|
||||
$f3->set($cacheKey, $return, 60);
|
||||
}
|
||||
}
|
||||
@@ -797,7 +827,7 @@ class Controller {
|
||||
/**
|
||||
* get environment specific configuration data
|
||||
* @param string $key
|
||||
* @return string|null
|
||||
* @return string|array|null
|
||||
*/
|
||||
static function getEnvironmentData($key){
|
||||
return Config::getEnvironmentData($key);
|
||||
|
||||
@@ -34,10 +34,11 @@ class Setup extends Controller {
|
||||
'DB_CCP_NAME',
|
||||
'DB_CCP_USER',
|
||||
'DB_CCP_PASS',
|
||||
'CCP_CREST_URL',
|
||||
'SSO_CCP_URL',
|
||||
'SSO_CCP_CLIENT_ID',
|
||||
'SSO_CCP_SECRET_KEY',
|
||||
'CCP_SSO_URL',
|
||||
'CCP_SSO_CLIENT_ID',
|
||||
'CCP_SSO_SECRET_KEY',
|
||||
'CCP_ESI_URL',
|
||||
'CCP_ESI_DATASOURCE',
|
||||
'CCP_XML',
|
||||
'SMTP_HOST',
|
||||
'SMTP_PORT',
|
||||
@@ -183,6 +184,8 @@ class Setup extends Controller {
|
||||
$this->exportTable($params['exportTable']);
|
||||
}elseif( !empty($params['clearCache']) ){
|
||||
$this->clearCache($f3);
|
||||
}elseif( !empty($params['invalidateCookies']) ){
|
||||
$this->invalidateCookies($f3);
|
||||
}
|
||||
|
||||
// set template data ----------------------------------------------------------------
|
||||
@@ -314,7 +317,7 @@ class Setup extends Controller {
|
||||
];
|
||||
|
||||
// obscure some values
|
||||
$obscureVars = ['SSO_CCP_CLIENT_ID', 'SSO_CCP_SECRET_KEY', 'SMTP_PASS'];
|
||||
$obscureVars = ['CCP_SSO_CLIENT_ID', 'CCP_SSO_SECRET_KEY', 'SMTP_PASS'];
|
||||
|
||||
foreach($this->environmentVars as $var){
|
||||
if( !in_array($var, $excludeVars) ){
|
||||
@@ -1203,6 +1206,21 @@ class Setup extends Controller {
|
||||
$f3->clear('CACHE');
|
||||
}
|
||||
|
||||
/**
|
||||
* clear all character authentication (Cookie) data
|
||||
* @param \Base $f3
|
||||
*/
|
||||
protected function invalidateCookies(\Base $f3){
|
||||
$this->getDB('PF');
|
||||
$authentidationModel = Model\BasicModel::getNew('CharacterAuthenticationModel');
|
||||
$results = $authentidationModel->find();
|
||||
if($results){
|
||||
foreach($results as $result){
|
||||
$result->erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert Bytes to string + suffix
|
||||
* @param int $bytes
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 23.05.2016
|
||||
* Time: 20:32
|
||||
*/
|
||||
|
||||
namespace data\mapper;
|
||||
|
||||
|
||||
class CcpCharacterMapper extends AbstractIterator {
|
||||
|
||||
|
||||
protected static $map = [
|
||||
'characterID' => ['character' => 'id'],
|
||||
'characterName' => ['character' => 'name'],
|
||||
|
||||
'race' => 'race',
|
||||
|
||||
'bloodlineID' => ['blood' => 'id'],
|
||||
'bloodline' => ['blood' => 'name'],
|
||||
|
||||
'ancestryID' => ['origin' => 'id'],
|
||||
'ancestry' => ['origin' => 'name'],
|
||||
|
||||
'corporationID' => ['corp' => 'id'],
|
||||
'corporation' => ['corp' => 'name'],
|
||||
'corporationDate' => ['corp' => 'date'],
|
||||
|
||||
'allianceID' => ['alli' => 'id'],
|
||||
'alliance' => ['alli' => 'name'],
|
||||
'allianceDate' => ['alli' => 'date'],
|
||||
|
||||
'securityStatus' => 'security'
|
||||
];
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 15.05.2016
|
||||
* Time: 22:04
|
||||
*/
|
||||
|
||||
namespace Data\Mapper;
|
||||
|
||||
|
||||
class CrestAlliance extends AbstractIterator {
|
||||
|
||||
protected static $map = [
|
||||
'id' => 'id',
|
||||
'name' => 'name'
|
||||
];
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 31.01.2016
|
||||
* Time: 19:09
|
||||
*/
|
||||
|
||||
namespace Data\Mapper;
|
||||
|
||||
|
||||
class CrestCharacter extends AbstractIterator {
|
||||
|
||||
protected static $map = [
|
||||
'id' => 'id',
|
||||
'name' => 'name'
|
||||
];
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 31.01.2016
|
||||
* Time: 03:55
|
||||
*/
|
||||
|
||||
namespace Data\Mapper;
|
||||
|
||||
|
||||
class CrestCorporation extends AbstractIterator {
|
||||
|
||||
protected static $map = [
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
'isNPC' => 'isNPC'
|
||||
];
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 09.04.2016
|
||||
* Time: 21:11
|
||||
*/
|
||||
|
||||
namespace Data\Mapper;
|
||||
|
||||
|
||||
class CrestStation extends AbstractIterator {
|
||||
|
||||
protected static $map = [
|
||||
'id' => 'id',
|
||||
'name' => 'name'
|
||||
];
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 07.02.2016
|
||||
* Time: 14:34
|
||||
*/
|
||||
|
||||
namespace Data\Mapper;
|
||||
|
||||
|
||||
class CrestSystem extends AbstractIterator {
|
||||
|
||||
protected static $map = [
|
||||
'id' => 'id',
|
||||
'name' => 'name'
|
||||
];
|
||||
}
|
||||
45
app/main/lib/ccpclient.php
Normal file
45
app/main/lib/ccpclient.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus4D
|
||||
* Date: 26.03.2017
|
||||
* Time: 19:17
|
||||
*/
|
||||
|
||||
namespace Lib;
|
||||
|
||||
use \Exodus4D\ESI\ESI;
|
||||
|
||||
class CcpClient extends \Prefab {
|
||||
|
||||
private $apiClient;
|
||||
|
||||
public function __construct(){
|
||||
$f3 = \Base::instance();
|
||||
|
||||
$this->apiClient = new ESI($f3);
|
||||
$this->apiClient->setEsiUrl( Config::getEnvironmentData('CCP_ESI_URL') );
|
||||
$this->apiClient->setEsiDatasource( Config::getEnvironmentData('CCP_ESI_DATASOURCE') );
|
||||
$this->apiClient->setUserAgent($this->getUserAgent($f3));
|
||||
|
||||
$f3->set('ccpClient', $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserAgent($f3){
|
||||
$userAgent = '';
|
||||
$userAgent .= $f3->get('PATHFINDER.NAME');
|
||||
$userAgent .= ' - ' . $f3->get('PATHFINDER.VERSION');
|
||||
$userAgent .= ' | ' . $f3->get('PATHFINDER.CONTACT');
|
||||
$userAgent .= ' (' . $_SERVER['SERVER_NAME'] . ')';
|
||||
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments){
|
||||
return call_user_func_array([$this->apiClient, $name], $arguments);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,12 @@ class Config extends \Prefab {
|
||||
const HIVE_KEY_PATHFINDER = 'PATHFINDER';
|
||||
const HIVE_KEY_ENVIRONMENT = 'ENVIRONMENT';
|
||||
|
||||
/**
|
||||
* environment config keys that should be parsed as array
|
||||
* -> use "," as delimiter in config files/data
|
||||
*/
|
||||
const ARRAY_KEYS = ['CCP_ESI_SCOPES'];
|
||||
|
||||
/**
|
||||
* all environment data
|
||||
* @var array
|
||||
@@ -79,6 +85,12 @@ class Config extends \Prefab {
|
||||
if( !empty($this->serverConfigData['ENV']) ){
|
||||
// get environment config from $_SERVER data
|
||||
$environmentData = (array)$this->serverConfigData['ENV'];
|
||||
|
||||
// some environment variables should be parsed as array
|
||||
array_walk($environmentData, function(&$item, $key){
|
||||
$item = (in_array($key, self::ARRAY_KEYS)) ? explode(',', $item) : $item;
|
||||
});
|
||||
|
||||
$environmentData['TYPE'] = 'PHP: environment variables';
|
||||
}else{
|
||||
// get environment data from *.ini file config
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
namespace lib;
|
||||
|
||||
use controller\LogController;
|
||||
|
||||
class Socket {
|
||||
|
||||
// max TTL time (ms)
|
||||
|
||||
@@ -15,8 +15,8 @@ class Web extends \Web {
|
||||
const ERROR_STATUS_LOG = 'HTTP %s: \'%s\' | url: %s \'%s\'%s';
|
||||
|
||||
/**
|
||||
* max number of CREST curls for a single endpoint until giving up...
|
||||
* this is because CREST is not very stable
|
||||
* max number of curls calls for a single resource until giving up...
|
||||
* this is because SSO API is not very stable
|
||||
*/
|
||||
const RETRY_COUNT_MAX = 3;
|
||||
|
||||
|
||||
@@ -50,6 +50,11 @@ class CharacterAuthenticationModel extends BasicModel{
|
||||
'type' => Schema::DT_TIMESTAMP,
|
||||
'default' => Schema::DF_CURRENT_TIMESTAMP,
|
||||
'index' => true
|
||||
],
|
||||
'scopeHash' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@@ -35,9 +35,6 @@ class CharacterLogModel extends BasicModel {
|
||||
]
|
||||
]
|
||||
],
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
'systemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true
|
||||
@@ -47,26 +44,6 @@ class CharacterLogModel extends BasicModel {
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'constellationId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true
|
||||
],
|
||||
'constellationName' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'regionId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true
|
||||
],
|
||||
'regionName' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
'shipTypeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true
|
||||
@@ -110,24 +87,6 @@ class CharacterLogModel extends BasicModel {
|
||||
$this->systemName = '';
|
||||
}
|
||||
|
||||
if( isset($logData['constellation']) ){
|
||||
$this->constellationId = (int)$logData['constellation']['id'];
|
||||
$this->constellationName = $logData['constellation']['name'];
|
||||
}else{
|
||||
$this->constellationId = null;
|
||||
$this->constellationName = '';
|
||||
}
|
||||
|
||||
if( isset($logData['region']) ){
|
||||
$this->regionId = (int)$logData['region']['id'];
|
||||
$this->regionName = $logData['region']['name'];
|
||||
}else{
|
||||
$this->regionId = null;
|
||||
$this->regionName = '';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
if( isset($logData['ship']) ){
|
||||
$this->shipTypeId = (int)$logData['ship']['typeId'];
|
||||
$this->shipTypeName = $logData['ship']['typeName'];
|
||||
@@ -161,16 +120,6 @@ class CharacterLogModel extends BasicModel {
|
||||
$logData->system->id = (int)$this->systemId;
|
||||
$logData->system->name = $this->systemName;
|
||||
|
||||
$logData->constellation = (object) [];
|
||||
$logData->constellation->id = (int)$this->constellationId;
|
||||
$logData->constellation->name = $this->constellationName;
|
||||
|
||||
$logData->region = (object) [];
|
||||
$logData->region->id = (int)$this->regionId;
|
||||
$logData->region->name = $this->regionName;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
$logData->ship = (object) [];
|
||||
$logData->ship->typeId = (int)$this->shipTypeId;
|
||||
$logData->ship->typeName = $this->shipTypeName;
|
||||
|
||||
@@ -75,15 +75,6 @@ class CharacterModel extends BasicModel {
|
||||
]
|
||||
]
|
||||
],
|
||||
'factionId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true
|
||||
],
|
||||
'factionName' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'shared' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
@@ -94,6 +85,11 @@ class CharacterModel extends BasicModel {
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
],
|
||||
'securityStatus' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'userCharacter' => [
|
||||
'has-one' => ['Model\UserCharacterModel', 'characterId']
|
||||
],
|
||||
@@ -178,7 +174,7 @@ class CharacterModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* set CREST accessToken for current session
|
||||
* set API accessToken for current session
|
||||
* -> update "tokenUpdated" column on change
|
||||
* -> this is required for expire checking!
|
||||
* @param string $accessToken
|
||||
@@ -191,6 +187,11 @@ class CharacterModel extends BasicModel {
|
||||
return $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* logLocation specifies whether the current system should be tracked or not
|
||||
* @param $logLocation
|
||||
* @return bool
|
||||
*/
|
||||
public function set_logLocation($logLocation){
|
||||
$logLocation = (bool)$logLocation;
|
||||
if(
|
||||
@@ -304,7 +305,7 @@ class CharacterModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* get CREST API "access_token" from OAuth
|
||||
* get ESI API "access_token" from OAuth
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getAccessToken(){
|
||||
@@ -405,32 +406,75 @@ class CharacterModel extends BasicModel {
|
||||
|
||||
/**
|
||||
* update character log (active system, ...)
|
||||
* -> CREST API request for character log data
|
||||
* -> API request for character log data
|
||||
* @param array $additionalOptions (optional) request options for cURL request
|
||||
* @return $this
|
||||
*/
|
||||
public function updateLog($additionalOptions = []){
|
||||
$deleteLog = true;
|
||||
|
||||
|
||||
//check if log update is enabled for this user
|
||||
if( $this->logLocation ){
|
||||
// Try to pull data from CREST
|
||||
$ssoController = new Sso();
|
||||
$logData = $ssoController->getCharacterLocationData($this->getAccessToken(), $additionalOptions);
|
||||
// Try to pull data from API
|
||||
if( $accessToken = $this->getAccessToken() ){
|
||||
|
||||
if($logData['timeout'] === false){
|
||||
if( empty($logData['system']) ){
|
||||
// character is not in-game
|
||||
if( $this->hasLog() ){
|
||||
// delete existing log
|
||||
$this->characterLog->erase();
|
||||
$this->save();
|
||||
}
|
||||
}else{
|
||||
$locationData = self::getF3()->ccpClient->getCharacterLocationData($this->_id, $accessToken, $additionalOptions);
|
||||
|
||||
if( !empty($locationData['system']['id']) ){
|
||||
// character is currently in-game
|
||||
|
||||
|
||||
// IDs for "systemId", "stationId and "shipTypeId" that require more data
|
||||
$lookupIds = [];
|
||||
|
||||
if( !$characterLog = $this->getLog() ){
|
||||
// create new log
|
||||
$characterLog = $this->rel('characterLog');
|
||||
$characterLog->characterId = $this->_id;
|
||||
}
|
||||
|
||||
// get current log data and modify on change
|
||||
$logData = json_decode(json_encode( $characterLog->getData()), true);
|
||||
|
||||
if($logData['system']['id'] !== $locationData['system']['id']){
|
||||
// system changed -> request "system name" for current system
|
||||
$lookupIds[] = $locationData['system']['id'];
|
||||
}
|
||||
|
||||
if( !empty($locationData['station']['id']) ){
|
||||
if( $logData['station']['id'] !== $locationData['station']['id'] ){
|
||||
// station changed -> request "station name" for current station
|
||||
$lookupIds[] = $locationData['station']['id'];
|
||||
}
|
||||
}else{
|
||||
unset($logData['station']);
|
||||
}
|
||||
|
||||
$logData = array_replace_recursive($logData, $locationData);
|
||||
|
||||
// get current ship data
|
||||
$shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions);
|
||||
|
||||
if( !empty($shipData['ship']['typeId']) ){
|
||||
if($logData['ship']['typeId'] !== $shipData['ship']['typeId']){
|
||||
// ship changed -> request "station name" for current station
|
||||
$lookupIds[] = $shipData['ship']['typeId'];
|
||||
}
|
||||
|
||||
// "shipName"/"shipId" could have changed...
|
||||
$logData = array_replace_recursive($logData, $shipData);
|
||||
}else{
|
||||
unset($logData['ship']);
|
||||
}
|
||||
|
||||
if( !empty($lookupIds) ){
|
||||
// get "more" information for some Ids (e.g. name)
|
||||
$universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupIds, $additionalOptions);
|
||||
$logData = array_replace_recursive($logData, $universeData);
|
||||
}
|
||||
|
||||
$deleteLog = false;
|
||||
$characterLog->setData($logData);
|
||||
$characterLog->save();
|
||||
|
||||
@@ -439,14 +483,23 @@ class CharacterModel extends BasicModel {
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
$deleteLog &&
|
||||
$this->hasLog()
|
||||
){
|
||||
// delete existing log
|
||||
$this->characterLog->erase();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* update character data from CCPs CREST API
|
||||
* update character data from CCPs ESI API
|
||||
* @return array (some status messages)
|
||||
*/
|
||||
public function updateFromCrest(){
|
||||
public function updateFromESI(){
|
||||
$status = [];
|
||||
|
||||
if( $accessToken = $this->getAccessToken() ){
|
||||
@@ -458,36 +511,14 @@ class CharacterModel extends BasicModel {
|
||||
!is_null( $verificationCharacterData = $ssoController->verifyCharacterData($accessToken) ) &&
|
||||
$verificationCharacterData->CharacterID === $this->_id
|
||||
){
|
||||
// get character data from CREST
|
||||
$characterData = $ssoController->getCharacterData($accessToken);
|
||||
if( isset($characterData->character) ){
|
||||
// get character data from API
|
||||
$characterData = $ssoController->getCharacterData($this->_id);
|
||||
if( !empty($characterData->character) ){
|
||||
$characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash;
|
||||
|
||||
$corporation = null;
|
||||
$alliance = null;
|
||||
if( isset($characterData->corporation) ){
|
||||
/**
|
||||
* @var $corporation CorporationModel
|
||||
*/
|
||||
$corporation = $this->rel('corporationId');
|
||||
$corporation->getById($characterData->corporation['id'], 0);
|
||||
$corporation->copyfrom($characterData->corporation, ['id', 'name', 'isNPC']);
|
||||
$corporation->save();
|
||||
}
|
||||
|
||||
if( isset($characterData->alliance) ){
|
||||
/**
|
||||
* @var $alliance AllianceModel
|
||||
*/
|
||||
$alliance = $this->rel('allianceId');
|
||||
$alliance->getById($characterData->alliance['id'], 0);
|
||||
$alliance->copyfrom($characterData->alliance, ['id', 'name']);
|
||||
$alliance->save();
|
||||
}
|
||||
|
||||
$this->copyfrom($characterData->character, ['name', 'ownerHash']);
|
||||
$this->set('corporationId', is_object($corporation) ? $corporation->get('id') : null);
|
||||
$this->set('allianceId', is_object($alliance) ? $alliance->get('id') : null);
|
||||
$this->copyfrom($characterData->character, ['ownerHash', 'securityStatus']);
|
||||
$this->corporationId = $characterData->corporation;
|
||||
$this->allianceId = $characterData->alliance;
|
||||
$this->save();
|
||||
}
|
||||
}else{
|
||||
|
||||
@@ -142,8 +142,8 @@ EXPIRE_SIGNATURES = 259200
|
||||
[PATHFINDER.LOGFILES]
|
||||
; error log
|
||||
ERROR = error
|
||||
; CREST error log
|
||||
CREST = crest
|
||||
; SSO error log
|
||||
SSO = sso
|
||||
; login information
|
||||
LOGIN = login
|
||||
; session warnings (suspect)
|
||||
|
||||
28
composer-dev.json
Normal file
28
composer-dev.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "exodus4d/pathfinder",
|
||||
"description": "Mapping tool for EVE ONLINE",
|
||||
"minimum-stability": "stable",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Friedrich",
|
||||
"email": "pathfinder@exodus4d.de"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Exodus4D\\Pathfinder\\": "app/main"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "../pathfinder_esi"
|
||||
}],
|
||||
"require": {
|
||||
"php-64bit": ">=7.0",
|
||||
"ext-zmq": "1.1.*",
|
||||
"react/zmq": "0.3.*",
|
||||
"exodus4d/pathfinder_esi": "dev-develop as 0.0.x-dev"
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,15 @@
|
||||
"Exodus4D\\Pathfinder\\": "app/main"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/exodus4d/pathfinder_esi"
|
||||
}],
|
||||
"require": {
|
||||
"php-64bit": ">=7.0",
|
||||
"ext-zmq": "1.1.*",
|
||||
"react/zmq": "0.3.*"
|
||||
"react/zmq": "0.3.*",
|
||||
"exodus4d/pathfinder_esi": "dev-master"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
index.php
11
index.php
@@ -1,6 +1,12 @@
|
||||
<?php
|
||||
session_name('pathfinder_session');
|
||||
$f3 = require('app/lib/base.php');
|
||||
|
||||
$composerAutoloader = 'vendor/autoload.php';
|
||||
if(file_exists($composerAutoloader)){
|
||||
require_once($composerAutoloader);
|
||||
}
|
||||
|
||||
$f3 = require_once('app/lib/base.php');
|
||||
|
||||
// load main config
|
||||
$f3->config('app/config.ini');
|
||||
@@ -8,6 +14,9 @@ $f3->config('app/config.ini');
|
||||
// load environment dependent config
|
||||
lib\Config::instance();
|
||||
|
||||
// initiate CCP API Client (ESI)
|
||||
lib\CcpClient::instance();
|
||||
|
||||
// initiate cron-jobs
|
||||
Cron::instance();
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ define(['jquery'], function($) {
|
||||
logIn: 'api/user/logIn', // ajax URL - login
|
||||
logout: 'api/user/logout', // ajax URL - logout
|
||||
deleteLog: 'api/user/deleteLog', // ajax URL - delete character log
|
||||
openIngameWindow: 'api/user/openIngameWindow', // ajax URL - open inGame Window
|
||||
saveUserConfig: 'api/user/saveAccount', // ajax URL - saves/update user account
|
||||
deleteAccount: 'api/user/deleteAccount', // ajax URL - delete Account data
|
||||
// access API
|
||||
|
||||
@@ -2719,7 +2719,7 @@ define([
|
||||
break;
|
||||
case 'info':
|
||||
// open map info dialog
|
||||
$(document).triggerMenuEvent('ShowMapInfo');
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'});
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -3015,7 +3015,7 @@ define([
|
||||
|
||||
// set current location data for header update
|
||||
headerUpdateData.currentSystemId = $(system).data('id');
|
||||
//headerUpdateData.currentSystemName = currentCharacterLog.system.name;
|
||||
headerUpdateData.currentSystemName = currentCharacterLog.system.name;
|
||||
}
|
||||
|
||||
system.updateSystemUserData(map, tempUserData, currentUserIsHere);
|
||||
|
||||
@@ -323,7 +323,7 @@ define([
|
||||
class: 'fa fa-street-view fa-fw'
|
||||
})
|
||||
).on('click', function(){
|
||||
$(document).triggerMenuEvent('ShowMapInfo');
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'});
|
||||
})
|
||||
).append(
|
||||
getMenuHeadline('Settings')
|
||||
@@ -496,7 +496,7 @@ define([
|
||||
|
||||
// active pilots
|
||||
$('.' + config.headActiveUserClass).find('a').on('click', function(){
|
||||
$(document).triggerMenuEvent('ShowMapInfo');
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'activity'});
|
||||
});
|
||||
|
||||
// current location
|
||||
@@ -661,9 +661,9 @@ define([
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('pf:menuShowMapInfo', function(e){
|
||||
$(document).on('pf:menuShowMapInfo', function(e, data){
|
||||
// show map information dialog
|
||||
$.fn.showMapInfoDialog();
|
||||
$.fn.showMapInfoDialog(data);
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ define([
|
||||
tableImageSmallCellClass: 'pf-table-image-small-cell', // class for table "small image" cells
|
||||
tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells
|
||||
tableCounterCellClass: 'pf-table-counter-cell', // class for table "counter" cells
|
||||
tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
|
||||
|
||||
loadingOptions: { // config for loading overlay
|
||||
icon: {
|
||||
@@ -625,8 +626,16 @@ define([
|
||||
// table init complete
|
||||
userTable.on( 'init.dt', function () {
|
||||
usersElement.hideLoadingAnimation();
|
||||
|
||||
// init table tooltips
|
||||
let tooltipElements = usersElement.find('[data-toggle="tooltip"]');
|
||||
tooltipElements.tooltip();
|
||||
});
|
||||
|
||||
let getIconForInformationWindow = () => {
|
||||
return '<i class="fa fa-fw fa-id-card ' + config.tableActionCellIconClass + '" title="open ingame" data-toggle="tooltip"></i>';
|
||||
};
|
||||
|
||||
// users table ================================================================================================
|
||||
// prepare users data for dataTables
|
||||
let currentMapUserData = Util.getCurrentMapUserData( mapData.config.id );
|
||||
@@ -681,7 +690,9 @@ define([
|
||||
searchable: true,
|
||||
data: 'log.ship',
|
||||
render: {
|
||||
_: 'typeName',
|
||||
_: function(data, type, row){
|
||||
return data.typeName + ' <i class="fa fa-fw fa-question-circle pf-help" title="' + data.name + '" data-toggle="tooltip"></i>';
|
||||
},
|
||||
sort: 'typeName'
|
||||
}
|
||||
},{
|
||||
@@ -702,7 +713,24 @@ define([
|
||||
title: 'pilot',
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
data: 'name'
|
||||
className: [config.tableActionCellClass].join(' '),
|
||||
data: 'name',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value += ' ' + getIconForInformationWindow();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// open character information window (ingame)
|
||||
$(cell).on('click', { tableApi: this.DataTable() }, function(e) {
|
||||
let rowData = e.data.tableApi.row(this).data();
|
||||
Util.openIngameWindow(rowData.id);
|
||||
});
|
||||
}
|
||||
},{
|
||||
targets: 4,
|
||||
title: '',
|
||||
@@ -721,9 +749,23 @@ define([
|
||||
title: 'corporation',
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
className: [config.tableActionCellClass].join(' '),
|
||||
data: 'corporation',
|
||||
render: {
|
||||
_: 'name'
|
||||
_: function (data, type, row, meta) {
|
||||
let value = data.name;
|
||||
if(type === 'display'){
|
||||
value += ' ' + getIconForInformationWindow();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// open character information window (ingame)
|
||||
$(cell).on('click', { tableApi: this.DataTable() }, function(e) {
|
||||
let cellData = e.data.tableApi.cell(this).data();
|
||||
Util.openIngameWindow(cellData.id);
|
||||
});
|
||||
}
|
||||
},{
|
||||
targets: 6,
|
||||
@@ -753,9 +795,9 @@ define([
|
||||
|
||||
/**
|
||||
* shows the map information modal dialog
|
||||
* @param options
|
||||
*/
|
||||
$.fn.showMapInfoDialog = function(){
|
||||
|
||||
$.fn.showMapInfoDialog = function(options){
|
||||
let activeMap = Util.getMapModule().getActiveMap();
|
||||
let mapData = activeMap.getMapDataFromClient({forceData: true});
|
||||
|
||||
@@ -770,7 +812,11 @@ define([
|
||||
mapInfoId: config.mapInfoId,
|
||||
mapInfoSystemsId: config.mapInfoSystemsId,
|
||||
mapInfoConnectionsId: config.mapInfoConnectionsId,
|
||||
mapInfoUsersId: config.mapInfoUsersId
|
||||
mapInfoUsersId: config.mapInfoUsersId,
|
||||
|
||||
// default open tab ----------
|
||||
openTabInformation: options.tab === 'information',
|
||||
openTabActivity: options.tab === 'activity'
|
||||
};
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
@@ -1899,6 +1899,34 @@ define([
|
||||
return $('.' + config.dialogClass).filter(':visible');
|
||||
};
|
||||
|
||||
/**
|
||||
* send Ajax request that remote opens an ingame Window
|
||||
* @param targetId
|
||||
*/
|
||||
let openIngameWindow = (targetId) => {
|
||||
targetId = parseInt(targetId);
|
||||
|
||||
if(targetId > 0){
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.openIngameWindow,
|
||||
data: {
|
||||
targetId: targetId
|
||||
},
|
||||
dataType: 'json'
|
||||
}).done(function(data){
|
||||
if(data.error.length > 0){
|
||||
showNotify({title: 'Open window in client', text: 'Remote window open failed', type: 'error'});
|
||||
}else{
|
||||
showNotify({title: 'Open window in client', text: 'Check your EVE client', type: 'success'});
|
||||
}
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
showNotify({title: jqXHR.status + ': openWindow', text: reason, type: 'error'});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* formats a price string into an ISK Price
|
||||
* @param price
|
||||
@@ -2066,6 +2094,7 @@ define([
|
||||
setDestination: setDestination,
|
||||
convertDateToString: convertDateToString,
|
||||
getOpenDialogs: getOpenDialogs,
|
||||
openIngameWindow: openIngameWindow,
|
||||
formatPrice: formatPrice,
|
||||
getLocalStorage: getLocalStorage,
|
||||
getDocumentPath: getDocumentPath,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,6 +16,7 @@ define(['jquery'], function($) {
|
||||
logIn: 'api/user/logIn', // ajax URL - login
|
||||
logout: 'api/user/logout', // ajax URL - logout
|
||||
deleteLog: 'api/user/deleteLog', // ajax URL - delete character log
|
||||
openIngameWindow: 'api/user/openIngameWindow', // ajax URL - open inGame Window
|
||||
saveUserConfig: 'api/user/saveAccount', // ajax URL - saves/update user account
|
||||
deleteAccount: 'api/user/deleteAccount', // ajax URL - delete Account data
|
||||
// access API
|
||||
|
||||
@@ -2719,7 +2719,7 @@ define([
|
||||
break;
|
||||
case 'info':
|
||||
// open map info dialog
|
||||
$(document).triggerMenuEvent('ShowMapInfo');
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'});
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -3015,7 +3015,7 @@ define([
|
||||
|
||||
// set current location data for header update
|
||||
headerUpdateData.currentSystemId = $(system).data('id');
|
||||
//headerUpdateData.currentSystemName = currentCharacterLog.system.name;
|
||||
headerUpdateData.currentSystemName = currentCharacterLog.system.name;
|
||||
}
|
||||
|
||||
system.updateSystemUserData(map, tempUserData, currentUserIsHere);
|
||||
|
||||
@@ -323,7 +323,7 @@ define([
|
||||
class: 'fa fa-street-view fa-fw'
|
||||
})
|
||||
).on('click', function(){
|
||||
$(document).triggerMenuEvent('ShowMapInfo');
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'});
|
||||
})
|
||||
).append(
|
||||
getMenuHeadline('Settings')
|
||||
@@ -496,7 +496,7 @@ define([
|
||||
|
||||
// active pilots
|
||||
$('.' + config.headActiveUserClass).find('a').on('click', function(){
|
||||
$(document).triggerMenuEvent('ShowMapInfo');
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'activity'});
|
||||
});
|
||||
|
||||
// current location
|
||||
@@ -661,9 +661,9 @@ define([
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('pf:menuShowMapInfo', function(e){
|
||||
$(document).on('pf:menuShowMapInfo', function(e, data){
|
||||
// show map information dialog
|
||||
$.fn.showMapInfoDialog();
|
||||
$.fn.showMapInfoDialog(data);
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ define([
|
||||
tableImageSmallCellClass: 'pf-table-image-small-cell', // class for table "small image" cells
|
||||
tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells
|
||||
tableCounterCellClass: 'pf-table-counter-cell', // class for table "counter" cells
|
||||
tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
|
||||
|
||||
loadingOptions: { // config for loading overlay
|
||||
icon: {
|
||||
@@ -625,8 +626,16 @@ define([
|
||||
// table init complete
|
||||
userTable.on( 'init.dt', function () {
|
||||
usersElement.hideLoadingAnimation();
|
||||
|
||||
// init table tooltips
|
||||
let tooltipElements = usersElement.find('[data-toggle="tooltip"]');
|
||||
tooltipElements.tooltip();
|
||||
});
|
||||
|
||||
let getIconForInformationWindow = () => {
|
||||
return '<i class="fa fa-fw fa-id-card ' + config.tableActionCellIconClass + '" title="open ingame" data-toggle="tooltip"></i>';
|
||||
};
|
||||
|
||||
// users table ================================================================================================
|
||||
// prepare users data for dataTables
|
||||
let currentMapUserData = Util.getCurrentMapUserData( mapData.config.id );
|
||||
@@ -681,7 +690,9 @@ define([
|
||||
searchable: true,
|
||||
data: 'log.ship',
|
||||
render: {
|
||||
_: 'typeName',
|
||||
_: function(data, type, row){
|
||||
return data.typeName + ' <i class="fa fa-fw fa-question-circle pf-help" title="' + data.name + '" data-toggle="tooltip"></i>';
|
||||
},
|
||||
sort: 'typeName'
|
||||
}
|
||||
},{
|
||||
@@ -702,7 +713,24 @@ define([
|
||||
title: 'pilot',
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
data: 'name'
|
||||
className: [config.tableActionCellClass].join(' '),
|
||||
data: 'name',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value += ' ' + getIconForInformationWindow();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// open character information window (ingame)
|
||||
$(cell).on('click', { tableApi: this.DataTable() }, function(e) {
|
||||
let rowData = e.data.tableApi.row(this).data();
|
||||
Util.openIngameWindow(rowData.id);
|
||||
});
|
||||
}
|
||||
},{
|
||||
targets: 4,
|
||||
title: '',
|
||||
@@ -721,9 +749,23 @@ define([
|
||||
title: 'corporation',
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
className: [config.tableActionCellClass].join(' '),
|
||||
data: 'corporation',
|
||||
render: {
|
||||
_: 'name'
|
||||
_: function (data, type, row, meta) {
|
||||
let value = data.name;
|
||||
if(type === 'display'){
|
||||
value += ' ' + getIconForInformationWindow();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// open character information window (ingame)
|
||||
$(cell).on('click', { tableApi: this.DataTable() }, function(e) {
|
||||
let cellData = e.data.tableApi.cell(this).data();
|
||||
Util.openIngameWindow(cellData.id);
|
||||
});
|
||||
}
|
||||
},{
|
||||
targets: 6,
|
||||
@@ -753,9 +795,9 @@ define([
|
||||
|
||||
/**
|
||||
* shows the map information modal dialog
|
||||
* @param options
|
||||
*/
|
||||
$.fn.showMapInfoDialog = function(){
|
||||
|
||||
$.fn.showMapInfoDialog = function(options){
|
||||
let activeMap = Util.getMapModule().getActiveMap();
|
||||
let mapData = activeMap.getMapDataFromClient({forceData: true});
|
||||
|
||||
@@ -770,7 +812,11 @@ define([
|
||||
mapInfoId: config.mapInfoId,
|
||||
mapInfoSystemsId: config.mapInfoSystemsId,
|
||||
mapInfoConnectionsId: config.mapInfoConnectionsId,
|
||||
mapInfoUsersId: config.mapInfoUsersId
|
||||
mapInfoUsersId: config.mapInfoUsersId,
|
||||
|
||||
// default open tab ----------
|
||||
openTabInformation: options.tab === 'information',
|
||||
openTabActivity: options.tab === 'activity'
|
||||
};
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
@@ -1899,6 +1899,34 @@ define([
|
||||
return $('.' + config.dialogClass).filter(':visible');
|
||||
};
|
||||
|
||||
/**
|
||||
* send Ajax request that remote opens an ingame Window
|
||||
* @param targetId
|
||||
*/
|
||||
let openIngameWindow = (targetId) => {
|
||||
targetId = parseInt(targetId);
|
||||
|
||||
if(targetId > 0){
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.openIngameWindow,
|
||||
data: {
|
||||
targetId: targetId
|
||||
},
|
||||
dataType: 'json'
|
||||
}).done(function(data){
|
||||
if(data.error.length > 0){
|
||||
showNotify({title: 'Open window in client', text: 'Remote window open failed', type: 'error'});
|
||||
}else{
|
||||
showNotify({title: 'Open window in client', text: 'Check your EVE client', type: 'success'});
|
||||
}
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
showNotify({title: jqXHR.status + ': openWindow', text: reason, type: 'error'});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* formats a price string into an ISK Price
|
||||
* @param price
|
||||
@@ -2066,6 +2094,7 @@ define([
|
||||
setDestination: setDestination,
|
||||
convertDateToString: convertDateToString,
|
||||
getOpenDialogs: getOpenDialogs,
|
||||
openIngameWindow: openIngameWindow,
|
||||
formatPrice: formatPrice,
|
||||
getLocalStorage: getLocalStorage,
|
||||
getDocumentPath: getDocumentPath,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="navbar-header pull-left">
|
||||
<ul class="nav navbar-nav {{dialogNavigationClass}}" role="tablist">
|
||||
<li class="active">
|
||||
<li class="{{#openTabInformation}}active{{/openTabInformation}}">
|
||||
<a role="tab" data-toggle="tab" data-name="infoSummary" href="#{{dialogSummaryContainerId}}">
|
||||
<i class="fa fa-street-view fa-fw"></i> Information
|
||||
</a>
|
||||
</li>
|
||||
<li class="">
|
||||
<li class="{{#openTabActivity}}active{{/openTabActivity}}">
|
||||
<a role="tab" data-toggle="tab" data-name="infoUsers" href="#{{dialogUsersContainerId}}">
|
||||
<i class="fa fa-fighter-jet fa-fw"></i> Activity
|
||||
</a>
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="tab-content">
|
||||
|
||||
{{! "summary" tab ------------------------------------------------------ }}
|
||||
<div role="tabpanel" class="tab-pane fade in active" id="{{dialogSummaryContainerId}}">
|
||||
<div role="tabpanel" class="tab-pane fade {{#openTabInformation}}in active{{/openTabInformation}}" id="{{dialogSummaryContainerId}}">
|
||||
<div class="alert alert-info fade in hidden-md hidden-lg">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><i class="fa fa-close"></i></button>
|
||||
<span class="txt-color txt-color-information">Info</span>
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
|
||||
{{! "users" tab -------------------------------------------------------- }}
|
||||
<div role="tabpanel" class="tab-pane fade" id="{{dialogUsersContainerId}}">
|
||||
<div role="tabpanel" class="tab-pane fade {{#openTabActivity}}in active{{/openTabActivity}}" id="{{dialogUsersContainerId}}">
|
||||
<h4><i class="fa fa-male fa-lg fa-fw"></i> Active pilots</h4>
|
||||
|
||||
<div id="{{mapInfoUsersId}}" class="pf-dynamic-area">
|
||||
|
||||
@@ -4,8 +4,14 @@
|
||||
{{#serviceStatus}}
|
||||
<li><i class="fa-li fa fa-server " aria-hidden="true"></i><span class="txt-color {{ style }}">{{ eve }}</span></li>
|
||||
{{/serviceStatus}}
|
||||
{{#userCounts}}
|
||||
<li><i class="fa-li fa fa-users" aria-hidden="true"></i>{{ userCounts }}</li>
|
||||
{{/userCounts}}
|
||||
{{#playerCount}}
|
||||
<li><i class="fa-li fa fa-users" aria-hidden="true"></i>{{ playerCount }}</li>
|
||||
{{/playerCount}}
|
||||
{{#startTime}}
|
||||
<li><i class="fa-li fa fa-clock-o" aria-hidden="true"></i>up {{ startTime }}</li>
|
||||
{{/startTime}}
|
||||
{{#serverVersion}}
|
||||
<li><i class="fa-li fa fa-certificate" aria-hidden="true"></i>v. {{ serverVersion }}</li>
|
||||
{{/serverVersion}}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -987,6 +987,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-md-6 pf-landing-pricing-panel">
|
||||
{* Cookie *}
|
||||
<div class="panel panel-default pricing-big">
|
||||
<div class="panel-heading text-left">
|
||||
<h3 class="panel-title">Cookies
|
||||
<i class="fa fa-fw fa-question-circle pf-help-light" title="Force all users to re-login through SSO. "></i>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="panel-body no-padding">
|
||||
<div class="btn-group btn-group-justified">
|
||||
<span class="btn btn-default disabled btn-fake">Invalidate all Cookie data</span>
|
||||
<a href="?invalidateCookies=1#pf-setup-administration" class="btn btn-warning" role="button">
|
||||
<i class="fa fa-fw fa-times"></i> Clear authentication data
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -552,7 +552,7 @@ $modal-header-border-color: #e5e5e5;
|
||||
$modal-footer-border-color: $modal-header-border-color;
|
||||
|
||||
$modal-lg: 1100px;
|
||||
$modal-md: 600px;
|
||||
$modal-md: 700px;
|
||||
$modal-sm: 300px;
|
||||
|
||||
|
||||
|
||||
@@ -216,6 +216,19 @@ select:active, select:hover {
|
||||
td{
|
||||
&.pf-table-action-cell{
|
||||
cursor: pointer;
|
||||
|
||||
// icon within <td> cell content that should be highlighted on hover
|
||||
> .pf-table-action-icon-cell{
|
||||
@extend .txt-color;
|
||||
@extend .txt-color-gray;
|
||||
@include transition( color 0.08s ease-out );
|
||||
}
|
||||
|
||||
&:hover{
|
||||
> .pf-table-action-icon-cell{
|
||||
@extend .txt-color-orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.pf-table-image-cell{
|
||||
|
||||
Reference in New Issue
Block a user