diff --git a/gatsby-config.ts b/gatsby-config.ts index 98d0b502c..a01f71e64 100644 --- a/gatsby-config.ts +++ b/gatsby-config.ts @@ -127,6 +127,7 @@ const config: GatsbyConfig = { ignore: [`**/.*`], }, }, + "gatsby-plugin-netlify-cms", "gatsby-plugin-catch-links", "gatsby-plugin-image", "gatsby-plugin-netlify", diff --git a/package.json b/package.json index 1650256ad..952b677e1 100644 --- a/package.json +++ b/package.json @@ -63,5 +63,10 @@ }, "engines": { "node": ">=18.0.0" + }, + "overrides": { + "react-server-dom-webpack@0.0.0-experimental-c8b778b7f-20220825": { + "react": "^18.2.0" + } } } diff --git a/static/admin/config.yml b/static/admin/config.yml new file mode 100644 index 000000000..f443555b7 --- /dev/null +++ b/static/admin/config.yml @@ -0,0 +1,196 @@ +backend: + name: git-gateway + branch: main +slug: + encoding: "ascii" + clean_accents: true + +# These fields are not used since they are overwritten in each collection, +# but are required. +media_folder: static/media +public_folder: /media + +collections: + - name: "meetings" + label: "Meetings" + label_singular: "Meeting" + folder: "content/meetings" + identifier_field: "title" + create: true + media_folder: "" + public_folder: "" + nested: + depth: 100 + summary: "{{time_start}} - Week {{week_number}}: {{title}}" + fields: + - {label: "Title", name: "title", hint: 'Example: "PWN II"', widget: "string"} + - {label: "Start Time", name: "time_start", widget: "datetime", default: "", date_format: "YYYY-MM-DD", time_format: "HH:mm", hint: "Ensure date matches date in path."} + - {label: "End Time", name: "time_close", widget: "datetime", default: "", date_format: "YYYY-MM-DD", time_format: "HH:mm", hint: "Leave blank to default to 1-hour duration.", required: false} + - {label: "Display Timezone", name: "timezone", widget: "string", hint: 'Leave blank for "America/Chicago." Examples: "America/Chicago" or "Etc/UTC"', required: false} + - {label: "Week Number", name: "week_number", hint: "Current week number in the semester, 0-indexed. Be careful not to scroll on the input box since it may change the value.", widget: "number", value_type: "int", min: 0, max: 52, step: 1} + - {label: "Credit", name: "credit", widget: "list", hint: "Separate names with commas. To add a name with whitespace, type without spaces first, then insert spaces. Also works with organization names."} + - {label: "Featured", name: "featured", widget: "boolean", default: true, hint: "The meeting will be shown on the home page if set to true.", required: false} + - {label: "Location", name: "location", widget: "string", hint: 'Example: "Siebel CS 1404 + Zoom"', required: false} + - {label: "Cover Image", name: "image", widget: "object", required: false, hint: "Deprecated: Use Card Image instead", fields: [ + {label: "Image Upload", name: "path", widget: "image", hint: "Image should be 16:9 aspect ratio with a minimum width of 512px.", choose_url: false, required: false}, + {label: "Alt text", name: "alt", widget: "string", hint: "Provide a description of the image for accessibility purposes.", required: false} + ]} + - {label: "Card Image", name: "card_image", widget: "object", required: false, fields: [ + # {label: "Foreground", name: "foreground", widget: "image", hint: "You usually do not need to set this for meetings.", choose_url: false, required: false}, + {label: "Background", name: "background", widget: "image", hint: "Image should be 16:9 aspect ratio with a minimum width of 512px.", choose_url: false, required: false}, + {label: "Background color", name: "background_color", widget: "string", hint: "Overridden by background image. Include the hashtag. Example: '#33cc55'", required: false}, + {label: "Alt text", name: "alt", widget: "string", hint: "Provide a description of the image for accessibility purposes.", required: false} + ]} + - {label: "Zoom/live video URL", name: "live_video_url", widget: "string", hint: "STRONGLY recommend setting this beforehand by scheduling the Zoom meeting so people can access the meeting before the event starts.", required: false} + - {label: "Slides", name: "slides", widget: "file", choose_url: false, hint: "Please upload PDFs instead of PPTX files. If downloading from Google Slides, ensure that skipped slides are not present.", required: false} + - {label: "Recording URL", name: "recording", widget: "string", required: false} + - {label: "Additional assets", name: "assets", widget: "list", field: {label: File, name: file, widget: "file", choose_url: false}, hint: "Zip or compress files together when possible.", required: false} + - {label: "Tags", name: "tags", widget: "list", hint: 'Separate tags with commas. Add a main topic tag: "web", "pwn", "rev", "crypto", "forensics", "jail", "osint", "misc". Plus, add tags for specific topics, such as "xss", "rsa", or "lockpicking". Try to re-use tags from past meetings.', max: 5, required: false} + - {label: "Body", name: "body", widget: "markdown", mode: "raw", required: false} + sortable_fields: ["date", "title"] + # adding a meta object with a path property allows editing the path of entries + # moving an existing entry will move the entire sub tree of the entry to the new location + meta: { path: { widget: string, label: "Path", index_file: "index", hint: 'Examples: "sp2023/2023-01-01" or "fa2023/2023-08-01". All characters must be lowercase.', pattern: ['^(fa|sp)[0-9]{4}/[0-9]{4}-[0-9]{2}-[0-9]{2}$', "Refer to example path"] } } + + - name: "events" + label: "Events" + label_singular: "Event" + folder: "content/events" + create: true + media_folder: "" + public_folder: "" + nested: + depth: 100 + summary: "{{time_start}} {{title}}" + fields: + - {label: "Title", name: "title", widget: "string", hint: 'Examples: "UIUCTF 2022" or "Fall CTF 2022"'} + - {label: "Series", name: "series", widget: "select", options: ["uiuctf", "fallctf"], hint: ''} + - {label: "Description", name: "description", widget: "string", hint: ''} + - {label: "Start Time", name: "time_start", widget: "datetime", default: "", date_format: "YYYY-MM-DD", time_format: "HH:mm", hint: 'Make sure to use UTC time!'} + - {label: "End Time", name: "time_close", widget: "datetime", default: "", date_format: "YYYY-MM-DD", time_format: "HH:mm", hint: 'Make sure to use UTC time!', required: false} + - {label: "Display Timezone", name: "timezone", widget: "string", hint: 'Leave blank for "America/Chicago." Examples: "America/Chicago" or "Etc/UTC"', required: false} + - {label: "Location", name: "location", widget: "string", hint: 'Examples: "Online" or "CIF 3039"', required: false} + - {label: "Card Image", name: "card_image", widget: "object", required: false, fields: [ + {label: "Foreground", name: "foreground", widget: "image", hint: "PNG/JPEG or SVG", choose_url: false, required: false}, + {label: "Background", name: "background", widget: "image", hint: "Image should be 16:9 aspect ratio with a minimum width of 512px.", choose_url: false, required: false}, + {label: "Background color", name: "background_color", widget: "string", hint: "Overridden by background image. Include the hashtag. Example: '#33cc55'", required: false}, + {label: "Alt text", name: "alt", widget: "string", hint: "Provide a description of the image for accessibility purposes.", required: false} + ]} + - {label: "Credit", name: "credit", widget: "list", hint: 'Separate names with commas. To add a name with whitespace, type without spaces first, then insert spaces.', required: false} + - {label: "Sponsors", name: "sponsors", widget: "list", hint: 'Be sure to create sponsor profiles first. Separate names with commas. To add a name with whitespace, type without spaces first, then insert spaces.', required: false} + - {label: "Links", name: "links", widget: "list", required: false, fields: [ + {label: "Name", name: "name", widget: "select", options: ["website", "ctftime"], hint: ''}, + {label: "URL", name: "url", widget: "string", hint: ''} + ]} + - {label: "Stats", name: "stats", widget: "list", hint: 'Examples: Participants, Teams, Solves, Rating Weight', required: false, fields: [ + {label: "Name", name: "name", widget: "string"}, + {label: "Value", name: "value", widget: "string"} + ]} + - {label: "Body", name: "body", widget: "markdown", mode: "raw", required: false} + meta: { path: { widget: string, label: "Path", index_file: "index", hint: 'Examples: "uiuctf/2022" or "fallctf/2022"' } } + + - name: "publications" + label: "Publications" + label_singular: "Publication" + folder: "content/publications" + create: true + media_folder: "" + public_folder: "" + fields: + - {label: "Title", name: "title", widget: "string", hint: 'Example: "Announcing our new site!"'} + - {label: "Publication Type", name: "publication_type", widget: "select", options: ["talk", "blog", "paper", "news", "writeup"], hint: ''} + - {label: "Publisher", name: "publisher", widget: "string", hint: 'Changes meaning depending on publication type. For talks: conferences and events. For blogs and news: the primary site source. For papers: journals/conferences.', required: false} + - {label: "Credit", name: "credit", widget: "list", hint: 'Separate names with commas. To add a name with whitespace, type without spaces first, then insert spaces.'} + - {label: "Date", name: "date", widget: "date", format: "YYYY-MM-DD", hint: ''} + - {label: "Description", name: "description", widget: "string", hint: 'Describe the content.', required: false} + - {label: "Card Image", name: "card_image", widget: "object", required: false, fields: [ + {label: "Foreground", name: "foreground", widget: "image", hint: "PNG/JPEG or SVG", choose_url: false, required: false}, + {label: "Background", name: "background", widget: "image", hint: "Image should be 16:9 aspect ratio with a minimum width of 512px.", choose_url: false, required: false}, + {label: "Background color", name: "background_color", widget: "string", hint: "Overridden by background image. Include the hashtag. Example: '#33cc55'", required: false}, + {label: "Alt text", name: "alt", widget: "string", hint: "Provide a description of the image for accessibility purposes.", required: false} + ]} + - {label: "Primary Link", name: "primary_link", widget: "object", hint: 'This is the primary source which represents the publication.', required: false, fields: [ + {label: "Name", name: "name", widget: "string", hint: 'Example: "Recording (YouTube)" or "Paper Download (Google Scholar)"', required: false}, + {label: "URL", name: "url", widget: "string", hint: '', required: false} + ]} + - {label: "Links", name: "links", widget: "list", required: false, fields: [ + {label: "Name", name: "name", widget: "string", hint: 'Example: "Recording (YouTube)" or "Paper Download (Google Scholar)"'}, + {label: "URL", name: "url", widget: "string", hint: ''} + ]} + - {label: "Tags", name: "tags", widget: "list", hint: 'Separate tags with commas.', required: false} + - {label: "Body", name: "body", widget: "markdown", mode: "raw", required: false} + meta: { path: { widget: string, label: "Path", index_file: "index", hint: 'Examples: "2022-02-20_new_site"' } } + + - name: "admins" + label: "Admins" + label_singular: "Admin" + folder: "content/profiles/admins" + create: true + media_folder: "" + public_folder: "" + fields: + - {label: "Name", name: "name", hint: "Use real name if possible.", widget: "string"} + - {label: "Profile Image", name: "profile_image", widget: "image", hint: "Profile image should be 1:1 aspect ratio with a minimum width of 128px.", choose_url: false} + - {label: "Handle", name: "handle", hint: "Do not prepend with `@` since it is done automatically.", required: false} + - {label: "Bio", name: "bio", hint: "Limit to 128 characters.", widget: "string", pattern: ['^.{0,128}', "128 character limit exceeded"], required: false} + - {label: "Links", name: "links", widget: "list", required: false, fields: [ + {label: "Name", name: "name", widget: "select", hint: 'Remember, practice good opsec! Sort these by how you want them to appear. Try to limit to 6 links or less.', options: [ + "bluesky", "ctftime", "discord", "email", "github", "instagram", "linkedin", "mastodon", "reddit", "twitter", "website", "youtube" + ]}, + {label: "URL", name: "url", widget: "string", hint: ''} + ]} + - {label: "Role", name: "role", hint: 'Examples: "Admin", "President"', widget: "string", required: false} + - {label: "Weight", name: "weight", hint: "Set to a higher value to sort this profile closer to the top, otherwise sort by name. Reserved for administrative roles, such as president or co-president.", widget: "number", required: false} + sortable_fields: ["weight", "name", "handle"] + slug: '{{name}}' + path: '{{slug}}/index' # https://answers.netlify.com/t/nested-collections-dynamic-path-slug/24352/2 + + - name: "helpers" + label: "Helpers" + label_singular: "Helper" + folder: "content/profiles/helpers" + create: true + media_folder: "" + public_folder: "" + fields: + - {label: "Name", name: "name", hint: "Use real name if possible.", widget: "string"} + - {label: "Profile Image", name: "profile_image", widget: "image", hint: "Profile image should be 1:1 aspect ratio with a minimum width of 128px.", choose_url: false} + - {label: "Handle", name: "handle", hint: "Do not prepend with `@` since it is done automatically.", required: false} + - {label: "Bio", name: "bio", hint: "Limit to 128 characters.", widget: "string", pattern: ['^.{0,128}', "128 character limit exceeded"], required: false} + - {label: "Links", name: "links", widget: "list", required: false, fields: [ + {label: "Name", name: "name", widget: "select", hint: 'Remember, practice good opsec! Sort these by how you want them to appear. Try to limit to 6 links or less.', options: [ + "bluesky", "ctftime", "discord", "email", "github", "instagram", "linkedin", "mastodon", "reddit", "twitter", "website", "youtube" + ]}, + {label: "URL", name: "url", widget: "string", hint: ''} + ]} + - {label: "Role", name: "role", hint: 'Examples: "Helper", "Project Lead"', widget: "string", required: false} + - {label: "Weight", name: "weight", hint: "Set to a higher value to sort this profile closer to the top, otherwise sort by name. Reserved for administrative roles, such as president or co-president.", widget: "number", required: false} + sortable_fields: ["weight", "name", "handle"] + slug: '{{name}}' + path: '{{slug}}/index' + + - name: "alumni" + label: "Alumni" + label_singular: "Alum" + folder: "content/profiles/alumni" + create: true + media_folder: "" + public_folder: "" + fields: + - {label: "Name", name: "name", hint: "Use real name if possible.", widget: "string"} + - {label: "Profile Image", name: "profile_image", widget: "image", hint: "Profile image should be 1:1 aspect ratio with a minimum width of 128px.", choose_url: false} + - {label: "Handle", name: "handle", hint: "Do not prepend with `@` since it is done automatically.", required: false} + - {label: "Bio", name: "bio", hint: "Limit to 128 characters.", widget: "string", pattern: ['^.{0,128}', "128 character limit exceeded"], required: false} + - {label: "Period", name: "period", widget: "string", hint: 'Example: "Class of 2020"', required: false} + - {label: "Work", name: "work", widget: "string", hint: 'Your current work or shenanigans. Examples: "Frontend Dev @ Microsoft" or "Weary Traveler"', required: false} + - {label: "Links", name: "links", widget: "list", required: false, fields: [ + {label: "Name", name: "name", widget: "select", hint: 'Remember, practice good opsec! Sort these by how you want them to appear. Try to limit to 6 links or less.', options: [ + "bluesky", "ctftime", "discord", "email", "github", "instagram", "linkedin", "mastodon", "reddit", "twitter", "website", "youtube" + ]}, + {label: "URL", name: "url", widget: "string", hint: ''} + ]} + - {label: "Role", name: "role", hint: 'Examples: "Alum", "Ex-President"', widget: "string", required: false} + - {label: "Weight", name: "weight", hint: "Set to a higher value to sort this profile closer to the top, otherwise sort by name. Reserved for administrative roles, such as president or co-president.", widget: "number", required: false} + sortable_fields: ["weight", "name", "handle"] + slug: '{{name}}' + path: '{{slug}}/index'