Event Customization - PuzzleServer/mainpuzzleserver GitHub Wiki
So you've got a puzzle event and you want to make it look fancy! Here's how you can do that without needing to deploy anything to the website or deal with git.
These are the 3 (big) steps you'll need to follow in order to create a functional web-based event:
- Upload Content for the homepage, rules, and FAQ
- Upload Styles for the event overall, and optionally for each specific puzzle
- Upload Puzzles as PDFs or HTML files
Learn more below about what all those files need to do.
First, you'll want some content on your homepage, and possibly also rules and a FAQ. In the past, these were supplied via "Partials", but changing those required checking files into the website's repo and redeploying it, which wasn't ideal. Then, a set of fields were provided to event admins that let them paste raw HTML into the event properties page. Those fields still exist, however it is recommended not to use them they're more error-prone than using separate files.
Instead, you should create stand-alone HTML files that will be embedded on the homepage (and rules and FAQ) via an iframe. Because these are fully usable files on their own, they can be styled any way you want! The only requirement is that they must link to a specific javascript file that will size them properly on the embedding page. The files must be named:
home-content.html
rules-content.html
faq-content.html
Step-by-step, do the following:
- Create a new file with the required name and the following content (note the included javascript file and existing style tag):
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>Homepage Content</title>
<script type="text/javascript" src="https://puzzlehunt.azurewebsites.net/js/embed-resize.js"></script>
<link rel="stylesheet" href="https://puzzlehunt.azurewebsites.net/css/site.min.css">
<style>
body {
overflow-y: hidden;
}
</style>
</head>
<body>
</body>
</html>
- Add everything else you need to that file. If you're going to use styles, make sure you also add them to that file instead of using a separate file. Otherwise, note that the link provided above to the main website's stylesheet should make any content in the separate file look decent by default, although not necessarily the same as content pasted into the event's property since content there also picks up styling from the site's Bootstrap library.
- Navigate to the event's Shared Resources page via site -> Puzzles -> Manage Shared Resources (url is
https://puzzlehunt.azurewebsites.net/{event's UrlString or ID}/admin/Puzzles/ResourceManagement
)
- Upload the file (no need to zip it up or put it in a sub-folder). It will then become available at
https://puzzleserverteststore.blob.core.windows.net/evt{event's ID}/resources/{filename}.html
and accessible from any page on the event.
Once the file exists, it will automatically be shown when the appropriate page is viewed. If any updates need to be made, just re-upload the edited file. If the event property also has content provided, that content will be shown on the page above the standalone file. Note that in the past, the Partials required setting the HomePartial property of the event. This property is no longer required and setting it won't affect anything if an event is customized by following these steps.
In addition to the embedded content above, you can also style the entire rest of the event! If you upload a CSS file named global-styles.css
to the Shared Resources page, it will be applied to every page in the event. Besides styling html elements, there are also pre-supplied classes to style specific parts of specific pages. These classes all end in -customizable
and are the preferred way of customizing your event. The full list of classes is as follows:
-
body-customizable
(the body tag) -
body-content-customizable
(the div immediately inside the body, exclusive of the side margins) -
announcement-customizable
(the announcement that admins can put directly under the navbar, defaults to red)
-
puzzle-title-customizable
(the title of the puzzle, implemented as an h2) -
puzzle-group-customizable
(the group the puzzle is in, implemented as an h3) -
dc-icon-customizable
(the DC icon next to the title, currently an image, useful for overriding the image to be something else uploaded to the Shared Resources and changeable by settingcontent:url("image.jpg");
for this class) -
puzzle-author-customizable
(the author of the puzzle, prepended by "by" via CSS)
-
errata-symbol-customizable
(the symbol before the title when the puzzle has errata, defaults to ⚠) -
solved-symbol-customizable
(the symbol before the title when the puzzle has been solved, defaults to ✔) -
puzzle-list-group-customizable
(the group the puzzle is a part of, which may be empty, applied to the entire table cell in the list) -
puzzle-list-title-customizable
(the puzzle title, which may be a link, applied to the entire table cell in the list) -
puzzle-list-answer-customizable
(the link to the puzzle's solution, applied to the entire table cell in the list) -
puzzle-list-submit-customizable
(the link to submit an answer to the puzzle, applied to the entire table cell in the list) -
puzzle-list-hints-customizable
(the link to view a puzzle's hints, applied to the entire table cell in the list) -
puzzle-list-feedback-customizable
(the link to submit feedback for a puzzle, applied to the entire table cell in the list) -
puzzle-list-submission-customizable
(a particular answer submitted for a puzzle, applied to the entire table cell in the list) -
puzzle-list-response-customizable
(the response text provided by the system, applied to the entire table cell in the list)
-
meta-list-piece-customizable
(a meta piece, applied to the entire table cell in the list)
The puzzle list page also sets the puzzle title as an id (with all spaces removed) for that entire table row. This is useful for styling individual rows. If the CSS file needs to reference any other files, for example fonts, it can do so via relative links like ./fontfile.ttf
or just the filename since all files uploaded to the Shared Resources page are located in the same resources folder mentioned in the url in Step 4 above.
Puzzle files also have an additional capability: every puzzle can specify a unique CSS file that will style only that puzzle's embedding page. This is especially useful for puzzles that want to use the same css file to style both the embedding page as well as the embedded puzzle itself. This file can be named anything you like, since its name is specified as part of the puzzle properties:
As implied by the very important red text above that field, there are multiple places these files can be located, depending on the file path in that field:
- If a full url (one that starts with https) is provided, it will be used as-is
- If a filename with no path (no / characters or prepending . characters) is provided, it will be assumed to live in the Shared Resources folder
- If a relative url is provided, it will be assumed to live at the same level as the other files in the puzzle materials
This last option must be used with caution. For more info on how to properly upload and use puzzle materials, continue reading the next section.
This is the most important section because there is no event without puzzles to play! First, in the event properties, make sure puzzle embedding is turned on:
This allows puzzle content to be shown on the same page as the answer submission box. This works with both PDF puzzles and HTML puzzles, and should only be disabled if the majority of your puzzle files are things like Office documents that a web browser can't natively display. If you're using HTML files, a specific file structure is recommended to ensure that no files with the exact same path and name exist between puzzles, and that puzzle files can access all material files uploaded alongside them.
- In the root folder for your puzzle event, create 3 folders. The name of the folders for the puzzles and solutions aren't important, but the resources folder MUST be named
resources
. This allows relative links in the puzzle to files in the Shared Resources on the website to be usable both locally in testing and online once uploaded without any changes to the links.
If you're using PuzzleJS in this event, everything in that repo'spuzzlejs
folder (at minimum) must be copied into yourresources
folder.
- Every file in your local resources folder will be uploaded to the Shared Resources section mentioned above. This is useful for shared libraries and styles for every puzzle in your event. One specific file you may want is this javascript snippet, which will allows embedded puzzles to automatically size their embedding frame to avoid double scrollbars on the page. The website is already set up to listen for this message, so simply including this code (in this example, in a file called
resize.js
) is all you need to do:
window.addEventListener("message", (e) => {
if ((e.origin.includes("puzzlehunt.azurewebsites.net")) || (e.origin.includes("localhost"))) {
e.source.postMessage({ width: document.body.scrollWidth, height: document.body.scrollHeight }, e.origin);
}
});
- If you really want to get fancy, you can also include code that runs on
DOMContentLoaded
that looks for url parametersembed=true
andprint=true
, only one of which will be supplied at a time.
- The embed parameter is applied to the iframe that embeds the puzzle content, and is useful if you want to have extra padding around the puzzle or info like its name and author that's shown by default when the puzzle is viewed on its own but needs to be removed upon load when embedded (since the embedding page already applies padding and shows the puzzle info).
- The print parameter is supplied when the puzzle's print link on the embedding page is clicked or ctrl+p is pressed, which opens the puzzle alone in a new tab. This could be useful if you want the print dialog to open automatically.
-
In the puzzles folder, create a separate folder for each puzzle, where each folder has a different name. If you are uploading solutions, it is recommended to mirror this structure, but with each folder appended with something like
-solution
so you don't get filename conflicts between puzzle and solution files when they're uploaded together into the materials section of the puzzle. Do not use spaces in any file or folder names. -
Within each puzzle folder, it is recommended to use a consistent filename for the main puzzle, like
index.html
.
- All resources for that specific puzzle can live alongside the main puzzle file or in any subfolder structure you wish. These files can be accessed using relative links that work both locally and online.
- Only standalone files like music or images should be in this folder; code like CSS and Javascript should be included in the main puzzle file (unless you're using libraries, which should be supplied along with the puzzle instead of downloaded from a separate CDN for security reasons). You can, however, use a separate CSS file if you want it to be accessible to the embedding page as mentioned previously in the Styles section.
- Links to shared resources will work using a relative link with the following structure:
../../resources/resource-name.file
, as long as you make sure the version uploaded to the site is the same as your local copy. - Overall, your puzzle file skeleton will look very similar to the homepage skeleton shown above, except the resource is now using a relative link (note that it's also to a different file. It's good practice for puzzles to not have dependencies on the website's files for style since those could change, and instead to only reference Shared Resources or their own files. Thus the different resize.js file that could include more functionality than the website's basic one):
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>My Awesome Puzzle</title>
<script type="text/javascript" src="../../resources/resize.js"></script>
<style>
body {
overflow-y: hidden;
}
</style>
</head>
<body>
</body>
</html>
- Once you're done with a puzzle and are ready to upload it, it's VERY important to do this correctly so that the root of the ZIP file contains exactly one folder named for your puzzle:
- Navigate to the puzzles folder on your local machine (not the folder containing the puzzle file)
- Right-click the folder for your puzzle
- Zip it up (the name of the ZIP file doesn't matter)
-
Upload the ZIP file to the Materials section of the puzzle (not the puzzle file!) and make sure the box is checked to automatically unzip the file:
-
Once uploaded, right-click the name of your main puzzle file (not the Raw link) and copy the link. Then, in the puzzle properties, paste that link into the Custom URL section of the puzzle. Note that the link will not have
{eventId}
in it by default; instead, it will have a random number that's the ID of your event. Feel free to replace that number with{eventId}
(including the curly braces). Also feel free to replace any instances of%2f
with a / forward slash (%2F
is the url-escaped forward slash character). -
You can also upload the solution files to the Materials and use the Custom Solution URL field in the same way. Note that every time you upload something to the materials section, the underlying files get placed in a randomly-named folder that can be seen in the Raw link for any file.
- This means that if you upload the puzzle in one zip file, and then upload the solution in a separate zip file at a different time, you will NOT be able to access files from the other upload using relative links. For example, accessing an image in the puzzle folder from the solution file will NOT be possible by using a link like
../example-puzzle/image.jpg
. Thus, it's recommended that solutions NOT depend on files from the puzzle (or vice versa) and instead self-contain all files they need. - If you need to modify your puzzle, a best-practice will be to delete ALL files for that puzzle (no need to touch the solution files since they shouldn't depend on the puzzle files) and then re-upload everything in a ZIP file instead of uploading new files individually.
- For an abridges version of these steps, see the page on How to Set up an HTML puzzle.
- For HTML puzzles that might be printed, don't forget that you can create content that will ONLY be visible when printed (or that will ONLY be visible online)! To do that, simply use the following CSS classes as necessary. Print-only content is useful if you want to include a message that a puzzle is only playable online (for example, if it has music), and online-only content is useful if you don't want to print an audio tag, since that would be kind of pointless.
@media screen {
.print-only {
display: none;
}
}
@media print {
.online-only {
display: none;
}
}