Skip to content

Commit

Permalink
Rework Extension to Remove Hard Coded Constants and Add some Features (
Browse files Browse the repository at this point in the history
…#811)

* Starting new feature development

* changes for 2018 test hunt

* Adopt service worker format

* Add link to Sheet

* Remove hunt and URL constants by sending that information from the web app to the extension

* Clean upand remove extension key

* remove extra debugging

* Formatting

* Address comments
  • Loading branch information
maximized authored Jan 3, 2025
1 parent 1861c8d commit 83a054a
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 29 deletions.
1 change: 0 additions & 1 deletion cardboard/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
# The second is a local version being used for development.
CSRF_TRUSTED_ORIGINS = [
"chrome-extension://fhldkjfidcbfienegpehemncionolmfa",
"chrome-extension://cahmppnjflkbimomgndbcmbdoafdegbi",
]

# This should be turned on in production to redirect HTTP to HTTPS
Expand Down
14 changes: 14 additions & 0 deletions chrome_extension/cardboard/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
chrome.runtime.onMessageExternal.addListener(
async (request, sender, sendResponse) => {
if (request.huntId != undefined) {
chrome.storage.local.set({ huntId: request.huntId });
}
if (sender.origin != undefined) {
chrome.storage.local.set({ origin: sender.origin });
}
if (sender.url != undefined) {
chrome.storage.local.set({ huntUrl: sender.url });
}
sendResponse({ success: true, message: "HuntId has been received" });
}
);
37 changes: 28 additions & 9 deletions chrome_extension/cardboard/manifest.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
{
"manifest_version": 3,
"name": "Cardboard Chrome Extension",
"version": "1.0.2",
"name": "Cardboard Chrome Extension:BETA_LOCAL",
"description": "THIS EXTENSION IS FOR BETA TESTING",
"version": "1.0.4",
"icons": {
"16": "images/cardboard_bird.png",
"32": "images/cardboard_bird.png",
"48": "images/cardboard_bird.png",
"128": "images/cardboard_bird.png"
},
"action": {
"default_popup": "popup.html"
"default_popup": "popup.html",
"default_icon": {
"16": "images/cardboard_bird.png",
"32": "images/cardboard_bird.png",
"48": "images/cardboard_bird.png",
"128": "images/cardboard_bird.png"
}
},
"permissions": [
"activeTab",
"cookies"
"cookies",
"storage"
],
"background": {
"service_worker": "background.js"
},
"host_permissions": [
"http://127.0.0.1/*",
"http://localhost/*",
"https://cardboard.rocks/*",
"https://www.cardboard.rocks/*"
]
"http://127.0.0.1/*",
"http://localhost/*",
"https://cardboard.rocks/*",
"https://www.cardboard.rocks/*"
],
"externally_connectable": {
"matches": [
"http://127.0.0.1/*",
"http://localhost/*",
"https://cardboard.rocks/*",
"https://www.cardboard.rocks/*"
]
}
}
15 changes: 13 additions & 2 deletions chrome_extension/cardboard/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<li>
<a>
<label for="puzzle_name">Puzzle Name: </label><br>
<input class="puzzle_name" type="text" id="puzzle_name" name="puzzle_name"><br>
<input class="puzzle_name" type="text" id="puzzle_name" name="puzzle_name" maxlength="80" oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);"><br>
<label for="puzzle_url">Puzzle URL: </label><br>
<input class="puzzle_url" type="text" id="puzzle_url" name="puzzle_url"><br>
<label for="puzzle_meta">Assigned Meta: </label><br>
Expand All @@ -27,7 +27,18 @@
</template>

<h1>Cardboard Extension</h1>
<button>Add Puzzle</button>
<div id="hunt_name_tooltip">
<b>Hunt</b>: <a id="hunt_name" target="_blank"></a>
</div>
<br/>
<div>
<button>Add Puzzle</button>
</div>
<br/>
<div id="cross_site_links">
<b id="puzzle_message"></b>
<a id="google_sheets_link" target="_blank" hidden>Google Sheet</a>
</div>
<ul></ul>

<script src="./popup.js" type="module"></script>
Expand Down
132 changes: 116 additions & 16 deletions chrome_extension/cardboard/popup.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,72 @@
// TODO: Default this to false and control this in the deployment process
const IS_PROD = true;
// Get hunt information from local Chrome storage
async function getHuntData() {
const data = await chrome.storage.local.get();
return data;
}

const PROD_URL = "https://cardboard.rocks";
const DEV_URL = "http://localhost:8000";
const hunt_data = await getHuntData();
const hunt_id = hunt_data["huntId"];
const hunt_url = hunt_data["huntUrl"];
const base_url = hunt_data["origin"];

// TODO: Make this configurable
const HUNT_NUMBER = 10;
// Exit script if hunt information isn't found.
if (hunt_id === undefined) {
throw new Error("No hunt_id found");
}
if (hunt_url === undefined) {
throw new Error("No hunt_url found");
}
if (base_url === undefined) {
throw new Error("No base_url found");
}

let TARGET_URL = DEV_URL;
if (IS_PROD) {
TARGET_URL = PROD_URL;
// Get hunt name from site.
const hunt_metadata = await fetch(`${base_url}/api/v1/hunts/${hunt_id}`, {
method: "GET",
});
const hunt_metadata_json = await hunt_metadata.json();
document.getElementById(
"hunt_name_tooltip"
).title = `To change hunts, go to ${base_url}/hunts/ and select a different hunt.`;

if (hunt_metadata_json.name != undefined) {
document.getElementById("hunt_name").textContent = hunt_metadata_json.name;
document.getElementById("hunt_name").href = hunt_url;
} else {
document.getElementById("hunt_name").textContent = "No hunt name found";
}

// Read all puzzles and filter down to those that are metas.
// Read all puzzles.
const hunt_puzzles = await fetch(
TARGET_URL + "/api/v1/hunts/" + HUNT_NUMBER + "/puzzles",
`${base_url}/api/v1/hunts/${hunt_id}/puzzles`,
{
method: "GET",
}
);
const response = await hunt_puzzles.json();
let metas = [];
let puzzles_by_url = new Map();
const NO_SHEET_SENTINEL = -1;

for (const puzzle of response) {
// Populate metas list for meta assignment dropdown.
if (puzzle.is_meta) {
metas.push(puzzle.name);
}

// Populate URL to Id map for all puzzles with sheets.
if (puzzle.has_sheet) {
puzzles_by_url.set(puzzle.url, puzzle.id);
} else {
puzzles_by_url.set(puzzle.url, NO_SHEET_SENTINEL);
}
}

const setTextInputValue = (el, str) => {
const maxlength = +el.getAttribute("maxlength");
el.value = str.substring(0, maxlength);
};

// There should only be one tab because there is only one currentWindow
// and active tab.
const tabs = await chrome.tabs.query({
Expand All @@ -39,10 +79,25 @@ for (const tab of tabs) {
const element = template.content.firstElementChild.cloneNode(true);

if (tab.title != undefined) {
element.querySelector(".puzzle_name").value = tab.title;
setTextInputValue(element.querySelector(".puzzle_name"), tab.title);
}
if (tab.url != undefined) {
element.querySelector(".puzzle_url").value = tab.url;
// If a puzzle has already been made using this URL
if (puzzles_by_url.get(tab.url) != undefined) {
// If the sheet has already been made, display a link to the sheet.
if (puzzles_by_url.get(tab.url) != NO_SHEET_SENTINEL) {
document.getElementById("puzzle_message").textContent =
"Puzzle already exists:";
document.getElementById("google_sheets_link").hidden = false;
document.getElementById(
"google_sheets_link"
).href = `${base_url}/puzzles/s/${puzzles_by_url.get(tab.url)}`;
} else {
document.getElementById("puzzle_message").textContent =
"Puzzle already exists but the sheet is still being created.\nPlease wait 10 seconds then open the extension again.";
}
}
}

// Add metas to dropdown
Expand All @@ -58,24 +113,60 @@ for (const tab of tabs) {
}
document.querySelector("ul").append(...elements);

function pollForCreatedPuzzlePeriodically(puzzle_url, interval, max_calls) {
let call_count = 0;
let interval_id = null;

async function pollAndCheck() {
call_count++;
const hunt_puzzles = await fetch(
`${base_url}/api/v1/hunts/${hunt_id}/puzzles`,
{
method: "GET",
}
);
const response = await hunt_puzzles.json();
for (const puzzle of response) {
// If the puzzle has been created and has a sheet, change the extension text to include a
// link to the sheet and stop polling the website.
if (puzzle.url === puzzle_url && puzzle.has_sheet) {
clearInterval(interval_id);
document.getElementById("google_sheets_link").hidden = false;
document.getElementById("puzzle_message").textContent =
"Puzzle created:";
document.getElementById(
"google_sheets_link"
).href = `${base_url}/puzzles/s/${puzzle.id}`;
}
}
if (call_count >= max_calls) {
clearInterval(interval_id);
console.log("Max calls reached");
}
}

pollAndCheck();
interval_id = setInterval(pollAndCheck, interval);
}

const button = document.querySelector("button");
button.addEventListener("click", async (e) => {
e.preventDefault();
// Get Cardboard cookie
const cardboard_cookie = await chrome.cookies.get({
url: TARGET_URL,
url: base_url,
name: "csrftoken",
});

if (cardboard_cookie) {
// Create puzzle. Puzzle name is limited to 30 characters
fetch(TARGET_URL + "/api/v1/hunts/" + HUNT_NUMBER + "/puzzles", {
// Create puzzle.
fetch(`${base_url}/api/v1/hunts/${hunt_id}/puzzles`, {
method: "POST",
mode: "cors",
body: JSON.stringify({
create_channels: document.getElementById("create_channels").checked,
is_meta: document.getElementById("is_meta").checked,
name: document.getElementById("puzzle_name").value.slice(0, 30),
name: document.getElementById("puzzle_name").value,
url: document.getElementById("puzzle_url").value,
assigned_meta: document.getElementById("puzzle_meta").value,
}),
Expand All @@ -84,6 +175,15 @@ button.addEventListener("click", async (e) => {
"Content-Type": "application/json",
},
});
document.getElementById("puzzle_message").textContent =
"Puzzle created, waiting for Google Sheet to be created...";

// Poll website every 2 seconds for 30 seconds waiting for the sheet to be created.
pollForCreatedPuzzlePeriodically(
document.getElementById("puzzle_url").value,
2000,
15
);
} else {
console.log("No cookie found");
}
Expand Down
20 changes: 19 additions & 1 deletion hunts/src/App.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
import React from "react";
import React, { useEffect } from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import store from "./store";
import { HuntViewMain } from "./HuntViewMain";

const sendTokenToChromeExtension = ({ extensionId, huntId }) => {
if (chrome && chrome.runtime) {
chrome.runtime.sendMessage(extensionId, { huntId }, (response) => {
if (!response.success) {
console.log("Error sending message: ", response);
return response;
}
});
}
};

const App = () => {
useEffect(() => {
sendTokenToChromeExtension({
extensionId: "fhldkjfidcbfienegpehemncionolmfa",
huntId: window.CURRENT_HUNT_ID,
});
}, []);

return <HuntViewMain huntId={window.CURRENT_HUNT_ID} />;
};

Expand Down

0 comments on commit 83a054a

Please sign in to comment.