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