- New map right "share map" added, closed #927

- Upgraded "[_Select2_](https://select2.org/)" js lib `v4.0.6-rc.1` → `v4.0.13`
- Fixed some issues where changed map settings (e.g. "share") do not get updated/stored, closed #889, closed #925
- Moved ajax endpoints for map create/update/delete into `/Api/Rest/` dir
- Minor UI improvements for "manual dialog" (fixed pixelated text)
This commit is contained in:
Mark Friedrich
2020-03-15 19:38:44 +01:00
parent c4333af431
commit 9a3f45fdc7
64 changed files with 1258 additions and 1360 deletions

View File

@@ -77,11 +77,11 @@ class AccessController extends Controller {
/**
* broadcast MapModel to clients
* @see broadcastMapData()
* @param Pathfinder\MapModel $map
* @param bool $noCache
*/
protected function broadcastMap(Pathfinder\MapModel $map) : void {
$this->broadcastMapData($this->getFormattedMapData($map));
protected function broadcastMap(Pathfinder\MapModel $map, bool $noCache = false) : void {
$this->broadcastMapData($this->getFormattedMapData($map, $noCache));
}
@@ -99,18 +99,13 @@ class AccessController extends Controller {
/**
* get formatted Map Data
* @param Pathfinder\MapModel $map
* @return array
* @throws \Exception
*/
/**
* @param Pathfinder\MapModel $map
* @param bool $noCache
* @return array|null
*/
protected function getFormattedMapData(Pathfinder\MapModel $map) : ?array {
protected function getFormattedMapData(Pathfinder\MapModel $map, bool $noCache = false) : ?array {
$data = null;
try{
$mapData = $map->getData();
$mapData = $map->getData($noCache);
$data = [
'config' => $mapData->mapData,
'data' => [

View File

@@ -203,7 +203,7 @@ class Admin extends Controller{
// character has access to that corporation -> create/update/delete rights...
if($corporationRightsData = (array)$settings['rights']){
// get existing corp rights
foreach($corporation->getRights(['addInactive' => true]) as $corporationRight){
foreach($corporation->getRights($corporation::RIGHTS, ['addInactive' => true]) as $corporationRight){
$corporationRightData = $corporationRightsData[$corporationRight->rightId->_id];
if(
$corporationRightData &&

View File

@@ -260,11 +260,11 @@ class Map extends Controller\AccessController {
// get SSO error messages that should be shown immediately ----------------------------------------------------
// -> e.g. errors while character switch from previous HTTP requests
if($f3->exists(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR, $message)){
if($f3->exists(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR, $text)){
$ssoError = (object) [];
$ssoError->type = 'error';
$ssoError->title = 'Login failed';
$ssoError->message = $message;
$ssoError->text = $text;
$return->error[] = $ssoError;
$f3->clear(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR);
}elseif($validInitData){
@@ -392,7 +392,7 @@ class Map extends Controller\AccessController {
}else{
$maxSystemsError = (object) [];
$maxSystemsError->type = 'error';
$maxSystemsError->message = 'Map has to many systems (' . $systemCount . ').'
$maxSystemsError->text = 'Map has to many systems (' . $systemCount . ').'
.' Max system count is ' . $defaultConfig['max_systems'] . ' for ' . $mapType->name . ' maps.';
$return->error[] = $maxSystemsError;
}
@@ -400,20 +400,20 @@ class Map extends Controller\AccessController {
// systems || connections missing
$missingConfigError = (object) [];
$missingConfigError->type = 'error';
$missingConfigError->message = 'Map data not valid (systems || connections) missing';
$missingConfigError->text = 'Map data not valid (systems || connections) missing';
$return->error[] = $missingConfigError;
}
}else{
$unknownMapScope= (object) [];
$unknownMapScope->type = 'error';
$unknownMapScope->message = 'Map scope unknown!';
$unknownMapScope->text = 'Map scope unknown!';
$return->error[] = $unknownMapScope;
}
}else{
// map config || systems/connections missing
$missingConfigError = (object) [];
$missingConfigError->type = 'error';
$missingConfigError->message = 'Map data not valid (config || data) missing';
$missingConfigError->text = 'Map data not valid (config || data) missing';
$return->error[] = $missingConfigError;
}
@@ -422,239 +422,20 @@ class Map extends Controller\AccessController {
}else{
$unknownMapType = (object) [];
$unknownMapType->type = 'error';
$unknownMapType->message = 'Map type unknown!';
$unknownMapType->text = 'Map type unknown!';
$return->error[] = $unknownMapType;
}
}else{
// map data missing
$missingDataError = (object) [];
$missingDataError->type = 'error';
$missingDataError->message = 'Map data missing';
$missingDataError->text = 'Map data missing';
$return->error[] = $missingDataError;
}
echo json_encode($return);
}
/**
* save a new map or update an existing map
* @param \Base $f3
* @throws \Exception
*/
public function save(\Base $f3){
$formData = (array)$f3->get('POST.formData');
$return = (object) [];
$return->error = [];
if( isset($formData['id']) ){
$activeCharacter = $this->getCharacter();
/**
* @var $map Pathfinder\MapModel
*/
$map = Pathfinder\AbstractPathfinderModel::getNew('MapModel');
$map->getById( (int)$formData['id'] );
if(
$map->dry() ||
$map->hasAccess($activeCharacter)
){
try{
// new map
$map->setData($formData);
$map = $map->save($activeCharacter);
$mapDefaultConf = Config::getMapsDefaultConfig();
// save global map access. Depends on map "type"
if($map->isPrivate()){
// share map between characters -> set access
if(isset($formData['mapCharacters'])){
// remove character (re-add later)
$accessCharacters = array_diff($formData['mapCharacters'], [$activeCharacter->_id]);
// avoid abuse -> respect share limits
$maxShared = max($mapDefaultConf['private']['max_shared'] - 1, 0);
$accessCharacters = array_slice($accessCharacters, 0, $maxShared);
// clear map access. In case something has removed from access list
$map->clearAccess();
if($accessCharacters){
/**
* @var $tempCharacter Pathfinder\CharacterModel
*/
$tempCharacter = Pathfinder\AbstractPathfinderModel::getNew('CharacterModel');
foreach($accessCharacters as $characterId){
$tempCharacter->getById( (int)$characterId );
if(
!$tempCharacter->dry() &&
$tempCharacter->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempCharacter);
}
$tempCharacter->reset();
}
}
}
// the current character itself should always have access
// just in case he removed himself :)
$map->setAccess($activeCharacter);
}elseif($map->isCorporation()){
$corporation = $activeCharacter->getCorporation();
if($corporation){
// the current user has to have a corporation when
// working on corporation maps!
// share map between corporations -> set access
if(isset($formData['mapCorporations'])){
// remove character corporation (re-add later)
$accessCorporations = array_diff($formData['mapCorporations'], [$corporation->_id]);
// avoid abuse -> respect share limits
$maxShared = max($mapDefaultConf['corporation']['max_shared'] - 1, 0);
$accessCorporations = array_slice($accessCorporations, 0, $maxShared);
// clear map access. In case something has removed from access list
$map->clearAccess();
if($accessCorporations){
/**
* @var $tempCorporation Pathfinder\CorporationModel
*/
$tempCorporation = Pathfinder\AbstractPathfinderModel::getNew('CorporationModel');
foreach($accessCorporations as $corporationId){
$tempCorporation->getById( (int)$corporationId );
if(
!$tempCorporation->dry() &&
$tempCorporation->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempCorporation);
}
$tempCorporation->reset();
}
}
}
// the corporation of the current user should always have access
$map->setAccess($corporation);
}
}elseif($map->isAlliance()){
$alliance = $activeCharacter->getAlliance();
if($alliance){
// the current user has to have a alliance when
// working on alliance maps!
// share map between alliances -> set access
if(isset($formData['mapAlliances'])){
// remove character alliance (re-add later)
$accessAlliances = array_diff($formData['mapAlliances'], [$alliance->_id]);
// avoid abuse -> respect share limits
$maxShared = max($mapDefaultConf['alliance']['max_shared'] - 1, 0);
$accessAlliances = array_slice($accessAlliances, 0, $maxShared);
// clear map access. In case something has removed from access list
$map->clearAccess();
if($accessAlliances){
/**
* @var $tempAlliance Pathfinder\AllianceModel
*/
$tempAlliance = Pathfinder\AbstractPathfinderModel::getNew('AllianceModel');
foreach($accessAlliances as $allianceId){
$tempAlliance->getById( (int)$allianceId );
if(
!$tempAlliance->dry() &&
$tempAlliance->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempAlliance);
}
$tempAlliance->reset();
}
}
}
// the alliance of the current user should always have access
$map->setAccess($alliance);
}
}
// reload the same map model (refresh)
// this makes sure all data is up2date
$map->getById($map->_id, 0);
// broadcast map Access -> and send map Data
$this->broadcastMapAccess($map);
$return->mapData = $map->getData();
}catch(Exception\ValidationException $e){
$return->error[] = $e->getError();
}
}else{
// map access denied
$captchaError = (object) [];
$captchaError->type = 'error';
$captchaError->message = 'Access denied';
$return->error[] = $captchaError;
}
}else{
// map id field missing
$idError = (object) [];
$idError->type = 'error';
$idError->message = 'Map id missing';
$return->error[] = $idError;
}
echo json_encode($return);
}
/**
* delete a map and all dependencies
* @param \Base $f3
* @throws \Exception
*/
public function delete(\Base $f3){
$mapData = (array)$f3->get('POST.mapData');
$mapId = (int)$mapData['id'];
$return = (object) [];
$return->deletedMapIds = [];
if($mapId){
$activeCharacter = $this->getCharacter();
/**
* @var $map Pathfinder\MapModel
*/
$map = Pathfinder\AbstractPathfinderModel::getNew('MapModel');
$map->getById($mapId);
if($map->hasAccess($activeCharacter)){
$map->setActive(false);
$map->save($activeCharacter);
$return->deletedMapIds[] = $mapId;
// broadcast map delete
$this->broadcastMapDeleted($mapId);
}
}
echo json_encode($return);
}
/**
* broadcast characters with map access rights to WebSocket server
* -> if characters with map access found -> broadcast mapData to them
@@ -665,7 +446,7 @@ class Map extends Controller\AccessController {
$mapAccess = [
'id' => $map->_id,
'name' => $map->name,
'characterIds' => array_map(function ($data){
'characterIds' => array_map(function($data){
return $data->id;
}, $map->getCharactersData())
];
@@ -673,15 +454,7 @@ class Map extends Controller\AccessController {
$this->getF3()->webSocket()->write('mapAccess', $mapAccess);
// map has (probably) active connections that should receive map Data
$this->broadcastMap($map);
}
/**
* broadcast map delete information to clients
* @param int $mapId
*/
protected function broadcastMapDeleted(int $mapId){
$this->getF3()->webSocket()->write('mapDeleted', $mapId);
$this->broadcastMap($map, true);
}
/**

View File

@@ -0,0 +1,239 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus 4D
* Date: 12.03.2020
* Time: 19:30
*/
namespace Exodus4D\Pathfinder\Controller\Api\Rest;
use Exodus4D\Pathfinder\Lib\Config;
use Exodus4D\Pathfinder\Model\Pathfinder;
class Map extends AbstractRestController {
/**
* error message missing character right for map delete
*/
const ERROR_MAP_DELETE = 'Character %s does not have sufficient rights for map delete';
/**
* @param \Base $f3
* @param $test
* @throws \Exception
*/
public function put(\Base $f3, $test){
$requestData = $this->getRequestData($f3);
/**
* @var $map Pathfinder\MapModel
*/
$map = Pathfinder\AbstractPathfinderModel::getNew('MapModel');
$mapData = $this->update($map, $requestData)->getData();
$this->out($mapData);
}
/**
* @param \Base $f3
* @param $params
* @throws \Exception
*/
public function patch(\Base $f3, $params){
$requestData = $this->getRequestData($f3);
$mapData = [];
if($mapId = (int)$params['id']){
$activeCharacter = $this->getCharacter();
/**
* @var $map Pathfinder\MapModel
*/
$map = Pathfinder\AbstractPathfinderModel::getNew('MapModel');
$map->getById($mapId);
if($map->hasAccess($activeCharacter)){
$mapData = $this->update($map, $requestData)->getData(true);
}
}
$this->out($mapData);
}
/**
* @param \Base $f3
* @param $params
* @throws \Exception
*/
public function delete(\Base $f3, $params){
$deletedMapIds = [];
if($mapId = (int)$params['id']){
$activeCharacter = $this->getCharacter();
/**
* @var $map Pathfinder\MapModel
*/
$map = Pathfinder\AbstractPathfinderModel::getNew('MapModel');
$map->getById($mapId);
if($map->hasAccess($activeCharacter)){
// check if character has delete right for map type
$hasRight = true;
if($map->isCorporation()){
if($corpRight = $activeCharacter->getCorporation()->getRights(['map_delete'])){
if($corpRight[0]->get('roleId', true) !== $activeCharacter->get('roleId', true)){
$hasRight = false;
}
}
}
if($hasRight){
$map->setActive(false);
$map->save($activeCharacter);
$deletedMapIds[] = $mapId;
// broadcast map delete
$this->broadcastMapDeleted($mapId);
}else{
$f3->set('HALT', true);
$f3->error(401, sprintf(self::ERROR_MAP_DELETE, $activeCharacter->name));
}
}
}
$this->out($deletedMapIds);
}
/**
* @param Pathfinder\MapModel $map
* @param array $mapData
* @return Pathfinder\MapModel
* @throws \Exception
*/
private function update(Pathfinder\MapModel $map, array $mapData) : Pathfinder\MapModel {
$activeCharacter = $this->getCharacter();
$map->setData($mapData);
$typeChange = $map->changed('typeId');
$map->save($activeCharacter);
// save global map access. Depends on map "type" --------------------------------------------------------------
/**
* @param Pathfinder\AbstractPathfinderModel $primaryModel
* @param array|null $modelIds
* @param int $maxShared
* @return int
*/
$setMapAccess = function(Pathfinder\AbstractPathfinderModel &$primaryModel, ?array $modelIds = [], int $maxShared = 3) use (&$map) : int {
$added = 0;
$deleted = 0;
if(is_array($modelIds)){
// remove primaryModel id (-> re-add later)
$modelIds = array_diff(array_map('intval', $modelIds), [$primaryModel->_id]);
// avoid abuse -> respect share limits (-1 is because the primaryModel has also access)
$modelIds = array_slice($modelIds, 0, max($maxShared - 1, 0));
// add the primaryModel id back (again)
$modelIds[] = $primaryModel->_id;
// clear map access for entities that do not match the map "mapType"
$deleted += $map->clearAccessByType();
$compare = $map->compareAccess($modelIds);
foreach((array)$compare['old'] as $modelId) {
$deleted += $map->removeFromAccess($modelId);
}
$modelClass = (new \ReflectionClass($primaryModel))->getShortName();
$tempModel = Pathfinder\AbstractPathfinderModel::getNew($modelClass);
foreach((array)$compare['new'] as $modelId) {
$tempModel->getById($modelId);
if(
$tempModel->valid() &&
(
$modelId == $primaryModel->_id || // primary model has always access (regardless of "shared" value)
$tempModel->shared == 1 // check if map shared is enabled
)
){
$added += (int)$map->setAccess($tempModel);
}
$tempModel->reset();
}
}
return $added + $deleted;
};
$accessChangeCount = 0;
$mapDefaultConf = Config::getMapsDefaultConfig();
if($map->isPrivate()){
$accessChangeCount = $setMapAccess(
$activeCharacter,
$typeChange ? [$activeCharacter->_id] : $mapData['mapCharacters'],
(int)$mapDefaultConf['private']['max_shared']
);
}elseif($map->isCorporation()){
if($corporation = $activeCharacter->getCorporation()){
$accessChangeCount = $setMapAccess(
$corporation,
$typeChange ? [$corporation->_id] : $mapData['mapCorporations'],
(int)$mapDefaultConf['corporation']['max_shared']
);
}
}elseif($map->isAlliance()){
if($alliance = $activeCharacter->getAlliance()){
$accessChangeCount = $setMapAccess(
$alliance,
$typeChange ? [$alliance->_id] : $mapData['mapAlliances'],
(int)$mapDefaultConf['alliance']['max_shared']
);
}
}
if($accessChangeCount){
$map->touch('updated');
$map->save($activeCharacter);
}
// reload the same map model (refresh)
// this makes sure all data is up2date
$map->getById($map->_id, 0);
// broadcast map Access -> and send map Data
$this->broadcastMapAccess($map);
return $map;
}
/**
* broadcast characters with map access rights to WebSocket server
* -> if characters with map access found -> broadcast mapData to them
* @param Pathfinder\MapModel $map
* @throws \Exception
*/
protected function broadcastMapAccess(Pathfinder\MapModel $map){
$mapAccess = [
'id' => $map->_id,
'name' => $map->name,
'characterIds' => array_map(function($data){
return $data->id;
}, $map->getCharactersData())
];
$this->getF3()->webSocket()->write('mapAccess', $mapAccess);
// map has (probably) active connections that should receive map Data
$this->broadcastMap($map, true);
}
/**
* broadcast map delete information to clients
* @param int $mapId
*/
private function broadcastMapDeleted(int $mapId){
$this->getF3()->webSocket()->write('mapDeleted', $mapId);
}
}

View File

@@ -46,7 +46,7 @@ class System extends Controller\AccessController {
}else{
$error = (object) [];
$error->type = 'error';
$error->message = $response['error'];
$error->text = $response['error'];
$return->error[] = $error;
}
}

View File

@@ -140,7 +140,7 @@ class User extends Controller\Controller{
}else{
$characterError = (object) [];
$characterError->type = 'warning';
$characterError->message = 'This can happen through "invalid cookies(SSO)", "login restrictions", "ESI problems".';
$characterError->text = 'This can happen through "invalid cookies(SSO)", "login restrictions", "ESI problems".';
$return->error[] = $characterError;
}
}
@@ -184,7 +184,7 @@ class User extends Controller\Controller{
}else{
$captchaError = (object) [];
$captchaError->type = 'error';
$captchaError->message = 'Could not create captcha image';
$captchaError->text = 'Could not create captcha image';
$return->error[] = $captchaError;
}
@@ -225,7 +225,7 @@ class User extends Controller\Controller{
}else{
$error = (object) [];
$error->type = 'error';
$error->message = $response['error'];
$error->text = $response['error'];
$return->error[] = $error;
}
}
@@ -289,7 +289,7 @@ class User extends Controller\Controller{
// captcha was send but not valid -> return error
$captchaError = (object)[];
$captchaError->type = 'error';
$captchaError->message = 'Captcha does not match';
$captchaError->text = 'Captcha does not match';
$return->error[] = $captchaError;
}
}
@@ -377,7 +377,7 @@ class User extends Controller\Controller{
// captcha not valid -> return error
$captchaError = (object) [];
$captchaError->type = 'error';
$captchaError->message = 'Captcha does not match';
$captchaError->text = 'Captcha does not match';
$return->error[] = $captchaError;
}

View File

@@ -487,7 +487,7 @@ class Controller {
* @param bool $deleteSession
* @param bool $deleteLog
* @param bool $deleteCookie
* @param int $status
* @param int $statusCode
* @throws \Exception
*/
protected function logoutCharacter(
@@ -496,7 +496,7 @@ class Controller {
bool $deleteSession = true,
bool $deleteLog = true,
bool $deleteCookie = false,
int $status = self::DEFAULT_STATUS_LOGOUT
int $statusCode = self::DEFAULT_STATUS_LOGOUT
){
$sessionCharacterData = (array)$f3->get(Api\User::SESSION_KEY_CHARACTERS);
@@ -526,11 +526,11 @@ class Controller {
}
if($f3->get('AJAX')){
$f3->status($status);
$f3->status($statusCode);
$return = (object) [];
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login');
$return->error[] = $this->getErrorObject($status, Config::getMessageFromHTTPStatus($status));
$return->error[] = $this->getErrorObject($statusCode, Config::getHttpStatusByCode($statusCode), 'Access denied: User not found');
echo json_encode($return);
}else{
@@ -639,18 +639,18 @@ class Controller {
/**
* @param int $code
* @param string $message
* @param string $status
* @param string $text
* @param null $trace
* @return \stdClass
*/
protected function getErrorObject(int $code, string $message = '', string $status = '', $trace = null): \stdClass{
protected function getErrorObject(int $code, string $status = '', string $text = '', $trace = null) : \stdClass {
$object = (object) [];
$object->type = 'error';
$object->code = $code;
$object->status = empty($status) ? @constant('Base::HTTP_' . $code) : $status;
if(!empty($message)){
$object->message = $message;
if(!empty($text)){
$object->text = $text;
}
if(!empty($trace)){
$object->trace = $trace;
@@ -660,15 +660,15 @@ class Controller {
/**
* @param string $title
* @param string $message
* @param string $text
* @param string $type
* @return \stdClass
*/
protected function getNotificationObject(string $title, $message = '', $type = 'danger') : \stdClass {
protected function getNotificationObject(string $title, $text = '', $type = 'danger') : \stdClass {
$notification = (object) [];
$notification->type = in_array($type, self::NOTIFICATION_TYPES) ? $type : 'danger';
$notification->title = $title;
$notification->message = $message;
$notification->text = $text;
return $notification;
}
@@ -762,7 +762,7 @@ class Controller {
if(count($matches) === 2){
$error->field = $matches[1][1];
$error->message = 'Value "' . $matches[0][1] . '" already exists';
$error->text = 'Value "' . $matches[0][1] . '" already exists';
}
}

View File

@@ -1570,7 +1570,7 @@ class Setup extends Controller {
// setup tables
foreach($this->databases[$dbAlias]['models'] as $modelClass){
$checkTables[] = call_user_func($modelClass . '::setup', $db);
$checkTables[] = call_user_func(Config::withNamespace($modelClass) . '::setup', $db);
}
}
return $checkTables;

View File

@@ -49,7 +49,7 @@ class PathfinderException extends \Exception {
$error->type = 'error';
$error->code = $this->getResponseCode();
$error->status = Config::getHttpStatusByCode($this->getResponseCode());
$error->message = $this->getMessage();
$error->text = $this->getMessage();
if(\Base::instance()->get('DEBUG') >= 1){
$error->trace = preg_split('/\R/', $this->getTraceAsString()); // no $this->>getTrace() here -> to much data
}

View File

@@ -132,7 +132,7 @@ class Config extends \Prefab {
* custom HTTP status codes
*/
const
HTTP_422='Unprocessable Entity';
HTTP_422 = 'Unprocessable Entity';
/**
* all environment data
@@ -463,22 +463,6 @@ class Config extends \Prefab {
return $data;
}
/**
* get custom $message for a a HTTP $status
* -> use this in addition to the very general Base::HTTP_XXX labels
* @param int $status
* @return string
*/
static function getMessageFromHTTPStatus(int $status) : string {
switch($status){
case 403:
$message = 'Access denied: User not found'; break;
default:
$message = '';
}
return $message;
}
/**
* use this function to "validate" the socket connection.
* The result will be CACHED for a few seconds!
@@ -569,7 +553,7 @@ class Config extends \Prefab {
}
/**
* get HTTP status message by HTTP return code
* get HTTP status by HTTP return code
* -> either from F3 or from self::Config constants
* @param int $code
* @return string

View File

@@ -54,21 +54,6 @@ abstract class AbstractPathfinderModel extends AbstractModel {
return true;
}
/**
* set "updated" field to current timestamp
* this is useful to mark a row as "changed"
*/
protected function setUpdated(){
if($this->_id > 0){
$this->db->exec(
["UPDATE " . $this->table . " SET updated=NOW() WHERE id=:id"],
[
[':id' => $this->_id]
]
);
}
}
/**
* get old and new value from field, in case field is configured with 'activity-log'
* @return array

View File

@@ -91,7 +91,8 @@ class CorporationModel extends AbstractPathfinderModel {
'map_update',
'map_delete',
'map_import',
'map_export'
'map_export',
'map_share'
];
/**
@@ -303,15 +304,16 @@ class CorporationModel extends AbstractPathfinderModel {
/**
* get all corporation rights
* @param array $names
* @param array $options
* @return CorporationRightModel[]
* @throws \Exception
*/
public function getRights($options = []) : array {
public function getRights($names = self::RIGHTS, $options = []) : array {
$corporationRights = [];
// get available rights
$right = self::getNew('RightModel');
if($rights = $right->find(['active = ? AND name IN (?)', 1, self::RIGHTS])){
if($rights = $right->find(['active = ? AND name IN (?)', 1, $names])){
// get already stored rights
if( !$options['addInactive'] ){
$this->filter('corporationRights', ['active = ?', 1]);

View File

@@ -217,14 +217,13 @@ class MapModel extends AbstractMapTrackingModel {
/**
* get data
* -> this includes system and connection data as well
* @return \stdClass
* @throws \Exception
* @param bool $noCache
* @return mixed|object|null
* @throws Exception\ConfigException
*/
public function getData(){
public function getData(bool $noCache = false){
// check if there is cached data
$mapDataAll = $this->getCacheData();
if(is_null($mapDataAll)){
if($noCache || is_null($mapDataAll = $this->getCacheData())){
// no cached map data found
$mapData = (object) [];
@@ -702,99 +701,134 @@ class MapModel extends AbstractMapTrackingModel {
/**
* set map access for an object (character, corporation or alliance)
* @param $obj
* @return bool
* @throws \Exception
*/
public function setAccess($obj){
public function setAccess($obj) : bool {
$newAccessGranted = false;
if($obj instanceof CharacterModel){
// private map
// check whether the user has already map access
$this->has('mapCharacters', ['active = 1 AND characterId = :characterId', ':characterId' => $obj->id]);
$result = $this->findone(['id = :id', ':id' => $this->id]);
if($result === false){
$result = $this->relFindOne('mapCharacters', self::getFilter('characterId', $obj->_id));
if(!$result){
// grant access for the character
$characterMap = self::getNew('CharacterMapModel');
$characterMap->characterId = $obj;
$characterMap->mapId = $this;
$characterMap->save();
$newAccessGranted = true;
if($characterMap->save()){
$newAccessGranted = true;
}
}
}elseif($obj instanceof CorporationModel){
// check whether the corporation already has map access
$this->has('mapCorporations', ['active = 1 AND corporationId = :corporationId', ':corporationId' => $obj->id]);
$result = $this->findone(['id = :id', ':id' => $this->id]);
if($result === false){
$result = $this->relFindOne('mapCorporations', self::getFilter('corporationId', $obj->_id));
if(!$result){
// grant access for this corporation
$corporationMap = self::getNew('CorporationMapModel');
$corporationMap->corporationId = $obj;
$corporationMap->mapId = $this;
$corporationMap->save();
$newAccessGranted = true;
if($corporationMap->save()){
$newAccessGranted = true;
}
}
}elseif($obj instanceof AllianceModel){
// check whether the alliance already has map access
$this->has('mapAlliances', ['active = 1 AND allianceId = :allianceId', ':allianceId' => $obj->id]);
$result = $this->findone(['id = :id', ':id' => $this->id]);
if($result === false){
$result = $this->relFindOne('mapAlliances', self::getFilter('allianceId', $obj->_id));
if(!$result){
$allianceMap = self::getNew('AllianceMapModel');
$allianceMap->allianceId = $obj;
$allianceMap->mapId = $this;
$allianceMap->save();
$newAccessGranted = true;
if($allianceMap->save()){
$newAccessGranted = true;
}
}
}
return $newAccessGranted;
}
if($newAccessGranted){
// mark this map as updated
$this->setUpdated();
/**
* @param $stack
* @return array
*/
public function compareAccess($stack) : array {
$result = [];
if($this->valid()){
if($this->isPrivate()){
$result = $this->mapCharacters ? $this->mapCharacters->compare($stack, 'characterId') : ['new' => $stack];
}elseif($this->isCorporation()){
$result = $this->mapCorporations ? $this->mapCorporations->compare($stack, 'corporationId') : ['new' => $stack];
}elseif($this->isAlliance()){
$result = $this->mapAlliances ? $this->mapAlliances->compare($stack, 'allianceId') : ['new' => $stack];
}
}
return $result;
}
/**
* @param int $id
* @return int
*/
public function removeFromAccess(int $id) : int {
$count = 0;
if($id && $this->valid()){
$result = null;
if($this->isPrivate()){
$result = $this->relFindOne('mapCharacters', self::getFilter('characterId', $id));
}elseif($this->isCorporation()){
$result = $this->relFindOne('mapCorporations', self::getFilter('corporationId', $id));
}elseif($this->isAlliance()){
$result = $this->relFindOne('mapAlliances', self::getFilter('allianceId', $id));
}
if($result && $result->erase()){
$count++;
}
}
return $count;
}
/**
* clear map access for entities that do not match the map "mapType"
* @return int
*/
public function clearAccessByType() : int {
$count = 0;
if($this->valid()){
if($this->isPrivate()){
$count = $this->clearAccess(['corporation', 'alliance']);
}elseif($this->isCorporation()){
$count = $this->clearAccess(['character', 'alliance']);
}elseif($this->isAlliance()){
$count = $this->clearAccess(['character', 'corporation']);
}
}
return $count;
}
/**
* clear access for a given type of objects
* @param array $clearKeys
* @return int
*/
public function clearAccess($clearKeys = ['character', 'corporation', 'alliance']){
public function clearAccess($clearKeys = ['character', 'corporation', 'alliance']) : int {
$count = 0;
foreach($clearKeys as $key){
$field = null;
switch($key){
case 'character':
foreach((array)$this->mapCharacters as $characterMapModel){
/**
* @var CharacterMapModel $characterMapModel
*/
$characterMapModel->erase();
case 'character': $field = 'mapCharacters'; break;
case 'corporation': $field = 'mapCorporations'; break;
case 'alliance': $field = 'mapAlliances'; break;
}
if($this->$field){
foreach((array)$this->$field as $model){
if($model->erase()){
$count++;
}
break;
case 'corporation':
foreach((array)$this->mapCorporations as $corporationMapModel){
/**
* @var CorporationMapModel $corporationMapModel
*/
$corporationMapModel->erase();
}
break;
case 'alliance':
foreach((array)$this->mapAlliances as $allianceMapModel){
/**
* @var AllianceMapModel $allianceMapModel
*/
$allianceMapModel->erase();
}
break;
}
}
}
return $count;
}
/**
@@ -1429,17 +1463,6 @@ class MapModel extends AbstractMapTrackingModel {
*/
$mapModel = parent::save($characterModel);
// check if map type has changed and clear access objects
if( !$mapModel->dry() ){
if( $mapModel->isPrivate() ){
$mapModel->clearAccess(['corporation', 'alliance']);
}elseif( $mapModel->isCorporation() ){
$mapModel->clearAccess(['character', 'alliance']);
}elseif( $mapModel->isAlliance() ){
$mapModel->clearAccess(['character', 'corporation']);
}
}
return $mapModel;
}

View File

@@ -76,6 +76,12 @@ class RightModel extends AbstractPathfinderModel {
'name' => 'map_export',
'label' => 'export',
'description' => 'Map export right'
],
[
'id' => 5,
'name' => 'map_share',
'label' => 'share',
'description' => 'Map share right'
]
];

View File

@@ -46,7 +46,7 @@ requirejs.config({
easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart
peityInlineChart: 'lib/jquery.peity.min', // v3.3.0 Inline Chart - http://benpickles.github.io/peity/
hoverIntent: 'lib/jquery.hoverIntent.min', // v1.10.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io
select2: 'lib/select2.min', // v4.0.13 Drop Down customization - https://select2.github.io
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info
blueImpGallery: 'lib/blueimp-gallery', // v2.21.3 Image Gallery - https://github.com/blueimp/Gallery

View File

@@ -38,8 +38,6 @@ define([], () => {
updateUserData: '/api/map/updateUserData', // ajax URL - main map user data trigger
updateUnloadData: '/api/map/updateUnloadData', // post URL - for my sync onUnload
// map API
saveMap: '/api/map/save', // ajax URL - save/update map
deleteMap: '/api/map/delete', // ajax URL - delete map
importMap: '/api/map/import', // ajax URL - import map
getMapConnectionData: '/api/map/getConnectionData', // ajax URL - get connection data
getMapLogData: '/api/map/getLogData', // ajax URL - get logs data
@@ -138,6 +136,18 @@ define([], () => {
class: 'fa-hat-wizard',
label: 'hat wizard',
unicode: '&#xf6e8;'
},{
class: 'fa-cross',
label: 'cross',
unicode: '&#xf654;'
}, {
class: 'fa-cannabis',
label: 'cannabis',
unicode: '&#xf55f;'
}, {
class: 'fa-spider',
label: 'spider',
unicode: '&#xf717;'
}, {
class: 'fa-plane',
label: 'plane',

View File

@@ -194,7 +194,7 @@ define([
currentUserIsHere &&
!userWasHere &&
Boolean(Util.getObjVal(Init, 'character.autoLocationSelect')) &&
Boolean(Util.getObjVal(Util.getCurrentUserData(), 'character.selectLocation'))
Boolean(Util.getCurrentCharacterData('selectLocation'))
){
Util.triggerMenuAction(map.getContainer(), 'SelectSystem', {systemId: system.data('id'), forceSelect: false});
}
@@ -2794,7 +2794,7 @@ define([
let mapElement = $(this);
let mapOverlay = MapOverlayUtil.getMapOverlay(mapElement, 'local');
let currentMapData = Util.getCurrentMapData(mapId);
let currentCharacterLog = Util.getCurrentCharacterLog();
let currentCharacterLog = Util.getCurrentCharacterData('log');
let clearLocal = true;
if(
@@ -2891,7 +2891,7 @@ define([
let compactView = mapElement.hasClass(MapUtil.config.mapCompactClass);
// get current character log data
let characterLogSystemId = Util.getObjVal(Util.getCurrentCharacterLog(), 'system.id') || 0;
let characterLogSystemId = Util.getObjVal(Util.getCurrentCharacterData('log'), 'system.id') || 0;
// data for header update
let headerUpdateData = {

View File

@@ -186,7 +186,7 @@ define([
systemData = options.systemData;
}else{
// ... check for current active system (characterLog) -----------------------------------------------------
let currentCharacterLog = Util.getCurrentCharacterLog();
let currentCharacterLog = Util.getCurrentCharacterData('log');
if(currentCharacterLog !== false){
// set system from 'characterLog' data as pre-selected system
systemData = Util.getObjVal(currentCharacterLog, 'system');

View File

@@ -2005,60 +2005,26 @@ define([
*/
let checkRight = (right, mapConfig) => {
let hasAccess = false;
let currentUserData = Util.getCurrentUserData();
if(currentUserData){
// ...there is an active user
let currentCharacterData = Util.getObjVal(currentUserData, 'character');
if(currentCharacterData){
// ... there is an active character
let currentCharacterRole = Util.getObjVal(currentCharacterData, 'role');
if(currentCharacterRole){
// ... active character has a role assigned
let mapType = Util.getObjVal(mapConfig, 'type.name');
let accessObjectId = Util.getCurrentUserInfo(mapType + 'Id');
let mapType = Util.getObjVal(mapConfig, 'type.name');
let mapAccess = Util.getObjVal(mapConfig, 'access.' + (mapType === 'private' ? 'character' : mapType)) || [];
// this is either Ally/Corp or Character Id
let accessObjectId = Util.getCurrentUserInfo(mapType + 'Id');
// check whether character has map access
let hasMapAccess = mapAccess.some((accessObj) => {
return (accessObj.id === accessObjectId);
});
if(hasMapAccess){
// ... this should ALWAYS be be true!
switch(mapType){
case 'private':
hasAccess = true;
break;
case 'corporation':
let objectRights = Util.getObjVal(currentCharacterData, mapType + '.rights') || [];
let objectRight = objectRights.find((objectRight) => {
return objectRight.right.name === right;
});
if(objectRight){
// ... Ally/Corp has the right we are looking for assigned with a required role
if(
currentCharacterRole.name === 'SUPER' ||
objectRight.role.name === 'MEMBER' ||
objectRight.role.name === currentCharacterRole.name
){
hasAccess = true;
}
}
break;
case 'alliance':
hasAccess = true;
break;
}
}
}
// check whether character has map access
let mapAccess = Util.getObjVal(mapConfig, 'access.' + (mapType === 'private' ? 'character' : mapType)) || [];
let hasMapAccess = mapAccess.some(accessObj => accessObj.id === accessObjectId);
if(hasMapAccess){
// ... this should ALWAYS be be true!
switch(mapType){
case 'private':
hasAccess = true;
break;
case 'corporation':
hasAccess = Util.hasRight(right, mapType);
break;
case 'alliance':
hasAccess = true;
break;
}
}
return hasAccess;
};

View File

@@ -451,7 +451,7 @@ define([
mapIds: mapId ? [mapId] : [],
getMapUserData: Util.getSyncType() === 'webSocket' ? 0 : 1,
mapTracking: locationToggle ? locationToggle.checked | 0 : 0, // location tracking
systemData: Util.getCurrentSystemData(mapId)
systemData: mapId ? Util.getCurrentSystemData(mapId) : []
};
if(newSystemPositions){
@@ -540,7 +540,8 @@ define([
// clear both main update request trigger timer
clearUpdateTimeouts();
let reason = status + ' ' + jqXHR.status + ': ' + error;
let reason = `${status} ${jqXHR.status}`;
let firstError = error;
let errorData = [];
let redirect = false; // redirect user to other page e.g. login
let reload = true; // reload current page (default: true)
@@ -552,6 +553,7 @@ define([
responseObj.error &&
responseObj.error.length > 0
){
firstError = responseObj.error[0].status;
errorData = responseObj.error;
}
@@ -569,7 +571,7 @@ define([
console.error(' ↪ %s Error response: %o', jqXHR.url, errorData);
$(document).trigger('pf:shutdown', {
status: jqXHR.status,
reason: reason,
reason: `${reason}: ${firstError}`,
error: errorData,
redirect: redirect,
reload: reload

View File

@@ -823,17 +823,17 @@ define([
});
// changes in current userData ----------------------------------------------------------------------------
documentElement.on('pf:changedUserData', (e, userData, changes) => {
documentElement.on('pf:changedUserData', (e, changes) => {
// update menu buttons (en/disable)
if(changes.characterId){
documentElement.trigger('pf:updateMenuOptions', {
menuGroup: 'userOptions',
payload: Boolean(Util.getObjVal(userData, 'character.id'))
payload: Boolean(Util.getCurrentCharacterData('id'))
});
}
// update header
updateHeaderUserData(userData, changes).then();
updateHeaderUserData(changes).then();
});
// shutdown the program -> show dialog --------------------------------------------------------------------
@@ -873,7 +873,7 @@ define([
// add error information (if available)
if(data.error && data.error.length){
for(let error of data.error){
options.content.textSmaller.push(error.message);
options.content.textSmaller.push(error.text);
}
}
@@ -929,7 +929,7 @@ define([
//modalElement.find('form').filter((i, form) => $(form).data('bs.validator')).validator('destroy');
// destroy all popovers
modalElement.find('.' + Util.config.popoverTriggerClass).popover('destroy');
modalElement.destroyPopover(true);
// destroy all Select2
modalElement.find('.' + Util.config.select2Class)
@@ -1050,18 +1050,17 @@ define([
/**
* update all header elements with current userData
* @param userData
* @param changes
* @returns {Promise<[any, any, any, any, any, any, any, any, any, any]>}
* @returns {Promise<[]>}
*/
let updateHeaderUserData = (userData, changes) => {
let updateHeaderUserData = changes => {
let updateTasks = [];
if(changes.characterLogLocation){
updateTasks.push(updateMapTrackingToggle(Boolean(Util.getObjVal(userData, 'character.logLocation'))));
updateTasks.push(updateMapTrackingToggle(Boolean(Util.getCurrentCharacterData('logLocation'))));
}
if(changes.charactersIds){
updateTasks.push(updateHeaderCharacterSwitch(userData, changes.characterId));
updateTasks.push(updateHeaderCharacterSwitch(changes.characterId));
}
if(
changes.characterSystemId ||
@@ -1070,7 +1069,7 @@ define([
changes.characterStructureId ||
changes.characterLogHistory
){
updateTasks.push(updateHeaderCharacterLocation(userData, changes.characterShipType));
updateTasks.push(updateHeaderCharacterLocation(changes.characterShipType));
}
return Promise.all(updateTasks);
@@ -1099,22 +1098,21 @@ define([
};
/**
* @param userData
* @param changedCharacter
* @returns {Promise<any>}
*/
let updateHeaderCharacterSwitch = (userData, changedCharacter) => {
let updateHeaderCharacterSwitch = changedCharacter => {
let executor = resolve => {
let userInfoElement = $('.' + config.headUserCharacterClass);
// toggle element
animateHeaderElement(userInfoElement, userInfoElement => {
if(changedCharacter){
// current character changed
userInfoElement.find('span').text(Util.getObjVal(userData, 'character.name'));
userInfoElement.find('img').attr('src', Util.eveImageUrl('characters', Util.getObjVal(userData, 'character.id')));
userInfoElement.find('span').text(Util.getCurrentCharacterData('name'));
userInfoElement.find('img').attr('src', Util.eveImageUrl('characters', Util.getCurrentCharacterData('id')));
}
// init "character switch" popover
userInfoElement.initCharacterSwitchPopover(userData);
userInfoElement.initCharacterSwitchPopover();
resolve({
action: 'updateHeaderCharacterSwitch',
@@ -1127,28 +1125,26 @@ define([
};
/**
*
* @param userData
* @param changedShip
* @returns {Promise<any>}
*/
let updateHeaderCharacterLocation = (userData, changedShip) => {
let updateHeaderCharacterLocation = changedShip => {
let executor = resolve => {
let userLocationElement = $('#' + Util.config.headUserLocationId);
let breadcrumbHtml = '';
let logData = Util.getObjVal(userData, 'character.log');
let logData = Util.getCurrentCharacterData('log');
let logDataAll = [];
if(logData){
let shipData = Util.getObjVal(userData, 'character.log.ship');
let shipData = Util.getObjVal(logData, 'ship');
let shipTypeId = Util.getObjVal(shipData, 'typeId') || 0;
let shipTypeName = Util.getObjVal(shipData, 'typeName') || '';
let stationData = Util.getObjVal(userData, 'character.log.station');
let stationData = Util.getObjVal(logData, 'station');
let stationId = Util.getObjVal(stationData, 'id') || 0;
let stationName = Util.getObjVal(stationData, 'name') || '';
let structureData = Util.getObjVal(userData, 'character.log.structure');
let structureData = Util.getObjVal(logData, 'structure');
let structureTypeId = Util.getObjVal(structureData, 'type.id') || 0;
let structureTypeName = Util.getObjVal(structureData, 'type.name') || '';
let structureId = Util.getObjVal(structureData, 'id') || 0;
@@ -1157,7 +1153,7 @@ define([
logDataAll.push(logData);
// check for log history data as well
let logHistoryData = Util.getObjVal(userData, 'character.logHistory');
let logHistoryData = Util.getCurrentCharacterData('logHistory');
if(logHistoryData){
// check if there are more history log entries than max visual limit
if(logHistoryData.length > config.headMaxLocationHistoryBreadcrumbs){

View File

@@ -53,8 +53,9 @@ define([
captchaImageId: config.captchaImageId,
formErrorContainerClass: Util.config.formErrorContainerClass,
ccpImageServer: Init.url.ccpImageServer,
roleLabel: Util.getLabelByRole(Util.getObjVal(Util.getCurrentUserData(), 'character.role')).prop('outerHTML'),
characterAutoLocationSelectEnabled: Boolean(Util.getObjVal(Init, 'character.autoLocationSelect'))
roleLabel: Util.getLabelByRole(Util.getCurrentCharacterData('role')).prop('outerHTML'),
characterAutoLocationSelectEnabled: Boolean(Util.getObjVal(Init, 'character.autoLocationSelect')),
hasRightCorporationShare: Util.hasRight('map_share', 'corporation')
};
let content = Mustache.render(template, data);

View File

@@ -193,8 +193,8 @@ define([
});
});
mapElement.initTooltips();
mapElement.hideLoadingAnimation();
mapElement.initTooltips({container: '.modal'});
};
/**
@@ -219,14 +219,6 @@ define([
systemsElement.showLoadingAnimation(config.loadingOptions);
systemTable.on('init.dt', function(){
systemsElement.hideLoadingAnimation();
// init table tooltips
let tooltipElements = systemsElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip();
});
let systemsDataTable = systemTable.DataTable({
pageLength: 20,
paging: true,
@@ -514,6 +506,9 @@ define([
}
],
initComplete: function(settings){
systemsElement.hideLoadingAnimation();
systemsElement.initTooltips({container: '.modal'});
Counter.initTableCounter(this, ['updated:name']);
}
});
@@ -542,15 +537,6 @@ define([
connectionsElement.showLoadingAnimation(config.loadingOptions);
// table init complete
connectionTable.on('init.dt', function(){
connectionsElement.hideLoadingAnimation();
// init table tooltips
let tooltipElements = connectionsElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip();
});
// connections table ------------------------------------------------------------------------------------------
let connectionDataTable = connectionTable.dataTable({
pageLength: 20,
@@ -701,6 +687,9 @@ define([
}
],
initComplete: function(settings){
connectionsElement.hideLoadingAnimation();
connectionsElement.initTooltips({container: '.modal'});
Counter.initTableCounter(this, ['updated:name']);
}
});
@@ -721,17 +710,6 @@ define([
usersElement.showLoadingAnimation(config.loadingOptions);
// table init complete
userTable.on('init.dt', function(){
usersElement.hideLoadingAnimation();
// init table tooltips
let tooltipElements = usersElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip({
container: usersElement.parent()
});
});
// users table ------------------------------------------------------------------------------------------------
// prepare users data for dataTables
let currentMapUserData = Util.getCurrentMapUserData( mapData.config.id );
@@ -941,7 +919,11 @@ define([
}
}
}
]
],
initComplete: function(settings){
usersElement.hideLoadingAnimation();
usersElement.initTooltips({container: '.modal'});
}
});
};

View File

@@ -9,8 +9,9 @@ define([
'app/render',
'bootbox',
'app/map/util',
'app/module_map'
], ($, Init, Util, Render, bootbox, MapUtil, ModuleMap) => {
'app/module_map',
'app/map/overlay/util',
], ($, Init, Util, Render, bootbox, MapUtil, ModuleMap, MapOverlayUtil) => {
'use strict';
let config = {
@@ -99,144 +100,39 @@ define([
'text!templates/form/map.html',
'mustache'
], (templateMapDialog, templateMapForm, Mustache) => {
let selectOption = value => () => (val, render) => {
if(render(val) === String(value)){
return 'selected';
}
};
let dialogTitle = 'Map settings';
// if there are no maps -> hide settings tab
let hideSettingsTab = false;
let hideEditTab = false;
let hideDownloadTab = false;
let hideEditTab = mapData === false;
let hideSettingsTab = mapData === false;
let hideDownloadTab = mapData === false;
let hasRightMapCreate = true;
let hasRightMapUpdate = true;
let hasRightMapExport = true;
let hasRightMapImport = true;
if(mapData === false){
hideSettingsTab = true;
hideEditTab = true;
hideDownloadTab = true;
}else{
hasRightMapUpdate = MapUtil.checkRight('map_update', mapData.config);
hasRightMapExport = MapUtil.checkRight('map_export', mapData.config);
hasRightMapImport = MapUtil.checkRight('map_import', mapData.config);
}
let hasRightMapUpdate = MapUtil ? MapUtil.checkRight('map_update', mapData.config) : true;
let hasRightMapExport = MapUtil ? MapUtil.checkRight('map_export', mapData.config) : true;
let hasRightMapImport = MapUtil ? MapUtil.checkRight('map_import', mapData.config) : true;
let hasRightMapShare = MapUtil ? MapUtil.checkRight('map_share', mapData.config) : true;
// available map "types" for a new or existing map
let mapTypes = MapUtil.getMapTypes(true);
let mapFormData = {
select2Class: Util.config.select2Class,
scope: MapUtil.getMapScopes(),
type: mapTypes,
icon: MapUtil.getMapIcons(),
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
formInfoContainerClass: Util.config.formInfoContainerClass
};
// render "new map" tab content -----------------------------------------------------------------------
let mapFormDataNew = $.extend({}, mapFormData, {
hasRightMapForm: hasRightMapCreate,
nameInputId: config.newNameInputId,
iconSelectId: config.newIconSelectId,
scopeSelectId: config.newScopeSelectId,
typeSelectId: config.newTypeSelectId
});
let contentNewMap = Mustache.render(templateMapForm, mapFormDataNew);
// render "edit map" tab content ----------------------------------------------------------------------
let mapFormDataEdit = $.extend({}, mapFormData, {
hasRightMapForm: hasRightMapUpdate,
nameInputId: config.editNameInputId,
iconSelectId: config.editIconSelectId,
scopeSelectId: config.editScopeSelectId,
typeSelectId: config.editTypeSelectId
});
let contentEditMap = Mustache.render(templateMapForm, mapFormDataEdit);
contentEditMap = $(contentEditMap);
// current map access info
let accessCharacter = [];
let accessCorporation = [];
let accessAlliance = [];
let deleteExpiredConnections = true;
let deleteEolConnections = true;
let persistentAliases = true;
let persistentSignatures = true;
let trackAbyssalJumps = true;
let logActivity = true;
let logHistory = true;
let slackWebHookURL = '';
let slackUsername = '';
let slackIcon = '';
let slackChannelHistory = '';
let slackChannelRally = '';
let slackEnabled = false;
let slackHistoryEnabled = false;
let slackRallyEnabled = false;
let slackSectionShow = false;
let discordUsername = '';
let discordWebHookURLRally = '';
let discordWebHookURLHistory = '';
let discordEnabled = false;
let discordRallyEnabled = false;
let discordHistoryEnabled = false;
let discordSectionShow = false;
if(mapData !== false){
// set current map information
contentEditMap.find('input[name="id"]').val( mapData.config.id );
contentEditMap.find('select[name="icon"]').val( mapData.config.icon );
contentEditMap.find('input[name="name"]').val( mapData.config.name );
contentEditMap.find('select[name="scopeId"]').val( mapData.config.scope.id );
contentEditMap.find('select[name="typeId"]').val( mapData.config.type.id );
accessCharacter = mapData.config.access.character;
accessCorporation = mapData.config.access.corporation;
accessAlliance = mapData.config.access.alliance;
deleteExpiredConnections = mapData.config.deleteExpiredConnections;
deleteEolConnections = mapData.config.deleteEolConnections;
persistentAliases = mapData.config.persistentAliases;
persistentSignatures = mapData.config.persistentSignatures;
trackAbyssalJumps = mapData.config.trackAbyssalJumps;
logActivity = mapData.config.logging.activity;
logHistory = mapData.config.logging.history;
slackWebHookURL = mapData.config.logging.slackWebHookURL;
slackUsername = mapData.config.logging.slackUsername;
slackIcon = mapData.config.logging.slackIcon;
slackChannelHistory = mapData.config.logging.slackChannelHistory;
slackChannelRally = mapData.config.logging.slackChannelRally;
slackEnabled = Boolean(Util.getObjVal(Init, 'slack.status'));
slackHistoryEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_slack_enabled'));
slackRallyEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled'));
slackSectionShow = (slackEnabled && slackWebHookURL.length > 0);
discordUsername = Util.getObjVal(mapData, 'config.logging.discordUsername');
discordWebHookURLRally = Util.getObjVal(mapData, 'config.logging.discordWebHookURLRally');
discordWebHookURLHistory = Util.getObjVal(mapData, 'config.logging.discordWebHookURLHistory');
discordEnabled = Boolean(Util.getObjVal(Init, 'discord.status'));
discordRallyEnabled = discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_discord_enabled'));
discordHistoryEnabled = discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_discord_enabled'));
discordSectionShow = (discordEnabled && (discordWebHookURLRally.length > 0 || discordWebHookURLHistory.length > 0));
// remove "#" from Slack channels
slackChannelHistory = slackChannelHistory.indexOf('#') === 0 ? slackChannelHistory.substr(1) : slackChannelHistory;
slackChannelRally = slackChannelRally.indexOf('#') === 0 ? slackChannelRally.substr(1) : slackChannelRally;
}
// render main dialog ---------------------------------------------------------------------------------
let mapDialogData = {
id: config.newMapDialogId,
mapData: mapData,
type: mapTypes,
hasRightMapUpdate,
hasRightMapExport,
hasRightMapImport,
hasRightMapShare,
// message container
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
@@ -253,9 +149,9 @@ define([
dialogMapSettingsContainerId: config.dialogMapSettingsContainerId,
dialogMapDownloadContainerId: config.dialogMapDownloadContainerId,
hideEditTab: hideEditTab,
hideSettingsTab: hideSettingsTab,
hideDownloadTab: hideDownloadTab,
hideEditTab,
hideSettingsTab,
hideDownloadTab,
// settings tab --------------
deleteExpiredConnectionsId : config.deleteExpiredConnectionsId,
@@ -263,57 +159,59 @@ define([
persistentAliasesId : config.persistentAliasesId,
persistentSignaturesId : config.persistentSignaturesId,
trackAbyssalJumpsId : config.trackAbyssalJumpsId,
deleteExpiredConnections: deleteExpiredConnections,
deleteEolConnections: deleteEolConnections,
persistentAliases: persistentAliases,
persistentSignatures: persistentSignatures,
trackAbyssalJumps: trackAbyssalJumps,
logHistoryId: config.logHistoryId,
logActivityId: config.logActivityId,
logActivity: logActivity,
logHistory: logHistory,
deleteExpiredConnections: true,
deleteEolConnections: true,
persistentAliases: true,
persistentSignatures: true,
trackAbyssalJumps: true,
logActivity: true,
logHistory: true,
slackWebHookURLId: config.slackWebHookURLId,
slackUsernameId: config.slackUsernameId,
slackIconId: config.slackIconId,
slackChannelHistoryId: config.slackChannelHistoryId,
slackChannelRallyId: config.slackChannelRallyId,
slackWebHookURL: slackWebHookURL,
slackUsername: slackUsername,
slackIcon: slackIcon,
slackChannelHistory: slackChannelHistory,
slackChannelRally: slackChannelRally,
slackEnabled: slackEnabled,
slackHistoryEnabled: slackHistoryEnabled,
slackRallyEnabled: slackRallyEnabled,
slackSectionShow: slackSectionShow,
slackWebHookURL: '',
slackUsername: '',
slackIcon: '',
slackChannelHistory: '',
slackChannelRally: '',
slackEnabled: false,
slackHistoryEnabled: false,
slackRallyEnabled: false,
slackSectionShow: false,
discordUsernameId: config.discordUsernameId,
discordWebHookURLRallyId: config.discordWebHookURLRallyId,
discordWebHookURLHistoryId: config.discordWebHookURLHistoryId,
discordUsername: discordUsername,
discordWebHookURLRally: discordWebHookURLRally,
discordWebHookURLHistory: discordWebHookURLHistory,
discordEnabled: discordEnabled,
discordRallyEnabled: discordRallyEnabled,
discordHistoryEnabled: discordHistoryEnabled,
discordSectionShow: discordSectionShow,
discordUsername: '',
discordWebHookURLRally: '',
discordWebHookURLHistory: '',
discordEnabled: false,
discordRallyEnabled: false,
discordHistoryEnabled: false,
discordSectionShow: false,
// map access ----------------
characterSelectId: config.characterSelectId,
corporationSelectId: config.corporationSelectId,
allianceSelectId: config.allianceSelectId,
// map access objects --------
accessCharacter: accessCharacter,
accessCorporation: accessCorporation,
accessAlliance: accessAlliance,
// access limitations --------
maxCharacter: Init.mapTypes.private.defaultConfig.max_shared,
maxCorporation: Init.mapTypes.corporation.defaultConfig.max_shared,
maxAlliance: Init.mapTypes.alliance.defaultConfig.max_shared,
accessCharacter: [],
accessCorporation: [],
accessAlliance: [],
// download tab --------------
dialogMapExportFormId: config.dialogMapExportFormId,
dialogMapImportFormId: config.dialogMapImportFormId,
@@ -323,25 +221,100 @@ define([
fieldImportId: config.fieldImportId,
dialogMapImportInfoId: config.dialogMapImportInfoId,
hasRightMapUpdate: hasRightMapUpdate,
hasRightMapExport: hasRightMapExport,
hasRightMapImport: hasRightMapImport,
formatFilename: function(){
// format filename from "map name" (initial)
return function(mapName, render){
let filename = render(mapName);
return formatFilename(filename);
};
}
formatFilename: () => (mapName, render) => formatFilename(render(mapName))
};
if(mapData !== false){
Object.assign(mapDialogData, {
deleteExpiredConnections: mapData.config.deleteExpiredConnections,
deleteEolConnections: mapData.config.deleteEolConnections,
persistentAliases: mapData.config.persistentAliases,
persistentSignatures: mapData.config.persistentSignatures,
trackAbyssalJumps: mapData.config.trackAbyssalJumps,
logActivity: mapData.config.logging.activity,
logHistory: mapData.config.logging.history,
slackWebHookURL: mapData.config.logging.slackWebHookURL,
slackUsername: mapData.config.logging.slackUsername,
slackIcon: mapData.config.logging.slackIcon,
slackChannelHistory: mapData.config.logging.slackChannelHistory,
slackChannelRally: mapData.config.logging.slackChannelRally,
slackEnabled: Boolean(Util.getObjVal(Init, 'slack.status')),
discordUsername: Util.getObjVal(mapData, 'config.logging.discordUsername'),
discordWebHookURLRally: Util.getObjVal(mapData, 'config.logging.discordWebHookURLRally'),
discordWebHookURLHistory: Util.getObjVal(mapData, 'config.logging.discordWebHookURLHistory'),
discordEnabled: Boolean(Util.getObjVal(Init, 'discord.status')),
accessCharacter: mapData.config.access.character,
accessCorporation: mapData.config.access.corporation,
accessAlliance: mapData.config.access.alliance
});
Object.assign(mapDialogData, {
// remove "#" from Slack channels
slackChannelHistory: mapDialogData.slackChannelHistory.indexOf('#') === 0 ? mapDialogData.slackChannelHistory.substr(1) : mapDialogData.slackChannelHistory,
slackChannelRally: mapDialogData.slackChannelRally.indexOf('#') === 0 ? mapDialogData.slackChannelRally.substr(1) : mapDialogData.slackChannelRally,
slackHistoryEnabled: mapDialogData.slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_slack_enabled')),
slackRallyEnabled: mapDialogData.slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled')),
slackSectionShow: (mapDialogData.slackEnabled && mapDialogData.slackWebHookURL.length > 0),
discordRallyEnabled: mapDialogData.discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_discord_enabled')),
discordHistoryEnabled: mapDialogData.discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_discord_enabled')),
discordSectionShow: (mapDialogData.discordEnabled && (mapDialogData.discordWebHookURLRally.length > 0 || mapDialogData.discordWebHookURLHistory.length > 0)),
});
}
let contentDialog = Mustache.render(templateMapDialog, mapDialogData);
contentDialog = $(contentDialog);
// set tab content
// "new map" + "edit map" tab base --------------------------------------------------------------------
let mapFormData = {
select2Class: Util.config.select2Class,
scope: MapUtil.getMapScopes(),
type: mapTypes,
icon: MapUtil.getMapIcons(),
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
formInfoContainerClass: Util.config.formInfoContainerClass
};
// render "new map" tab content -----------------------------------------------------------------------
let mapFormDataNew = Object.assign({}, mapFormData, {
hasRightMapForm: hasRightMapCreate,
nameInputId: config.newNameInputId,
iconSelectId: config.newIconSelectId,
scopeSelectId: config.newScopeSelectId,
typeSelectId: config.newTypeSelectId,
mapId: 0,
mapIcon: undefined,
mapName: undefined,
mapScopeId: undefined,
mapTypeId: undefined
});
let contentNewMap = Mustache.render(templateMapForm, mapFormDataNew);
$('#' + config.dialogMapNewContainerId, contentDialog).html(contentNewMap);
$('#' + config.dialogMapEditContainerId, contentDialog).html(contentEditMap);
// render "edit map" tab content ----------------------------------------------------------------------
if(!hideEditTab){
let mapFormDataEdit = Object.assign({}, mapFormData, {
hasRightMapForm: hasRightMapUpdate,
nameInputId: config.editNameInputId,
iconSelectId: config.editIconSelectId,
scopeSelectId: config.editScopeSelectId,
typeSelectId: config.editTypeSelectId,
mapId: mapData.config.id,
mapIcon: selectOption(mapData.config.icon),
mapName: mapData.config.name,
mapScopeId: selectOption(mapData.config.scope.id),
mapTypeId: selectOption(mapData.config.type.id)
});
let contentEditMap = Mustache.render(templateMapForm, mapFormDataEdit);
$('#' + config.dialogMapEditContainerId, contentDialog).html(contentEditMap);
}
let mapInfoDialog = bootbox.dialog({
title: dialogTitle,
@@ -355,7 +328,6 @@ define([
label: '<i class="fas fa-check fa-fw"></i>&nbsp;save',
className: 'btn-success',
callback: function(){
// get the current active form
let form = $('#' + config.newMapDialogId).find('form').filter(':visible');
@@ -375,9 +347,7 @@ define([
});
// check whether the form is valid
let formValid = form.isValidForm();
if(formValid === true){
if(form.isValidForm()){
// lock dialog
let dialogContent = mapInfoDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
@@ -386,68 +356,36 @@ define([
let formData = form.getFormValues();
// add value prefixes (Slack channels)
let tmpVal;
if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelHistory')) === 'string' && tmpVal.length){
formData.slackChannelHistory = '#' + tmpVal;
}
if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelRally')) === 'string' && tmpVal.length){
formData.slackChannelRally = '#' + tmpVal;
}
Object.keys(formData).map((key, index) => {
if(['slackChannelHistory', 'slackChannelRally'].includes(key))
formData[key] = (formData[key].length ? '#' : '') + formData[key];
});
// checkbox fix -> settings tab
if( form.find('#' + config.deleteExpiredConnectionsId).length ){
formData.deleteExpiredConnections = formData.hasOwnProperty('deleteExpiredConnections') ? parseInt( formData.deleteExpiredConnections ) : 0;
}
if( form.find('#' + config.deleteEolConnectionsId).length ){
formData.deleteEolConnections = formData.hasOwnProperty('deleteEolConnections') ? parseInt( formData.deleteEolConnections ) : 0;
}
if( form.find('#' + config.persistentAliasesId).length ){
formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0;
}
if( form.find('#' + config.persistentSignaturesId).length ){
formData.persistentSignatures = formData.hasOwnProperty('persistentSignatures') ? parseInt( formData.persistentSignatures ) : 0;
}
if( form.find('#' + config.trackAbyssalJumpsId).length ){
formData.trackAbyssalJumps = formData.hasOwnProperty('trackAbyssalJumps') ? parseInt( formData.trackAbyssalJumps ) : 0;
}
if( form.find('#' + config.logHistoryId).length ){
formData.logHistory = formData.hasOwnProperty('logHistory') ? parseInt( formData.logHistory ) : 0;
}
if( form.find('#' + config.logActivityId).length ){
formData.logActivity = formData.hasOwnProperty('logActivity') ? parseInt( formData.logActivity ) : 0;
}
MapOverlayUtil.getMapOverlay($(mapData.map.getContainer()), 'timer').startMapUpdateCounter();
let requestData = {formData: formData};
let method = formData.id ? 'PATCH' : 'PUT';
$.ajax({
type: 'POST',
url: Init.path.saveMap,
data: requestData,
dataType: 'json'
}).done(function(responseData){
if(responseData.error.length){
form.showFormMessage(responseData.error);
}else{
// success
Util.showNotify({title: dialogTitle, text: 'Map: ' + responseData.mapData.mapData.name, type: 'success'});
Util.request(method, 'map', formData.id, formData, {
formElement: form // for error form messages
}, context => {
// always do
dialogContent.hideLoadingAnimation();
}).then(
payload => {
let mapData = Util.getObjVal(payload, 'data.mapData');
Util.showNotify({title: dialogTitle, text: `Map: ${Util.getObjVal(mapData, 'name')}`, type: 'success'});
// update map-tab Element
let tabLinkEls = Util.getMapTabLinkElements(Util.getMapModule()[0], responseData.mapData.mapData.id);
let tabLinkEls = Util.getMapTabLinkElements(Util.getMapModule()[0], Util.getObjVal(mapData, 'id'));
if(tabLinkEls.length === 1){
ModuleMap.updateTabData(tabLinkEls[0], responseData.mapData.mapData);
ModuleMap.updateTabData(tabLinkEls[0], mapData);
}
$(mapInfoDialog).modal('hide');
Util.triggerMenuAction(document, 'Close');
}
}).fail(function(jqXHR, status, error){
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveMap', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
}).always(function(){
dialogContent.hideLoadingAnimation();
});
},
Util.handleAjaxErrorResponse
);
}
return false;
@@ -487,11 +425,11 @@ define([
// get current active form(tab)
let form = $('#' + config.newMapDialogId).find('form').filter(':visible');
form.showFormMessage([{type: 'info', message: 'Creating new maps or change settings may take a few seconds'}]);
form.showFormMessage([{type: 'info', text: 'Creating new maps or change settings may take a few seconds'}]);
if(mapData === false){
// no map data found (probably new user
form.showFormMessage([{type: 'warning', message: 'No maps found. Create a new map before you can start'}]);
form.showFormMessage([{type: 'warning', text: 'No maps found. Create a new map before you can start'}]);
}
// init "download tab" ============================================================================
@@ -573,7 +511,7 @@ define([
importData.mapData.push( JSON.parse( readEvent.target.result ) );
}catch(error){
filesCountFail++;
importFormElement.showFormMessage([{type: 'error', message: 'File can not be parsed'}]);
importFormElement.showFormMessage([{type: 'error', text: 'File can not be parsed'}]);
}
// start import when all files are parsed
@@ -640,7 +578,7 @@ define([
}
});
}else{
importFormElement.showFormMessage([{type: 'error', message: 'The File APIs are not fully supported in this browser.'}]);
importFormElement.showFormMessage([{type: 'error', text: 'The File APIs are not fully supported in this browser.'}]);
}
}
});
@@ -789,39 +727,32 @@ define([
* @param mapData
*/
$.fn.showDeleteMapDialog = function(mapData){
let mapName = mapData.config.name;
let mapNameStr = '<span class="txt-color txt-color-danger">' + mapName + '</span>';
let mapId = Util.getObjVal(mapData, 'config.id');
let mapName = Util.getObjVal(mapData, 'config.name');
if(!mapId) return;
let mapNameStr = `<span class="txt-color txt-color-danger">${mapName}</span>`;
let mapDeleteDialog = bootbox.confirm({
message: 'Delete map "' + mapNameStr + '"?',
message: `Delete map "${mapNameStr}"?`,
buttons: {
confirm: {
label: '<i class="fas fa-trash fa-fw"></i>&nbsp;delete map',
className: 'btn-danger'
}
},
callback: function(result){
callback: result => {
if(result){
// lock dialog
let dialogContent = mapDeleteDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
let data = {mapData: mapData.config};
$.ajax({
type: 'POST',
url: Init.path.deleteMap,
data: data,
dataType: 'json'
}).done(function(data){
Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'});
}).fail(function(jqXHR, status, error){
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
}).always(function(){
$(mapDeleteDialog).modal('hide');
});
Util.request('DELETE', 'map', mapId, {}, {}).then(
payload => {
Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'});
},
Util.handleAjaxErrorResponse
).finally(() => mapDeleteDialog.modal('hide'));
return false;
}
@@ -830,5 +761,4 @@ define([
};
});

View File

@@ -632,7 +632,7 @@ define([
// get current ship data ----------------------------------------------------------
massShipCell.parent().toggle(showShip);
if(showShip){
shipData = Util.getObjVal(Util.getCurrentCharacterLog(), 'ship');
shipData = Util.getObjVal(Util.getCurrentCharacterData('log'), 'ship');
if(shipData){
if(shipData.mass){
massShip = parseInt(shipData.mass);

View File

@@ -807,13 +807,13 @@ define([
// -> add a modal button for pre-fill modal with it
// -> systemId must match systemId from current character log
let currentUserData = Util.getCurrentUserData();
let characterStructureId = Util.getCurrentCharacterData('log.structure.id') || 0;
let characterStructureName = Util.getCurrentCharacterData('log.structure.name') || '';
let characterStructureTypeId = Util.getCurrentCharacterData('log.structure.type.id') || 0;
let characterStructureTypeName = Util.getCurrentCharacterData('log.structure.type.name') || '';
let isCurrentLocation = false;
let characterStructureId = Util.getObjVal(currentUserData, 'character.log.structure.id') || 0;
let characterStructureName = Util.getObjVal(currentUserData, 'character.log.structure.name') || '';
let characterStructureTypeId = Util.getObjVal(currentUserData, 'character.log.structure.type.id') || 0;
let characterStructureTypeName = Util.getObjVal(currentUserData, 'character.log.structure.type.name') || '';
if(this._systemData.id === Util.getObjVal(currentUserData, 'character.log.system.id')){
if(this._systemData.id === Util.getCurrentCharacterData('log.system.id')){
isCurrentLocation = true;
}

View File

@@ -732,7 +732,7 @@ define([
let systemSecClass = this._config.systemSecurityClassPrefix + tempSystemSec.replace('.', '-');
// check for wormhole
let icon = 'fas fa-square';
let icon = 'fas fa-square-full';
if(isWormholeSystemName(systemName)){
icon = 'fas fa-dot-circle';
}

View File

@@ -1243,7 +1243,7 @@ define([
* @returns {*}
*/
enrichParsedSignatureData(signatureData){
let characterData = Util.getObjVal(Util.getCurrentUserData(), 'character');
let characterData = Util.getCurrentCharacter();
let timestamp = Math.floor((new Date()).getTime() / 1000);
for(let i = 0; i < signatureData.length; i++){

View File

@@ -259,30 +259,31 @@ define([
* @param errors
*/
$.fn.showFormMessage = function(errors){
let formElement = $(this);
let errorMessage = [];
let warningMessage = [];
let infoMessage = [];
for(let i = 0; i < errors.length; i++){
if(errors[i].type === 'error'){
errorMessage.push( errors[i].message );
for (let error of errors) {
let message = `${error.text}`;
if(error.type === 'error'){
message = `${error.status} - ${message}`;
errorMessage.push(message);
// mark form field as invalid in case of a validation error
if(
errors[i].field &&
errors[i].field.length > 0
error.field &&
error.field.length > 0
){
let formField = formElement.find('[name="' + errors[i].field + '"]');
let formField = formElement.find('[name="' + error.field + '"]');
let formGroup = formField.parents('.form-group').removeClass('has-success').addClass('has-error');
let formHelp = formGroup.find('.help-block').text(errors[i].message);
let formHelp = formGroup.find('.help-block').text(error.text);
}
}else if(errors[i].type === 'warning'){
warningMessage.push( errors[i].message );
}else if(errors[i].type === 'info'){
infoMessage.push( errors[i].message );
}else if(error.type === 'warning'){
warningMessage.push(message);
}else if(error.type === 'info'){
infoMessage.push(message);
}
}
@@ -595,10 +596,10 @@ define([
/**
* add character switch popover
* @param userData
*/
$.fn.initCharacterSwitchPopover = function(userData){
$.fn.initCharacterSwitchPopover = function(){
let elements = $(this);
let userData = getCurrentUserData();
let eventNamespace = 'hideCharacterPopup';
requirejs(['text!templates/tooltip/character_switch.html', 'mustache'], function(template, Mustache){
@@ -1820,14 +1821,16 @@ define([
// check if userData is valid
if(userData && userData.character && userData.characters){
// check new vs. old userData for changes
let changes = compareUserData(getCurrentUserData(), userData);
// check if there is any change
if(Object.values(changes).some(val => val)){
$(document).trigger('pf:changedUserData', [userData, changes]);
}
Init.currentUserData = userData;
isSet = true;
// check if there is any change
if(Object.values(changes).some(val => val)){
$(document).trigger('pf:changedUserData', [changes]);
}
}else{
console.error('Could not set userData %o. Missing or malformed obj', userData);
}
@@ -1836,28 +1839,71 @@ define([
};
/**
* get currentUserData from "global" variable
* get currentUserData from "global" var
* @returns {*}
*/
let getCurrentUserData = () => {
return Init.currentUserData;
};
/**
* get currentCharacterData
* @see getCurrentUserData
* @returns {*|boolean}
*/
let getCurrentCharacter = () => getObjVal(getCurrentUserData(), 'character') || false;
/**
* get data from currentCharacterData (e.g. id)
* @see getCurrentCharacter
* @param key
* @returns {*|boolean}
*/
let getCurrentCharacterData = key => getObjVal(getCurrentCharacter(), key) || false;
/**
* get either active characterID or characterId from initial page load
* @returns {number}
*/
let getCurrentCharacterId = () => {
let currentCharacterId = parseInt(getObjVal(getCurrentUserData(), 'character.id')) || 0;
let currentCharacterId = parseInt(getCurrentCharacterData('id')) || 0;
if(!currentCharacterId){
// no active character... -> get default characterId from initial page load
currentCharacterId = parseInt(document.body.getAttribute('data-character-id'));
}
return currentCharacterId;
};
/**
* get information for the current mail user
* @param option
* @returns {boolean}
*/
let getCurrentUserInfo = option => {
let currentUserData = getCurrentUserData();
let userInfo = false;
if(currentUserData){
// user data is set -> user data will be set AFTER the main init request!
let characterData = currentUserData.character;
if(characterData){
if(option === 'privateId'){
userInfo = characterData.id;
}
if(option === 'allianceId' && characterData.alliance){
userInfo = characterData.alliance.id;
}
if(option === 'corporationId' && characterData.corporation){
userInfo = characterData.corporation.id;
}
}
}
return userInfo;
};
/**
* compares two userData objects for changes that are relevant
* @param oldUserData
@@ -1886,6 +1932,29 @@ define([
};
};
/**
* checks if currentCharacter has a role that matches a specific right
* @param right
* @param objKey
* @returns {boolean}
*/
let hasRight = (right, objKey) => {
let hasRight = false;
let objectRights = getCurrentCharacterData(`${objKey}.rights`) || [];
let objectRight = objectRights.find(objectRight => objectRight.right.name === right);
if(objectRight){
let characterRole = getCurrentCharacterData('role');
if(
characterRole.name === 'SUPER' ||
objectRight.role.name === 'MEMBER' ||
objectRight.role.name === characterRole.name
){
hasRight = true;
}
}
return hasRight;
};
/**
* get a unique ID for each tab
* -> store ID in session storage
@@ -2021,31 +2090,36 @@ define([
* global ajax error handler -> handles .fail() requests
* @param payload
*/
let handleAjaxErrorResponse = (payload) => {
let handleAjaxErrorResponse = payload => {
// handle only request errors
if(payload.action === 'request'){
let jqXHR = payload.data.jqXHR;
let reason = '';
if(jqXHR.responseJSON){
// ... valid JSON response
let response = jqXHR.responseJSON;
if(response.error && response.error.length > 0){
// build error notification reason from errors
reason = response.error.map(error => error.message ? error.message : error.status).join('\n');
// check if errors might belong to a HTML form -> check "context"
if(payload.context.formElement){
// show form messages e.g. validation errors
payload.context.formElement.showFormMessage(response.error);
}
}
}else{
reason = 'Invalid JSON response';
}
showNotify({title: jqXHR.status + ': ' + payload.name, text: reason, type: 'error'});
if(payload.action !== 'request'){
console.error('Unhandled HTTP response error. Invalid payload %o', payload);
return;
}
let jqXHR = payload.data.jqXHR;
let title = `${jqXHR.status}: ${jqXHR.statusText} - ${payload.name}`;
let reason = '';
if(jqXHR.responseJSON){
// ... valid JSON response
let response = jqXHR.responseJSON;
if(response.error && response.error.length > 0){
// build error notification reason from errors
reason = response.error.map(error => error.text || error.status).join('\n');
// check if errors might belong to a HTML form -> check "context"
if(payload.context.formElement){
// show form messages e.g. validation errors
payload.context.formElement.showFormMessage(response.error);
}
}
}else{
reason = 'Invalid JSON response';
}
showNotify({title: title, text: reason, type: 'error'});
};
/**
@@ -2822,43 +2896,6 @@ define([
Init.currentMapData = Init.currentMapData.filter(mapData => mapData.config.id !== mapId);
};
/**
* get the current log data for the current user character
* @returns {boolean}
*/
let getCurrentCharacterLog = () => getObjVal(getCurrentUserData(), 'character.log') || false;
/**
* get information for the current mail user
* @param option
* @returns {boolean}
*/
let getCurrentUserInfo = option => {
let currentUserData = getCurrentUserData();
let userInfo = false;
if(currentUserData){
// user data is set -> user data will be set AFTER the main init request!
let characterData = currentUserData.character;
if(characterData){
if(option === 'privateId'){
userInfo = characterData.id;
}
if(option === 'allianceId' && characterData.alliance){
userInfo = characterData.alliance.id;
}
if(option === 'corporationId' && characterData.corporation){
userInfo = characterData.corporation.id;
}
}
}
return userInfo;
};
/**
* get "nearBy" systemData based on a jump radius around a currentSystem
* @param currentSystemData
@@ -3646,13 +3683,14 @@ define([
deleteCurrentMapData: deleteCurrentMapData,
setCurrentUserData: setCurrentUserData,
getCurrentUserData: getCurrentUserData,
getCurrentCharacter: getCurrentCharacter,
getCurrentCharacterData: getCurrentCharacterData,
getCurrentCharacterId: getCurrentCharacterId,
setCurrentSystemData: setCurrentSystemData,
getCurrentSystemData: getCurrentSystemData,
deleteCurrentSystemData:deleteCurrentSystemData,
getCurrentLocationData: getCurrentLocationData,
getCurrentUserInfo: getCurrentUserInfo,
getCurrentCharacterLog: getCurrentCharacterLog,
findInViewport: findInViewport,
initScrollSpy: initScrollSpy,
getConfirmationTemplate: getConfirmationTemplate,
@@ -3674,6 +3712,7 @@ define([
getLocalStore: getLocalStore,
getResizeManager: getResizeManager,
clearSessionStorage: clearSessionStorage,
hasRight: hasRight,
getBrowserTabId: getBrowserTabId,
singleDoubleClick: singleDoubleClick,
getTableId: getTableId,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -46,7 +46,7 @@ requirejs.config({
easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart
peityInlineChart: 'lib/jquery.peity.min', // v3.3.0 Inline Chart - http://benpickles.github.io/peity/
hoverIntent: 'lib/jquery.hoverIntent.min', // v1.10.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io
select2: 'lib/select2.min', // v4.0.13 Drop Down customization - https://select2.github.io
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info
blueImpGallery: 'lib/blueimp-gallery', // v2.21.3 Image Gallery - https://github.com/blueimp/Gallery

View File

@@ -38,8 +38,6 @@ define([], () => {
updateUserData: '/api/map/updateUserData', // ajax URL - main map user data trigger
updateUnloadData: '/api/map/updateUnloadData', // post URL - for my sync onUnload
// map API
saveMap: '/api/map/save', // ajax URL - save/update map
deleteMap: '/api/map/delete', // ajax URL - delete map
importMap: '/api/map/import', // ajax URL - import map
getMapConnectionData: '/api/map/getConnectionData', // ajax URL - get connection data
getMapLogData: '/api/map/getLogData', // ajax URL - get logs data
@@ -138,6 +136,18 @@ define([], () => {
class: 'fa-hat-wizard',
label: 'hat wizard',
unicode: '&#xf6e8;'
},{
class: 'fa-cross',
label: 'cross',
unicode: '&#xf654;'
}, {
class: 'fa-cannabis',
label: 'cannabis',
unicode: '&#xf55f;'
}, {
class: 'fa-spider',
label: 'spider',
unicode: '&#xf717;'
}, {
class: 'fa-plane',
label: 'plane',

View File

@@ -194,7 +194,7 @@ define([
currentUserIsHere &&
!userWasHere &&
Boolean(Util.getObjVal(Init, 'character.autoLocationSelect')) &&
Boolean(Util.getObjVal(Util.getCurrentUserData(), 'character.selectLocation'))
Boolean(Util.getCurrentCharacterData('selectLocation'))
){
Util.triggerMenuAction(map.getContainer(), 'SelectSystem', {systemId: system.data('id'), forceSelect: false});
}
@@ -2794,7 +2794,7 @@ define([
let mapElement = $(this);
let mapOverlay = MapOverlayUtil.getMapOverlay(mapElement, 'local');
let currentMapData = Util.getCurrentMapData(mapId);
let currentCharacterLog = Util.getCurrentCharacterLog();
let currentCharacterLog = Util.getCurrentCharacterData('log');
let clearLocal = true;
if(
@@ -2891,7 +2891,7 @@ define([
let compactView = mapElement.hasClass(MapUtil.config.mapCompactClass);
// get current character log data
let characterLogSystemId = Util.getObjVal(Util.getCurrentCharacterLog(), 'system.id') || 0;
let characterLogSystemId = Util.getObjVal(Util.getCurrentCharacterData('log'), 'system.id') || 0;
// data for header update
let headerUpdateData = {

View File

@@ -186,7 +186,7 @@ define([
systemData = options.systemData;
}else{
// ... check for current active system (characterLog) -----------------------------------------------------
let currentCharacterLog = Util.getCurrentCharacterLog();
let currentCharacterLog = Util.getCurrentCharacterData('log');
if(currentCharacterLog !== false){
// set system from 'characterLog' data as pre-selected system
systemData = Util.getObjVal(currentCharacterLog, 'system');

View File

@@ -2005,60 +2005,26 @@ define([
*/
let checkRight = (right, mapConfig) => {
let hasAccess = false;
let currentUserData = Util.getCurrentUserData();
if(currentUserData){
// ...there is an active user
let currentCharacterData = Util.getObjVal(currentUserData, 'character');
if(currentCharacterData){
// ... there is an active character
let currentCharacterRole = Util.getObjVal(currentCharacterData, 'role');
if(currentCharacterRole){
// ... active character has a role assigned
let mapType = Util.getObjVal(mapConfig, 'type.name');
let accessObjectId = Util.getCurrentUserInfo(mapType + 'Id');
let mapType = Util.getObjVal(mapConfig, 'type.name');
let mapAccess = Util.getObjVal(mapConfig, 'access.' + (mapType === 'private' ? 'character' : mapType)) || [];
// this is either Ally/Corp or Character Id
let accessObjectId = Util.getCurrentUserInfo(mapType + 'Id');
// check whether character has map access
let hasMapAccess = mapAccess.some((accessObj) => {
return (accessObj.id === accessObjectId);
});
if(hasMapAccess){
// ... this should ALWAYS be be true!
switch(mapType){
case 'private':
hasAccess = true;
break;
case 'corporation':
let objectRights = Util.getObjVal(currentCharacterData, mapType + '.rights') || [];
let objectRight = objectRights.find((objectRight) => {
return objectRight.right.name === right;
});
if(objectRight){
// ... Ally/Corp has the right we are looking for assigned with a required role
if(
currentCharacterRole.name === 'SUPER' ||
objectRight.role.name === 'MEMBER' ||
objectRight.role.name === currentCharacterRole.name
){
hasAccess = true;
}
}
break;
case 'alliance':
hasAccess = true;
break;
}
}
}
// check whether character has map access
let mapAccess = Util.getObjVal(mapConfig, 'access.' + (mapType === 'private' ? 'character' : mapType)) || [];
let hasMapAccess = mapAccess.some(accessObj => accessObj.id === accessObjectId);
if(hasMapAccess){
// ... this should ALWAYS be be true!
switch(mapType){
case 'private':
hasAccess = true;
break;
case 'corporation':
hasAccess = Util.hasRight(right, mapType);
break;
case 'alliance':
hasAccess = true;
break;
}
}
return hasAccess;
};

View File

@@ -451,7 +451,7 @@ define([
mapIds: mapId ? [mapId] : [],
getMapUserData: Util.getSyncType() === 'webSocket' ? 0 : 1,
mapTracking: locationToggle ? locationToggle.checked | 0 : 0, // location tracking
systemData: Util.getCurrentSystemData(mapId)
systemData: mapId ? Util.getCurrentSystemData(mapId) : []
};
if(newSystemPositions){
@@ -540,7 +540,8 @@ define([
// clear both main update request trigger timer
clearUpdateTimeouts();
let reason = status + ' ' + jqXHR.status + ': ' + error;
let reason = `${status} ${jqXHR.status}`;
let firstError = error;
let errorData = [];
let redirect = false; // redirect user to other page e.g. login
let reload = true; // reload current page (default: true)
@@ -552,6 +553,7 @@ define([
responseObj.error &&
responseObj.error.length > 0
){
firstError = responseObj.error[0].status;
errorData = responseObj.error;
}
@@ -569,7 +571,7 @@ define([
console.error(' ↪ %s Error response: %o', jqXHR.url, errorData);
$(document).trigger('pf:shutdown', {
status: jqXHR.status,
reason: reason,
reason: `${reason}: ${firstError}`,
error: errorData,
redirect: redirect,
reload: reload

View File

@@ -823,17 +823,17 @@ define([
});
// changes in current userData ----------------------------------------------------------------------------
documentElement.on('pf:changedUserData', (e, userData, changes) => {
documentElement.on('pf:changedUserData', (e, changes) => {
// update menu buttons (en/disable)
if(changes.characterId){
documentElement.trigger('pf:updateMenuOptions', {
menuGroup: 'userOptions',
payload: Boolean(Util.getObjVal(userData, 'character.id'))
payload: Boolean(Util.getCurrentCharacterData('id'))
});
}
// update header
updateHeaderUserData(userData, changes).then();
updateHeaderUserData(changes).then();
});
// shutdown the program -> show dialog --------------------------------------------------------------------
@@ -873,7 +873,7 @@ define([
// add error information (if available)
if(data.error && data.error.length){
for(let error of data.error){
options.content.textSmaller.push(error.message);
options.content.textSmaller.push(error.text);
}
}
@@ -929,7 +929,7 @@ define([
//modalElement.find('form').filter((i, form) => $(form).data('bs.validator')).validator('destroy');
// destroy all popovers
modalElement.find('.' + Util.config.popoverTriggerClass).popover('destroy');
modalElement.destroyPopover(true);
// destroy all Select2
modalElement.find('.' + Util.config.select2Class)
@@ -1050,18 +1050,17 @@ define([
/**
* update all header elements with current userData
* @param userData
* @param changes
* @returns {Promise<[any, any, any, any, any, any, any, any, any, any]>}
* @returns {Promise<[]>}
*/
let updateHeaderUserData = (userData, changes) => {
let updateHeaderUserData = changes => {
let updateTasks = [];
if(changes.characterLogLocation){
updateTasks.push(updateMapTrackingToggle(Boolean(Util.getObjVal(userData, 'character.logLocation'))));
updateTasks.push(updateMapTrackingToggle(Boolean(Util.getCurrentCharacterData('logLocation'))));
}
if(changes.charactersIds){
updateTasks.push(updateHeaderCharacterSwitch(userData, changes.characterId));
updateTasks.push(updateHeaderCharacterSwitch(changes.characterId));
}
if(
changes.characterSystemId ||
@@ -1070,7 +1069,7 @@ define([
changes.characterStructureId ||
changes.characterLogHistory
){
updateTasks.push(updateHeaderCharacterLocation(userData, changes.characterShipType));
updateTasks.push(updateHeaderCharacterLocation(changes.characterShipType));
}
return Promise.all(updateTasks);
@@ -1099,22 +1098,21 @@ define([
};
/**
* @param userData
* @param changedCharacter
* @returns {Promise<any>}
*/
let updateHeaderCharacterSwitch = (userData, changedCharacter) => {
let updateHeaderCharacterSwitch = changedCharacter => {
let executor = resolve => {
let userInfoElement = $('.' + config.headUserCharacterClass);
// toggle element
animateHeaderElement(userInfoElement, userInfoElement => {
if(changedCharacter){
// current character changed
userInfoElement.find('span').text(Util.getObjVal(userData, 'character.name'));
userInfoElement.find('img').attr('src', Util.eveImageUrl('characters', Util.getObjVal(userData, 'character.id')));
userInfoElement.find('span').text(Util.getCurrentCharacterData('name'));
userInfoElement.find('img').attr('src', Util.eveImageUrl('characters', Util.getCurrentCharacterData('id')));
}
// init "character switch" popover
userInfoElement.initCharacterSwitchPopover(userData);
userInfoElement.initCharacterSwitchPopover();
resolve({
action: 'updateHeaderCharacterSwitch',
@@ -1127,28 +1125,26 @@ define([
};
/**
*
* @param userData
* @param changedShip
* @returns {Promise<any>}
*/
let updateHeaderCharacterLocation = (userData, changedShip) => {
let updateHeaderCharacterLocation = changedShip => {
let executor = resolve => {
let userLocationElement = $('#' + Util.config.headUserLocationId);
let breadcrumbHtml = '';
let logData = Util.getObjVal(userData, 'character.log');
let logData = Util.getCurrentCharacterData('log');
let logDataAll = [];
if(logData){
let shipData = Util.getObjVal(userData, 'character.log.ship');
let shipData = Util.getObjVal(logData, 'ship');
let shipTypeId = Util.getObjVal(shipData, 'typeId') || 0;
let shipTypeName = Util.getObjVal(shipData, 'typeName') || '';
let stationData = Util.getObjVal(userData, 'character.log.station');
let stationData = Util.getObjVal(logData, 'station');
let stationId = Util.getObjVal(stationData, 'id') || 0;
let stationName = Util.getObjVal(stationData, 'name') || '';
let structureData = Util.getObjVal(userData, 'character.log.structure');
let structureData = Util.getObjVal(logData, 'structure');
let structureTypeId = Util.getObjVal(structureData, 'type.id') || 0;
let structureTypeName = Util.getObjVal(structureData, 'type.name') || '';
let structureId = Util.getObjVal(structureData, 'id') || 0;
@@ -1157,7 +1153,7 @@ define([
logDataAll.push(logData);
// check for log history data as well
let logHistoryData = Util.getObjVal(userData, 'character.logHistory');
let logHistoryData = Util.getCurrentCharacterData('logHistory');
if(logHistoryData){
// check if there are more history log entries than max visual limit
if(logHistoryData.length > config.headMaxLocationHistoryBreadcrumbs){

View File

@@ -53,8 +53,9 @@ define([
captchaImageId: config.captchaImageId,
formErrorContainerClass: Util.config.formErrorContainerClass,
ccpImageServer: Init.url.ccpImageServer,
roleLabel: Util.getLabelByRole(Util.getObjVal(Util.getCurrentUserData(), 'character.role')).prop('outerHTML'),
characterAutoLocationSelectEnabled: Boolean(Util.getObjVal(Init, 'character.autoLocationSelect'))
roleLabel: Util.getLabelByRole(Util.getCurrentCharacterData('role')).prop('outerHTML'),
characterAutoLocationSelectEnabled: Boolean(Util.getObjVal(Init, 'character.autoLocationSelect')),
hasRightCorporationShare: Util.hasRight('map_share', 'corporation')
};
let content = Mustache.render(template, data);

View File

@@ -193,8 +193,8 @@ define([
});
});
mapElement.initTooltips();
mapElement.hideLoadingAnimation();
mapElement.initTooltips({container: '.modal'});
};
/**
@@ -219,14 +219,6 @@ define([
systemsElement.showLoadingAnimation(config.loadingOptions);
systemTable.on('init.dt', function(){
systemsElement.hideLoadingAnimation();
// init table tooltips
let tooltipElements = systemsElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip();
});
let systemsDataTable = systemTable.DataTable({
pageLength: 20,
paging: true,
@@ -514,6 +506,9 @@ define([
}
],
initComplete: function(settings){
systemsElement.hideLoadingAnimation();
systemsElement.initTooltips({container: '.modal'});
Counter.initTableCounter(this, ['updated:name']);
}
});
@@ -542,15 +537,6 @@ define([
connectionsElement.showLoadingAnimation(config.loadingOptions);
// table init complete
connectionTable.on('init.dt', function(){
connectionsElement.hideLoadingAnimation();
// init table tooltips
let tooltipElements = connectionsElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip();
});
// connections table ------------------------------------------------------------------------------------------
let connectionDataTable = connectionTable.dataTable({
pageLength: 20,
@@ -701,6 +687,9 @@ define([
}
],
initComplete: function(settings){
connectionsElement.hideLoadingAnimation();
connectionsElement.initTooltips({container: '.modal'});
Counter.initTableCounter(this, ['updated:name']);
}
});
@@ -721,17 +710,6 @@ define([
usersElement.showLoadingAnimation(config.loadingOptions);
// table init complete
userTable.on('init.dt', function(){
usersElement.hideLoadingAnimation();
// init table tooltips
let tooltipElements = usersElement.find('[data-toggle="tooltip"]');
tooltipElements.tooltip({
container: usersElement.parent()
});
});
// users table ------------------------------------------------------------------------------------------------
// prepare users data for dataTables
let currentMapUserData = Util.getCurrentMapUserData( mapData.config.id );
@@ -941,7 +919,11 @@ define([
}
}
}
]
],
initComplete: function(settings){
usersElement.hideLoadingAnimation();
usersElement.initTooltips({container: '.modal'});
}
});
};

View File

@@ -9,8 +9,9 @@ define([
'app/render',
'bootbox',
'app/map/util',
'app/module_map'
], ($, Init, Util, Render, bootbox, MapUtil, ModuleMap) => {
'app/module_map',
'app/map/overlay/util',
], ($, Init, Util, Render, bootbox, MapUtil, ModuleMap, MapOverlayUtil) => {
'use strict';
let config = {
@@ -99,144 +100,39 @@ define([
'text!templates/form/map.html',
'mustache'
], (templateMapDialog, templateMapForm, Mustache) => {
let selectOption = value => () => (val, render) => {
if(render(val) === String(value)){
return 'selected';
}
};
let dialogTitle = 'Map settings';
// if there are no maps -> hide settings tab
let hideSettingsTab = false;
let hideEditTab = false;
let hideDownloadTab = false;
let hideEditTab = mapData === false;
let hideSettingsTab = mapData === false;
let hideDownloadTab = mapData === false;
let hasRightMapCreate = true;
let hasRightMapUpdate = true;
let hasRightMapExport = true;
let hasRightMapImport = true;
if(mapData === false){
hideSettingsTab = true;
hideEditTab = true;
hideDownloadTab = true;
}else{
hasRightMapUpdate = MapUtil.checkRight('map_update', mapData.config);
hasRightMapExport = MapUtil.checkRight('map_export', mapData.config);
hasRightMapImport = MapUtil.checkRight('map_import', mapData.config);
}
let hasRightMapUpdate = MapUtil ? MapUtil.checkRight('map_update', mapData.config) : true;
let hasRightMapExport = MapUtil ? MapUtil.checkRight('map_export', mapData.config) : true;
let hasRightMapImport = MapUtil ? MapUtil.checkRight('map_import', mapData.config) : true;
let hasRightMapShare = MapUtil ? MapUtil.checkRight('map_share', mapData.config) : true;
// available map "types" for a new or existing map
let mapTypes = MapUtil.getMapTypes(true);
let mapFormData = {
select2Class: Util.config.select2Class,
scope: MapUtil.getMapScopes(),
type: mapTypes,
icon: MapUtil.getMapIcons(),
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
formInfoContainerClass: Util.config.formInfoContainerClass
};
// render "new map" tab content -----------------------------------------------------------------------
let mapFormDataNew = $.extend({}, mapFormData, {
hasRightMapForm: hasRightMapCreate,
nameInputId: config.newNameInputId,
iconSelectId: config.newIconSelectId,
scopeSelectId: config.newScopeSelectId,
typeSelectId: config.newTypeSelectId
});
let contentNewMap = Mustache.render(templateMapForm, mapFormDataNew);
// render "edit map" tab content ----------------------------------------------------------------------
let mapFormDataEdit = $.extend({}, mapFormData, {
hasRightMapForm: hasRightMapUpdate,
nameInputId: config.editNameInputId,
iconSelectId: config.editIconSelectId,
scopeSelectId: config.editScopeSelectId,
typeSelectId: config.editTypeSelectId
});
let contentEditMap = Mustache.render(templateMapForm, mapFormDataEdit);
contentEditMap = $(contentEditMap);
// current map access info
let accessCharacter = [];
let accessCorporation = [];
let accessAlliance = [];
let deleteExpiredConnections = true;
let deleteEolConnections = true;
let persistentAliases = true;
let persistentSignatures = true;
let trackAbyssalJumps = true;
let logActivity = true;
let logHistory = true;
let slackWebHookURL = '';
let slackUsername = '';
let slackIcon = '';
let slackChannelHistory = '';
let slackChannelRally = '';
let slackEnabled = false;
let slackHistoryEnabled = false;
let slackRallyEnabled = false;
let slackSectionShow = false;
let discordUsername = '';
let discordWebHookURLRally = '';
let discordWebHookURLHistory = '';
let discordEnabled = false;
let discordRallyEnabled = false;
let discordHistoryEnabled = false;
let discordSectionShow = false;
if(mapData !== false){
// set current map information
contentEditMap.find('input[name="id"]').val( mapData.config.id );
contentEditMap.find('select[name="icon"]').val( mapData.config.icon );
contentEditMap.find('input[name="name"]').val( mapData.config.name );
contentEditMap.find('select[name="scopeId"]').val( mapData.config.scope.id );
contentEditMap.find('select[name="typeId"]').val( mapData.config.type.id );
accessCharacter = mapData.config.access.character;
accessCorporation = mapData.config.access.corporation;
accessAlliance = mapData.config.access.alliance;
deleteExpiredConnections = mapData.config.deleteExpiredConnections;
deleteEolConnections = mapData.config.deleteEolConnections;
persistentAliases = mapData.config.persistentAliases;
persistentSignatures = mapData.config.persistentSignatures;
trackAbyssalJumps = mapData.config.trackAbyssalJumps;
logActivity = mapData.config.logging.activity;
logHistory = mapData.config.logging.history;
slackWebHookURL = mapData.config.logging.slackWebHookURL;
slackUsername = mapData.config.logging.slackUsername;
slackIcon = mapData.config.logging.slackIcon;
slackChannelHistory = mapData.config.logging.slackChannelHistory;
slackChannelRally = mapData.config.logging.slackChannelRally;
slackEnabled = Boolean(Util.getObjVal(Init, 'slack.status'));
slackHistoryEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_slack_enabled'));
slackRallyEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled'));
slackSectionShow = (slackEnabled && slackWebHookURL.length > 0);
discordUsername = Util.getObjVal(mapData, 'config.logging.discordUsername');
discordWebHookURLRally = Util.getObjVal(mapData, 'config.logging.discordWebHookURLRally');
discordWebHookURLHistory = Util.getObjVal(mapData, 'config.logging.discordWebHookURLHistory');
discordEnabled = Boolean(Util.getObjVal(Init, 'discord.status'));
discordRallyEnabled = discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_discord_enabled'));
discordHistoryEnabled = discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_discord_enabled'));
discordSectionShow = (discordEnabled && (discordWebHookURLRally.length > 0 || discordWebHookURLHistory.length > 0));
// remove "#" from Slack channels
slackChannelHistory = slackChannelHistory.indexOf('#') === 0 ? slackChannelHistory.substr(1) : slackChannelHistory;
slackChannelRally = slackChannelRally.indexOf('#') === 0 ? slackChannelRally.substr(1) : slackChannelRally;
}
// render main dialog ---------------------------------------------------------------------------------
let mapDialogData = {
id: config.newMapDialogId,
mapData: mapData,
type: mapTypes,
hasRightMapUpdate,
hasRightMapExport,
hasRightMapImport,
hasRightMapShare,
// message container
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
@@ -253,9 +149,9 @@ define([
dialogMapSettingsContainerId: config.dialogMapSettingsContainerId,
dialogMapDownloadContainerId: config.dialogMapDownloadContainerId,
hideEditTab: hideEditTab,
hideSettingsTab: hideSettingsTab,
hideDownloadTab: hideDownloadTab,
hideEditTab,
hideSettingsTab,
hideDownloadTab,
// settings tab --------------
deleteExpiredConnectionsId : config.deleteExpiredConnectionsId,
@@ -263,57 +159,59 @@ define([
persistentAliasesId : config.persistentAliasesId,
persistentSignaturesId : config.persistentSignaturesId,
trackAbyssalJumpsId : config.trackAbyssalJumpsId,
deleteExpiredConnections: deleteExpiredConnections,
deleteEolConnections: deleteEolConnections,
persistentAliases: persistentAliases,
persistentSignatures: persistentSignatures,
trackAbyssalJumps: trackAbyssalJumps,
logHistoryId: config.logHistoryId,
logActivityId: config.logActivityId,
logActivity: logActivity,
logHistory: logHistory,
deleteExpiredConnections: true,
deleteEolConnections: true,
persistentAliases: true,
persistentSignatures: true,
trackAbyssalJumps: true,
logActivity: true,
logHistory: true,
slackWebHookURLId: config.slackWebHookURLId,
slackUsernameId: config.slackUsernameId,
slackIconId: config.slackIconId,
slackChannelHistoryId: config.slackChannelHistoryId,
slackChannelRallyId: config.slackChannelRallyId,
slackWebHookURL: slackWebHookURL,
slackUsername: slackUsername,
slackIcon: slackIcon,
slackChannelHistory: slackChannelHistory,
slackChannelRally: slackChannelRally,
slackEnabled: slackEnabled,
slackHistoryEnabled: slackHistoryEnabled,
slackRallyEnabled: slackRallyEnabled,
slackSectionShow: slackSectionShow,
slackWebHookURL: '',
slackUsername: '',
slackIcon: '',
slackChannelHistory: '',
slackChannelRally: '',
slackEnabled: false,
slackHistoryEnabled: false,
slackRallyEnabled: false,
slackSectionShow: false,
discordUsernameId: config.discordUsernameId,
discordWebHookURLRallyId: config.discordWebHookURLRallyId,
discordWebHookURLHistoryId: config.discordWebHookURLHistoryId,
discordUsername: discordUsername,
discordWebHookURLRally: discordWebHookURLRally,
discordWebHookURLHistory: discordWebHookURLHistory,
discordEnabled: discordEnabled,
discordRallyEnabled: discordRallyEnabled,
discordHistoryEnabled: discordHistoryEnabled,
discordSectionShow: discordSectionShow,
discordUsername: '',
discordWebHookURLRally: '',
discordWebHookURLHistory: '',
discordEnabled: false,
discordRallyEnabled: false,
discordHistoryEnabled: false,
discordSectionShow: false,
// map access ----------------
characterSelectId: config.characterSelectId,
corporationSelectId: config.corporationSelectId,
allianceSelectId: config.allianceSelectId,
// map access objects --------
accessCharacter: accessCharacter,
accessCorporation: accessCorporation,
accessAlliance: accessAlliance,
// access limitations --------
maxCharacter: Init.mapTypes.private.defaultConfig.max_shared,
maxCorporation: Init.mapTypes.corporation.defaultConfig.max_shared,
maxAlliance: Init.mapTypes.alliance.defaultConfig.max_shared,
accessCharacter: [],
accessCorporation: [],
accessAlliance: [],
// download tab --------------
dialogMapExportFormId: config.dialogMapExportFormId,
dialogMapImportFormId: config.dialogMapImportFormId,
@@ -323,25 +221,100 @@ define([
fieldImportId: config.fieldImportId,
dialogMapImportInfoId: config.dialogMapImportInfoId,
hasRightMapUpdate: hasRightMapUpdate,
hasRightMapExport: hasRightMapExport,
hasRightMapImport: hasRightMapImport,
formatFilename: function(){
// format filename from "map name" (initial)
return function(mapName, render){
let filename = render(mapName);
return formatFilename(filename);
};
}
formatFilename: () => (mapName, render) => formatFilename(render(mapName))
};
if(mapData !== false){
Object.assign(mapDialogData, {
deleteExpiredConnections: mapData.config.deleteExpiredConnections,
deleteEolConnections: mapData.config.deleteEolConnections,
persistentAliases: mapData.config.persistentAliases,
persistentSignatures: mapData.config.persistentSignatures,
trackAbyssalJumps: mapData.config.trackAbyssalJumps,
logActivity: mapData.config.logging.activity,
logHistory: mapData.config.logging.history,
slackWebHookURL: mapData.config.logging.slackWebHookURL,
slackUsername: mapData.config.logging.slackUsername,
slackIcon: mapData.config.logging.slackIcon,
slackChannelHistory: mapData.config.logging.slackChannelHistory,
slackChannelRally: mapData.config.logging.slackChannelRally,
slackEnabled: Boolean(Util.getObjVal(Init, 'slack.status')),
discordUsername: Util.getObjVal(mapData, 'config.logging.discordUsername'),
discordWebHookURLRally: Util.getObjVal(mapData, 'config.logging.discordWebHookURLRally'),
discordWebHookURLHistory: Util.getObjVal(mapData, 'config.logging.discordWebHookURLHistory'),
discordEnabled: Boolean(Util.getObjVal(Init, 'discord.status')),
accessCharacter: mapData.config.access.character,
accessCorporation: mapData.config.access.corporation,
accessAlliance: mapData.config.access.alliance
});
Object.assign(mapDialogData, {
// remove "#" from Slack channels
slackChannelHistory: mapDialogData.slackChannelHistory.indexOf('#') === 0 ? mapDialogData.slackChannelHistory.substr(1) : mapDialogData.slackChannelHistory,
slackChannelRally: mapDialogData.slackChannelRally.indexOf('#') === 0 ? mapDialogData.slackChannelRally.substr(1) : mapDialogData.slackChannelRally,
slackHistoryEnabled: mapDialogData.slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_slack_enabled')),
slackRallyEnabled: mapDialogData.slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled')),
slackSectionShow: (mapDialogData.slackEnabled && mapDialogData.slackWebHookURL.length > 0),
discordRallyEnabled: mapDialogData.discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_discord_enabled')),
discordHistoryEnabled: mapDialogData.discordEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_discord_enabled')),
discordSectionShow: (mapDialogData.discordEnabled && (mapDialogData.discordWebHookURLRally.length > 0 || mapDialogData.discordWebHookURLHistory.length > 0)),
});
}
let contentDialog = Mustache.render(templateMapDialog, mapDialogData);
contentDialog = $(contentDialog);
// set tab content
// "new map" + "edit map" tab base --------------------------------------------------------------------
let mapFormData = {
select2Class: Util.config.select2Class,
scope: MapUtil.getMapScopes(),
type: mapTypes,
icon: MapUtil.getMapIcons(),
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass,
formInfoContainerClass: Util.config.formInfoContainerClass
};
// render "new map" tab content -----------------------------------------------------------------------
let mapFormDataNew = Object.assign({}, mapFormData, {
hasRightMapForm: hasRightMapCreate,
nameInputId: config.newNameInputId,
iconSelectId: config.newIconSelectId,
scopeSelectId: config.newScopeSelectId,
typeSelectId: config.newTypeSelectId,
mapId: 0,
mapIcon: undefined,
mapName: undefined,
mapScopeId: undefined,
mapTypeId: undefined
});
let contentNewMap = Mustache.render(templateMapForm, mapFormDataNew);
$('#' + config.dialogMapNewContainerId, contentDialog).html(contentNewMap);
$('#' + config.dialogMapEditContainerId, contentDialog).html(contentEditMap);
// render "edit map" tab content ----------------------------------------------------------------------
if(!hideEditTab){
let mapFormDataEdit = Object.assign({}, mapFormData, {
hasRightMapForm: hasRightMapUpdate,
nameInputId: config.editNameInputId,
iconSelectId: config.editIconSelectId,
scopeSelectId: config.editScopeSelectId,
typeSelectId: config.editTypeSelectId,
mapId: mapData.config.id,
mapIcon: selectOption(mapData.config.icon),
mapName: mapData.config.name,
mapScopeId: selectOption(mapData.config.scope.id),
mapTypeId: selectOption(mapData.config.type.id)
});
let contentEditMap = Mustache.render(templateMapForm, mapFormDataEdit);
$('#' + config.dialogMapEditContainerId, contentDialog).html(contentEditMap);
}
let mapInfoDialog = bootbox.dialog({
title: dialogTitle,
@@ -355,7 +328,6 @@ define([
label: '<i class="fas fa-check fa-fw"></i>&nbsp;save',
className: 'btn-success',
callback: function(){
// get the current active form
let form = $('#' + config.newMapDialogId).find('form').filter(':visible');
@@ -375,9 +347,7 @@ define([
});
// check whether the form is valid
let formValid = form.isValidForm();
if(formValid === true){
if(form.isValidForm()){
// lock dialog
let dialogContent = mapInfoDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
@@ -386,68 +356,36 @@ define([
let formData = form.getFormValues();
// add value prefixes (Slack channels)
let tmpVal;
if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelHistory')) === 'string' && tmpVal.length){
formData.slackChannelHistory = '#' + tmpVal;
}
if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelRally')) === 'string' && tmpVal.length){
formData.slackChannelRally = '#' + tmpVal;
}
Object.keys(formData).map((key, index) => {
if(['slackChannelHistory', 'slackChannelRally'].includes(key))
formData[key] = (formData[key].length ? '#' : '') + formData[key];
});
// checkbox fix -> settings tab
if( form.find('#' + config.deleteExpiredConnectionsId).length ){
formData.deleteExpiredConnections = formData.hasOwnProperty('deleteExpiredConnections') ? parseInt( formData.deleteExpiredConnections ) : 0;
}
if( form.find('#' + config.deleteEolConnectionsId).length ){
formData.deleteEolConnections = formData.hasOwnProperty('deleteEolConnections') ? parseInt( formData.deleteEolConnections ) : 0;
}
if( form.find('#' + config.persistentAliasesId).length ){
formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0;
}
if( form.find('#' + config.persistentSignaturesId).length ){
formData.persistentSignatures = formData.hasOwnProperty('persistentSignatures') ? parseInt( formData.persistentSignatures ) : 0;
}
if( form.find('#' + config.trackAbyssalJumpsId).length ){
formData.trackAbyssalJumps = formData.hasOwnProperty('trackAbyssalJumps') ? parseInt( formData.trackAbyssalJumps ) : 0;
}
if( form.find('#' + config.logHistoryId).length ){
formData.logHistory = formData.hasOwnProperty('logHistory') ? parseInt( formData.logHistory ) : 0;
}
if( form.find('#' + config.logActivityId).length ){
formData.logActivity = formData.hasOwnProperty('logActivity') ? parseInt( formData.logActivity ) : 0;
}
MapOverlayUtil.getMapOverlay($(mapData.map.getContainer()), 'timer').startMapUpdateCounter();
let requestData = {formData: formData};
let method = formData.id ? 'PATCH' : 'PUT';
$.ajax({
type: 'POST',
url: Init.path.saveMap,
data: requestData,
dataType: 'json'
}).done(function(responseData){
if(responseData.error.length){
form.showFormMessage(responseData.error);
}else{
// success
Util.showNotify({title: dialogTitle, text: 'Map: ' + responseData.mapData.mapData.name, type: 'success'});
Util.request(method, 'map', formData.id, formData, {
formElement: form // for error form messages
}, context => {
// always do
dialogContent.hideLoadingAnimation();
}).then(
payload => {
let mapData = Util.getObjVal(payload, 'data.mapData');
Util.showNotify({title: dialogTitle, text: `Map: ${Util.getObjVal(mapData, 'name')}`, type: 'success'});
// update map-tab Element
let tabLinkEls = Util.getMapTabLinkElements(Util.getMapModule()[0], responseData.mapData.mapData.id);
let tabLinkEls = Util.getMapTabLinkElements(Util.getMapModule()[0], Util.getObjVal(mapData, 'id'));
if(tabLinkEls.length === 1){
ModuleMap.updateTabData(tabLinkEls[0], responseData.mapData.mapData);
ModuleMap.updateTabData(tabLinkEls[0], mapData);
}
$(mapInfoDialog).modal('hide');
Util.triggerMenuAction(document, 'Close');
}
}).fail(function(jqXHR, status, error){
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveMap', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
}).always(function(){
dialogContent.hideLoadingAnimation();
});
},
Util.handleAjaxErrorResponse
);
}
return false;
@@ -487,11 +425,11 @@ define([
// get current active form(tab)
let form = $('#' + config.newMapDialogId).find('form').filter(':visible');
form.showFormMessage([{type: 'info', message: 'Creating new maps or change settings may take a few seconds'}]);
form.showFormMessage([{type: 'info', text: 'Creating new maps or change settings may take a few seconds'}]);
if(mapData === false){
// no map data found (probably new user
form.showFormMessage([{type: 'warning', message: 'No maps found. Create a new map before you can start'}]);
form.showFormMessage([{type: 'warning', text: 'No maps found. Create a new map before you can start'}]);
}
// init "download tab" ============================================================================
@@ -573,7 +511,7 @@ define([
importData.mapData.push( JSON.parse( readEvent.target.result ) );
}catch(error){
filesCountFail++;
importFormElement.showFormMessage([{type: 'error', message: 'File can not be parsed'}]);
importFormElement.showFormMessage([{type: 'error', text: 'File can not be parsed'}]);
}
// start import when all files are parsed
@@ -640,7 +578,7 @@ define([
}
});
}else{
importFormElement.showFormMessage([{type: 'error', message: 'The File APIs are not fully supported in this browser.'}]);
importFormElement.showFormMessage([{type: 'error', text: 'The File APIs are not fully supported in this browser.'}]);
}
}
});
@@ -789,39 +727,32 @@ define([
* @param mapData
*/
$.fn.showDeleteMapDialog = function(mapData){
let mapName = mapData.config.name;
let mapNameStr = '<span class="txt-color txt-color-danger">' + mapName + '</span>';
let mapId = Util.getObjVal(mapData, 'config.id');
let mapName = Util.getObjVal(mapData, 'config.name');
if(!mapId) return;
let mapNameStr = `<span class="txt-color txt-color-danger">${mapName}</span>`;
let mapDeleteDialog = bootbox.confirm({
message: 'Delete map "' + mapNameStr + '"?',
message: `Delete map "${mapNameStr}"?`,
buttons: {
confirm: {
label: '<i class="fas fa-trash fa-fw"></i>&nbsp;delete map',
className: 'btn-danger'
}
},
callback: function(result){
callback: result => {
if(result){
// lock dialog
let dialogContent = mapDeleteDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
let data = {mapData: mapData.config};
$.ajax({
type: 'POST',
url: Init.path.deleteMap,
data: data,
dataType: 'json'
}).done(function(data){
Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'});
}).fail(function(jqXHR, status, error){
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
}).always(function(){
$(mapDeleteDialog).modal('hide');
});
Util.request('DELETE', 'map', mapId, {}, {}).then(
payload => {
Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'});
},
Util.handleAjaxErrorResponse
).finally(() => mapDeleteDialog.modal('hide'));
return false;
}
@@ -830,5 +761,4 @@ define([
};
});

View File

@@ -632,7 +632,7 @@ define([
// get current ship data ----------------------------------------------------------
massShipCell.parent().toggle(showShip);
if(showShip){
shipData = Util.getObjVal(Util.getCurrentCharacterLog(), 'ship');
shipData = Util.getObjVal(Util.getCurrentCharacterData('log'), 'ship');
if(shipData){
if(shipData.mass){
massShip = parseInt(shipData.mass);

View File

@@ -807,13 +807,13 @@ define([
// -> add a modal button for pre-fill modal with it
// -> systemId must match systemId from current character log
let currentUserData = Util.getCurrentUserData();
let characterStructureId = Util.getCurrentCharacterData('log.structure.id') || 0;
let characterStructureName = Util.getCurrentCharacterData('log.structure.name') || '';
let characterStructureTypeId = Util.getCurrentCharacterData('log.structure.type.id') || 0;
let characterStructureTypeName = Util.getCurrentCharacterData('log.structure.type.name') || '';
let isCurrentLocation = false;
let characterStructureId = Util.getObjVal(currentUserData, 'character.log.structure.id') || 0;
let characterStructureName = Util.getObjVal(currentUserData, 'character.log.structure.name') || '';
let characterStructureTypeId = Util.getObjVal(currentUserData, 'character.log.structure.type.id') || 0;
let characterStructureTypeName = Util.getObjVal(currentUserData, 'character.log.structure.type.name') || '';
if(this._systemData.id === Util.getObjVal(currentUserData, 'character.log.system.id')){
if(this._systemData.id === Util.getCurrentCharacterData('log.system.id')){
isCurrentLocation = true;
}

View File

@@ -732,7 +732,7 @@ define([
let systemSecClass = this._config.systemSecurityClassPrefix + tempSystemSec.replace('.', '-');
// check for wormhole
let icon = 'fas fa-square';
let icon = 'fas fa-square-full';
if(isWormholeSystemName(systemName)){
icon = 'fas fa-dot-circle';
}

View File

@@ -1243,7 +1243,7 @@ define([
* @returns {*}
*/
enrichParsedSignatureData(signatureData){
let characterData = Util.getObjVal(Util.getCurrentUserData(), 'character');
let characterData = Util.getCurrentCharacter();
let timestamp = Math.floor((new Date()).getTime() / 1000);
for(let i = 0; i < signatureData.length; i++){

View File

@@ -259,30 +259,31 @@ define([
* @param errors
*/
$.fn.showFormMessage = function(errors){
let formElement = $(this);
let errorMessage = [];
let warningMessage = [];
let infoMessage = [];
for(let i = 0; i < errors.length; i++){
if(errors[i].type === 'error'){
errorMessage.push( errors[i].message );
for (let error of errors) {
let message = `${error.text}`;
if(error.type === 'error'){
message = `${error.status} - ${message}`;
errorMessage.push(message);
// mark form field as invalid in case of a validation error
if(
errors[i].field &&
errors[i].field.length > 0
error.field &&
error.field.length > 0
){
let formField = formElement.find('[name="' + errors[i].field + '"]');
let formField = formElement.find('[name="' + error.field + '"]');
let formGroup = formField.parents('.form-group').removeClass('has-success').addClass('has-error');
let formHelp = formGroup.find('.help-block').text(errors[i].message);
let formHelp = formGroup.find('.help-block').text(error.text);
}
}else if(errors[i].type === 'warning'){
warningMessage.push( errors[i].message );
}else if(errors[i].type === 'info'){
infoMessage.push( errors[i].message );
}else if(error.type === 'warning'){
warningMessage.push(message);
}else if(error.type === 'info'){
infoMessage.push(message);
}
}
@@ -595,10 +596,10 @@ define([
/**
* add character switch popover
* @param userData
*/
$.fn.initCharacterSwitchPopover = function(userData){
$.fn.initCharacterSwitchPopover = function(){
let elements = $(this);
let userData = getCurrentUserData();
let eventNamespace = 'hideCharacterPopup';
requirejs(['text!templates/tooltip/character_switch.html', 'mustache'], function(template, Mustache){
@@ -1820,14 +1821,16 @@ define([
// check if userData is valid
if(userData && userData.character && userData.characters){
// check new vs. old userData for changes
let changes = compareUserData(getCurrentUserData(), userData);
// check if there is any change
if(Object.values(changes).some(val => val)){
$(document).trigger('pf:changedUserData', [userData, changes]);
}
Init.currentUserData = userData;
isSet = true;
// check if there is any change
if(Object.values(changes).some(val => val)){
$(document).trigger('pf:changedUserData', [changes]);
}
}else{
console.error('Could not set userData %o. Missing or malformed obj', userData);
}
@@ -1836,28 +1839,71 @@ define([
};
/**
* get currentUserData from "global" variable
* get currentUserData from "global" var
* @returns {*}
*/
let getCurrentUserData = () => {
return Init.currentUserData;
};
/**
* get currentCharacterData
* @see getCurrentUserData
* @returns {*|boolean}
*/
let getCurrentCharacter = () => getObjVal(getCurrentUserData(), 'character') || false;
/**
* get data from currentCharacterData (e.g. id)
* @see getCurrentCharacter
* @param key
* @returns {*|boolean}
*/
let getCurrentCharacterData = key => getObjVal(getCurrentCharacter(), key) || false;
/**
* get either active characterID or characterId from initial page load
* @returns {number}
*/
let getCurrentCharacterId = () => {
let currentCharacterId = parseInt(getObjVal(getCurrentUserData(), 'character.id')) || 0;
let currentCharacterId = parseInt(getCurrentCharacterData('id')) || 0;
if(!currentCharacterId){
// no active character... -> get default characterId from initial page load
currentCharacterId = parseInt(document.body.getAttribute('data-character-id'));
}
return currentCharacterId;
};
/**
* get information for the current mail user
* @param option
* @returns {boolean}
*/
let getCurrentUserInfo = option => {
let currentUserData = getCurrentUserData();
let userInfo = false;
if(currentUserData){
// user data is set -> user data will be set AFTER the main init request!
let characterData = currentUserData.character;
if(characterData){
if(option === 'privateId'){
userInfo = characterData.id;
}
if(option === 'allianceId' && characterData.alliance){
userInfo = characterData.alliance.id;
}
if(option === 'corporationId' && characterData.corporation){
userInfo = characterData.corporation.id;
}
}
}
return userInfo;
};
/**
* compares two userData objects for changes that are relevant
* @param oldUserData
@@ -1886,6 +1932,29 @@ define([
};
};
/**
* checks if currentCharacter has a role that matches a specific right
* @param right
* @param objKey
* @returns {boolean}
*/
let hasRight = (right, objKey) => {
let hasRight = false;
let objectRights = getCurrentCharacterData(`${objKey}.rights`) || [];
let objectRight = objectRights.find(objectRight => objectRight.right.name === right);
if(objectRight){
let characterRole = getCurrentCharacterData('role');
if(
characterRole.name === 'SUPER' ||
objectRight.role.name === 'MEMBER' ||
objectRight.role.name === characterRole.name
){
hasRight = true;
}
}
return hasRight;
};
/**
* get a unique ID for each tab
* -> store ID in session storage
@@ -2021,31 +2090,36 @@ define([
* global ajax error handler -> handles .fail() requests
* @param payload
*/
let handleAjaxErrorResponse = (payload) => {
let handleAjaxErrorResponse = payload => {
// handle only request errors
if(payload.action === 'request'){
let jqXHR = payload.data.jqXHR;
let reason = '';
if(jqXHR.responseJSON){
// ... valid JSON response
let response = jqXHR.responseJSON;
if(response.error && response.error.length > 0){
// build error notification reason from errors
reason = response.error.map(error => error.message ? error.message : error.status).join('\n');
// check if errors might belong to a HTML form -> check "context"
if(payload.context.formElement){
// show form messages e.g. validation errors
payload.context.formElement.showFormMessage(response.error);
}
}
}else{
reason = 'Invalid JSON response';
}
showNotify({title: jqXHR.status + ': ' + payload.name, text: reason, type: 'error'});
if(payload.action !== 'request'){
console.error('Unhandled HTTP response error. Invalid payload %o', payload);
return;
}
let jqXHR = payload.data.jqXHR;
let title = `${jqXHR.status}: ${jqXHR.statusText} - ${payload.name}`;
let reason = '';
if(jqXHR.responseJSON){
// ... valid JSON response
let response = jqXHR.responseJSON;
if(response.error && response.error.length > 0){
// build error notification reason from errors
reason = response.error.map(error => error.text || error.status).join('\n');
// check if errors might belong to a HTML form -> check "context"
if(payload.context.formElement){
// show form messages e.g. validation errors
payload.context.formElement.showFormMessage(response.error);
}
}
}else{
reason = 'Invalid JSON response';
}
showNotify({title: title, text: reason, type: 'error'});
};
/**
@@ -2822,43 +2896,6 @@ define([
Init.currentMapData = Init.currentMapData.filter(mapData => mapData.config.id !== mapId);
};
/**
* get the current log data for the current user character
* @returns {boolean}
*/
let getCurrentCharacterLog = () => getObjVal(getCurrentUserData(), 'character.log') || false;
/**
* get information for the current mail user
* @param option
* @returns {boolean}
*/
let getCurrentUserInfo = option => {
let currentUserData = getCurrentUserData();
let userInfo = false;
if(currentUserData){
// user data is set -> user data will be set AFTER the main init request!
let characterData = currentUserData.character;
if(characterData){
if(option === 'privateId'){
userInfo = characterData.id;
}
if(option === 'allianceId' && characterData.alliance){
userInfo = characterData.alliance.id;
}
if(option === 'corporationId' && characterData.corporation){
userInfo = characterData.corporation.id;
}
}
}
return userInfo;
};
/**
* get "nearBy" systemData based on a jump radius around a currentSystem
* @param currentSystemData
@@ -3646,13 +3683,14 @@ define([
deleteCurrentMapData: deleteCurrentMapData,
setCurrentUserData: setCurrentUserData,
getCurrentUserData: getCurrentUserData,
getCurrentCharacter: getCurrentCharacter,
getCurrentCharacterData: getCurrentCharacterData,
getCurrentCharacterId: getCurrentCharacterId,
setCurrentSystemData: setCurrentSystemData,
getCurrentSystemData: getCurrentSystemData,
deleteCurrentSystemData:deleteCurrentSystemData,
getCurrentLocationData: getCurrentLocationData,
getCurrentUserInfo: getCurrentUserInfo,
getCurrentCharacterLog: getCurrentCharacterLog,
findInViewport: findInViewport,
initScrollSpy: initScrollSpy,
getConfirmationTemplate: getConfirmationTemplate,
@@ -3674,6 +3712,7 @@ define([
getLocalStore: getLocalStore,
getResizeManager: getResizeManager,
clearSessionStorage: clearSessionStorage,
hasRight: hasRight,
getBrowserTabId: getBrowserTabId,
singleDoubleClick: singleDoubleClick,
getTableId: getTableId,

File diff suppressed because one or more lines are too long

View File

@@ -36,13 +36,16 @@
<div class="tab-content">
<div role="tabpanel" class="tab-pane fade {{#openTabNew}}in active{{/openTabNew}}" id="{{dialogMapNewContainerId}}"></div>
<div role="tabpanel" class="tab-pane fade {{#openTabEdit}}in active{{/openTabEdit}}" id="{{dialogMapEditContainerId}}"></div>
{{^hideEditTab}}
<div role="tabpanel" class="tab-pane fade {{#openTabEdit}}in active{{/openTabEdit}}" id="{{dialogMapEditContainerId}}"></div>
{{/hideEditTab}}
{{^hideSettingsTab}}
<div role="tabpanel" class="tab-pane fade {{#openTabSettings}}in active{{/openTabSettings}}" id="{{dialogMapSettingsContainerId}}">
{{#hasRightMapUpdate}}
<form role="form" class="form-horizontal">
<form role="form" class="form-horizontal">
{{#hasRightMapUpdate}}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-8">
<h4 class="pf-dynamic-area">Map options</h4>
@@ -201,9 +204,9 @@
<i class="fas fa-fw fa-question-circle pf-help-light" title="Send new 'Rally point' notifications to a Slack channel"></i>
</label>
<div class="col-sm-6 col-md-8">
<div class="input-icon-left" {{^slackRallyEnabled}}title="Globally disabled for this map type"{{/slackRallyEnabled}}>
<div class="input-icon-left" {{^slackRallyEnabled}}title="Globally disabled for this map type" {{/slackRallyEnabled}}>
<i class="fas fa-hashtag"></i>
<input name="slackChannelRally" type="text" class="form-control" id="{{slackChannelRallyId}}" value="{{slackChannelRally}}" placeholder="map_rally" pattern="^[_\-A-z0-9]{1,}$" data-error="Allowed chars (A-z, 0-9, _, -)" {{^slackRallyEnabled}}disabled{{/slackRallyEnabled}}>
<input name="slackChannelRally" type="text" class="form-control" id="{{slackChannelRallyId}}" value="{{slackChannelRally}}" placeholder="map_rally" pattern="^[_\-A-z0-9]{1,}$" data-error="Allowed chars (A-z, 0-9, _, -)" {{^slackRallyEnabled}}disabled{{/slackRallyEnabled}} />
</div>
</div>
<div class="note help-block with-errors"></div>
@@ -303,9 +306,17 @@
{{/discordEnabled}}
</div>
{{/hasRightMapUpdate}}
{{^hasRightMapUpdate}}
<div class="alert alert-info">
<span class="txt-color txt-color-info">Restricted</span>
<small>You don´t have the required roles.</small>
</div>
{{/hasRightMapUpdate}}
<h4 class="pf-dynamic-area">Share settings</h4>
{{#hasRightMapShare}}
<div class="row">
<div class="col-sm-11">
<blockquote>
@@ -313,8 +324,7 @@
Use this feature with caution! - Shared map entities have full map access.
They even can take over control by removing other entities from this list.
</p>
<small>Reduce this risk by creating a new map for joined OPs.
</small>
<small>Reduce this risk by creating a new map for joined OPs.</small>
</blockquote>
</div>
</div>
@@ -385,15 +395,16 @@
</div>
{{/accessAlliance.length}}
<input type="hidden" name="id" value="{{ mapData.config.id }}" />
</form>
{{/hasRightMapUpdate}}
{{^hasRightMapUpdate}}
<div class="alert alert-info">
<span class="txt-color txt-color-info">Restricted</span>
<small>You don´t have the required roles.</small>
</div>
{{/hasRightMapUpdate}}
{{/hasRightMapShare}}
{{^hasRightMapShare}}
<div class="alert alert-info">
<span class="txt-color txt-color-info">Restricted</span>
<small>You don´t have the required roles.</small>
</div>
{{/hasRightMapShare}}
<input type="hidden" name="id" value="{{ mapData.config.id }}" />
</form>
</div>
{{/hideSettingsTab}}

View File

@@ -184,18 +184,25 @@
</div>
{{#corporation}}
<h4 class="pf-dynamic-area"><img src="{{ccpImageServer}}/Corporation/{{id}}_64.png">&nbsp;&nbsp;Corporation maps "<em class="pf-map-type-corporation">{{name}}</em>"</h4>
<div class="form-group">
<div class="col-sm-9">
<label class="control-label" for="corporationSharing">
<input id="corporationSharing" type="checkbox" name="corporationSharing" data-toggle="toggle" value="1" {{#shared}}checked{{/shared}}>
&nbsp;map invite for corporation maps
</label>
</div>
<div class="col-sm-3"></div>
</div>
<h4 class="pf-dynamic-area"><img src="{{ccpImageServer}}/Corporation/{{id}}_64.png">&nbsp;&nbsp;Corporation maps "<em class="pf-map-type-corporation">{{name}}</em>"</h4>
{{#hasRightCorporationShareUpdate}}
<div class="form-group">
<div class="col-sm-9">
<label class="control-label" for="corporationSharing">
<input id="corporationSharing" type="checkbox" name="corporationSharing" data-toggle="toggle" value="1" {{#shared}}checked{{/shared}}>
&nbsp;map invite for corporation maps
</label>
</div>
<div class="col-sm-3"></div>
</div>
{{/hasRightCorporationShareUpdate}}
{{^hasRightCorporationShareUpdate}}
<div class="alert alert-info">
<span class="txt-color txt-color-info">Restricted</span>
<small>You don´t have the required roles.</small>
</div>
{{/hasRightCorporationShareUpdate}}
{{/corporation}}
{{#alliance}}

View File

@@ -8,7 +8,7 @@
<div class="col-sm-8">
<select name="icon" id="{{iconSelectId}}" class="form-control pf-form-icon-field {{select2Class}}">
{{#icon}}
<option value="{{class}}" >{{{unicode}}}</option>
<option value="{{class}}" {{#mapIcon}}{{class}}{{/mapIcon}}>{{{unicode}}}</option>
{{/icon}}
</select>
</div>
@@ -18,7 +18,7 @@
<div class="form-group">
<label for="{{nameInputId}}" class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<input name="name" type="text" class="form-control" id="{{nameInputId}}" value="" placeholder="Map name" data-error="Name required" data-minlength="3" data-minlength-error="Min. of 3 characters" required>
<input name="name" type="text" class="form-control" id="{{nameInputId}}" value="{{mapName}}" placeholder="Map name" data-error="Name required" data-minlength="3" data-minlength-error="Min. of 3 characters" required>
<span class="note help-block with-errors">Choose a meaningful name</span>
</div>
</div>
@@ -32,7 +32,7 @@
<div class="col-sm-10">
<select name="scopeId" id="{{scopeSelectId}}" class="form-control {{select2Class}}">
{{#scope}}
<option value="{{id}}">{{label}}</option>
<option value="{{id}}" {{#mapScopeId}}{{id}}{{/mapScopeId}}>{{label}}</option>
{{/scope}}
</select>
</div>
@@ -44,7 +44,7 @@
<div class="col-sm-10">
<select name="typeId" id="{{typeSelectId}}" class="form-control {{select2Class}}">
{{#type}}
<option value="{{id}}">{{label}}</option>
<option value="{{id}}" {{#mapTypeId}}{{id}}{{/mapTypeId}}>{{label}}</option>
{{/type}}
</select>
</div>
@@ -52,7 +52,7 @@
</div>
</div>
<input type="hidden" name="id" value="0" />
<input type="hidden" name="id" value="{{mapId}}" />
<div class="{{formInfoContainerClass}} alert alert-info" style="display: none;">

View File

@@ -20,8 +20,8 @@
<div class="alert {{@notificationTypeClass}}">
<span class="txt-color {{@notificationColorClass}}">{{ @notification->title }}</span>
<check if="{{ @notification->message }}">
<small>{{ @notification->message }}</small>
<check if="{{ @notification->text }}">
<small>{{ @notification->text }}</small>
</check>
</div>
</check>

View File

@@ -11,8 +11,8 @@
Use your browsers Back button to navigate to the page you have previously come
<a href="{{ @errorData->redirectUrl }}">from</a>.<br>
<check if="{{ @errorData->message }}">
Message: {{ @errorData->message }}
<check if="{{ @errorData->text }}">
Message: {{ @errorData->text }}
</check>
</p>
</div>

View File

@@ -12,8 +12,8 @@
Status: '{{ @errorData->status }}'<br>
<check if="{{ @errorData->message }}">
Message: {{ @errorData->message }}
<check if="{{ @errorData->text }}">
Message: {{ @errorData->text }}
</check>
</p>
</div>

View File

@@ -24,7 +24,7 @@
<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><i class="fas fa-times"></i></button>
<span class="txt-color txt-color-danger">({{ @errorData->code }}) {{ @errorData->status }}</span>
<small> {{ @errorData->message }}</small>
<small> {{ @errorData->text }}</small>
</div>
</check>
</div>

View File

@@ -767,6 +767,10 @@
padding-right: 8px; // overwrite default
}
.table{
text-align: left;
}
.pf-setup-body-cronjob{
.panel-footer{
display: flex; // align multiple error/warning/.. elements

View File

@@ -59,8 +59,9 @@ $mapModuleGridRowMinHeight: 38px;
@media screen and (min-width: $screen-lg){
grid-template-areas:
"area2 map map map"
"area2 area1 area1 area3"
"area2 area1 area1 area3";
grid-template-columns: minmax($colMinWidth, 1fr) 1fr minmax($colMinWidth, 1fr) 1fr;
grid-template-columns: minmax($colMinWidth, 1fr) 1fr 1fr minmax($colMinWidth, 1fr);
}
}
}

View File

@@ -34,15 +34,16 @@
}
.select2-hidden-accessible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
pointer-events: none;
border: 0 !important;
clip: rect(0 0 0 0) !important;
-webkit-clip-path: inset(50%) !important;
clip-path: inset(50%) !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important;
}
@import "theme/pathfinder/_layout";

View File

@@ -26,6 +26,7 @@
border: none;
font-size: 100%;
margin-top: 5px;
padding: 0;
&::-webkit-search-cancel-button {
-webkit-appearance: none;

View File

@@ -18,6 +18,10 @@
text-overflow: ellipsis;
white-space: nowrap;
}
.select2-selection__clear {
position: relative;
}
}
&[dir="rtl"] {

View File

@@ -6,7 +6,6 @@ $selection-color: #444 !default;
$border-color: $gray-light !default;
$border-radius: 0px !default;
$border-radius-choice: 2px !default;
$focus-border-color: $teal-lighter !default;

View File

@@ -3,7 +3,9 @@
color: $gray-dark;
border: 1px solid $border-color;
border-radius: $border-radius;
cursor: text;
outline: 0;
&:focus {
@@ -24,9 +26,12 @@
.select2-selection__choice {
background-color: $gray-lighter;
border: 1px solid $border-color;
border-radius: $border-radius-choice;
border-radius: $border-radius;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px;
@@ -35,27 +40,22 @@
.select2-selection__choice__remove {
color: $remove-color;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 4px;
&:hover {
color: $remove-hover-color;
}
}
.select2-selection__placeholder {
color: $gray-light;
}
}
&[dir="rtl"] {
.select2-selection--multiple {
.select2-selection__choice {
float: right;
}
.select2-selection__choice {
margin-left: 5px;
margin-right: auto;
}

View File

@@ -2,6 +2,7 @@
background-color: $gray-dark;
border: 1px solid $border-color;
border-radius: $border-radius;
outline: 0;
height: 32px;
padding: 6px 16px;
@@ -19,10 +20,13 @@
div[class^='col-']:first-child{
padding-left: 0; // overwrite default padding for "selected" option(s) in custom Bootstrap col layout
}
> .fas {
margin-left: 8px; // arrow icon for e.g. wormhole "selected" option
}
}
.select2-selection__clear {
color: $remove-color;
cursor: pointer;
float: right;
font-weight: bold;
@@ -40,14 +44,19 @@
.select2-selection__arrow {
background-color: $gray-dark;
border: none;
border-left: none;
border-left: 1px solid transparent;
border-top-right-radius: $border-radius;
border-bottom-right-radius: $border-radius;
height: 30px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;

View File

@@ -32,7 +32,7 @@
@import "library/x-editable/_bootstrap-editable"; // X-editable - v1.5.1
@import "library/slidebars/_slidebars"; // Slidebars Navigation
@import "library/easy-pie-chart/_easyPieChart"; // Easy Pie Chart 2.1.6
@import "library/select2/_core"; // Select2 4.0.3
@import "library/select2/_core"; // Select2 4.0.13
@import "library/blue-imp-gallery/_blueimp-gallery"; // Blue Imp Gallery
@import "library/blue-imp-gallery/_bootstrap-image-gallery"; // Blue Imp Gallery Bootstrap
@import "library/blue-imp-gallery/_bootstrap-image-gallery"; // Blue Imp Gallery Bootstrap