diff --git a/Makefile b/Makefile index 4e2678354..a2fd78ace 100644 --- a/Makefile +++ b/Makefile @@ -60,12 +60,12 @@ comma:=, space:= $() $() coverpkg=$(subst $(space),$(comma), $(filter-out %/mock %/assets, $(shell go list ./main/... ./sys/... ./storage/... ./service/...))) -startproxeus=PROXEUS_DATA_DIR=$(1)/data PROXEUS_SETTINGS_FILE=$(1)/settings/main.json PROXEUS_TEST_MODE=true artifacts/proxeus & +startproxeus=PROXEUS_DATA_DIR=$(1)/data PROXEUS_SETTINGS_FILE=$(1)/settings/main.json PROXEUS_TEST_MODE=true artifacts/proxeus -ServiceAddress 0.0.0.0:1323 & stopproxeus=pkill proxeus startds=curl -s http://localhost:2115 > /dev/null || ( PROXEUS_DATA_DIR=$(1) docker-compose up -d document-service && touch $(1)/ds-started ) startnodes=curl -s http://localhost:8011 > /dev/null || (PROXEUS_PLATFORM_DOMAIN=http://$(DOCKER_GATEWAY):1323 NODE_CRYPTO_RATES_URL=http://localhost:8011 REGISTER_RETRY_INTERVAL=1 docker-compose -f docker-compose.yml -f docker-compose-extra.override.yml up -d node-crypto-forex-rates && touch $(1)/nodes-started ) startmongo=nc -z localhost 27017 2> /dev/null || (docker run -d -p 27017:27017 -p 27018:27018 -p 27019:27019 proxeus/mongo-dev-cluster && sleep 10 && touch $(1)/mongo-started) -waitforproxeus=echo "Waiting for Proxeus to start" ; curl --head -X GET --retry 120 --retry-connrefused --retry-delay 1 --silent -o /dev/null http://localhost:1323 && echo "Proxeus started" || echo "Unable to start Proxeus" +waitforproxeus=echo "Waiting for Proxeus to start" ; curl --head -X GET --retry 120 --retry-connrefused --retry-delay 1 --silent -o /dev/null http://0.0.0.0:1323 && echo "Proxeus started" || echo "Unable to start Proxeus" ifeq ($(coverage),true) COVERAGE_OPTS=-coverprofile artifacts/$@.coverage -coverpkg="$(coverpkg)" diff --git a/main/handlers/api/handlers.go b/main/handlers/api/handlers.go index 8489dc683..aa4f1c4cc 100644 --- a/main/handlers/api/handlers.go +++ b/main/handlers/api/handlers.go @@ -632,6 +632,24 @@ func LoginHandler(e echo.Context) (err error) { }) } +// Returns custom css style if exists +// +// @returns +// +// 200 => string +// 500 => Server error +func GetCustomAppearanceStyle(e echo.Context) error { + c := e.(*www.Context) + + settings := c.System().GetSettings() + if len(settings.PlatformDomain) == 0 { + settings.PlatformDomain = e.Request().Host + } + + c.Response().Header().Set(echo.HeaderContentType, "text/css") + return c.String(http.StatusOK, settings.CustomStyleCSS) +} + // Validates user session cookie func ValidateUserSession(e echo.Context) (err error) { c := e.(*www.Context) diff --git a/main/handlers/routes.go b/main/handlers/routes.go index 867a69d17..50a757c3b 100644 --- a/main/handlers/routes.go +++ b/main/handlers/routes.go @@ -64,6 +64,7 @@ func MainHostedAPI(e *echo.Echo, s *www.Security, version string) { routesNoCache := []r{ // config {GET, PUBLIC, "/api/config", api.ConfigHandler(version)}, + {GET, PUBLIC, "/api/appearance-css", api.GetCustomAppearanceStyle}, // authentication {GET, PUBLIC, "/api/session/validate", api.ValidateUserSession}, diff --git a/sys/model/settings.go b/sys/model/settings.go index a1cc84271..3e5a05a9f 100644 --- a/sys/model/settings.go +++ b/sys/model/settings.go @@ -9,6 +9,7 @@ type Settings struct { DefaultRole string `json:"defaultRole" default:"creator" usage:"Default role that is going to be used for new registrations. Value is case insensitive."` SessionExpiry string `json:"sessionExpiry" validate:"required=true" default:"1h" usage:"Session expiry like 1h as one hour, 1m as one minute or 1s as one second."` CacheExpiry string `json:"cacheExpiry" validate:"required=true" default:"24h" usage:"Common cache expiry which will be used for email tokens or similar."` + CustomStyleCSS string `json:"customStyleCSS" default:"" usage:"Set custom css styles"` BlockchainNet string `json:"blockchainNet" validate:"required=true" default:"sepolia" usage:"Ethereum blockchain net like mainnet or goerli, sepolia, polygon, polygon-mumbai."` InfuraApiKey string `json:"infuraApiKey" validate:"required=true" usage:"API Key to access Infura node."` BlockchainContractAddress string `json:"blockchainContractAddress" validate:"required=true" default:"" usage:"Ethereum contract address which will be used to register files and verify them."` diff --git a/ui/core/public/app.html b/ui/core/public/app.html index 3a0f0b07a..d9044e508 100644 --- a/ui/core/public/app.html +++ b/ui/core/public/app.html @@ -1,20 +1,25 @@ - - - - - - - - - proxeus - - - -
- - + + + + + + + + + + proxeus + + + + +
+ + + + diff --git a/ui/core/public/frontend.html b/ui/core/public/frontend.html index c35d2a2f0..748ce21ed 100644 --- a/ui/core/public/frontend.html +++ b/ui/core/public/frontend.html @@ -1,20 +1,25 @@ - - - - - - - - - proxeus - - - -
- - + + + + + + + + + + proxeus + + + + +
+ + + + diff --git a/ui/core/public/user.html b/ui/core/public/user.html index c07089076..d5369d30f 100644 --- a/ui/core/public/user.html +++ b/ui/core/public/user.html @@ -1,20 +1,25 @@ - - - - - - - - - proxeus - - + + + + + + + + + + proxeus + + + -
- - + We're sorry but proxeus doesn't work properly without JavaScript enabled. Please enable it to + continue. + +
+ + + + diff --git a/ui/core/src/components/nav-tabs/NavTabs.vue b/ui/core/src/components/nav-tabs/NavTabs.vue index 6dc7c9901..9d0fde639 100644 --- a/ui/core/src/components/nav-tabs/NavTabs.vue +++ b/ui/core/src/components/nav-tabs/NavTabs.vue @@ -31,6 +31,10 @@ export default { selectTab (selectedTab) { this.tabs.forEach(tab => { tab.isActive = (tab.title === selectedTab.title) + + if (tab.isActive && tab.onSelect) { + tab.onSelect(tab) + } }) } } diff --git a/ui/core/src/components/nav-tabs/Tab.vue b/ui/core/src/components/nav-tabs/Tab.vue index 77528adfd..ebb3bcb5f 100644 --- a/ui/core/src/components/nav-tabs/Tab.vue +++ b/ui/core/src/components/nav-tabs/Tab.vue @@ -1,5 +1,5 @@ @@ -9,13 +9,16 @@ export default { name: 'tab', props: { title: { required: true }, - selected: { default: false } + selected: { default: false }, + onSelect: { type: Function } }, computed: { href () { return '#' + this.name.toLowerCase().replace(/ /g, '-') } }, + updated () { + }, data () { return { isActive: false diff --git a/ui/core/src/libs/legacy/formbuilder.js b/ui/core/src/libs/legacy/formbuilder.js index 994e918a7..4ba5223de 100644 --- a/ui/core/src/libs/legacy/formbuilder.js +++ b/ui/core/src/libs/legacy/formbuilder.js @@ -723,7 +723,7 @@ var FT_Workspace = function (fb, jqEl, comps) { inertia: false, autoScroll: autoScroll, onstart: function (e) { - if (e.interaction.downEvent.button === 2) { + if (e.button === 2) { return false } @@ -807,7 +807,7 @@ var FT_Workspace = function (fb, jqEl, comps) { return true }, onmove: function (e, a, b, c, d) { - if (e.interaction.downEvent.button === 2) { + if (e.button === 2) { return false } _.mousePos.y = e.clientY diff --git a/ui/core/src/views/appDependentComponents/SettingsInner.vue b/ui/core/src/views/appDependentComponents/SettingsInner.vue index 98d53a9f8..0752a5899 100644 --- a/ui/core/src/views/appDependentComponents/SettingsInner.vue +++ b/ui/core/src/views/appDependentComponents/SettingsInner.vue @@ -102,6 +102,9 @@ {{$t('Sparkpost API Key explanation','Set the Sparkpost API key which will be used to send out emails.')}} + +
+
@@ -234,6 +237,39 @@ export default { redirectToHome () { window.location.href = '/' }, + onSelectAppearenceTab () { + if (this.customStyleEditor) { + return + } + + const customStyleEditorElement = document.getElementById('customStyle_cssCode') + const customStyleEditor = ace.edit(customStyleEditorElement) + + customStyleEditor.session.setMode('ace/mode/css') + customStyleEditor.setOptions({ + minLines: 50, + maxLines: 100, + enableLiveAutocompletion: true + }) + + if (this.settings?.customStyleCSS) { + customStyleEditor.getSession().setValue(this.settings.customStyleCSS) + } + + customStyleEditor.getSession().on('change', () => { + const cssCode = customStyleEditor.getSession().getValue() + + if (this.settings) { + this.settings.customStyleCSS = cssCode + } + }) + + this.customStyleEditor = customStyleEditor + }, + reloadCustomStyle () { + const linkElement = document.getElementById('customstylelink') + linkElement.setAttribute('href', `/api/appearance-css?t=${Date.now()}`) + }, newFormReady () { return this.app.roles && this.app.roles.length > 0 && this.settings !== null }, @@ -242,6 +278,8 @@ export default { }, powerUp () { axios.post('/api/init', { settings: this.settings, user: this.user }).then(res => { + this.reloadCustomStyle() + this.cleanErr() this.user = res.data this.$notify({