diff --git a/README.md b/README.md index cfc542ad942..c425a8dcccd 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Intercode is a web application that: [The original Intercode](https://github.com/neinteractiveliterature/intercode-classic) was written in PHP by Barry Tannenbaum for Intercon New England, and has since been used by several other conventions around the world. -Intercode 2 was a ground-up rewrite of Intercode, making it more robust, more flexible, and more modern. Starting at version 3.0.0, we've used [semantic versioning](https://semver.org/) for our releases. +Intercode 2 was a ground-up rewrite of Intercode, making it more robust, more flexible, and more modern. Starting at version 3.0.0, we've used [semantic versioning](https://semver.org/) for our releases. -# Overall Architecture +## Overall Architecture - **Backend**: Ruby on Rails application exposing a GraphQL API and an OpenID Connect-enabled OAuth2 server - **Frontend**: React and Apollo-based single-page JavaScript app @@ -21,48 +21,273 @@ Intercode 2 was a ground-up rewrite of Intercode, making it more robust, more fl - **Background queue system**: Amazon SQS + Shoryuken (this might change in the future) - **Production infrastructure**: For [New England Interactive Literature](http://interactiveliterature.org)'s installation of Intercode, we're hosting it on [Fly](https://fly.io). -# Getting Started with Developing Intercode +## Getting Started with Developing Intercode - Intercode in development mode uses `intercode.test` as its cookie domain. If you use `localhost` to visit the site, that will mysteriously fail. I'm going to try to make the site detect the wrong domain and redirect you, but for now, please just use the `intercode.test` domain name. - We used to support a Docker Compose-based development workflow, but this has been deprecated. Please run Rails locally using the instructions below. -## Developer Setup with local Rails - -This is the classic Rails development setup, and should work for Mac and Linux users. - -1. Clone this repository: `git clone https://github.com/neinteractiveliterature/intercode.git` -2. Make sure you have a working C/C++ development toolchain installed. On macOS, that's Xcode and its Command Line Tools. -3. Install [rbenv](https://github.com/sstephenson/rbenv#readme) -4. Install [ruby-build](https://github.com/sstephenson/ruby-build#readme) -5. Edit your hosts file (typically found in `/etc/hosts` on Mac and Linux systems) and add the following line: `127.0.0.1 intercode.test` -6. `cd` to the Intercode source folder, all the remaining steps should be performed in that folder. -7. Copy `.env.development.local.sample` to `.env.development.local` and follow the instructions in - that file to set up keys for various external services. You'll almost certainly need to set up - reCAPTCHA; the others are probably best left until you really need to. -8. Install all the dependencies of Intercode: - - 1. Install the Ruby version Intercode requires: `rbenv install` - 2. Install Bundler: `gem install bundler` - 3. Install PostgreSQL. With Homebrew: `brew install postgres` - 4. Make sure you have Node.js installed. With Homebrew: `brew install node` - 5. Make sure you have Yarn installed. With Homebrew: `brew install yarn` - 6. `bundle install` - -9. Generate self-signed certificates to support HTTPS - - 1. `gem install toys` - 2. `toys setup_tls` - -10. Set up your local database: `bin/rails db:create db:migrate` -11. Install JavaScript packages: `yarn install` -12. Start up the Intercode server: `bin/rails server` -13. Start up the Webpack server: `yarn run start` -14. You should now be able to go to and see the app running! -15. Click the user icon in the upper right of the navigation bar and choose "Sign up" to sign up for - an account in your local copy of Intercode. -16. To make yourself a superadmin in your local copy, run `./bin/rails console`. At the Rails - console prompt, type `User.first.update!(site_admin: true)`. This will set the `site_admin` - flag on your account in the database, giving you superadmin powers. +### Developer Setup with local Rails + +This is the classic Rails development setup, and should work for Mac and Linux users. Windows users should use WSL. + +#### Dev tooling setup using mise + +In this tutorial, we're going to set up [mise-en-place](https://mise.jdx.dev) to manage the versions of Ruby and Node.js used to run Intercode. This will be a globally-installed tool on your system, so if you don't want to do it this way, know that there are other options such as [rbenv](https://github.com/sstephenson/rbenv#readme). + +First, run this command to install mise globally: + +```sh +curl https://mise.run | sh +``` + +This will download mise and then give you a command to run to activate mise in your shell. Run that command as well, then run: + +```sh +mise doctor +``` + +Hopefully, this will run with no issues. If it does find problems, follow its instructions to correct them. + +Now we need to set some settings in mise so that it will correctly pick up the tool versions Intercode uses. Run these commands: + +```sh +mise settings add idiomatic_version_file_enable_tools ruby +mise settings add idiomatic_version_file_enable_tools node +mise settings ruby.compile=false +``` + +(The last one isn't strictly necessary but it should save a lot of time on the installation.) + +#### Setting up other dependencies + +On Linux and WSL, you'll need to have a few packages installed before setting up Intercode. For Debian and Ubuntu, this command should do it: + +```sh +sudo apt install build-essential git postgresql libmariadb-dev libvips zlib1g-dev libffi-dev libssl-dev libyaml-dev +``` + +On macOS, you should have [Homebrew](https://brew.sh/) installed. Homebrew will also guide you through installing the Xcode command line tools. Once that's done, run this: + +```sh +brew install mysql-client postgresql +``` + +In order to connect to your local PostgreSQL instance and set up Intercode, you'll need to have a user with enough permissions to log in and create databases. First, run this: + +```sh +sudo -u postgres psql postgres +``` + +This should bring you into a Postgres command prompt. You'll need to run a few commands that include your local username on your Linux or macOS machine: + +```sql +CREATE ROLE [your username]; +ALTER USER [your username] login; +ALTER USER [your username] createdb; +``` + +Now exit the Postgres command prompt using Ctrl-D, and run: + +```sh +psql postgres +``` + +This should let you into Postgres, this time as your local user account. + +#### Setting up Intercode + +First, clone this repository: + +```sh +git clone https://github.com/neinteractiveliterature/intercode.git +``` + +cd into the checked-out repository and have mise install Ruby and Node automatically: + +```sh +cd intercode +mise install +``` + +Intercode ships with a sample settings file that has credentials for external services the app uses. We'll need to make a copy of it so that we can set up those credentials as needed. For now, we'll leave all the actual credentials blank: + +```sh +cp .env.development.local.sample .env.development.local +``` + +Now let's install the Ruby and Node.js dependencies of the app: + +```sh +bundle install +corepack enable +yarn install +``` + +#### Setting up the database + +In theory, it should be possible to set up your local database using this command: + +```sh +bin/rails db:create db:migrate db:seed +``` + +There are a few things that can go wrong here. Let's go through some common types of errors and how you can fix them: + +
+ +`PG::ConnectionBad: connection to server at "::1", port 5432 failed: fe_sendauth: no password supplied (PG::ConnectionBad)` + +If you're seeing something like this, you probably need to force Rails to connect to the database server using a UNIX socket as opposed to trying to connect via localhost TCP port 5432. To do this, we'll need to change the `DATABASE_URL` environment variables. Start by copying these lines from `.env.development` into `.env.development.local`: + +```text +DEVELOPMENT_DATABASE_URL=postgresql://localhost/intercode_development +TEST_DATABASE_URL=postgresql://localhost/intercode_test +``` + +Then, add the path to the UNIX socket as a `?host=` parameter at the end of both URLs. On Debian, the path to the socket is `/var/run/postgresql`, so these lines would become: + +```text +DEVELOPMENT_DATABASE_URL=postgresql://localhost/intercode_development?host=/var/run/postgresql +TEST_DATABASE_URL=postgresql://localhost/intercode_test?host=/var/run/postgresql +``` + +
+ +
+ +`psql:/home/debian/intercode/db/structure.sql:4: ERROR: unrecognized configuration parameter "transaction_timeout"` + +If you're seeing something like this, you're probably running an older version of PostgreSQL than the one Intercode supports. We tend to track new PostgreSQL releases pretty closely, so you probably +need the latest version available. + +
+ +#### Setting up weird web serving nonsense + +Intercode uses a somewhat unfortunate custom setup for local HTTP. Because some features require HTTPS, we generate a self-signed CA and certificate. In addition, Intercode expects to have different domain names for each convention it hosts, so we set up \*.intercode.test as a private fake DNS namespace for the local copy of Intercode to use. + +First, let's generate the self-signed certificates: + +```sh +gem install toys +toys setup_tls +``` + +On macOS, the above command will prompt for your password and install the CA in your local keychain. On other OSes, you'll have to do this step manually from your browser (later in the process). + +Now, let's set up the private DNS namespace. The setup for this differs somewhat between different operating systems: + +
+macOS + +On macOS, create a file called `/etc/resolver/intercode.test` with the following contents: + +```text +domain intercode.test +nameserver 127.0.0.1 +``` + +To test that this is working, try running `ping randomname.intercode.test`. It should start pinging your local machine on 127.0.0.1. + +
+ +
+ +Linux + +On Linux, there's no built-in way to do wildcard domain resolution like there is with macOS's resolver. But, we can use dnsmasq as a DNS resolver proxy and configure it to resolve \*.intercode.test to 127.0.0.1. First, install dnsmasq: + +```sh +sudo apt install dnsmasq +``` + +Then create a file called `/etc/dnsmasq.d/dnsmasq-intercode.conf` with the following contents: + +```text +address=/intercode.test/127.0.0.1 +``` + +Now we need to get dnsmasq to play nice with systemd, which at least in Debian's setup, it won't do by default. First, edit `/etc/dnsmasq.conf` and add these lines: + +```text +listen-address=127.0.0.2 +bind-interfaces +``` + +This will make dnsmasq listen on 127.0.0.2, which won't conflict with systemd-resolved. We also need to get it to stop trying to listen on 127.0.0.1. To do that, edit `/etc/default/dnsmasq` and find the commented-out line that says `DNSMASQ_EXCEPT="lo"`. Uncomment it: + +```text +DNSMASQ_EXCEPT="lo" +``` + +Now restart dnsmasq: + +```sh +sudo systemctl restart dnsmasq +``` + +Once that's done, edit `/etc/systemd/resolved.conf` and find the commented-out line that begins with `DNS=`. Change it to say: + +```text +DNS=127.0.0.2 +``` + +Now restart systemd-resolved: + +```sh +sudo systemctl restart systemd-resolved.service +``` + +To test that this is working, try running `ping randomname.intercode.test`. It should start pinging your local machine on 127.0.0.1. + +
+ +
+Windows + +On Windows, there's no built-in way to do wildcard domain resolution like there is with macOS's resolver. But, we can use a DNS resolver proxy such as [Acrylic](https://mayakron.altervista.org/support/acrylic/Home.htm) and configure it to resolve \*.intercode.test to 127.0.0.1. + +I have not personally tried this, but if someone does and would like to contribute instructions to this README, I would be forever grateful! + +<3, Nat + +
+ +#### Starting Intercode for the first time + +You'll need two terminals (or two terminal tabs) for this. In the first, start up the Rails backend server: + +```sh +bin/rails server +``` + +In the second, start up the Vite frontend server: + +```sh +yarn run start +``` + +You should now be able to go to and see the app running! + +If you don't, you probably need to import the self-signed CA to your local keychain. In Chrome and Chromium-based browsers, you'll probably get an error that allows you to go ahead and trust the CA, which you should do. + +In Firefox, the browser will likely hang forever when trying to load the page. You'll have to go into Firefox's security settings and go to Certificates, and import the `dev_ca.crt` file (generated in a previous step) as a trusted CA for websites. + +#### Making yourself a local admin + +Now let's make you a local administrator. Open a third terminal and run: + +```sh +bin/rails console +``` + +At the Rails console prompt, use a command like this to create an admin user: + +```ruby +User.create!(email: 'your email address here', first_name: 'your first name here', last_name: 'your last name', password: 'your password', site_admin: true) +``` + +This will create an account for you in your local database with the `site_admin` flag turned on. You should now be able to log into your local copy of Intercode and access all the admin functionality. ## Testing production builds @@ -74,17 +299,17 @@ If you want to test how the app runs in production, but using your local develop 4. Run something like the following command, changing the asset host as necessary for your setup: `docker run -it -p 5051:3000 -e DATABASE_URL=postgresql://postgres@docker.for.mac.localhost/intercode_development -e RAILS_LOG_TO_STDOUT=true -e ASSETS_HOST=//intercont.intercode.test:5050 -e RAILS_SERVE_STATIC_FILES=true local-intercode-production bin/rails` 5. Visit , probably using Firefox (it seems to deal better than Chrome with self-signed certificates these days). -# Contacting us +## Contacting us To contact the Intercode project team, you can: - [File an issue or feature request here](https://github.com/neinteractiveliterature/intercode/issues) - [Email Nat Budin](mailto:natbudin@gmail.com). -# Code of Conduct +## Code of Conduct Participants in the Intercode project are expected to follow the Contributor Covenant. For details, [see CODE_OF_CONDUCT.md](https://github.com/neinteractiveliterature/intercode/blob/main/CODE_OF_CONDUCT.md). -# License +## License Intercode is released under the terms and conditions of the MIT license. Please see the LICENSE file for the full legalese. diff --git a/app/models/root_site.rb b/app/models/root_site.rb index 2e1285f1f20..1d90a4818de 100644 --- a/app/models/root_site.rb +++ b/app/models/root_site.rb @@ -23,8 +23,8 @@ # rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective class RootSite < ApplicationRecord - belongs_to :root_page, class_name: 'Page' - belongs_to :default_layout, class_name: 'CmsLayout' + belongs_to :root_page, class_name: "Page", optional: true + belongs_to :default_layout, class_name: "CmsLayout", optional: true def self.instance RootSite.first @@ -62,10 +62,22 @@ def cms_graphql_queries CmsGraphqlQuery.global end + def notification_templates + NotificationTemplate.none + end + + def forms + Form.none + end + def host Rails.application.config.action_mailer.default_url_options[:host] end + def name + "Root site" + end + def url Rails.application.routes.url_helpers.root_url(Rails.application.config.action_mailer.default_url_options) end diff --git a/app/services/clear_cms_content_service.rb b/app/services/clear_cms_content_service.rb index c44264716c5..72d0a14ef8a 100644 --- a/app/services/clear_cms_content_service.rb +++ b/app/services/clear_cms_content_service.rb @@ -9,14 +9,18 @@ def initialize(convention:) private def inner_call - convention.update!(root_page: nil, default_layout: nil, user_con_profile_form: nil) - convention.cms_navigation_items.destroy_all - convention.pages.destroy_all - convention.cms_partials.destroy_all - convention.cms_layouts.destroy_all - convention.cms_files.destroy_all - convention.forms.destroy_all + cms_parent.update!(root_page: nil, default_layout: nil, user_con_profile_form: nil) + cms_parent.cms_navigation_items.destroy_all + cms_parent.pages.destroy_all + cms_parent.cms_partials.destroy_all + cms_parent.cms_layouts.destroy_all + cms_parent.cms_files.destroy_all + cms_parent.forms.destroy_all success end + + def cms_parent + @cms_parent ||= convention || RootSite.instance + end end diff --git a/app/services/cms_content_loaders/cms_files.rb b/app/services/cms_content_loaders/cms_files.rb index 405949e02f4..5a9d2a2151d 100644 --- a/app/services/cms_content_loaders/cms_files.rb +++ b/app/services/cms_content_loaders/cms_files.rb @@ -7,8 +7,15 @@ def storage_adapter end def create_item(_identifier, attrs) - File.open(attrs[:path], 'rb') do |file| + File.open(attrs[:path], "rb") do |file| cms_parent.cms_files.create!(file: { io: file, filename: File.basename(attrs[:path]) }) end end + + def existing_content_identifiers + @existing_content_identifiers ||= + Set.new( + storage_adapter.cms_parent_association.joins(file_attachment: :blob).pluck(active_storage_blobs: "filename") + ) + end end diff --git a/app/services/cms_content_storage_adapters/base.rb b/app/services/cms_content_storage_adapters/base.rb index 028cf50bd06..35244ea448c 100644 --- a/app/services/cms_content_storage_adapters/base.rb +++ b/app/services/cms_content_storage_adapters/base.rb @@ -105,7 +105,7 @@ def item_from_database(identifier) def merge_items(item_lists) item_lists_by_identifier = item_lists.map { |item_list| item_list.index_by(&:identifier) } - item_lists_by_identifier.inject(&:merge).values + (item_lists_by_identifier.inject(&:merge) || {}).values end def basename_without_extension(path, extension) diff --git a/app/services/cms_content_storage_adapters/cms_files.rb b/app/services/cms_content_storage_adapters/cms_files.rb index 7ab00107480..6b37b30bc90 100644 --- a/app/services/cms_content_storage_adapters/cms_files.rb +++ b/app/services/cms_content_storage_adapters/cms_files.rb @@ -1,6 +1,6 @@ class CmsContentStorageAdapters::CmsFiles < CmsContentStorageAdapters::Base def subdir - 'files' + "files" end def cms_parent_association @@ -8,7 +8,7 @@ def cms_parent_association end def identifier_attribute - 'file' + "file" end def read_item_attrs(item) @@ -16,18 +16,18 @@ def read_item_attrs(item) end def identifier_for_path(_content_set, path) - basename_without_extension(path, '') + basename_without_extension(path, "") end def identifier_for_model(model) - model.file.identifier + model.file.filename.to_s end def path_for_identifier(content_set, identifier) - content_set.content_path(File.join('files', identifier)) + content_set.content_path(File.join("files", identifier)) end def serialize_item(item, io) - io.write(item.model.file.read) + io.write(item.model.file.download) end end diff --git a/app/services/export_cms_content_set_service.rb b/app/services/export_cms_content_set_service.rb index 25764745346..aa81eb86881 100644 --- a/app/services/export_cms_content_set_service.rb +++ b/app/services/export_cms_content_set_service.rb @@ -2,7 +2,7 @@ class ExportCmsContentSetService < CivilService::Service attr_reader :convention, :content_set, :content_set_name, :inherit - validates_presence_of :convention, :content_set_name + validates_presence_of :content_set_name validate :ensure_no_conflicting_folder def initialize(convention:, content_set_name:, inherit: ["standard"]) @@ -27,7 +27,7 @@ def inner_call CmsContentStorageAdapters::CmsGraphqlQueries, CmsContentStorageAdapters::Forms, CmsContentStorageAdapters::CmsFiles - ].each { |adapter_class| export_content_from_adapter(adapter_class.new(convention, content_set)) } + ].each { |adapter_class| export_content_from_adapter(adapter_class.new(cms_parent, content_set)) } success end @@ -65,8 +65,8 @@ def export_metadata metadata = { "inherit" => inherit, "navigation_items" => serialize_root_navigation_items, - "root_page_slug" => convention.root_page&.slug, - "default_layout_name" => convention.default_layout&.name, + "root_page_slug" => cms_parent.root_page&.slug, + "default_layout_name" => cms_parent.default_layout&.name, "variables" => serialize_variables }.compact @@ -80,7 +80,7 @@ def export_item(item, &) end def serialize_root_navigation_items - serialize_navigation_items(convention.cms_navigation_items.root.order(:position)) + serialize_navigation_items(cms_parent.cms_navigation_items.root.order(:position)) end def serialize_navigation_items(items) @@ -95,7 +95,7 @@ def serialize_navigation_items(items) end def serialize_variables - convention.cms_variables.order(:key).each_with_object({}) { |variable, hash| hash[variable.key] = variable.value } + cms_parent.cms_variables.order(:key).each_with_object({}) { |variable, hash| hash[variable.key] = variable.value } end def inherited_content_sets @@ -106,4 +106,8 @@ def ensure_no_conflicting_folder return unless Dir.exist?(content_set.root_path) errors.add(:base, "Folder called #{content_set.root_path} already exists") end + + def cms_parent + @cms_parent ||= convention || RootSite.instance + end end diff --git a/app/services/load_cms_content_set_service.rb b/app/services/load_cms_content_set_service.rb index dedc0bc9e10..3268ef129c0 100644 --- a/app/services/load_cms_content_set_service.rb +++ b/app/services/load_cms_content_set_service.rb @@ -2,7 +2,7 @@ class LoadCmsContentSetService < CivilService::Service attr_reader :convention, :content_set, :content_set_name - validates_presence_of :convention, :content_set_name + validates_presence_of :content_set_name validate :ensure_content_set_exists validate :ensure_no_conflicting_user_con_profile_form @@ -23,7 +23,7 @@ def inner_call CmsContentLoaders::CmsGraphqlQueries, CmsContentLoaders::Forms, CmsContentLoaders::CmsFiles - ].each { |loader_class| loader_class.new(cms_parent: convention, content_set: content_set).call! } + ].each { |loader_class| loader_class.new(cms_parent: cms_parent, content_set: content_set).call! } load_navigation_items load_variables @@ -34,7 +34,7 @@ def inner_call def load_navigation_items return unless content_set.metadata[:navigation_items] content_set.metadata[:navigation_items].each_with_index do |navigation_item, i| - root_item = convention.cms_navigation_items.new(position: i + 1) + root_item = cms_parent.cms_navigation_items.new(position: i + 1) populate_navigation_item(root_item, navigation_item) root_item.save! end @@ -42,30 +42,34 @@ def load_navigation_items def load_variables return unless content_set.metadata[:variables] - content_set.metadata[:variables].each { |key, value| convention.cms_variables.create!(key: key, value: value) } + content_set.metadata[:variables].each { |key, value| cms_parent.cms_variables.create!(key: key, value: value) } end def populate_navigation_item(item, data) item.title = data[:title] case data[:item_type] - when 'section' + when "section" data[:navigation_links].each_with_index do |link_data, i| - link = item.navigation_links.new(position: i + 1, parent: convention) + link = item.navigation_links.new(position: i + 1, parent: cms_parent) populate_navigation_item(link, link_data) end - when 'link' - item.page = convention.pages.find_by(slug: data[:page_slug]) + when "link" + item.page = cms_parent.pages.find_by(slug: data[:page_slug]) end end + def cms_parent + @cms_parent ||= convention || RootSite.instance || RootSite.new + end + def ensure_content_set_exists return if Dir.exist?(content_set.root_path) errors.add(:base, "No content set found at #{content_set.root_path}") end def ensure_no_conflicting_user_con_profile_form - return unless content_set.user_con_profile_form && convention.user_con_profile_form - errors.add(:base, "#{convention.name} already has a user_con_profile form") + return unless content_set.user_con_profile_form && cms_parent.user_con_profile_form + errors.add(:base, "#{cms_parent.name} already has a user_con_profile form") end end diff --git a/cms_content_sets/root_site/files/neilSquare.png b/cms_content_sets/root_site/files/neilSquare.png new file mode 100644 index 00000000000..28f8031d11a Binary files /dev/null and b/cms_content_sets/root_site/files/neilSquare.png differ diff --git a/cms_content_sets/root_site/files/neilhosting-plain.svg b/cms_content_sets/root_site/files/neilhosting-plain.svg new file mode 100644 index 00000000000..8a2ea3220a6 --- /dev/null +++ b/cms_content_sets/root_site/files/neilhosting-plain.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/cms_content_sets/root_site/files/news_20191003_legend.png b/cms_content_sets/root_site/files/news_20191003_legend.png new file mode 100644 index 00000000000..6ab278dde35 Binary files /dev/null and b/cms_content_sets/root_site/files/news_20191003_legend.png differ diff --git a/cms_content_sets/root_site/files/news_20191003_popup.png b/cms_content_sets/root_site/files/news_20191003_popup.png new file mode 100644 index 00000000000..ee6532798f4 Binary files /dev/null and b/cms_content_sets/root_site/files/news_20191003_popup.png differ diff --git a/cms_content_sets/root_site/files/news_20191003_run.png b/cms_content_sets/root_site/files/news_20191003_run.png new file mode 100644 index 00000000000..20077802fc2 Binary files /dev/null and b/cms_content_sets/root_site/files/news_20191003_run.png differ diff --git a/cms_content_sets/root_site/layouts/Default.liquid b/cms_content_sets/root_site/layouts/Default.liquid new file mode 100644 index 00000000000..7776cd18e2b --- /dev/null +++ b/cms_content_sets/root_site/layouts/Default.liquid @@ -0,0 +1,12 @@ + + + + {{ content_for_head }} + + {{ content_for_navbar }} + +
+ {{ content_for_layout }} +
+ + diff --git a/cms_content_sets/root_site/layouts/NEIL Hosting Layout.liquid b/cms_content_sets/root_site/layouts/NEIL Hosting Layout.liquid new file mode 100644 index 00000000000..559063d0143 --- /dev/null +++ b/cms_content_sets/root_site/layouts/NEIL Hosting Layout.liquid @@ -0,0 +1,35 @@ +--- +navbar_classes: navbar-fixed-top navbar-expand-md mb-4 navbar-light bg-light +admin_notes: '' +--- + + + + {{ content_for_head }} + + + + + +
+ {{ content_for_navbar }} + +
+ {{ content_for_layout }} +
+
+ {% include 'copyright' %} +
+
+ + diff --git a/cms_content_sets/root_site/metadata.yml b/cms_content_sets/root_site/metadata.yml new file mode 100644 index 00000000000..5da3f9b4087 --- /dev/null +++ b/cms_content_sets/root_site/metadata.yml @@ -0,0 +1,6 @@ +--- +inherit: [] +navigation_items: [] +root_page_slug: root +default_layout_name: NEIL Hosting Layout +variables: {} diff --git a/cms_content_sets/root_site/pages/all-conventions.liquid b/cms_content_sets/root_site/pages/all-conventions.liquid new file mode 100644 index 00000000000..1ac3205c0f4 --- /dev/null +++ b/cms_content_sets/root_site/pages/all-conventions.liquid @@ -0,0 +1,40 @@ +--- +name: All conventions +admin_notes: '' +skip_clickwrap_agreement: false +hidden_from_search: false +--- +{% assign conventions_sorted = conventions | sort:'starts_at' %} +{% assign conventions_sorted_reverse = conventions_sorted|reverse %} +{% assign now = "now" | date: "%Y-%m-%d %H:%M" %} + +

Upcoming Conventions

+
    + {% for convention in conventions_sorted %} + {% if now < convention.ends_at %} + {% unless convention.name contains "[demo]" or convention.canceled %} + {% include 'convention_li'%} + {% endunless %} + {% endif %} + {% endfor %} +
+ +

Past Conventions

+
    + {% for convention in conventions_sorted_reverse %} + {% if now > convention.ends_at %} + {% unless convention.name contains "[demo]" or convention.canceled %} + {% include 'convention_li'%} + {% endunless %} + {% endif %} +{% endfor %} +
+ +

Canceled Conventions

+
    + {% for convention in conventions_sorted %} + {% if convention.canceled %} + {% include 'convention_li'%} + {% endif %} + {% endfor %} +
\ No newline at end of file diff --git a/cms_content_sets/root_site/pages/all-conventions/all.liquid b/cms_content_sets/root_site/pages/all-conventions/all.liquid new file mode 100644 index 00000000000..68e887ba0e4 --- /dev/null +++ b/cms_content_sets/root_site/pages/all-conventions/all.liquid @@ -0,0 +1,26 @@ +--- +name: All Conventions + Demos +skip_clickwrap_agreement: false +hidden_from_search: false +--- +{% assign conventions_sorted = conventions | sort:'starts_at' %} +{% assign conventions_sorted_reverse = conventions_sorted|reverse %} +{% assign now = "now" | date: "%Y-%m-%d %H:%M" %} + +

Upcoming Conventions

+
    + {% for convention in conventions_sorted %} + {% if now < convention.ends_at %} + {% include 'convention_li'%} + {% endif %} + {% endfor %} +
+ +

Past Conventions

+
    + {% for convention in conventions_sorted_reverse %} + {% if now > convention.ends_at %} + {% include 'convention_li'%} + {% endif %} +{% endfor %} +
\ No newline at end of file diff --git a/cms_content_sets/root_site/pages/changelog.liquid b/cms_content_sets/root_site/pages/changelog.liquid new file mode 100644 index 00000000000..27be7b090a7 --- /dev/null +++ b/cms_content_sets/root_site/pages/changelog.liquid @@ -0,0 +1,105 @@ +--- +name: Changelog +admin_notes: '' +skip_clickwrap_agreement: false +hidden_from_search: false +--- +

Intercode Changelog

+
    +
  • +
    +

    + Email forwarding + + March 15, 2020 +

    + +

    + Intercode can now forward emails recieved by a convention domain to appropriate staff members. For example, if your convention is hosted at 2020.example.com, and you have a staff position called Webmaster whose contact email is set as webmaster@2020.example.com, Intercode can now automatically forward emails received at that address to all the people in that staff position. +

    + +

    + Additionally, staff positions can now have CC addresses (which will also receive email sent to that staff position) and aliases (additional email addresses that can be used to contact that staff position). +

    + +

    + In order to take advantage of this feature, conventions will need to set the MX record on their domain name appropriately. If you'd like to do this, please contact us at hosting@neilhosting.net for instructions! +

    +
    +
  • + +
  • +
    +

    + SMS notifications + + January 27, 2020 +

    + +

    + Intercode is now able to send SMS (text message) notifications! For conventions that keep online event signups open during the con itself, Intercode will deliver notifications of signups, withdrawals, and waitlist pulls starting 24 hours before the start of the convention. The text of notifications is customizable via the "Notification templates" feature in the Admin section. +

    + +

    + Users can opt out of SMS notifications via a new setting in their user profile for a convention. Texts will come from 415-NEIL-010 (415-634-5010). +

    +
    +
  • + +
  • +
    +

    New Intercode Schedule Design October 3, 2019

    +

    We’ve rolling out some changes to how the schedule and run buckets display in Intercode 2. Here’s the highlights:

    +

    Schedule

    +
      +
    • + Instead of having a thin green bar that empties as people sign up for an event, the event will have rounded ends, and act as a progress bar that goes up as people sign up. Once the event is full, it will be lightened to show that it is full. +
    • +
    • + Events that have unlimited slots will remain rectangular, and have a gradient background to show that you can sign up for them. +
    • +
    • + Events that have no slots (i.e. consuite) will remain rectangular, and have a solid background. +
    • +
    • + The “you are signed up for this game” has changed to a user/head icon instead of the checkbox that looked like an interactive element. +
    • +
    • + The concom only “Schedule With Counts” view has been updated to show the percentage of signups in the same style, as a background progress bar on each event. +
    • +
    • + The sorting of events on the schedule has been updated to better group multiple runs of events together if they’re sequential. +
    • +
    +
    Schedule Legend Example
    + +

    Signup Buckets

    +
      +
    • + Instead of having a broken line that turns from colored segments to gray, we’re displaying a set of empty circles that get replaced by the same user/head icon that we use on the schedule as people sign up. This should be easier to read at a glance. +
    • +
    • + When you click on a run in the schedule view, the same set of circles/heads are displayed there as well. +
    • +
    +
    Signup Bucket Examples
    +
    +
    + +
    +
    + +
    +
    +
    +
  • + + +
      \ No newline at end of file diff --git a/cms_content_sets/root_site/pages/cookies.liquid b/cms_content_sets/root_site/pages/cookies.liquid new file mode 100644 index 00000000000..ef3fe849918 --- /dev/null +++ b/cms_content_sets/root_site/pages/cookies.liquid @@ -0,0 +1,47 @@ +--- +name: Cookie policy +admin_notes: GDPR-required cookie policy +skip_clickwrap_agreement: true +hidden_from_search: false +--- +

      Cookie Policy for NEIL Hosting

      + +

      This is the Cookie Policy for NEIL Hosting and all sites hosted by it.

      + +

      What Are Cookies

      + +

      This site uses cookies, which are tiny pieces of data that are downloaded to your computer, to improve your experience. This page describes what information they gather, how we use it and why we sometimes need to store these cookies. We will also share how you can prevent these cookies from being stored; however, this may break certain elements of the site's functionality.

      + +

      For more general information on cookies, please read "What Are Cookies".

      + +

      How We Use Cookies

      + +

      We use cookies for a variety of reasons detailed below. Unfortunately in most cases there are no industry standard options for disabling cookies without completely disabling the functionality and features they add to this site. It is recommended that you leave on all cookies if you are not sure whether you need them or not in case they are used to provide a service that you use.

      + +

      The Cookies We Set

      + +
      +
      Session cookies
      +
      We use cookies when you are logged in so that we can remember this fact. This prevents you from having to log in every single time you visit a new page. These cookies are typically removed or cleared when you log out to ensure that you can only access restricted features and areas when logged in.
      + +
      Consent cookies
      +
      Convention organizers may choose to include cookie consent banners on their web sites. For those sites, when you see a cookie banner and click the button to accept the cookie policy, we use cookies to remember this fact and not show the cookie banner to you again.
      +
      + +

      Third Party Cookies

      + +

      In some special cases we also use cookies provided by trusted third parties. The following section details which third party cookies you might encounter through this site.

      + +
      +
      Stripe cookies
      +
      Our payments provider, Stripe, sets cookies for fraud detection and prevention. We only load Stripe's scripts when you view a checkout page, so these cookies are not set until necessary.
      +
      + + +

      Disabling Cookies

      + +

      You can prevent the setting of cookies by adjusting the settings on your browser (see your browser Help for how to do this). Be aware that disabling cookies will affect the functionality of this and many other websites that you visit. Disabling cookies will usually result in also disabling certain functionality and features of the this site. Therefore it is recommended that you do not disable cookies.

      + +

      More Information

      + +

      For more information, please contact us at webmaster@interactiveliterature.org.

      diff --git a/cms_content_sets/root_site/pages/host-with-us.liquid b/cms_content_sets/root_site/pages/host-with-us.liquid new file mode 100644 index 00000000000..edae58f4d74 --- /dev/null +++ b/cms_content_sets/root_site/pages/host-with-us.liquid @@ -0,0 +1,41 @@ +--- +name: Host with us +admin_notes: '' +skip_clickwrap_agreement: false +hidden_from_search: false +--- +

      Host with us

      + +

      + New England Interactive Literature is now + offering web site hosting for larp conventions. Using our open-source platform, + Intercode, your + convention can take advantage of a highly customizable content management system, + self-registration for games, and ticket sales via Stripe. +

      + +

      + Pricing is as follows: +

      + +
        +
      • Free for conventions that are free of charge to attend
      • +
      • + $0.50 per attendee for conventions that charge for tickets +
      • +
      + +

      + + The full terms of service, including more details on the logistics of using NEIL Hosting, + are available here. + +

      + +

      + + +   + Interested? Email our web team. + +

      \ No newline at end of file diff --git a/cms_content_sets/root_site/pages/root.liquid b/cms_content_sets/root_site/pages/root.liquid new file mode 100644 index 00000000000..7e37f9624cd --- /dev/null +++ b/cms_content_sets/root_site/pages/root.liquid @@ -0,0 +1,78 @@ +--- +name: Main page +admin_notes: '' +skip_clickwrap_agreement: false +hidden_from_search: false +--- +{% assign conventions_sorted = conventions | sort:'starts_at' %} +{% assign conventions_sorted_reverse = conventions_sorted|reverse %} +{% assign now = "now" | date: "%Y-%m-%d %H:%M" %} + +
      + NEIL Hosting +
      + +
      +
      +
      +
      + Upcoming Conventions +
      +
        + {% for convention in conventions_sorted %} + {% if now < convention.ends_at %} + {% unless convention.name contains "[demo]" or convention.canceled %} + {% include 'convention_li'%} + {% endunless%} + {% endif %} + {% endfor %} +
      + +
      +
      +
      +
      +
      + Hosting available +
      +
      + NEIL Hosting is now available for all larp conventions! We provide a modern, battle-tested platform for convention registration and site hosting. +
      + +
      +
      +
      + Documentation +
      +
      + We're working on a documentation site for Intercode, including a user guide and a GraphQL API reference. It's a work in progress. +
      + +
      +
      +
      + Intercode Blog +
      +
      + We're constantly making improvements to our platform which are available to all conventions hosted by us as soon as they are deployed. For larger changes, we publish a blog so you can keep appraised of changes. +
      + +
      +
      +
      \ No newline at end of file diff --git a/cms_content_sets/root_site/partials/account_form_text.liquid b/cms_content_sets/root_site/partials/account_form_text.liquid new file mode 100644 index 00000000000..b36597a59a8 --- /dev/null +++ b/cms_content_sets/root_site/partials/account_form_text.liquid @@ -0,0 +1,21 @@ +--- +admin_notes: '' +invariant: false +--- +{% assign conlist = '' |split: '' %} +{% assign cons = conventions | sort: "starts_at" | reverse %} +{% for con in cons %} + {% unless con.name contains '[demo]' %} + {% assign name = con.name| split: '_%' %} + {% assign conlist = conlist | concat: name %} + {% endunless %} +{% endfor %} +
      +
      + NEIL Hosting +
      +
      +

      This account is shared between all NEIL Intercon conventions and NELCOs, as well as many other events that use NEIL Hosting. Only the organizers of the events you actually sign in to have access to your personal information.

      + {% unless user %}

      If you've attended any of these events, please log in instead.

      {% endunless %}
      +
      + \ No newline at end of file diff --git a/cms_content_sets/root_site/partials/convention_li.liquid b/cms_content_sets/root_site/partials/convention_li.liquid new file mode 100644 index 00000000000..c75d25209ad --- /dev/null +++ b/cms_content_sets/root_site/partials/convention_li.liquid @@ -0,0 +1,28 @@ +--- +admin_notes: '' +invariant: false +--- +{% assign startMonth = convention.starts_at | date: "%m" %} +{% assign endMonth = convention.ends_at | date: "%m" %} +{% assign startDay = convention.starts_at | date: "%d" %} +{% assign endDay = convention.ends_at | date: "%d" %} + +
      +
      {{ convention.name }}
      + {{convention.organization.name}} +
      +

      + {% if startMonth == endMonth %} + {% if startDay == endDay %} + {{ convention.starts_at | date: "%b %d, %Y" }} + {{ convention.starts_at | date: "%B %d, %Y" }} + {% else %} + {{ convention.starts_at | date: "%b %d" }} to {{convention.ends_at|date: "%d, %Y"}} + {{ convention.starts_at | date: "%B %d" }} to {{convention.ends_at|date: "%d, %Y"}} + {% endif %} + {% else %} + {{ convention.starts_at | date: "%b %d" }} to {{convention.ends_at|date: "%b %d, %Y"}} + {{ convention.starts_at | date: "%B %d" }} to {{convention.ends_at|date: "%B %d, %Y"}} + {% endif %} +

      +
      \ No newline at end of file diff --git a/cms_content_sets/root_site/partials/copyright.liquid b/cms_content_sets/root_site/partials/copyright.liquid new file mode 100644 index 00000000000..ad9395e62f2 --- /dev/null +++ b/cms_content_sets/root_site/partials/copyright.liquid @@ -0,0 +1,13 @@ +--- +admin_notes: '' +invariant: false +--- +{% assign current_date = "now" %} + \ No newline at end of file diff --git a/db/seeds.rb b/db/seeds.rb index 4edb1e857ee..bc5f2f69df0 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,6 @@ # # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) # Mayor.create(name: 'Emanuel', city: cities.first) + +LoadCmsContentSetService.new(convention: nil, content_set_name: "root_site").call! +RootSite.instance.update!(site_name: "Root site") diff --git a/db/structure.sql b/db/structure.sql index ccd200c0a5c..fed09025f77 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2708,7 +2708,7 @@ CREATE TABLE public.signups ( counted boolean, requested_bucket_key character varying, expires_at timestamp without time zone, - CONSTRAINT bucket_key_null_for_non_slot_occupying_states CHECK (((bucket_key IS NULL) OR ((state)::text = ANY ((ARRAY['confirmed'::character varying, 'ticket_purchase_hold'::character varying])::text[])))) + CONSTRAINT bucket_key_null_for_non_slot_occupying_states CHECK (((bucket_key IS NULL) OR ((state)::text = ANY (ARRAY[('confirmed'::character varying)::text, ('ticket_purchase_hold'::character varying)::text])))) );