diff --git a/config/schema.py b/config/schema.py
index 7802f97a..b9bfe46c 100644
--- a/config/schema.py
+++ b/config/schema.py
@@ -1,6 +1,6 @@
# -*- coding: iso8859-15 -*-
from os import name
-from config.project_globals import ScopedSession, app
+from config.project_globals import DBSession, ScopedSession, app
from flask_graphql import GraphQLView
from config.settings import NGINX_CONFIG_FILE, VERSION
from graphene import relay
@@ -28,22 +28,40 @@ def resolve_uploadLimit(self, info):
return limit
+def get_config(name):
+ config = DBSession.query(ConfigModel).filter(
+ ConfigModel.name == name).first()
+ if config:
+ return config.value
+
+
class SystemConfig(graphene.ObjectType):
termsOfService = graphene.String()
def resolve_termsOfService(self, info):
- with ScopedSession() as local_db_session:
- config = local_db_session.query(ConfigModel).filter(
- ConfigModel.name == TERMS_OF_SERVICE
- ).first()
- if config:
- return config.value
+ return get_config(TERMS_OF_SERVICE)
+
+
+class FoyerConfig(graphene.ObjectType):
+ title = graphene.String()
+ description = graphene.String()
+ menu = graphene.String()
+
+ def resolve_title(self, info):
+ return get_config('FOYER_TITLE')
+
+ def resolve_description(self, info):
+ return get_config('FOYER_DESCRIPTION')
+
+ def resolve_menu(self, info):
+ return get_config('FOYER_MENU')
class Query(graphene.ObjectType):
node = relay.Node.Field()
nginx = graphene.Field(NginxConfig)
system = graphene.Field(SystemConfig)
+ foyer = graphene.Field(FoyerConfig)
def resolve_nginx(self, info):
return NginxConfig()
@@ -51,6 +69,9 @@ def resolve_nginx(self, info):
def resolve_system(self, info):
return SystemConfig()
+ def resolve_foyer(self, info):
+ return FoyerConfig()
+
class UpdateTermsOfService(graphene.Mutation):
"""Mutation to update Terms of Service's URL."""
@@ -76,8 +97,38 @@ def mutate(self, info, url):
return UpdateTermsOfService(url=url)
+class SaveConfig(graphene.Mutation):
+ """Mutation to customise foyer."""
+ success = graphene.Boolean(description="True if the config was saved.")
+
+ class Arguments:
+ name = graphene.String(
+ required=True, description="The name of the config.")
+ value = graphene.String(
+ required=True, description="The value of the config.")
+
+ # decorate this with jwt login decorator.
+ def mutate(self, info, name, value):
+ with ScopedSession() as local_db_session:
+ config = local_db_session.query(ConfigModel).filter(
+ ConfigModel.name == name).first()
+
+ if name == TERMS_OF_SERVICE:
+ # Use UpdateTermsOfService mutation instead.
+ return SaveConfig(success=False)
+
+ if config:
+ config.value = value
+ else:
+ config = ConfigModel(name=name, value=value)
+ local_db_session.add(config)
+
+ return SaveConfig(success=True)
+
+
class Mutation(graphene.ObjectType):
updateTermsOfService = UpdateTermsOfService.Field()
+ saveConfig = SaveConfig.Field()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/config/sqlfiles/create.sql b/config/sqlfiles/create.sql
index add9830f..962698c8 100644
--- a/config/sqlfiles/create.sql
+++ b/config/sqlfiles/create.sql
@@ -6,3 +6,9 @@ CREATE TABLE "public"."config" (
"created_on" timestamp DEFAULT (now() at time zone 'utc'),
PRIMARY KEY ("id")
);
+
+-- Seed foyer config
+INSERT INTO "config" ("name", "value") VALUES ('FOYER_TITLE', 'CYBERFORMANCE PLATFORM');
+INSERT INTO "config" ("name", "value") VALUES ('FOYER_DESCRIPTION', 'UpStage is an online venue for live performance: remote performers collaborate in real time using digital media, and online audiences anywhere in the world join events by going to a web page, without having to download and install any additional software. UpStage is available free to anyone who would like to use it.');
+INSERT INTO "config" ("name", "value") VALUES ('FOYER_MENU', 'UpStage User Manual (https://docs.upstage.live/)
+Customise Foyer (/backstage/admin/foyer-customisation)');
\ No newline at end of file
diff --git a/studio/media.py b/studio/media.py
index 09e84997..26a7c16a 100644
--- a/studio/media.py
+++ b/studio/media.py
@@ -208,8 +208,6 @@ def mutate(self, info):
local_db_session.flush()
total += media.size
- local_db_session.commit()
- local_db_session.close()
return CalcSizes(size=total)
@@ -398,8 +396,6 @@ def mutate(self, info, id, approved):
else:
local_db_session.delete(asset_usage)
local_db_session.flush()
- local_db_session.commit()
- local_db_session.close()
permissions = DBSession.query(AssetUsageModel).filter(
AssetUsageModel.asset_id == asset_id).all()
return ConfirmPermission(success=True, permissions=permissions)
diff --git a/ui/dashboard/src/components/NavBar.vue b/ui/dashboard/src/components/NavBar.vue
index 50671b63..ede94624 100644
--- a/ui/dashboard/src/components/NavBar.vue
+++ b/ui/dashboard/src/components/NavBar.vue
@@ -17,63 +17,40 @@
-
-
-
-
-
-
-
- Develop
-
-
-
Donate
-
-
-
Stage
-
-
-
-
- {{ stage.name }}
-
-
-
-
+ :href="menu.url"
+ :target="menu.url?.startsWith('http') ? '_blank' : ''"
+ >{{ menu.title }}
+
+
+
@@ -103,25 +80,22 @@ import { computed, ref } from "vue";
import { loggedIn, logout } from "@/utils/auth";
import Logo from "./Logo";
import { useStore } from "vuex";
-import Loading from "@/components/Loading";
export default {
- components: { Logo, Loading },
+ components: { Logo },
setup() {
const store = useStore();
const expanded = ref(false);
const toggleExpanded = () => (expanded.value = !expanded.value);
- const loadingStages = computed(() => store.getters["cache/loadingStages"]);
- const liveStages = computed(() => store.getters["cache/liveStages"]);
+ const navigations = computed(() => store.getters["config/navigations"]);
return {
expanded,
toggleExpanded,
loggedIn,
logout,
- loadingStages,
- liveStages,
+ navigations
};
},
};
diff --git a/ui/dashboard/src/router/index.js b/ui/dashboard/src/router/index.js
index 423248e3..7e2dd13c 100644
--- a/ui/dashboard/src/router/index.js
+++ b/ui/dashboard/src/router/index.js
@@ -95,6 +95,11 @@ const routes = [
name: 'System Configurations',
component: () => import('../views/backstage/Admin/SystemConfigurations.vue'),
},
+ {
+ path: 'foyer-customisation',
+ name: 'Foyer Customisation',
+ component: () => import('../views/backstage/Admin/FoyerCustomisation.vue'),
+ }
]
},
{
diff --git a/ui/dashboard/src/services/graphql/config.js b/ui/dashboard/src/services/graphql/config.js
index 9650a429..87130e6a 100644
--- a/ui/dashboard/src/services/graphql/config.js
+++ b/ui/dashboard/src/services/graphql/config.js
@@ -12,6 +12,11 @@ export default {
system {
termsOfService
}
+ foyer {
+ title
+ description
+ menu
+ }
}
`),
updateTermsOfService: (variables) => client.request(gql`
@@ -20,5 +25,12 @@ export default {
url
}
}
- `, variables)
+ `, variables),
+ saveConfig: (name, value) => client.request(gql`
+ mutation SaveConfig($name: String!, $value: String!) {
+ saveConfig(name: $name, value: $value) {
+ success
+ }
+ }
+ `, { name, value }),
}
diff --git a/ui/dashboard/src/store/modules/config.js b/ui/dashboard/src/store/modules/config.js
index e31bdee4..c377b0cd 100644
--- a/ui/dashboard/src/store/modules/config.js
+++ b/ui/dashboard/src/store/modules/config.js
@@ -4,7 +4,8 @@ export default {
namespaced: true,
state: {
nginx: {},
- system: {}
+ system: {},
+ foyer: null
},
getters: {
uploadLimit(state) {
@@ -12,6 +13,37 @@ export default {
},
termsOfService(state) {
return state.system.termsOfService
+ },
+ foyer(state) {
+ return state.foyer ?? {}
+ },
+ navigations(state) {
+ if (!state.foyer) {
+ return []
+ }
+ try {
+ const lines = state.foyer.menu.split("\n").filter(line => line.trim().length > 0)
+ const navigations = []
+ for (const line of lines) {
+ // Syntax:
()
+ const url = line.match(/\(([^)]+)\)/) // get the url part
+ let title = line.replace('>', '')
+ title = title.includes("(") ? title.split("(")[0].trim() : title.trim() // get the title part
+ const menu = { title, url: url ? url[1] : null }
+ if (line.trim().startsWith(">")) {
+ const parent = navigations[navigations.length - 1]
+ if (!parent.children) {
+ parent.children = []
+ }
+ parent.children.push(menu)
+ } else {
+ navigations.push(menu)
+ }
+ }
+ return navigations
+ } catch (error) {
+ console.log(error)
+ }
}
},
mutations: {
diff --git a/ui/dashboard/src/styles/custom.scss b/ui/dashboard/src/styles/custom.scss
index b7426c1d..194c1c93 100644
--- a/ui/dashboard/src/styles/custom.scss
+++ b/ui/dashboard/src/styles/custom.scss
@@ -126,7 +126,6 @@ div.moveable-control-box {
margin: 8px 12px;
padding: 0 20px !important;
justify-content: center;
- text-transform: uppercase;
border: 1px solid transparent;
}
.vertical-divider {
diff --git a/ui/dashboard/src/styles/mixins.scss b/ui/dashboard/src/styles/mixins.scss
index ec0d5b12..c7ed1ae4 100644
--- a/ui/dashboard/src/styles/mixins.scss
+++ b/ui/dashboard/src/styles/mixins.scss
@@ -17,7 +17,6 @@
@mixin textShadow() {
color: $black;
text-shadow: -3px 0 $primary;
- text-transform: uppercase;
}
@mixin differentiatedToolbar($color) {
background-color: $color;
diff --git a/ui/dashboard/src/views/Home.vue b/ui/dashboard/src/views/Home.vue
index 728b3661..c58f7a60 100644
--- a/ui/dashboard/src/views/Home.vue
+++ b/ui/dashboard/src/views/Home.vue
@@ -2,14 +2,8 @@
-
CYBERFORMANCE PLATFORM
-
- UpStage is an online venue for live performance: remote performers
- collaborate in real time using digital media, and online audiences
- anywhere in the world join events by going to a web page, without
- having to download and install any additional software. UpStage is
- available free to anyone who would like to use it.
-
+
+
@@ -53,6 +47,7 @@ export default {
const loading = computed(() => store.getters["cache/loadingStages"]);
const liveStages = computed(() => store.getters["cache/liveStages"]);
+ const foyer = computed(() => store.getters["config/foyer"]);
const upcomingStages = computed(
() => store.getters["cache/upcomingStages"]
);
@@ -67,6 +62,7 @@ export default {
loading,
upcomingStages,
absolutePath,
+ foyer,
};
},
};
@@ -89,6 +85,7 @@ export default {
max-width: 800px;
margin: auto;
box-shadow: 10px 10px 0 0 $primary;
+ white-space: pre-wrap;
}
.column {
padding: 2rem;
diff --git a/ui/dashboard/src/views/backstage/Admin/FoyerCustomisation.vue b/ui/dashboard/src/views/backstage/Admin/FoyerCustomisation.vue
new file mode 100644
index 00000000..ad363604
--- /dev/null
+++ b/ui/dashboard/src/views/backstage/Admin/FoyerCustomisation.vue
@@ -0,0 +1,125 @@
+
+
+
+ Title
+
+
+
+
+
+
+ Save
+
+
+
+ {{ title }}
+
+ Edit
+
+
+
+
+
+ Description
+
+
+
+
+
+
+ Save
+
+
+
+ {{ description }}
+
+ Edit
+
+
+
+
+
+
+ Menu
+
+
+
+
+
+
+
+
+ Save
+
+
+
+ {{ menu }}
+
+ Edit
+
+
+
+
+
+
+
+
diff --git a/ui/dashboard/src/views/backstage/Admin/index.vue b/ui/dashboard/src/views/backstage/Admin/index.vue
index 24b2a001..7715903b 100644
--- a/ui/dashboard/src/views/backstage/Admin/index.vue
+++ b/ui/dashboard/src/views/backstage/Admin/index.vue
@@ -14,17 +14,13 @@
- Player Management
-
+ >Player Management
- Batch User Creation
-
+ >Batch User Creation
@@ -33,9 +29,13 @@
- System Configurations
-
+ >System Configurations
+
+
+ Foyer Customisation