Skip to content

Commit

Permalink
Issue 325 Custom foyer links and text (#694)
Browse files Browse the repository at this point in the history
* title and description mutation

* navigation customisations

* text area description

* no text-transform and support html

* relative customise foyer link

* remove unnecessary commit and close
  • Loading branch information
teddyfullstack committed Dec 6, 2021
1 parent 1f29c29 commit c3fb5f3
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 93 deletions.
65 changes: 58 additions & 7 deletions config/schema.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -28,29 +28,50 @@ 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()

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."""
Expand All @@ -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()


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
6 changes: 6 additions & 0 deletions config/sqlfiles/create.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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)');
4 changes: 0 additions & 4 deletions studio/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down Expand Up @@ -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)
Expand Down
96 changes: 35 additions & 61 deletions ui/dashboard/src/components/NavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,63 +17,40 @@

<div :class="{ 'navbar-menu': true, 'is-active': expanded }">
<div class="navbar-start">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-arrowless"> About </a>
<div class="navbar-dropdown">
<a class="navbar-item" href="#"> FAQs </a>
<a class="navbar-item" href="#"> Contacts </a>
<a class="navbar-item" href="#"> Diversity + Inclusion </a>
</div>
</div>
<div class="vertical-divider" />
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-arrowless"> &nbsp; Learn &nbsp; </a>
<div class="navbar-dropdown">
<a class="navbar-item" href="#"> Research </a>
<div
v-if="!navigations"
class="navbar-item"
style="text-transform: none;"
>Cannot display navigation properly, please check your menu syntax in Admin section!</div>
<template v-else>
<template v-for="(menu, i) in navigations" :key="i">
<div v-if="menu.children" class="navbar-item has-dropdown is-hoverable">
<a
v-if="menu.url"
class="navbar-link is-arrowless"
:href="menu.url"
:target="menu.url?.startsWith('http') ? '_blank' : ''"
>{{ menu.title }}</a>
<a v-else class="navbar-link is-arrowless">{{ menu.title }}</a>
<div class="navbar-dropdown">
<a
v-for="(submenu, j) in menu.children"
:key="{ j }"
class="navbar-item"
:href="submenu.url"
:target="submenu.url?.startsWith('http') ? '_blank' : ''"
>{{ submenu.title }}</a>
</div>
</div>
<a
v-else
class="navbar-item"
href="https://github.com/upstage-org/documentation"
target="_blank"
>
Documentation
</a>
</div>
</div>
<div class="vertical-divider" />
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-arrowless"> Festivals </a>
<div class="navbar-dropdown">
<a class="navbar-item" href="#"> 10th Birthday </a>
<a class="navbar-item" href="#"> 121212 </a>
</div>
</div>
<div class="vertical-divider" />
<a
class="navbar-item"
href="https://github.com/upstage-org/upstage/"
target="_blank"
>
Develop
</a>
<div class="vertical-divider" />
<a class="navbar-item" to="/#">Donate</a>
<div class="vertical-divider" />
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-arrowless"> Stage </a>
<div class="navbar-dropdown">
<Loading v-if="loadingStages" />
<template v-else>
<router-link
v-for="stage in liveStages"
:key="stage.id"
class="navbar-item"
:to="`/${stage.fileLocation}`"
>
{{ stage.name }}
</router-link>
</template>
</div>
</div>
:href="menu.url"
:target="menu.url?.startsWith('http') ? '_blank' : ''"
>{{ menu.title }}</a>
<div v-if="i < navigations.length - 1" class="vertical-divider" />
</template>
</template>
</div>
<div class="navbar-end">
Expand Down Expand Up @@ -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
};
},
};
Expand Down
5 changes: 5 additions & 0 deletions ui/dashboard/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
}
]
},
{
Expand Down
14 changes: 13 additions & 1 deletion ui/dashboard/src/services/graphql/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export default {
system {
termsOfService
}
foyer {
title
description
menu
}
}
`),
updateTermsOfService: (variables) => client.request(gql`
Expand All @@ -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 }),
}
34 changes: 33 additions & 1 deletion ui/dashboard/src/store/modules/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,46 @@ export default {
namespaced: true,
state: {
nginx: {},
system: {}
system: {},
foyer: null
},
getters: {
uploadLimit(state) {
return state.nginx.uploadLimit ?? 1024 * 1024
},
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: <title> (<url>)
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: {
Expand Down
1 change: 0 additions & 1 deletion ui/dashboard/src/styles/custom.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 0 additions & 1 deletion ui/dashboard/src/styles/mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
@mixin textShadow() {
color: $black;
text-shadow: -3px 0 $primary;
text-transform: uppercase;
}
@mixin differentiatedToolbar($color) {
background-color: $color;
Expand Down
Loading

0 comments on commit c3fb5f3

Please sign in to comment.