close #57 added in/export feature
This commit is contained in:
@@ -141,6 +141,125 @@ class Map extends \Controller\AccessController {
|
||||
echo json_encode($initData);
|
||||
}
|
||||
|
||||
/**
|
||||
* import new map data
|
||||
* @param $f3
|
||||
*/
|
||||
public function import($f3){
|
||||
$importData = (array)$f3->get('POST');
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
if(
|
||||
isset($importData['typeId']) &&
|
||||
count($importData['mapData']) > 0
|
||||
){
|
||||
$user = $this->_getUser();
|
||||
|
||||
if($user){
|
||||
$activeCharacter = $user->getActiveUserCharacter();
|
||||
|
||||
$map = Model\BasicModel::getNew('MapModel');
|
||||
$system = Model\BasicModel::getNew('SystemModel');
|
||||
$connection = Model\BasicModel::getNew('ConnectionModel');
|
||||
|
||||
foreach($importData['mapData'] as $mapData){
|
||||
if(
|
||||
isset($mapData['config']) &&
|
||||
isset($mapData['data'])
|
||||
){
|
||||
|
||||
|
||||
if(
|
||||
isset($mapData['data']['systems']) &&
|
||||
isset($mapData['data']['connections'])
|
||||
){
|
||||
$map->setData($mapData['config']);
|
||||
$map->typeId = (int)$importData['typeId'];
|
||||
$map->save();
|
||||
|
||||
// new system IDs will be generated
|
||||
// therefore we need to temp store a mapping between IDs
|
||||
$tempSystemIdMapping = [];
|
||||
|
||||
foreach($mapData['data']['systems'] as $systemData){
|
||||
$system->setData($systemData);
|
||||
$system->mapId = $map;
|
||||
$system->createdCharacterId = $activeCharacter->characterId;
|
||||
$system->updatedCharacterId = $activeCharacter->characterId;
|
||||
$system->save();
|
||||
|
||||
$tempSystemIdMapping[$systemData['id']] = $system->id;
|
||||
$system->reset();
|
||||
}
|
||||
|
||||
foreach($mapData['data']['connections'] as $connectionData){
|
||||
// check if source and target IDs match with new system ID
|
||||
if(
|
||||
isset( $tempSystemIdMapping[$connectionData['source']] ) &&
|
||||
isset( $tempSystemIdMapping[$connectionData['target']] )
|
||||
){
|
||||
$connection->setData($connectionData);
|
||||
$connection->mapId = $map;
|
||||
$connection->source = $tempSystemIdMapping[$connectionData['source']];
|
||||
$connection->target = $tempSystemIdMapping[$connectionData['target']];
|
||||
$connection->save();
|
||||
|
||||
$connection->reset();
|
||||
}
|
||||
}
|
||||
|
||||
// map access info should not automatically imported
|
||||
if($map->isPrivate()){
|
||||
$map->setAccess($user);
|
||||
}elseif($map->isCorporation()){
|
||||
$corporation = $activeCharacter->getCharacter()->getCorporation();
|
||||
if($corporation){
|
||||
$map->setAccess($corporation);
|
||||
}
|
||||
}elseif($map->isAlliance()){
|
||||
$alliance = $activeCharacter->getCharacter()->getAlliance();
|
||||
if($alliance){
|
||||
$map->setAccess($alliance);
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
// systems || connections missing
|
||||
$missingConfigError = (object) [];
|
||||
$missingConfigError->type = 'error';
|
||||
$missingConfigError->message = 'Map data not valid (systems || connections) missing';
|
||||
$return->error[] = $missingConfigError;
|
||||
}
|
||||
|
||||
}else{
|
||||
// map config || systems/connections missing
|
||||
$missingConfigError = (object) [];
|
||||
$missingConfigError->type = 'error';
|
||||
$missingConfigError->message = 'Map data not valid (config || data) missing';
|
||||
$return->error[] = $missingConfigError;
|
||||
}
|
||||
|
||||
|
||||
$map->reset();
|
||||
}
|
||||
}else{
|
||||
// user not found
|
||||
$return->error[] = $this->getUserLoggedOffError();
|
||||
}
|
||||
}else{
|
||||
// map data missing
|
||||
$missingDataError = (object) [];
|
||||
$missingDataError->type = 'error';
|
||||
$missingDataError->message = 'Map data missing';
|
||||
$return->error[] = $missingDataError;
|
||||
}
|
||||
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* save a new map or update an existing map
|
||||
* @param $f3
|
||||
@@ -337,7 +456,7 @@ class Map extends \Controller\AccessController {
|
||||
$return->error = [];
|
||||
|
||||
if($user){
|
||||
// -> get active user object
|
||||
// -> get active character
|
||||
$activeCharacter = $user->getActiveUserCharacter();
|
||||
|
||||
$cacheKey = 'user_map_data_' . $activeCharacter->id;
|
||||
|
||||
@@ -115,7 +115,6 @@ class BasicModel extends \DB\Cortex {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* extent the fieldConf Array with static fields for each table
|
||||
*/
|
||||
@@ -128,7 +127,7 @@ class BasicModel extends \DB\Cortex {
|
||||
'type' => Schema::DT_TIMESTAMP
|
||||
],
|
||||
'updated' => [
|
||||
'type' => Schema::DT_TIMESTAMP
|
||||
'type' => Schema::DF_CURRENT_TIMESTAMP
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@@ -66,6 +66,15 @@ class ConnectionModel extends BasicModel{
|
||||
return $connectionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for id
|
||||
* @param $id
|
||||
*/
|
||||
public function set_id($id){
|
||||
// connection id should never been set automatically
|
||||
// -> no return
|
||||
}
|
||||
|
||||
/**
|
||||
* check object for model access
|
||||
* @param $accessObject
|
||||
|
||||
@@ -63,10 +63,21 @@ class MapModel extends BasicModel {
|
||||
|
||||
foreach((array)$data as $key => $value){
|
||||
|
||||
if($key == 'created'){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!is_array($value)){
|
||||
if($this->exists($key)){
|
||||
$this->$key = $value;
|
||||
}
|
||||
}else{
|
||||
// special array data
|
||||
if($key == 'scope'){
|
||||
$this->scopeId = (int)$value['id'];
|
||||
}elseif($key == 'type'){
|
||||
$this->typeId = (int)$value['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,18 +166,30 @@ class MapModel extends BasicModel {
|
||||
return $mapDataAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for id
|
||||
* @param $id
|
||||
*/
|
||||
public function set_id($id){
|
||||
// map id should never been set automatically
|
||||
// -> no return
|
||||
}
|
||||
|
||||
/**
|
||||
* search for a system by id
|
||||
* @param $systemId
|
||||
* @return null
|
||||
*/
|
||||
public function getSystem($systemId){
|
||||
$systems = $this->getSystems();
|
||||
$searchSystem = null;
|
||||
foreach($systems as $system){
|
||||
if($system->id == $systemId){
|
||||
$searchSystem = $system;
|
||||
break;
|
||||
|
||||
if($systemId > 0){
|
||||
$systems = $this->getSystems();
|
||||
foreach($systems as $system){
|
||||
if($system->id == $systemId){
|
||||
$searchSystem = $system;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,10 @@ class SystemModel extends BasicModel {
|
||||
|
||||
foreach((array)$systemData as $key => $value){
|
||||
|
||||
if($key == 'created'){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!is_array($value)){
|
||||
if($this->exists($key)){
|
||||
$this->$key = $value;
|
||||
@@ -138,6 +142,15 @@ class SystemModel extends BasicModel {
|
||||
return $systemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for id
|
||||
* @param $id
|
||||
*/
|
||||
public function set_id($id){
|
||||
// map id should never been set automatically
|
||||
// -> no return
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for system security value
|
||||
* @param $trueSec
|
||||
|
||||
@@ -27,6 +27,7 @@ define(['jquery'], function($) {
|
||||
// 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
|
||||
// system API
|
||||
searchSystem: 'api/system/search', // ajax URL - search system by name
|
||||
saveSystem: 'api/system/save', // ajax URL - saves system to map
|
||||
|
||||
@@ -7,8 +7,9 @@ define([
|
||||
'app/init',
|
||||
'app/util',
|
||||
'app/render',
|
||||
'bootbox'
|
||||
], function($, Init, Util, Render, bootbox) {
|
||||
'bootbox',
|
||||
'app/ccp'
|
||||
], function($, Init, Util, Render, bootbox, CCP) {
|
||||
'use strict';
|
||||
|
||||
var config = {
|
||||
@@ -17,11 +18,36 @@ define([
|
||||
dialogMapCreateContainerId: 'pf-map-dialog-create', // id for the "new map" container
|
||||
dialogMapEditContainerId: 'pf-map-dialog-edit', // id for the "edit" container
|
||||
dialogMapSettingsContainerId: 'pf-map-dialog-settings', // id for the "settings" container
|
||||
dialogMapDownloadContainerId: 'pf-map-dialog-download', // id for the "download" container
|
||||
|
||||
userSelectId: 'pf-map-dialog-user-select', // id for "user" select
|
||||
corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select
|
||||
allianceSelectId: 'pf-map-dialog-alliance-select' // id for "alliance" select
|
||||
allianceSelectId: 'pf-map-dialog-alliance-select', // id for "alliance" select
|
||||
|
||||
dialogMapExportFormId: 'pf-map-dialog-form-export', // id for "export" form
|
||||
dialogMapImportFormId: 'pf-map-dialog-form-import', // id for "import" form
|
||||
|
||||
buttonExportId: 'pf-map-dialog-button-export', // id for "export" button
|
||||
buttonImportId: 'pf-map-dialog-button-import', // id for "import" button
|
||||
|
||||
fieldExportId: 'pf-map-filename-export', // id for "export" filename field
|
||||
fieldImportId: 'pf-map-filename-import', // id for "import" filename field
|
||||
dialogMapImportInfoId: 'pf-map-import-container', // id for "info" container
|
||||
dragDropElementClass: 'pf-form-dropzone' // class for "drag&drop" zone
|
||||
};
|
||||
|
||||
/**
|
||||
* format a given string into a valid filename
|
||||
* @param filename
|
||||
* @returns {string}
|
||||
*/
|
||||
var formatFilename = function(filename){
|
||||
filename = filename.replace(/[^a-zA-Z0-9]/g,'_');
|
||||
|
||||
var nowDate = new Date();
|
||||
var filenameDate = nowDate.toISOString().slice(0,10).replace(/-/g, '_');
|
||||
|
||||
return (filename + '_' + filenameDate).replace(/__/g,'_');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -46,15 +72,20 @@ define([
|
||||
// if there are no maps -> hide settings tab
|
||||
var hideSettingsTab = false;
|
||||
var hideEditTab = false;
|
||||
var hideDownloadTab = false;
|
||||
|
||||
if(mapData === false){
|
||||
hideSettingsTab = true;
|
||||
hideEditTab = true;
|
||||
hideDownloadTab = true;
|
||||
}
|
||||
|
||||
// available map "types" for a new or existing map
|
||||
var mapTypes = Util.getMapTypes(true);
|
||||
|
||||
var data = {
|
||||
scope: Util.getMapScopes(),
|
||||
type: Util.getMapTypes(true),
|
||||
type: mapTypes,
|
||||
icon: Util.getMapIcons(),
|
||||
formErrorContainerClass: Util.config.formErrorContainerClass,
|
||||
formWarningContainerClass: Util.config.formWarningContainerClass,
|
||||
@@ -89,18 +120,29 @@ define([
|
||||
// render main dialog -----------------------------------------------------
|
||||
data = {
|
||||
id: config.newMapDialogId,
|
||||
mapData: mapData,
|
||||
type: mapTypes,
|
||||
isInGameBrowser: CCP.isInGameBrowser(),
|
||||
|
||||
// message container
|
||||
formErrorContainerClass: Util.config.formErrorContainerClass,
|
||||
formWarningContainerClass: Util.config.formWarningContainerClass,
|
||||
formInfoContainerClass: Util.config.formInfoContainerClass,
|
||||
|
||||
// default open tab ----------
|
||||
openTabNew: options.tab === 'new',
|
||||
openTabEdit: options.tab === 'edit',
|
||||
openTabSettings: options.tab === 'settings',
|
||||
openTabDownload: options.tab === 'download',
|
||||
|
||||
dialogMapCreateContainerId: config.dialogMapCreateContainerId,
|
||||
dialogMapEditContainerId: config.dialogMapEditContainerId,
|
||||
dialogMapSettingsContainerId: config.dialogMapSettingsContainerId,
|
||||
dialogMapDownloadContainerId: config.dialogMapDownloadContainerId,
|
||||
|
||||
hideEditTab: hideEditTab,
|
||||
hideSettingsTab: hideSettingsTab,
|
||||
hideDownloadTab: hideDownloadTab,
|
||||
|
||||
// settings tab --------------
|
||||
userSelectId: config.userSelectId,
|
||||
@@ -115,18 +157,29 @@ define([
|
||||
// access limitations --------
|
||||
maxUser: Init.maxSharedCount.user,
|
||||
maxCorporation: Init.maxSharedCount.corporation,
|
||||
maxAlliance: Init.maxSharedCount.alliance
|
||||
maxAlliance: Init.maxSharedCount.alliance,
|
||||
|
||||
// download tab --------------
|
||||
dialogMapExportFormId: config.dialogMapExportFormId,
|
||||
dialogMapImportFormId: config.dialogMapImportFormId,
|
||||
buttonExportId: config.buttonExportId,
|
||||
buttonImportId: config.buttonImportId,
|
||||
fieldExportId: config.fieldExportId,
|
||||
fieldImportId: config.fieldImportId,
|
||||
dialogMapImportInfoId: config.dialogMapImportInfoId,
|
||||
|
||||
formatFilename: function(){
|
||||
// format filename from "map name" (initial)
|
||||
return function (mapName, render) {
|
||||
var filename = render(mapName);
|
||||
return formatFilename(filename);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var contentDialog = Mustache.render(templateMapDialog, data);
|
||||
contentDialog = $(contentDialog);
|
||||
|
||||
// set mapId for "settings" tab
|
||||
if(mapData !== false){
|
||||
contentDialog.find('input[name="id"]').val( mapData.config.id );
|
||||
}
|
||||
|
||||
|
||||
// set tab content
|
||||
$('#' + config.dialogMapCreateContainerId, contentDialog).html(contentNewMap);
|
||||
$('#' + config.dialogMapEditContainerId, contentDialog).html(contentEditMap);
|
||||
@@ -185,7 +238,7 @@ define([
|
||||
|
||||
dialogContent.hideLoadingAnimation();
|
||||
|
||||
if(responseData.error.length > 0){
|
||||
if(responseData.error.length){
|
||||
form.showFormMessage(responseData.error);
|
||||
}else{
|
||||
// success
|
||||
@@ -216,15 +269,23 @@ define([
|
||||
|
||||
|
||||
// after modal is shown =======================================================================
|
||||
mapInfoDialog.on('shown.bs.modal', function(e) {
|
||||
mapInfoDialog.on('shown.bs.modal', function(e){
|
||||
|
||||
mapInfoDialog.initTooltips();
|
||||
|
||||
// prevent "disabled" tabs from being clicked... "bootstrap" bugFix...
|
||||
mapInfoDialog.find('.navbar a[data-toggle=tab]').on('click', function(e){
|
||||
if ($(this).hasClass('disabled')){
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// set form validator
|
||||
mapInfoDialog.find('form').initFormValidation();
|
||||
|
||||
// events for tab change
|
||||
mapInfoDialog.find('.navbar a').on('shown.bs.tab', function (e) {
|
||||
mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){
|
||||
|
||||
var selectElementUser = mapInfoDialog.find('#' + config.userSelectId);
|
||||
var selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId);
|
||||
@@ -232,7 +293,6 @@ define([
|
||||
|
||||
if($(e.target).attr('href') === '#' + config.dialogMapSettingsContainerId){
|
||||
// "settings" tab
|
||||
|
||||
initSettingsSelectFields(mapInfoDialog);
|
||||
}else{
|
||||
if( $(selectElementUser).data('select2') !== undefined ){
|
||||
@@ -247,6 +307,13 @@ define([
|
||||
$(selectElementAlliance).select2('destroy');
|
||||
}
|
||||
}
|
||||
|
||||
// no "save" dialog button on "in/export" tab
|
||||
if($(e.target).attr('href') === '#' + config.dialogMapDownloadContainerId){
|
||||
mapInfoDialog.find('button.btn-success').hide();
|
||||
}else{
|
||||
mapInfoDialog.find('button.btn-success').show();
|
||||
}
|
||||
});
|
||||
|
||||
// show form messages -------------------------------------
|
||||
@@ -259,17 +326,223 @@ define([
|
||||
// no map data found (probably new user
|
||||
form.showFormMessage([{type: 'warning', message: 'No maps found. Create a new map before you can start'}]);
|
||||
}
|
||||
|
||||
// init select fields in case "settings" tab is open by default
|
||||
if(options.tab === 'settings'){
|
||||
initSettingsSelectFields(mapInfoDialog);
|
||||
}
|
||||
|
||||
// init "download tab" ========================================================================
|
||||
var downloadTabElement = mapInfoDialog.find('#' + config.dialogMapDownloadContainerId);
|
||||
if(downloadTabElement.length){
|
||||
// tab exists
|
||||
|
||||
// export map data ------------------------------------------------------------------------
|
||||
downloadTabElement.find('#' + config.buttonExportId).on('click', { mapData: mapData }, function(e){
|
||||
|
||||
var exportForm = $('#' + config.dialogMapExportFormId);
|
||||
var validExportForm = exportForm.isValidForm();
|
||||
|
||||
if(validExportForm){
|
||||
// set map data right before download
|
||||
$(this).setExportMapData(e.data.mapData);
|
||||
}else{
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// import map data ------------------------------------------------------------------------
|
||||
// check if "FileReader" API is supported
|
||||
var importFormElement = downloadTabElement.find('#' + config.dialogMapImportFormId);
|
||||
if(window.File && window.FileReader && window.FileList && window.Blob){
|
||||
|
||||
// show file info in UI
|
||||
downloadTabElement.find('#' + config.fieldImportId).on('change', function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
var infoContainerElement = importFormElement.find('#' + config.dialogMapImportInfoId);
|
||||
infoContainerElement.hide().empty();
|
||||
importFormElement.hideFormMessage('all');
|
||||
|
||||
var output = [];
|
||||
var files = e.target.files;
|
||||
|
||||
for (var i = 0, f; !!(f = files[i]); i++) {
|
||||
output.push(( i + 1 ) + '. file: ' + f.name + ' - ' +
|
||||
f.size + ' bytes; last modified: ' +
|
||||
f.lastModifiedDate.toLocaleDateString() );
|
||||
}
|
||||
|
||||
if(output.length > 0){
|
||||
infoContainerElement.html( output ).show();
|
||||
}
|
||||
|
||||
importFormElement.validator('validate');
|
||||
});
|
||||
|
||||
// drag&drop
|
||||
var importData = {};
|
||||
importData.mapData = [];
|
||||
var files = [];
|
||||
var filesCount = 0;
|
||||
var filesCountFail = 0;
|
||||
|
||||
// onLoad for FileReader API
|
||||
var readerOnLoad = function(readEvent) {
|
||||
console.log(readEvent)
|
||||
// get file content
|
||||
try{
|
||||
importData.mapData.push( JSON.parse( readEvent.target.result ) );
|
||||
}catch(error){
|
||||
filesCountFail++;
|
||||
importFormElement.showFormMessage([{type: 'error', message: 'File can not be parsed'}]);
|
||||
}
|
||||
|
||||
// start import when all files are parsed
|
||||
if(
|
||||
filesCount === files.length &&
|
||||
filesCountFail === 0
|
||||
){
|
||||
importMaps(importData);
|
||||
}
|
||||
};
|
||||
|
||||
var handleDragOver = function(dragEvent) {
|
||||
dragEvent.stopPropagation();
|
||||
dragEvent.preventDefault();
|
||||
dragEvent.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
|
||||
};
|
||||
|
||||
var handleFileSelect = function(evt){
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
||||
importData = importFormElement.getFormValues();
|
||||
importData.mapData = [];
|
||||
filesCount = 0;
|
||||
filesCountFail = 0;
|
||||
|
||||
files = evt.dataTransfer.files; // FileList object.
|
||||
|
||||
for (var file; !!(file = files[filesCount]); filesCount++){
|
||||
var reader = new FileReader();
|
||||
reader.onload = readerOnLoad;
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
|
||||
var dropZone = downloadTabElement.find('.' + config.dragDropElementClass);
|
||||
dropZone[0].addEventListener('dragover', handleDragOver, false);
|
||||
dropZone[0].addEventListener('drop', handleFileSelect, false);
|
||||
|
||||
// import "button"
|
||||
downloadTabElement.find('#' + config.buttonImportId).on('click', function(e) {
|
||||
|
||||
importFormElement.validator('validate');
|
||||
var validImportForm = importFormElement.isValidForm();
|
||||
|
||||
if(validImportForm){
|
||||
importData = importFormElement.getFormValues();
|
||||
importData.mapData = [];
|
||||
|
||||
var fileElement = downloadTabElement.find('#' + config.fieldImportId);
|
||||
files = fileElement[0].files;
|
||||
filesCount = 0;
|
||||
filesCountFail = 0;
|
||||
|
||||
for (var file; !!(file = files[filesCount]); filesCount++){
|
||||
var reader = new FileReader();
|
||||
reader.onload = readerOnLoad;
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
}else{
|
||||
importFormElement.showFormMessage([{type: 'error', message: 'The File APIs are not fully supported in this browser.'}]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// init select fields in case "settings" tab is open by default
|
||||
if(options.tab === 'settings'){
|
||||
initSettingsSelectFields(mapInfoDialog);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* import new map(s) data
|
||||
* @param importData
|
||||
*/
|
||||
var importMaps = function(importData){
|
||||
|
||||
var importForm = $('#' + config.dialogMapImportFormId);
|
||||
importForm.hideFormMessage('all');
|
||||
|
||||
// lock dialog
|
||||
var dialogContent = importForm.parents('.modal-content');
|
||||
dialogContent.showLoadingAnimation();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.importMap,
|
||||
data: importData,
|
||||
dataType: 'json'
|
||||
}).done(function(responseData){
|
||||
if(responseData.error.length){
|
||||
// form.showFormMessage(responseData.error);
|
||||
importForm.showFormMessage(responseData.error);
|
||||
}else{
|
||||
// success
|
||||
|
||||
Util.showNotify({title: 'Import finished', text: 'Map(s) imported', type: 'success'});
|
||||
}
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
var reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': importMap', text: reason, type: 'error'});
|
||||
}).always(function() {
|
||||
importForm.find('input, select').resetFormFields().trigger('change');
|
||||
dialogContent.hideLoadingAnimation();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set json map data for export to an element (e.g. <a>-Tag or button) for download
|
||||
* @param mapData
|
||||
* @returns {*}
|
||||
*/
|
||||
$.fn.setExportMapData = function(mapData){
|
||||
|
||||
var fieldExport = $('#' + config.fieldExportId);
|
||||
var filename = '';
|
||||
var mapDataEncoded = '';
|
||||
|
||||
if(fieldExport.length){
|
||||
filename = fieldExport.val();
|
||||
|
||||
if(filename.length > 0){
|
||||
// remove object properties that should not be included in export
|
||||
// -> e.g. jsPlumb object,...
|
||||
var allowedKeys = ['config', 'data'];
|
||||
|
||||
var replace = function(obj, keys) {
|
||||
var dup = {};
|
||||
for (var key in obj) {
|
||||
if (keys.indexOf(key) !== -1) {
|
||||
dup[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return dup;
|
||||
};
|
||||
|
||||
mapDataEncoded = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify( replace(mapData, allowedKeys) ));
|
||||
}
|
||||
}
|
||||
|
||||
return this.each(function(){
|
||||
var exportButton = $(this);
|
||||
exportButton.attr('href', 'data:' + mapDataEncoded);
|
||||
exportButton.attr('download', filename + '.json');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* init select2 fields within the settings dialog
|
||||
|
||||
@@ -202,7 +202,11 @@ define([
|
||||
return this.each(function(){
|
||||
var field = $(this);
|
||||
|
||||
field.val('');
|
||||
if( !field.is('select') ){
|
||||
// "input"
|
||||
field.val('');
|
||||
}
|
||||
|
||||
field.parents('.form-group').removeClass('has-error has-success');
|
||||
});
|
||||
};
|
||||
@@ -293,6 +297,12 @@ define([
|
||||
case 'info':
|
||||
messageElement = formElement.find('.' + config.formInfoContainerClass);
|
||||
break;
|
||||
case 'all':
|
||||
messageElement = formElement.find(
|
||||
'.' + config.formErrorContainerClass + ', ' +
|
||||
'.' + config.formWarningContainerClass + ', ' +
|
||||
'.' + config.formInfoContainerClass
|
||||
);
|
||||
}
|
||||
|
||||
if(messageElement){
|
||||
@@ -1603,7 +1613,6 @@ define([
|
||||
redirect(data.reroute, ['logout']);
|
||||
}
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
|
||||
var reason = status + ' ' + error;
|
||||
showNotify({title: jqXHR.status + ': logout', text: reason, type: 'error'});
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,13 +3,32 @@
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="navbar-header pull-left">
|
||||
<ul class="nav navbar-nav {{dialogNavigationClass}}" role="tablist">
|
||||
<li class="{{#openTabNew}}active{{/openTabNew}}"><a role="tab" data-toggle="tab" data-name="newMap" href="#{{dialogMapCreateContainerId}}"><i class="fa fa-plus fa-fw"></i> New map</a></li>
|
||||
<li class="{{#openTabNew}}active{{/openTabNew}}">
|
||||
<a role="tab" data-toggle="tab" data-name="newMap" href="#{{dialogMapCreateContainerId}}">
|
||||
<i class="fa fa-plus fa-fw"></i> New map
|
||||
</a>
|
||||
</li>
|
||||
{{^hideEditTab}}
|
||||
<li class="{{#openTabEdit}}active{{/openTabEdit}}"><a role="tab" data-toggle="tab" data-name="editMap" href="#{{dialogMapEditContainerId}}"><i class="fa fa-edit fa-fw"></i> Edit map</a></li>
|
||||
<li class="{{#openTabEdit}}active{{/openTabEdit}}">
|
||||
<a role="tab" data-toggle="tab" data-name="editMap" href="#{{dialogMapEditContainerId}}">
|
||||
<i class="fa fa-edit fa-fw"></i> Edit map
|
||||
</a>
|
||||
</li>
|
||||
{{/hideEditTab}}
|
||||
{{^hideSettingsTab}}
|
||||
<li class="{{#openTabSettings}}active{{/openTabSettings}}"><a role="tab" data-toggle="tab" data-name="settings" href="#{{dialogMapSettingsContainerId}}"><i class="fa fa-gears fa-fw"></i> Settings</a></li>
|
||||
<li class="{{#openTabSettings}}active{{/openTabSettings}}">
|
||||
<a role="tab" data-toggle="tab" data-name="settings" href="#{{dialogMapSettingsContainerId}}">
|
||||
<i class="fa fa-gears fa-fw"></i> Settings
|
||||
</a>
|
||||
</li>
|
||||
{{/hideSettingsTab}}
|
||||
{{^hideDownloadTab}}
|
||||
<li class="{{#openTabDownload}}active{{/openTabDownload}} {{#isInGameBrowser}}disabled{{/isInGameBrowser}}" {{#isInGameBrowser}}title="Not working in IGB"{{/isInGameBrowser}}>
|
||||
<a role="tab" data-toggle="tab" data-name="download" href="#{{dialogMapDownloadContainerId}}" {{#isInGameBrowser}}class="disabled"{{/isInGameBrowser}}>
|
||||
<i class="fa fa-exchange fa-fw"></i> Import/Export
|
||||
</a>
|
||||
</li>
|
||||
{{/hideDownloadTab}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -17,108 +36,181 @@
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane fade {{#openTabNew}}in active{{/openTabNew}}" id="{{dialogMapCreateContainerId}}"></div>
|
||||
<div role="tabpanel" class="tab-pane fade {{#openTabEdit}}in active{{/openTabEdit}}" id="{{dialogMapEditContainerId}}"></div>
|
||||
<div role="tabpanel" class="tab-pane fade {{#openTabSettings}}in active{{/openTabSettings}}" id="{{dialogMapSettingsContainerId}}">
|
||||
<form role="form" class="form-horizontal">
|
||||
<h2><i class="fa fa-share-alt fa-fw"></i> Share settings</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-11">
|
||||
<blockquote>
|
||||
<p>
|
||||
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>
|
||||
</blockquote>
|
||||
{{^hideSettingsTab}}
|
||||
<div role="tabpanel" class="tab-pane fade {{#openTabSettings}}in active{{/openTabSettings}}" id="{{dialogMapSettingsContainerId}}">
|
||||
<form role="form" class="form-horizontal">
|
||||
<h4><i class="fa fa-share-alt fa-fw"></i> Share settings</h4>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-11">
|
||||
<blockquote>
|
||||
<p>
|
||||
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>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{! user search ----------------------------------------------------- }}
|
||||
{{#accessUser.length}}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="{{userSelectId}}">Username</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group" title="add/remove user">
|
||||
<label for="{{userSelectId}}"></label>
|
||||
<select id="{{userSelectId}}" name="mapUsers[]" multiple="multiple">
|
||||
{{#accessUser}}
|
||||
{{! user search ----------------------------------------------------- }}
|
||||
{{#accessUser.length}}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="{{userSelectId}}">Username</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group" title="add/remove user">
|
||||
<label for="{{userSelectId}}"></label>
|
||||
<select id="{{userSelectId}}" name="mapUsers[]" multiple="multiple">
|
||||
{{#accessUser}}
|
||||
<option value="{{id}}" selected>{{name}}</option>
|
||||
{{/accessUser}}
|
||||
</select>
|
||||
<span class="help-block with-errors">Search user name (max {{maxUser}})</span>
|
||||
{{/accessUser}}
|
||||
</select>
|
||||
<span class="help-block with-errors">Search user name (max {{maxUser}})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/accessUser.length}}
|
||||
{{/accessUser.length}}
|
||||
|
||||
{{! corporation search ---------------------------------------------- }}
|
||||
{{#accessCorporation.length}}
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" for="{{corporationSelectId}}">Corporations</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group" title="add/remove corporations">
|
||||
<label for="{{corporationSelectId}}"></label>
|
||||
<select id="{{corporationSelectId}}" name="mapCorporations[]" multiple="multiple">
|
||||
{{#accessCorporation}}
|
||||
{{! corporation search ---------------------------------------------- }}
|
||||
{{#accessCorporation.length}}
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" for="{{corporationSelectId}}">Corporations</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group" title="add/remove corporations">
|
||||
<label for="{{corporationSelectId}}"></label>
|
||||
<select id="{{corporationSelectId}}" name="mapCorporations[]" multiple="multiple">
|
||||
{{#accessCorporation}}
|
||||
<option value="{{id}}" selected="selected" >{{name}}</option>
|
||||
{{/accessCorporation}}
|
||||
</select>
|
||||
<span class="help-block with-errors">Search corporation name (max {{maxCorporation}})</span>
|
||||
{{/accessCorporation}}
|
||||
</select>
|
||||
<span class="help-block with-errors">Search corporation name (max {{maxCorporation}})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/accessCorporation.length}}
|
||||
{{/accessCorporation.length}}
|
||||
|
||||
{{! alliance search ------------------------------------------------- }}
|
||||
{{#accessAlliance.length}}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="{{allianceSelectId}}">Alliances</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group" title="add/remove alliances">
|
||||
<label for="{{allianceSelectId}}"></label>
|
||||
<select id="{{allianceSelectId}}" name="mapAlliances[]" multiple="multiple" >
|
||||
{{#accessAlliance}}
|
||||
<option value="{{id}}" selected="selected" >{{name}}</option>
|
||||
{{/accessAlliance}}
|
||||
</select>
|
||||
<span class="help-block with-errors">Search alliance name (max {{maxAlliance}})</span>
|
||||
{{! alliance search ------------------------------------------------- }}
|
||||
{{#accessAlliance.length}}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="{{allianceSelectId}}">Alliances</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group" title="add/remove alliances">
|
||||
<label for="{{allianceSelectId}}"></label>
|
||||
<select id="{{allianceSelectId}}" name="mapAlliances[]" multiple="multiple" >
|
||||
{{#accessAlliance}}
|
||||
<option value="{{id}}" selected="selected" >{{name}}</option>
|
||||
{{/accessAlliance}}
|
||||
</select>
|
||||
<span class="help-block with-errors">Search alliance name (max {{maxAlliance}})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/accessAlliance.length}}
|
||||
{{/accessAlliance.length}}
|
||||
|
||||
<input type="hidden" name="id" value="0" />
|
||||
</form>
|
||||
<input type="hidden" name="id" value="{{ mapData.config.id }}" />
|
||||
|
||||
</form>
|
||||
</div>
|
||||
{{/hideSettingsTab}}
|
||||
|
||||
|
||||
{{^hideDownloadTab}}
|
||||
<div role="tabpanel" class="tab-pane fade {{#openTabDownload}}in active{{/openTabDownload}}" id="{{dialogMapDownloadContainerId}}">
|
||||
|
||||
<h4 class="pf-dynamic-area">Map export</h4>
|
||||
<form role="form" class="form-horizontal" id="{{dialogMapExportFormId}}">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="{{fieldExportId}}">Export name</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="{{fieldExportId}}" type="text" name="{{fieldExportId}}" value="{{#formatFilename}}{{mapData.config.name}}{{/formatFilename}}" pattern="^[_a-zA-Z0-9]{1,}$" data-minlength="3" data-minlength-error="Min. of 3 characters" data-error="Invalid format: _ a-z A-Z 0-9" required>
|
||||
<div class="input-group-btn">
|
||||
<a type="button" id="{{buttonExportId}}" class="btn btn-default" href="" download="data.json">
|
||||
<i class="fa fa-fw fa-upload"></i> Export
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<h4 class="pf-dynamic-area">Map Import</h4>
|
||||
<form role="form" class="form-horizontal" id="{{dialogMapImportFormId}}">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type" class="col-sm-2 control-label">Type</label>
|
||||
<div class="col-sm-3">
|
||||
<select name="typeId" id="type" class="form-control" title="Alliance/Corporation maps require character authentication" data-placement="top">
|
||||
{{#type}}
|
||||
<option value="{{id}}">{{label}}</option>
|
||||
{{/type}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="{{fieldImportId}}">Import file</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="input-group">
|
||||
<input class="form-control" id="{{fieldImportId}}" type="file" name="{{fieldImportId}}" accept=".json" data-error="Select a valid file" required>
|
||||
<div class="input-group-btn">
|
||||
<button type="button" id="{{buttonImportId}}" class="btn btn-default">
|
||||
<i class="fa fa-fw fa-download"></i> Import
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pf-form-dropzone">Drop map file here</div>
|
||||
|
||||
<h4 id="{{dialogMapImportInfoId}}" class="pf-dynamic-area" style="display: none;"></h4>
|
||||
|
||||
<div class="{{formErrorContainerClass}} alert alert-danger" style="display: none;">
|
||||
<span class="txt-color txt-color-danger">Error</span>
|
||||
<small> (important non-critical information)</small>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{/hideDownloadTab}}
|
||||
|
||||
|
||||
<div class="{{formInfoContainerClass}} alert alert-info" style="display: none;">
|
||||
<span class="txt-color txt-color-information">Information</span>
|
||||
<small> (important non-critical information)</small>
|
||||
</div>
|
||||
|
||||
<div class="{{formWarningContainerClass}} alert alert-warning" style="display: none;">
|
||||
<span class="txt-color txt-color-warning">Warning</span>
|
||||
<small> (important non-critical information)</small>
|
||||
</div>
|
||||
|
||||
<div class="{{formErrorContainerClass}} alert alert-danger" style="display: none;">
|
||||
<span class="txt-color txt-color-danger">Error</span>
|
||||
<small> (important non-critical information)</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="{{formInfoContainerClass}} alert alert-info" style="display: none;">
|
||||
<span class="txt-color txt-color-information">Information</span>
|
||||
<small> (important non-critical information)</small>
|
||||
</div>
|
||||
|
||||
<div class="{{formWarningContainerClass}} alert alert-warning" style="display: none;">
|
||||
<span class="txt-color txt-color-warning">Warning</span>
|
||||
<small> (important non-critical information)</small>
|
||||
</div>
|
||||
|
||||
<div class="{{formErrorContainerClass}} alert alert-danger" style="display: none;">
|
||||
<span class="txt-color txt-color-danger">Error</span>
|
||||
<small> (important non-critical information)</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -346,7 +346,7 @@ $navbar-default-link-hover-color: $teal-lightest;
|
||||
$navbar-default-link-hover-bg: transparent;
|
||||
$navbar-default-link-active-color: $teal-lighter;
|
||||
$navbar-default-link-active-bg: transparent;
|
||||
$navbar-default-link-disabled-color: #ccc;
|
||||
$navbar-default-link-disabled-color: $gray;
|
||||
$navbar-default-link-disabled-bg: transparent;
|
||||
|
||||
// Navbar brand label
|
||||
|
||||
@@ -236,8 +236,8 @@
|
||||
}
|
||||
|
||||
// sharing dialog =========================================
|
||||
#pf-sharing-dialog{
|
||||
.pf-dynamic-area{
|
||||
h2, h4{
|
||||
&.pf-dynamic-area{
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,39 @@ input, select{
|
||||
@include box-shadow(0 0 0 50px $gray-dark inset !important);
|
||||
-webkit-text-fill-color: $gray-lighter;
|
||||
}
|
||||
|
||||
// file input
|
||||
&::-webkit-file-upload-button{
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: $gray-light;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
// drag&drop zone
|
||||
.pf-form-dropzone{
|
||||
border: 2px dashed $gray-darker;
|
||||
height: 100px;
|
||||
background-color: darken($gray, 3%);
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
line-height: 100px;
|
||||
margin: 15px 0;
|
||||
color: $gray-darker;
|
||||
@include border-radius(10px);
|
||||
@include transition( color 0.18s ease-out, border-color 0.18s ease-out);
|
||||
|
||||
&:hover{
|
||||
color: $teal-lighter;
|
||||
border-color: $teal-lighter;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// fix for bootstrap-toggle plugin
|
||||
.toggle{
|
||||
&.btn:active{
|
||||
|
||||
@@ -729,11 +729,11 @@ select:active, select:hover {
|
||||
|
||||
}
|
||||
|
||||
.tooltip.top .tooltip-arrow,{
|
||||
.tooltip.top .tooltip-arrow{
|
||||
border-top-color: $gray-light;
|
||||
}
|
||||
|
||||
.tooltip.right .tooltip-arrow,{
|
||||
.tooltip.right .tooltip-arrow{
|
||||
border-right-color: $gray-light;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user