Allow all facilitators + free-text story author (#1514)#1552
Conversation
The new/edit story form restricted the author dropdown to active, confirmed users, so facilitators who were inactive, never entered in the CMS, or only known by first name could not be credited. This mirrors the earlier fix to the spotlighted-facilitator dropdown. - Author dropdown now lists all users (drops the has_access filter), matching how the spotlighted-facilitator dropdown lists all people. - Add an optional free-text "author name" that overrides the dropdown and credit preference, for facilitators not in the CMS. - When a free-text author is given without selecting a user, default created_by to the current admin so the required association is set. - Suppress the author profile link on the show page when a free-text name is used so it doesn't link to the admin creator. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| @story = Story.new(story_params.except(:category_ids, :sector_ids)) | ||
| # A free-text author name covers facilitators who aren't in the CMS, so an | ||
| # author User may not be selected. Fall back to the current admin as creator. | ||
| @story.created_by_id ||= current_user.id |
There was a problem hiding this comment.
Free-text authors may not be in the CMS, so no author User is selected. We default created_by to the current admin here to satisfy the required association while author_name carries the display credit.
| }.freeze | ||
|
|
||
| def author_credit | ||
| free_text = try(:author_name) |
There was a problem hiding this comment.
try(:author_name) so the shared concern keeps working for StoryIdea, which has no author_name column. Free text intentionally overrides the credit preference.
| <div class="text-sm text-gray-600 space-y-0.5"> | ||
| <p><strong>Story by:</strong> | ||
| <% if @story.created_by.person&.profile_is_searchable %> | ||
| <% if @story.author_name.blank? && @story.created_by.person&.profile_is_searchable %> |
There was a problem hiding this comment.
Suppress the profile link for free-text authors — otherwise it would link to the admin creator rather than the credited facilitator.
| .order(:created_at) | ||
| @people = Person.order(Arel.sql("LOWER(first_name), LOWER(last_name)")) | ||
| @users = User.has_access.includes(:person).left_joins(:person).order(Arel.sql("people.first_name IS NULL, LOWER(people.first_name), LOWER(people.last_name), LOWER(users.email)")) | ||
| @users = User.includes(:person).left_joins(:person).order(Arel.sql("people.first_name IS NULL, LOWER(people.first_name), LOWER(people.last_name), LOWER(users.email)")) |
There was a problem hiding this comment.
Dropped has_access so inactive/unconfirmed facilitators are selectable, mirroring how the spotlighted-facilitator dropdown lists all people.
There was a problem hiding this comment.
Pull request overview
This PR expands story author attribution to support (1) selecting any facilitator/user (including inactive/unconfirmed) and (2) entering a free-text author name when the facilitator isn’t in the CMS, while ensuring the show page displays the correct credit and avoids linking to the wrong profile.
Changes:
- Adds
stories.author_name(nullable) and a corresponding free-text input on the story form. - Removes the
User.has_accessfilter so the story author dropdown includes inactive facilitators. - Updates author credit display logic and creation defaults (
created_by_id) to support free-text author attribution.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/requests/stories_spec.rb | Adds request coverage for free-text author creation and inactive facilitators appearing in the author dropdown. |
| spec/models/story_spec.rb | Adds model coverage ensuring author_name overrides author credit behavior when present. |
| db/schema.rb | Captures schema version bump and new stories.author_name column. |
| db/migrate/20260604120000_add_author_name_to_stories.rb | Adds reversible migration for stories.author_name. |
| app/views/stories/show.html.erb | Suppresses profile link when free-text author name is used. |
| app/views/stories/_form.html.erb | Adds free-text author input and keeps author dropdown for created_by_id. |
| app/models/concerns/author_creditable.rb | Updates author_credit to prefer free-text author name when present. |
| app/controllers/stories_controller.rb | Defaults created_by_id on create when no dropdown author is selected; permits author_name; broadens dropdown user scope. |
| def author_credit | ||
| free_text = try(:author_name) | ||
| return free_text if free_text.present? | ||
| person = created_by&.person |
| <% if @story.author_name.blank? && @story.created_by.person&.profile_is_searchable %> | ||
| <%= link_to @story.author_credit, person_path(@story.created_by) %> | ||
| <% else %> |
|
I'm still having a hard time with author being a |
Closes #1514
What is the goal of this PR and why is this important?
User.has_access), so facilitators who were inactive, never entered in the CMS, or only known by a first name could not be credited as the story author.How did you approach the change?
has_accessfilter from@usersinStoriesController#set_form_variables, so the author dropdown lists all users — matching how the spotlighted-facilitator dropdown lists allPersonrecords.author_namecolumn (migration) plus an "Or type an author name" text input on the form, for facilitators not in the CMS or where only a first name is known.AuthorCreditable#author_creditnow returnsauthor_namewhen present, overriding thecreated_byperson and the credit preference. The concern usestry(:author_name), soStoryIdea(no such column) is unaffected.createdefaultscreated_byto the current admin so the requiredcreated_by_idis satisfied.UI Testing Checklist
Anything else to add?
author_creditoverride; request specs for free-text create (defaultingcreated_by) and inactive facilitators appearing in the dropdown. Fullstoriesmodel + request specs pass.db/schema.rbchange is limited to the new column + version bump (unrelated local dev-DB drift was excluded).🤖 Generated with Claude Code