close #57 added in/export feature

This commit is contained in:
Exodus4D
2015-11-09 21:46:53 +01:00
parent b2690587a8
commit b805676690
14 changed files with 690 additions and 121 deletions

View File

@@ -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;

View File

@@ -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
]
];

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>

View File

@@ -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

View File

@@ -236,8 +236,8 @@
}
// sharing dialog =========================================
#pf-sharing-dialog{
.pf-dynamic-area{
h2, h4{
&.pf-dynamic-area{
min-height: 0;
}
}

View File

@@ -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{

View File

@@ -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;
}