From 74a3a5d9204b39ab37fa4ea89c41ac2fb30093d3 Mon Sep 17 00:00:00 2001
From: DallinFromEarth <112426674+DallinFromEarth@users.noreply.github.com>
Date: Wed, 25 Sep 2024 16:04:26 -0600
Subject: [PATCH 01/38] add customization options to banner message
---
.../byu/cs/controller/ConfigController.java | 16 ++++++-
.../byu/cs/dataAccess/ConfigurationDao.java | 2 +
src/main/resources/frontend/src/App.vue | 18 +------
.../frontend/src/components/BannerMessage.vue | 48 +++++++++++++++++++
.../frontend/src/services/configService.ts | 9 +++-
.../frontend/src/stores/appConfig.ts | 12 +++++
.../src/views/AdminView/ConfigView.vue | 35 +++++++++++---
7 files changed, 114 insertions(+), 26 deletions(-)
create mode 100644 src/main/resources/frontend/src/components/BannerMessage.vue
diff --git a/src/main/java/edu/byu/cs/controller/ConfigController.java b/src/main/java/edu/byu/cs/controller/ConfigController.java
index fdd93423f..ea7b786e3 100644
--- a/src/main/java/edu/byu/cs/controller/ConfigController.java
+++ b/src/main/java/edu/byu/cs/controller/ConfigController.java
@@ -18,6 +18,8 @@
import java.util.EnumMap;
import java.util.Map;
+import static spark.Spark.halt;
+
public class ConfigController {
private static final Logger LOGGER = LoggerFactory.getLogger(SubmissionController.class);
@@ -51,6 +53,8 @@ private static JsonObject getPublicConfig() throws DataAccessException {
JsonObject response = new JsonObject();
response.addProperty("bannerMessage", dao.getConfiguration(ConfigurationDao.Configuration.BANNER_MESSAGE, String.class));
+ response.addProperty("bannerLink", dao.getConfiguration(ConfigurationDao.Configuration.BANNER_LINK, String.class));
+ response.addProperty("bannerColor", dao.getConfiguration(ConfigurationDao.Configuration.BANNER_COLOR, String.class));
response.addProperty("phases", dao.getConfiguration(ConfigurationDao.Configuration.STUDENT_SUBMISSIONS_ENABLED, String.class));
return response;
@@ -109,13 +113,23 @@ private static JsonObject getPrivateConfig() throws DataAccessException {
JsonObject jsonObject = new Gson().fromJson(req.body(), JsonObject.class);
String message = new Gson().fromJson(jsonObject.get("bannerMessage"), String.class);
+ String link = new Gson().fromJson(jsonObject.get("bannerLink"), String.class);
+ String color = new Gson().fromJson(jsonObject.get("bannerColor"), String.class);
+
+ // If they give us a color, and it's not long enough or is missing #
+ if (!color.isEmpty() && ((color.length() != 7) || !color.startsWith("#"))) {
+ halt(400, "Invalid hex color code. Must provide a hex code starting with a # symbol, followed by 6 hex digits");
+ }
+
dao.setConfiguration(ConfigurationDao.Configuration.BANNER_MESSAGE, message, String.class);
+ dao.setConfiguration(ConfigurationDao.Configuration.BANNER_LINK, link, String.class);
+ dao.setConfiguration(ConfigurationDao.Configuration.BANNER_COLOR, color, String.class);
User user = req.session().attribute("user");
if (message.isEmpty()) {
logConfigChange("cleared the banner message", user.netId());
} else {
- logConfigChange("set the banner message to: '%s'".formatted(message), user.netId());
+ logConfigChange("set the banner message to: '%s' with link: {%s}".formatted(message, link), user.netId());
}
res.status(200);
diff --git a/src/main/java/edu/byu/cs/dataAccess/ConfigurationDao.java b/src/main/java/edu/byu/cs/dataAccess/ConfigurationDao.java
index 039d64d9b..cf3af92d1 100644
--- a/src/main/java/edu/byu/cs/dataAccess/ConfigurationDao.java
+++ b/src/main/java/edu/byu/cs/dataAccess/ConfigurationDao.java
@@ -7,6 +7,8 @@ public interface ConfigurationDao {
enum Configuration {
STUDENT_SUBMISSIONS_ENABLED,
BANNER_MESSAGE,
+ BANNER_LINK,
+ BANNER_COLOR,
GITHUB_ASSIGNMENT_NUMBER,
PHASE0_ASSIGNMENT_NUMBER,
PHASE1_ASSIGNMENT_NUMBER,
diff --git a/src/main/resources/frontend/src/App.vue b/src/main/resources/frontend/src/App.vue
index ccbe7b4f3..9c9be978a 100644
--- a/src/main/resources/frontend/src/App.vue
+++ b/src/main/resources/frontend/src/App.vue
@@ -7,6 +7,7 @@ import router from '@/router'
import '@/assets/fontawesome/css/fontawesome.css'
import '@/assets/fontawesome/css/solid.css'
import { useAppConfigStore } from '@/stores/appConfig'
+import BannerMessage from '@/components/BannerMessage.vue'
const greeting = computed(() => {
if (useAuthStore().isLoggedIn) {
@@ -24,12 +25,6 @@ const logOut = async () => {
router.push({name: "login"})
}
-const bannerMessage = computed(() => {
- if (useAuthStore().isLoggedIn) {
- return useAppConfigStore().bannerMessage
- }
-});
-
onMounted( async () => {
await useAppConfigStore().updateConfig();
})
@@ -41,9 +36,7 @@ onMounted( async () => {
This is where you can submit your assignments and view your scores.
{{ greeting }} Logout
{{ useAuthStore().user?.repoUrl }}
-
-
-
+
@@ -51,13 +44,6 @@ onMounted( async () => {
\ No newline at end of file
diff --git a/src/main/resources/frontend/src/services/configService.ts b/src/main/resources/frontend/src/services/configService.ts
index 37656981c..517fc1536 100644
--- a/src/main/resources/frontend/src/services/configService.ts
+++ b/src/main/resources/frontend/src/services/configService.ts
@@ -22,8 +22,13 @@ export const getConfig = async ():Promise => {
}
}
-export const setBannerMessage = async (message: String): Promise => {
- await doSetConfigItem("POST", '/api/admin/config/banner', {"bannerMessage": message});
+export const setBanner = async (message: String, link: String, color: String): Promise => {
+ await doSetConfigItem("POST", '/api/admin/config/banner', {
+ "bannerMessage": message,
+ "bannerLink": link,
+ "bannerColor": color
+ }
+ );
}
export const setLivePhases = async (phases: Array): Promise => {
diff --git a/src/main/resources/frontend/src/stores/appConfig.ts b/src/main/resources/frontend/src/stores/appConfig.ts
index 1cba95bf6..0d468ff38 100644
--- a/src/main/resources/frontend/src/stores/appConfig.ts
+++ b/src/main/resources/frontend/src/stores/appConfig.ts
@@ -9,6 +9,8 @@ type ImportMeta = {
export type Config = {
bannerMessage: string
+ bannerLink: string
+ bannerColor: string
phases: Array
courseNumber?: number
assignmentIds?: string // Map
@@ -39,6 +41,12 @@ export const useAppConfigStore = defineStore('appConfig', () => {
const updateConfig = async () => {
const latestConfig = await getConfig();
bannerMessage.value = latestConfig.bannerMessage;
+ bannerLink.value = latestConfig.bannerLink;
+ bannerColor.value = latestConfig.bannerColor;
+ if (bannerColor.value.length == 0) {
+ bannerColor.value = "#4fa0ff"
+ }
+
for (const phase of listOfPhases() as Phase[]) {
activePhaseList.value[phase] = latestConfig.phases.includes(phase);
}
@@ -55,6 +63,8 @@ export const useAppConfigStore = defineStore('appConfig', () => {
const backendUrl = ref(env.VITE_APP_BACKEND_URL);
const bannerMessage: Ref = ref("");
+ const bannerLink: Ref = ref("");
+ const bannerColor: Ref = ref("");
// using the enum, if phaseActivationList[phase] == true, then that phase is active
const activePhaseList: Ref = ref>([]);
const assignmentIds: Ref