- <%= image_tag user.avatar.url(:thumb), class: 'img-circle avatar-thumb' %>
+ <% if user.avatar.attached? %>
+ <%= image_tag url_for(user.avatar), class: "img-circle avatar" %>
+ <% end %>
<%= user.name %>
diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb
index 5460e9adf..99beeddda 100644
--- a/app/views/users/_form.html.erb
+++ b/app/views/users/_form.html.erb
@@ -42,10 +42,10 @@
<%= f.label :avatar, class: "block font-medium mb-1 text-gray-700 string required" %>
- <% if @user.avatar.present? %>
- <%= image_tag @user.avatar.url(:thumb),
+ <% if @user.avatar.attached? %>
+ <%= image_tag url_for(@user.avatar),
id: "avatar-preview",
- class: "w-32 h-32 rounded-full object-cover border border-gray-300 shadow-sm" %>
+ class: "w-32 h-32 rounded-full object-cover border border-gray-300 shadow-sm img-rounded avatar" %>
<% else %>
<%= image_tag "missing.png",
id: "avatar-preview",
@@ -98,6 +98,24 @@
<%= f.input :notes, as: :text, input_html: { rows: 3, class: "w-full" } %>
+
+ <%= f.fields_for :project_users do |project_user_form| %>
+ <%= render "project_user_fields",
+ f: project_user_form,
+ projects: @user.projects,
+ user: @user %>
+ <% end %>
+ <%= link_to_add_association "add role",
+ f,
+ :project_users,
+ html_options: {
+ locals: {
+ projects: @user.projects,
+ },
+ },
+ class: "smaller" %>
+
+
@@ -139,4 +157,3 @@
});
});
-
diff --git a/app/views/users/_project_user_fields.html.erb b/app/views/users/_project_user_fields.html.erb
index 8dd0f21b4..4d69083c4 100644
--- a/app/views/users/_project_user_fields.html.erb
+++ b/app/views/users/_project_user_fields.html.erb
@@ -1,13 +1,16 @@
- <%= link_to_remove_association 'remove role', f %>
+ <%= link_to_remove_association "remove role", f %>
<%= f.label :project_id %>
- <%= f.select :project_id, current_user.projects.map { |p| [p.name, p.id ] } %>
+ <%= f.select :project_id, current_user.projects.map { |p| [p.name, p.id] } %>
<%= f.label :position %>
- <%= f.select :position, ProjectUser.positions, selected: f.object.position ? ProjectUser.positions[f.object.position] : 1 %>
+ <%= f.select :position,
+ ProjectUser.positions.keys.map { |k| [k.humanize, k] },
+ selected: f.object.position.presence || "liaison" %>
-
\ No newline at end of file
+
+
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
index d859516f7..0202e2e20 100644
--- a/app/views/users/index.html.erb
+++ b/app/views/users/index.html.erb
@@ -39,7 +39,7 @@
<% if user.avatar.present? %>
- <%= image_tag user.avatar.url(:thumb), class: "w-10 h-10 rounded-full object-cover border border-gray-300 shadow-sm mx-auto" %>
+ <%= image_tag url_for(user.avatar), class: "w-10 h-10 rounded-full object-cover border border-gray-300 shadow-sm mx-auto" %>
<% else %>
<%= image_tag "missing.png", class: "w-10 h-10 rounded-full object-cover border border-dashed border-gray-300 mx-auto" %>
<% end %>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index d6ca0a21b..453489d65 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -16,163 +16,149 @@
+
+ <%= @user.name %>
+
+
+ <% if @user.avatar.attached? %>
+ <%= image_tag url_for(@user.avatar),
+ id: "avatar-preview",
+ class: "w-32 h-32 rounded-full object-cover border border-gray-300 shadow-sm img-rounded avatar" %>
+ <% else %>
+ <%= image_tag "missing.png",
+ id: "avatar-preview",
+ class: "w-32 h-32 rounded-full object-cover border border-dashed border-gray-300" %>
+ <% end %>
+
+
+ First name:
+ <%= @user.first_name %>
+
+
+ Last name:
+ <%= @user.last_name %>
+
+
+ Email:
+ <%= @user.email %>
+
+
+ Address:
+ <%= @user.address %>
+
+
+ Address2:
+ <%= @user.address2 %>
+
+
+ City:
+ <%= @user.city %>
+
+
+ City2:
+ <%= @user.city2 %>
+
+
+ State:
+ <%= @user.state %>
+
+
+ State2:
+ <%= @user.state2 %>
+
+
+ Zip:
+ <%= @user.zip %>
+
+
+ Zip2:
+ <%= @user.zip2 %>
+
+
+ Phone:
+ <%= @user.phone %>
+
+
+ Phone2:
+ <%= @user.phone2 %>
+
+
+ Phone3:
+ <%= @user.phone3 %>
+
+
+ Birthday:
+ <%= @user.birthday %>
+
+
+ Best time to call:
+ <%= @user.best_time_to_call %>
+
+
+ Comment:
+ <%= @user.comment %>
+
+
+ Notes:
+ <%= @user.notes %>
+
+
+ Inactive:
+ <%= @user.inactive %>
+
+
+ Super user:
+ <%= @user.super_user %>
+
+
+ Agency:
+ <%= @user.agency_id %>
+
+
+ Facilitator:
+ <%= @user.facilitator_id %>
+
+
+ Reset password token:
+ <%= @user.reset_password_token %>
+
+
+ Reset password sent at:
+ <%= @user.reset_password_sent_at %>
+
+
+ Remember created at:
+ <%= @user.remember_created_at %>
+
+
+ Sign in count:
+ <%= @user.sign_in_count %>
+
+
+ Current sign in at:
+ <%= @user.current_sign_in_at %>
+
+
+ Current sign in ip:
+ <%= @user.current_sign_in_ip %>
+
+
+ Last sign in at:
+ <%= @user.last_sign_in_at %>
+
+
+ Last sign in ip:
+ <%= @user.last_sign_in_ip %>
+
-
- First name:
- <%= @user.first_name %>
-
-
- Last name:
- <%= @user.last_name %>
-
-
- Email:
- <%= @user.email %>
-
-
- Address:
- <%= @user.address %>
-
-
- Address2:
- <%= @user.address2 %>
-
-
- City:
- <%= @user.city %>
-
-
- City2:
- <%= @user.city2 %>
-
-
- State:
- <%= @user.state %>
-
-
- State2:
- <%= @user.state2 %>
-
-
- Zip:
- <%= @user.zip %>
-
-
- Zip2:
- <%= @user.zip2 %>
-
-
- Phone:
- <%= @user.phone %>
-
-
- Phone2:
- <%= @user.phone2 %>
-
-
- Phone3:
- <%= @user.phone3 %>
-
-
- Birthday:
- <%= @user.birthday %>
-
-
- Best time to call:
- <%= @user.best_time_to_call %>
-
-
- Comment:
- <%= @user.comment %>
-
-
- Notes:
- <%= @user.notes %>
-
-
- Confirmed:
- <%= @user.confirmed %>
-
-
- Inactive:
- <%= @user.inactive %>
-
-
- Legacy:
- <%= @user.legacy %>
-
-
- Legacy:
- <%= @user.legacy_id %>
-
-
- Super user:
- <%= @user.super_user %>
-
-
- Agency:
- <%= @user.agency_id %>
-
-
- Facilitator:
- <%= @user.facilitator_id %>
-
-
- Reset password token:
- <%= @user.reset_password_token %>
-
-
- Reset password sent at:
- <%= @user.reset_password_sent_at %>
-
-
- Remember created at:
- <%= @user.remember_created_at %>
-
-
- Sign in count:
- <%= @user.sign_in_count %>
-
-
- Current sign in at:
- <%= @user.current_sign_in_at %>
-
-
- Current sign in ip:
- <%= @user.current_sign_in_ip %>
-
-
- Last sign in at:
- <%= @user.last_sign_in_at %>
-
-
- Last sign in ip:
- <%= @user.last_sign_in_ip %>
-
-
- Avatar file name:
- <%= @user.avatar_file_name %>
-
-
- Avatar content type:
- <%= @user.avatar_content_type %>
-
-
- Avatar file size:
- <%= @user.avatar_file_size %>
-
-
- Avatar updated at:
- <%= @user.avatar_updated_at %>
-
-
- Subscribecode:
- <%= @user.subscribecode %>
-
-
+
+ associated projects:
+
+ <%= @user.decorate.display_agencies %>
+
diff --git a/app/views/workshops/_flex.html.erb b/app/views/workshops/_flex.html.erb
new file mode 100644
index 000000000..001d9726e
--- /dev/null
+++ b/app/views/workshops/_flex.html.erb
@@ -0,0 +1,25 @@
+<% thumbnail_image = url_for(workshop.thumbnail_image) %>
+
+
+
+
+
+ <%= link_to workshop.title, link_route %>
+ <%= "#{workshop.windows_type.label unless workshop.windows_type.nil?} - #{workshop.date}" %>
+ Author:
+ <%= workshop.author %>
+ <%= truncate(
+ workshop.formatted_objective,
+ length: 120,
+ omission: "... ",
+ separator: " ",
+ ) %>
+ <%= link_to "Read more", link_route, target: "blank" %>
+
+
+
diff --git a/app/views/workshops/_show.html.erb b/app/views/workshops/_show.html.erb
new file mode 100644
index 000000000..fddbc830e
--- /dev/null
+++ b/app/views/workshops/_show.html.erb
@@ -0,0 +1,584 @@
+<% header_image = url_for(workshop.header_image) %>
+
+
+
+
+
+ <%= workshop.title %>
+
+
+
+ <%= "Author: #{workshop.author}" %>
+ <%= "Category: #{workshop.windows_type.label if workshop.windows_type}" %>
+ <% if sectors && sectors.any? %>
+ <%= workshop.date %>
+
+ <% sectors.each do |sector| %>
+ <%= sector.name %>
+ <% end %>
+
+ <% else %>
+ <%= "Added: #{workshop.date}" %>
+ <% end %>
+
+
+
+
+
+
+ <%= workshop.rating_as_stars %>
+
+
+
+
+ <%= workshop.led_count %>
+
+
+
+
+
+ <% if current_user.has_bookmarked_workshop?(workshop) %>
+
+ <%= link_to '', bookmark_path(@bookmark), method: :delete, class: 'btn-bookmark__unmark-layer' %>
+ <%= image_tag("bookmark-active.png", class:"img-responsive") %>
+ <%= 'Un-bookmark' %>
+
+ <% else %>
+ <%= form_for new_bookmark, url: bookmarks_path, html: { class: 'inline bookmark-inline' } do |f| %>
+ <%= f.hidden_field :bookmarkable_id %>
+ <%= f.hidden_field :bookmarkable_type %>
+
+ <%= label_tag 'addBookmark', '', class: '' %>
+ <%= image_tag("bookmark.png", class:"img-responsive") %>
+ <%= 'Bookmark' %>
+
+ <%= f.submit 'Bookmark', class: 'btn-small', id: 'addBookmark' %>
+ <% end %>
+
+ <% end %>
+
+
+ <%= link_to '', 'javascript:window.print()', class: 'print-button' %>
+ <%= image_tag("printer.png", class:"img-responsive") %>
+ <%= 'Print' %>
+
+
+
+
+ <%= link_to 'Submit Workshop Log', new_workshop_log_path(windows_type_id: workshop.windows_type_id, workshop_id: workshop.id, breadcrumb: 'Search Curriculum'), class: 'btn btn-small' %>
+
+ Workshop Variations
+
+ Related Quotes
+
+ Related Stories
+
+
+
+
+
+
+
+
+ -
+
+
+ English
+
+
+
+ <% unless workshop.extra_field.blank? %>
+
+
+
+ <%= workshop.extra_field.html_safe %>
+
+
+
+ <% end %>
+
+ <% if workshop.legacy %>
+
+
+ <%= workshop.display_objective %>
+
+
+ <% else %>
+ <% unless workshop.objective.blank? %>
+
+ Objective
+
+
+
+ <%= workshop.objective_fixed_img_urls %>
+
+
+
+ <% end %>
+
+ <% unless workshop.age_range.blank? %>
+
+ Age Range
+
+
+
+ <%= workshop.age_range.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.materials.blank? %>
+
+ Materials
+
+
+
+ <%= workshop.materials.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.optional_materials.blank? %>
+
+ Optional Materials
+
+
+
+ <%= workshop.optional_materials.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.time_frame_total == "00:00" %>
+
+ Suggested Time Frame
+
+
+
+ <% unless workshop.time_intro.to_i == 0 %>
+
+ Intro
+ <%= workshop.time_intro.to_i %> min
+
+ <% end %>
+
+ <% unless workshop.time_demonstration.to_i == 0 %>
+
+ Demonstration
+ <%= workshop.time_demonstration.to_i %> min
+
+ <% end %>
+
+ <% unless workshop.time_opening.to_i == 0 %>
+
+ Opening
+ <%= workshop.time_opening.to_i %> min
+
+ <% end %>
+
+ <% unless workshop.time_warm_up.to_i == 0 %>
+
+ Warm Up
+ <%= workshop.time_warm_up.to_i %> min
+
+ <% end %>
+
+ <% unless workshop.time_creation.to_i == 0 %>
+
+ Creation
+ <%= workshop.time_creation.to_i %> min
+
+ <% end %>
+
+ <% unless workshop.time_closing.to_i == 0 %>
+
+ Closing
+ <%= workshop.time_closing.to_i %> min
+
+ <% end %>
+
+
+ Total
+ <%= show_time(workshop.time_frame_total)%>
+
+
+
+ <% end %>
+
+ <% unless workshop.setup.blank? %>
+
+ Setup
+
+
+
+ <%= workshop.setup.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.introduction.blank? %>
+
+ Introduction
+
+
+
+ <%= workshop.introduction.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.demonstration.blank? %>
+
+ Demonstration
+
+
+
+ <%= workshop.demonstration.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.opening_circle.blank? %>
+
+ Opening
+
+
+
+ <%= workshop.opening_circle.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.warm_up.blank? %>
+
+ Warm Up
+
+
+
+ <%= workshop.warm_up.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.visualization.blank? %>
+
+ Visualization
+
+
+
+ <%= workshop.visualization.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.creation.blank? %>
+
+ Creation
+
+
+
+ <%= workshop.creation.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.closing.blank? %>
+
+ Closing
+
+
+
+ <%= workshop.closing.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.notes.blank? %>
+
+ Notes
+
+
+
+ <%= workshop.notes.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.tips.blank? %>
+
+ Tips
+
+
+
+ <%= workshop.tips.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.misc1.blank? %>
+
+ Miscellaneous
+
+
+
+ <%= workshop.misc1.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.misc2.blank? %>
+
+ Miscellaneous 2
+
+
+
+ <%= workshop.misc2.html_safe %>
+
+
+
+ <% end %>
+
+ <% end %>
+
+
+
+<% if workshop.has_spanish_fields? %>
+ -
+
+ Español
+
+
+
+ <% unless workshop.extra_field_spanish.blank? %>
+
+
+
+ <%= workshop.extra_field_spanish.html_safe %>
+
+
+
+ <% end %>
+ <% unless workshop.objective_spanish.blank? %>
+
+ Objetivo
+
+
+
+ <%= workshop.objective_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.materials_spanish.blank? %>
+
+ Materiales
+
+
+
+ <%= workshop.materials_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.optional_materials_spanish.blank? %>
+
+ Materiales Opcionales
+
+
+
+ <%= workshop.optional_materials_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.timeframe_spanish.blank? %>
+
+ Periodo de tiempo
+
+
+
+ <%= workshop.timeframe_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.age_range_spanish.blank? %>
+
+ Age Range
+
+
+
+ <%= workshop.age_range_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.setup_spanish.blank? %>
+
+ Preparativos
+
+
+
+ <%= workshop.setup_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.introduction_spanish.blank? %>
+
+ Introducción
+
+
+
+ <%= workshop.introduction_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.demonstration_spanish.blank? %>
+
+ Demostración
+
+
+
+ <%= workshop.demonstration_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.opening_circle_spanish.blank? %>
+
+ Círculo de apertura
+
+
+
+ <%= workshop.opening_circle_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.warm_up_spanish.blank? %>
+
+ Comenzando
+
+
+
+ <%= workshop.warm_up_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.visualization_spanish.blank? %>
+
+ Visualización
+
+
+
+ <%= workshop.visualization_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.creation_spanish.blank? %>
+
+ Creación
+
+
+
+ <%= workshop.creation_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.closing_spanish.blank? %>
+
+ Clausura
+
+
+
+ <%= workshop.closing_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.notes_spanish.blank? %>
+
+ Notas
+
+
+
+ <%= workshop.notes_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.tips_spanish.blank? %>
+
+ Consejos
+
+
+
+ <%= workshop.tips_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.misc1_spanish.blank? %>
+
+ Misc 1
+
+
+
+ <%= workshop.misc1_spanish.html_safe %>
+
+
+
+ <% end %>
+
+ <% unless workshop.misc2_spanish.blank? %>
+
+ Miscellaneous 2
+
+
+
+ <%= workshop.misc2_spanish.html_safe %>
+
+
+
+ <% end %>
+
+
+<% end %>
+
+
+
+
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 4b9b325ab..c31722e9a 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -30,6 +30,7 @@
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
+ Rails.application.routes.default_url_options[:host] ||= "http://localhost:3000"
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
@@ -70,4 +71,10 @@
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
+
+ # needed for codespaces
+ config.hosts << ".app.github.dev"
+
+ feature_flag_variables = File.join(Rails.root, 'config', 'feature_flag_variables.rb')
+ load(feature_flag_variables) if File.exist?(feature_flag_variables)
end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index ad8a6766b..50fee5f97 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -48,6 +48,7 @@
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
+ Rails.application.routes.default_url_options[:host] ||= "http://localhost:3000"
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
config.assume_ssl = true
diff --git a/config/environments/test.rb b/config/environments/test.rb
index dbaffb570..1752480ee 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -18,8 +18,16 @@
# Configure public file server for tests with cache-control for performance.
config.public_file_server.headers = { "cache-control" => "public, max-age=3600" }
- # Show full error reports.
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ "Cache-Control" => "public, max-age=#{1.hour.to_i}"
+ }
+
+ # Show full error reports and disable caching.
config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
config.cache_store = :null_store
# Render exception templates for rescuable exceptions and raise for other exceptions.
@@ -30,6 +38,7 @@
# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
+ Rails.application.routes.default_url_options[:host] ||= "http://localhost:3000"
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
@@ -50,4 +59,7 @@
# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true
+ # config.action_view.raise_on_missing_translations = true
+ feature_flag_variables = File.join(Rails.root, "config", "feature_flag_variables.rb")
+ load(feature_flag_variables) if File.exist?(feature_flag_variables)
end
diff --git a/config/feature_flag_variables.rb b/config/feature_flag_variables.rb
new file mode 100644
index 000000000..e69de29bb
diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb
new file mode 100644
index 000000000..ed0cc66b9
--- /dev/null
+++ b/config/initializers/active_storage.rb
@@ -0,0 +1,4 @@
+ActiveSupport.on_load(:active_storage) do
+ host = Rails.application.routes.default_url_options[:host] || "http://localhost:3000"
+ ActiveStorage::Current.url_options = { host: host }
+end
diff --git a/db/migrate/20250912130346_create_active_storage_tables.active_storage.rb b/db/migrate/20250912130346_create_active_storage_tables.active_storage.rb
new file mode 100644
index 000000000..ed3dfe6a2
--- /dev/null
+++ b/db/migrate/20250912130346_create_active_storage_tables.active_storage.rb
@@ -0,0 +1,48 @@
+# This migration comes from active_storage (originally 20170806125915)
+class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
+ def change
+ # Use Active Record's configured type for primary and foreign keys
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
+
+ create_table :active_storage_blobs, id: primary_key_type do |t|
+ t.string :key, null: false
+ t.string :filename, null: false
+ t.string :content_type
+ t.text :metadata
+ t.string :service_name, null: false
+ t.bigint :byte_size, null: false
+ t.string :checksum, null: false
+ t.datetime :created_at, null: false
+
+ t.index [ :key ], unique: true
+ end
+
+ create_table :active_storage_attachments, id: primary_key_type do |t|
+ t.string :name, null: false
+ t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
+ t.references :blob, null: false, type: foreign_key_type
+
+ t.datetime :created_at, null: false
+
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
+ t.foreign_key :active_storage_blobs, column: :blob_id
+ end
+
+ create_table :active_storage_variant_records, id: primary_key_type do |t|
+ t.belongs_to :blob, null: false, index: false, type: foreign_key_type
+ t.string :variation_digest, null: false
+
+ t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
+ t.foreign_key :active_storage_blobs, column: :blob_id
+ end
+ end
+
+ private
+ def primary_and_foreign_key_types
+ config = Rails.configuration.generators
+ setting = config.options[config.orm][:primary_key_type]
+ primary_key_type = setting || :primary_key
+ foreign_key_type = setting || :bigint
+ [primary_key_type, foreign_key_type]
+ end
+end
diff --git a/db/migrate/20250918000354_create_workshop_series_membership.rb b/db/migrate/20250918000354_create_workshop_series_membership.rb
index b70210e75..414052d4f 100644
--- a/db/migrate/20250918000354_create_workshop_series_membership.rb
+++ b/db/migrate/20250918000354_create_workshop_series_membership.rb
@@ -1,5 +1,5 @@
class CreateWorkshopSeriesMembership < ActiveRecord::Migration[8.1]
- def change
+ def up
create_table :workshop_series_memberships do |t|
t.integer :workshop_parent_id, null: false
t.integer :workshop_child_id, null: false
@@ -8,7 +8,6 @@ def change
t.integer :series_order, default: 1, null: false
t.timestamps
-
end
add_foreign_key :workshop_series_memberships, :workshops, column: :workshop_parent_id
@@ -17,4 +16,11 @@ def change
add_index :workshop_series_memberships, [:workshop_parent_id, :workshop_child_id], unique: true,
name: "index_workshop_series_memberships_on_parent_and_child"
end
+
+ def down
+ remove_foreign_key :workshop_series_memberships, column: :workshop_parent_id
+ remove_foreign_key :workshop_series_memberships, column: :workshop_child_id
+ remove_index :workshop_series_memberships, name: "index_workshop_series_memberships_on_parent_and_child"
+ drop_table :workshop_series_memberships
+ end
end
diff --git a/db/schema.rb b/db/schema.rb
index c61a55248..e3d718570 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,35 @@
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2025_10_25_164126) do
- create_table "addresses", charset: "utf8mb3", force: :cascade do |t|
+ create_table "active_storage_attachments", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
+ t.bigint "blob_id", null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.string "name", null: false
+ t.bigint "record_id", null: false
+ t.string "record_type", null: false
+ t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
+ t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
+ end
+
+ create_table "active_storage_blobs", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
+ t.string "key", null: false
+ t.string "filename", null: false
+ t.string "content_type"
+ t.text "metadata"
+ t.string "service_name", null: false
+ t.bigint "byte_size", null: false
+ t.string "checksum", null: false
+ t.datetime "created_at", null: false
+ t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
+ end
+
+ create_table "active_storage_variant_records", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
+ t.bigint "blob_id", null: false
+ t.string "variation_digest", null: false
+ t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
+ end
+
+ create_table "addresses", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "city", null: false
t.string "country"
t.string "county"
@@ -28,7 +56,7 @@
t.index ["organization_id"], name: "index_addresses_on_organization_id"
end
- create_table "admins", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "admins", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil
t.datetime "current_sign_in_at", precision: nil
t.string "current_sign_in_ip"
@@ -47,7 +75,7 @@
t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
end
- create_table "age_ranges", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "age_ranges", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "name"
t.datetime "updated_at", precision: nil, null: false
@@ -55,14 +83,14 @@
t.index ["windows_type_id"], name: "index_age_ranges_on_windows_type_id"
end
- create_table "answer_options", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "answer_options", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "name"
t.integer "order"
t.datetime "updated_at", precision: nil, null: false
end
- create_table "attachments", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "attachments", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "file_content_type"
t.string "file_file_name"
@@ -73,14 +101,14 @@
t.datetime "updated_at", precision: nil, null: false
end
- create_table "banners", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "banners", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.text "content"
t.datetime "created_at", precision: nil, null: false
t.boolean "show"
t.datetime "updated_at", precision: nil, null: false
end
- create_table "bookmark_annotations", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "bookmark_annotations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.text "annotation", size: :medium
t.integer "bookmark_id"
t.datetime "created_at", precision: nil, null: false
@@ -88,7 +116,7 @@
t.index ["bookmark_id"], name: "index_bookmark_annotations_on_bookmark_id"
end
- create_table "bookmarks", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "bookmarks", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.integer "bookmarkable_id"
t.string "bookmarkable_type"
t.datetime "created_at", precision: nil, null: false
@@ -97,7 +125,7 @@
t.index ["user_id"], name: "index_bookmarks_on_user_id"
end
- create_table "categories", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "categories", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "legacy_id"
t.integer "metadatum_id"
@@ -107,7 +135,7 @@
t.index ["metadatum_id"], name: "index_categories_on_metadatum_id"
end
- create_table "categorizable_items", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "categorizable_items", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.integer "categorizable_id"
t.string "categorizable_type"
t.integer "category_id"
@@ -119,7 +147,7 @@
t.index ["category_id"], name: "index_categorizable_items_on_category_id"
end
- create_table "ckeditor_assets", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "ckeditor_assets", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "actual_url"
t.integer "assetable_id"
t.string "assetable_type", limit: 30
@@ -135,7 +163,7 @@
t.index ["assetable_type", "type", "assetable_id"], name: "idx_ckeditor_assetable_type"
end
- create_table "event_registrations", charset: "utf8mb3", force: :cascade do |t|
+ create_table "event_registrations", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "email"
t.bigint "event_id"
@@ -145,7 +173,7 @@
t.index ["event_id"], name: "index_event_registrations_on_event_id"
end
- create_table "events", charset: "utf8mb3", force: :cascade do |t|
+ create_table "events", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", null: false
t.integer "created_by_id"
t.text "description"
@@ -158,7 +186,7 @@
t.index ["created_by_id"], name: "index_events_on_created_by_id"
end
- create_table "facilitator_organizations", charset: "utf8mb3", force: :cascade do |t|
+ create_table "facilitator_organizations", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "facilitator_id", null: false
t.bigint "organization_id", null: false
@@ -168,7 +196,7 @@
t.index ["organization_id"], name: "index_facilitator_organizations_on_organization_id"
end
- create_table "facilitators", charset: "utf8mb3", force: :cascade do |t|
+ create_table "facilitators", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "city", null: false
t.string "country", null: false
t.datetime "created_at", null: false
@@ -185,7 +213,7 @@
t.string "zip", null: false
end
- create_table "faqs", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "faqs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.text "answer", size: :medium
t.datetime "created_at", precision: nil, null: false
t.boolean "inactive"
@@ -194,7 +222,7 @@
t.datetime "updated_at", precision: nil, null: false
end
- create_table "footers", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "footers", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "adult_program"
t.string "children_program"
t.datetime "created_at", precision: nil, null: false
@@ -203,7 +231,7 @@
t.datetime "updated_at", precision: nil, null: false
end
- create_table "form_builders", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "form_builders", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.text "description", size: :medium
t.string "name"
@@ -213,7 +241,7 @@
t.index ["windows_type_id"], name: "index_form_builders_on_windows_type_id"
end
- create_table "form_field_answer_options", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "form_field_answer_options", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.integer "answer_option_id"
t.datetime "created_at", precision: nil, null: false
t.integer "form_field_id"
@@ -222,7 +250,7 @@
t.index ["form_field_id"], name: "index_form_field_answer_options_on_form_field_id"
end
- create_table "form_fields", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "form_fields", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.integer "answer_datatype"
t.integer "answer_type"
t.datetime "created_at", precision: nil, null: false
@@ -237,7 +265,7 @@
t.index ["form_id"], name: "index_form_fields_on_form_id"
end
- create_table "forms", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "forms", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "form_builder_id"
t.integer "owner_id"
@@ -246,7 +274,7 @@
t.index ["form_builder_id"], name: "index_forms_on_form_builder_id"
end
- create_table "images", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "images", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "file_content_type"
t.string "file_file_name"
@@ -259,7 +287,7 @@
t.index ["owner_id"], name: "index_images_on_owner_id"
end
- create_table "locations", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "locations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "city"
t.string "country"
t.datetime "created_at", precision: nil, null: false
@@ -267,7 +295,7 @@
t.datetime "updated_at", precision: nil, null: false
end
- create_table "media_files", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "media_files", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "file_content_type"
t.string "file_file_name"
t.integer "file_file_size"
@@ -276,7 +304,7 @@
t.integer "workshop_log_id"
end
- create_table "metadata", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "metadata", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "legacy_id"
t.string "name"
@@ -284,7 +312,7 @@
t.datetime "updated_at", precision: nil, null: false
end
- create_table "monthly_reports", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "monthly_reports", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "best_call_time"
t.boolean "call_requested"
t.text "comments", size: :medium
@@ -307,7 +335,7 @@
t.index ["project_user_id"], name: "index_monthly_reports_on_project_user_id"
end
- create_table "notifications", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "notifications", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "noticeable_id"
t.string "noticeable_type"
@@ -315,7 +343,7 @@
t.datetime "updated_at", precision: nil, null: false
end
- create_table "organizations", charset: "utf8mb3", force: :cascade do |t|
+ create_table "organizations", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "agency_type", null: false
t.string "agency_type_other"
t.date "close_date"
@@ -330,26 +358,26 @@
t.string "website_url"
end
- create_table "permissions", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "permissions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "legacy_id"
t.string "security_cat"
t.datetime "updated_at", precision: nil, null: false
end
- create_table "project_obligations", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "project_obligations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "name"
t.datetime "updated_at", precision: nil, null: false
end
- create_table "project_statuses", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "project_statuses", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "name"
t.datetime "updated_at", precision: nil, null: false
end
- create_table "project_users", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "project_users", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.integer "agency_id"
t.datetime "created_at", precision: nil, null: false
t.string "filemaker_code"
@@ -362,7 +390,7 @@
t.index ["user_id"], name: "index_project_users_on_user_id"
end
- create_table "projects", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "projects", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.text "description", size: :medium
t.string "district"
@@ -384,7 +412,7 @@
t.index ["windows_type_id"], name: "index_projects_on_windows_type_id"
end
- create_table "quotable_item_quotes", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "quotable_item_quotes", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "legacy_id"
t.integer "quotable_id"
@@ -394,7 +422,7 @@
t.index ["quote_id"], name: "index_quotable_item_quotes_on_quote_id"
end
- create_table "quotes", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "quotes", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "age"
t.datetime "created_at", precision: nil, null: false
t.string "gender", limit: 1
@@ -408,7 +436,7 @@
t.index ["workshop_id"], name: "index_quotes_on_workshop_id"
end
- create_table "report_form_field_answers", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "report_form_field_answers", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.text "answer", size: :medium
t.integer "answer_option_id"
t.datetime "created_at", precision: nil
@@ -420,7 +448,7 @@
t.index ["report_id"], name: "index_report_form_field_answers_on_report_id"
end
- create_table "reports", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "reports", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.integer "adults_first_time", default: 0
t.integer "adults_ongoing", default: 0
t.integer "children_first_time", default: 0
@@ -450,7 +478,7 @@
t.index ["windows_type_id"], name: "index_reports_on_windows_type_id"
end
- create_table "resources", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "resources", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "agency"
t.string "author"
t.datetime "created_at", precision: nil, null: false
@@ -475,7 +503,7 @@
t.index ["workshop_id"], name: "index_resources_on_workshop_id"
end
- create_table "sectorable_items", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "sectorable_items", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.boolean "inactive", default: true
t.integer "sector_id"
@@ -486,14 +514,14 @@
t.index ["sectorable_type", "sectorable_id"], name: "index_sectorable_items_on_sectorable_type_and_sectorable_id"
end
- create_table "sectors", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "sectors", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.string "name"
t.boolean "published", default: false
t.datetime "updated_at", precision: nil, null: false
end
- create_table "user_form_form_fields", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "user_form_form_fields", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "form_field_id"
t.text "text", size: :medium
@@ -503,7 +531,7 @@
t.index ["user_form_id"], name: "index_user_form_form_fields_on_user_form_id"
end
- create_table "user_forms", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "user_forms", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "form_id"
t.datetime "updated_at", precision: nil, null: false
@@ -512,7 +540,7 @@
t.index ["user_id"], name: "index_user_forms_on_user_id"
end
- create_table "user_permissions", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "user_permissions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "permission_id"
t.datetime "updated_at", precision: nil, null: false
@@ -521,7 +549,7 @@
t.index ["user_id"], name: "index_user_permissions_on_user_id"
end
- create_table "users", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "users", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "address"
t.string "address2"
t.integer "agency_id"
@@ -570,7 +598,7 @@
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
- create_table "windows_types", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "windows_types", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "legacy_id"
t.string "name"
@@ -578,7 +606,7 @@
t.datetime "updated_at", precision: nil, null: false
end
- create_table "workshop_age_ranges", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "workshop_age_ranges", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.integer "age_range_id"
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
@@ -587,7 +615,7 @@
t.index ["workshop_id"], name: "index_workshop_age_ranges_on_workshop_id"
end
- create_table "workshop_logs", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "workshop_logs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.text "challenges", size: :medium
t.text "comments", size: :medium
t.datetime "created_at", precision: nil, null: false
@@ -612,7 +640,7 @@
t.index ["workshop_id"], name: "index_workshop_logs_on_workshop_id"
end
- create_table "workshop_resources", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "workshop_resources", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.integer "resource_id"
t.datetime "updated_at", precision: nil, null: false
@@ -621,7 +649,7 @@
t.index ["workshop_id"], name: "index_workshop_resources_on_workshop_id"
end
- create_table "workshop_series_memberships", charset: "utf8mb3", force: :cascade do |t|
+ create_table "workshop_series_memberships", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "series_description"
t.string "series_description_spanish"
@@ -634,7 +662,7 @@
t.index ["workshop_parent_id", "workshop_child_id"], name: "index_workshop_series_memberships_on_parent_and_child", unique: true
end
- create_table "workshop_variations", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "workshop_variations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.text "code", size: :medium
t.datetime "created_at", precision: nil, null: false
t.integer "created_by_id"
@@ -650,7 +678,7 @@
t.index ["workshop_id"], name: "index_workshop_variations_on_workshop_id"
end
- create_table "workshops", id: :integer, charset: "utf8mb3", force: :cascade do |t|
+ create_table "workshops", id: :integer, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.text "age_range", size: :medium
t.text "age_range_spanish", size: :medium
t.string "author_location"
@@ -739,6 +767,8 @@
t.index ["year", "month"], name: "index_workshops_on_year_and_month"
end
+ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
+ add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "addresses", "organizations"
add_foreign_key "age_ranges", "windows_types"
add_foreign_key "bookmark_annotations", "bookmarks"
diff --git a/spec/models/attachment_spec.rb b/spec/models/attachment_spec.rb
index be7b9e489..d7a28537c 100644
--- a/spec/models/attachment_spec.rb
+++ b/spec/models/attachment_spec.rb
@@ -1,13 +1,13 @@
-require 'rails_helper'
+require "rails_helper"
RSpec.describe Attachment do
# pending "add some examples to (or delete) #{__FILE__}"
- describe 'associations' do
+ describe "associations" do
it { should belong_to(:owner) } # Polymorphic
end
- describe 'validations' do
+ describe "validations" do
# Paperclip matchers (might require paperclip-matchers gem and setup)
# it { should have_attached_file(:file) }
# it { should validate_attachment_content_type(:file)
@@ -17,10 +17,14 @@
# Basic presence test if needed (though Paperclip handles some)
# subject { build(:attachment, owner: create(:user)) } # Requires owner for validity
# it { should validate_presence_of(:owner) } # Testing polymorphic presence can be tricky
+
+ it { should validate_content_type_of(:file).allowing(Attachment::ACCEPTED_CONTENT_TYPES) }
+ it { should validate_content_type_of(:file).rejecting("text/plain", "text/xml") }
end
# it 'is valid with an owner' do
# # Note: Factory needs an owner to be valid for create
# expect(build(:attachment, owner: create(:user))).to be_valid
# end
-end
\ No newline at end of file
+end
+
diff --git a/spec/models/image_spec.rb b/spec/models/image_spec.rb
index d277e68ea..a566a244b 100644
--- a/spec/models/image_spec.rb
+++ b/spec/models/image_spec.rb
@@ -1,20 +1,23 @@
-require 'rails_helper'
+require "rails_helper"
RSpec.describe Image do
# pending "add some examples to (or delete) #{__FILE__}"
- describe 'associations' do
+ describe "associations" do
it { should belong_to(:owner) } # Polymorphic
it { should belong_to(:report).optional } # Assuming report can be optional
end
- describe 'validations' do
+ describe "validations" do
# Paperclip matchers (might require paperclip-matchers gem and setup)
# it { should have_attached_file(:file) }
# it { should validate_attachment_content_type(:file)
# .allowing('image/jpeg', 'image/png', 'image/gif')
# .rejecting('text/plain', 'application/pdf') }
# Presence validation for file itself is usually handled by Paperclip/ActiveStorage
+
+ it { should validate_content_type_of(:file).allowing(Image::ACCEPTED_CONTENT_TYPES) }
+ it { should validate_content_type_of(:file).rejecting("text/plain", "text/xml") }
end
# it 'is valid with valid attributes' do
@@ -22,4 +25,5 @@
# # expect(build(:image)).to be_valid
# pending("Requires functional owner/report factories and associations uncommented")
# end
-end
\ No newline at end of file
+end
+
diff --git a/spec/models/media_file_spec.rb b/spec/models/media_file_spec.rb
index dbcbba81b..db2957a3d 100644
--- a/spec/models/media_file_spec.rb
+++ b/spec/models/media_file_spec.rb
@@ -1,23 +1,23 @@
-require 'rails_helper'
+require "rails_helper"
RSpec.describe MediaFile do
# pending "add some examples to (or delete) #{__FILE__}"
- describe 'associations' do
+ describe "associations" do
it { should belong_to(:report).optional }
# it { should belong_to(:workshop).optional } # Fails: DB missing workshop_id column?
it { should belong_to(:workshop_log).optional }
end
- describe 'validations' do
- # Using shoulda matchers for Paperclip validations
- it { should have_attached_file(:file) }
- it { should validate_attachment_content_type(:file).allowing(MediaFile::FORM_FILE_CONTENT_TYPES).rejecting('text/plain', 'text/html') }
+ describe "validations" do
+ it { should validate_content_type_of(:file).allowing(MediaFile::FORM_FILE_CONTENT_TYPES) }
+ it { should validate_content_type_of(:file).rejecting("text/plain", "text/xml") }
end
- it 'is valid with valid attributes' do
+ it "is valid with valid attributes" do
report = build_stubbed(:report)
media_file = build(:media_file, report: report)
expect(media_file).to be_valid
end
-end
\ No newline at end of file
+end
+
diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb
index 51a3a5d32..a8868e0bc 100644
--- a/spec/models/report_spec.rb
+++ b/spec/models/report_spec.rb
@@ -1,8 +1,7 @@
-require 'rails_helper'
+require "rails_helper"
RSpec.describe Report do
-
- describe 'associations' do
+ describe "associations" do
it { should belong_to(:user) }
it { should belong_to(:project) }
it { should belong_to(:windows_type) }
@@ -24,17 +23,9 @@
it { should accept_nested_attributes_for(:quotable_item_quotes) }
end
- describe 'validations' do
- # Paperclip validation for form_file
- it { should have_attached_file(:form_file) }
- it { should validate_attachment_content_type(:form_file).allowing(Report::FORM_FILE_CONTENT_TYPES).rejecting('text/plain', 'text/html') }
-
- # Custom validation for image would need a more involved test
- context 'with invalid image' do
- it 'validates associated image' do
- report = build(:report, image: build(:image, :invalid_format))
- expect(report).not_to be_valid
- end
- end
+ describe "validations" do
+ it { should validate_content_type_of(:form_file).allowing(Report::FORM_FILE_CONTENT_TYPES) }
+ it { should validate_content_type_of(:form_file).rejecting("text/plain", "text/xml") }
end
-end
\ No newline at end of file
+end
+
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index b261b76de..e9857bc43 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1,4 +1,4 @@
-require 'rails_helper'
+require "rails_helper"
RSpec.describe User do
# Use FactoryBot
@@ -10,11 +10,11 @@
create(:permission, :combined)
end
- describe 'associations' do
+ describe "associations" do
# Need create for association tests to work correctly with callbacks
subject { create(:user) }
- it { should belong_to(:facilitator).optional}
+ it { should belong_to(:facilitator).optional }
it { should have_many(:workshops) }
it { should have_many(:workshop_logs) }
it { should have_many(:reports) }
@@ -44,7 +44,7 @@
it { should accept_nested_attributes_for(:project_users).allow_destroy(true) }
end
- describe 'validations' do
+ describe "validations" do
# Devise validations (presence tested manually below, uniqueness tested with subject)
subject { create(:user) } # Use create for uniqueness tests
it { should validate_uniqueness_of(:email).case_insensitive }
@@ -52,17 +52,17 @@
# Manual presence tests (using build is fine here)
let(:user) { build(:user) }
- it 'is valid with valid attributes' do
+ it "is valid with valid attributes" do
expect(user).to be_valid
end
- it 'is invalid without an email' do
+ it "is invalid without an email" do
user.email = nil
expect(user).not_to be_valid
expect(user.errors[:email]).to include("can't be blank")
end
- it 'is invalid without a password' do
+ it "is invalid without a password" do
user.password = nil
expect(user).not_to be_valid
expect(user.errors[:password]).to include("can't be blank")
@@ -72,8 +72,9 @@
# it { should validate_presence_of(:first_name) }
# it { should validate_presence_of(:last_name) }
- # Paperclip avatar validations
- # it { should validate_attachment_content_type(:avatar).allowing('image/png', 'image/jpeg', 'image/gif').rejecting('text/plain', 'application/pdf') }
+ # Avatar validations
+ it { should validate_content_type_of(:avatar).allowing(User::ACCEPTED_CONTENT_TYPES) }
+ it { should validate_content_type_of(:avatar).rejecting("text/plain", "text/xml") }
end
describe "bookmark_associations" do
@@ -113,26 +114,26 @@
end
end
- describe '#full_name' do
+ describe "#full_name" do
# These tests remain relevant
let(:user) { build(:user) }
- context 'when first_name is present' do
- it 'returns the full name' do
+ context "when first_name is present" do
+ it "returns the full name" do
user.first_name = "John"
user.last_name = "Doe"
expect(user.full_name).to eq("John Doe")
end
end
- context 'when first_name is nil' do
- it 'returns the email' do
+ context "when first_name is nil" do
+ it "returns the email" do
user.first_name = nil
expect(user.full_name).to eq(user.email)
end
end
- context 'when first_name is empty' do
- it 'returns the email' do
+ context "when first_name is empty" do
+ it "returns the email" do
user.first_name = ""
expect(user.full_name).to eq(user.email)
end
diff --git a/spec/models/workshop_spec.rb b/spec/models/workshop_spec.rb
index 4c7898c7c..71ea2f59b 100644
--- a/spec/models/workshop_spec.rb
+++ b/spec/models/workshop_spec.rb
@@ -1,4 +1,4 @@
-require 'rails_helper'
+require "rails_helper"
RSpec.describe Workshop do
# pending "add some examples to (or delete) #{__FILE__}"
@@ -8,7 +8,7 @@
create(:permission, :combined)
end
- describe 'associations' do
+ describe "associations" do
# Need create for association tests to work correctly with callbacks/scopes
subject { create(:workshop) } # Assumes functional factory
@@ -45,7 +45,7 @@
# it { should have_attached_file(:header) }
end
- describe 'validations' do
+ describe "validations" do
# Requires associations for create
subject { build(:workshop, user: create(:user), windows_type: create(:windows_type)) }
@@ -56,24 +56,31 @@
# it { should validate_attachment_content_type(:thumbnail).allowing('image/png', 'image/jpeg', 'image/gif') }
# it { should validate_attachment_content_type(:header).allowing('image/png', 'image/jpeg', 'image/gif') }
+ it { should validate_content_type_of(:header).allowing(Workshop::ACCEPTED_CONTENT_TYPES) }
+ it { should validate_content_type_of(:header).rejecting("text/plain", "text/xml") }
+
+ it { should validate_content_type_of(:thumbnail).allowing(Workshop::ACCEPTED_CONTENT_TYPES) }
+ it { should validate_content_type_of(:thumbnail).rejecting("text/plain", "text/xml") }
+
# Conditional presence validation for legacy workshops (month, year)
- context 'when legacy is true' do
+ context "when legacy is true" do
before { allow(subject).to receive(:legacy).and_return(true) }
# Cannot easily test conditional validation with shoulda-matchers, test manually
# it { should validate_presence_of(:month) }
# it { should validate_presence_of(:year) }
end
- context 'when legacy is false' do
+ context "when legacy is false" do
before { allow(subject).to receive(:legacy).and_return(false) }
# it { should_not validate_presence_of(:month) }
# it { should_not validate_presence_of(:year) }
end
end
- it 'is valid with valid attributes' do
+ it "is valid with valid attributes" do
# Note: Factory needs associations uncommented for create
# expect(build(:workshop)).to be_valid
end
# Add tests for scopes, methods like #rating, #log_count, SearchCop etc.
-end
\ No newline at end of file
+end
+
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 7b4460273..bc5f09919 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -13,6 +13,8 @@
# it.
#
# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+require "active_storage_validations/matchers"
+
if ENV["CI"]
require "simplecov"
require "simplecov_json_formatter"
@@ -27,7 +29,7 @@
end
SimpleCov.at_exit do
SimpleCov.result.format!
- File.write("coverage/summary.json", JSON.dump({ covered_percent: SimpleCov.result.covered_percent }))
+ File.write("coverage/summary.json", JSON.dump({covered_percent: SimpleCov.result.covered_percent}))
end
SimpleCov.start "rails" do
enable_coverage :branch
@@ -114,4 +116,6 @@
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
+
+ config.include ActiveStorageValidations::Matchers
end
diff --git a/spec/system/login_spec.rb b/spec/system/login_spec.rb
index 3f8503d2d..861c02c11 100644
--- a/spec/system/login_spec.rb
+++ b/spec/system/login_spec.rb
@@ -28,6 +28,5 @@
File.write("tmp/capybara-page.html", page.html)
expect(page).to have_css("#avatar", wait: 5)
- expect(page).to have_css("img[src*='missing.png']")
end
end
diff --git a/spec/views/users/new.html.erb_spec.rb b/spec/views/users/new.html.erb_spec.rb
index 624cd8d21..42bdc9ae0 100644
--- a/spec/views/users/new.html.erb_spec.rb
+++ b/spec/views/users/new.html.erb_spec.rb
@@ -1,35 +1,13 @@
+# spec/views/users/new.html.erb_spec.rb
require 'rails_helper'
-RSpec.describe "users/new", type: :view do
- before(:each) do
- assign(:user, User.new(
- first_name: "MyString",
- last_name: "MyString",
- email: "MyString@example.com",
- password: "MyString",
- password_confirmation: "MyString",
- address: "MyString",
- address2: "MyString",
- city: "MyString",
- city2: "MyString",
- state: "MyString",
- state2: "MyString",
- zip: "MyString",
- zip2: "MyString",
- phone: "MyString",
- phone2: "MyString",
- phone3: "MyString",
- best_time_to_call: "MyString",
- comment: "MyText",
- notes: "MyText",
- inactive: false,
- legacy: false,
- super_user: false,
- sign_in_count: 1,
- current_sign_in_ip: "MyString",
- last_sign_in_ip: "MyString",
- subscribecode: "MyString"
- ))
+RSpec.describe "users/new.html.erb", type: :view do
+ let(:user) { build_stubbed(:user) }
+
+ before do
+ allow(view).to receive(:current_user).and_return(user)
+ assign(:user, User.new)
+ render
end
it "renders new user form" do
|