studio-engine 0.47.2 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +204 -0
- data/app/controllers/studio/onboarding_controller.rb +5 -1
- data/app/controllers/studio/profiles_controller.rb +170 -0
- data/app/models/concerns/studio/user_profile.rb +111 -0
- data/app/views/components/_user_nav.html.erb +40 -6
- data/app/views/studio/profiles/_avatar_section.html.erb +93 -0
- data/app/views/studio/profiles/_first_name_section.html.erb +26 -0
- data/app/views/studio/profiles/_google_section.html.erb +56 -0
- data/app/views/studio/profiles/show.html.erb +73 -0
- data/lib/studio/oauth_identity.rb +95 -0
- data/lib/studio/profile_image.rb +43 -0
- data/lib/studio/profile_sections.rb +153 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +89 -0
- metadata +11 -2
data/lib/studio.rb
CHANGED
|
@@ -7,6 +7,9 @@ require "studio/environment_banner"
|
|
|
7
7
|
require "studio/theme_resolver"
|
|
8
8
|
require "studio/ui_primitives"
|
|
9
9
|
require "studio/sidebar_sections"
|
|
10
|
+
require "studio/profile_sections"
|
|
11
|
+
require "studio/profile_image"
|
|
12
|
+
require "studio/oauth_identity"
|
|
10
13
|
require "studio/username_generator"
|
|
11
14
|
require "studio/s3"
|
|
12
15
|
require "studio/image_cache"
|
|
@@ -83,6 +86,49 @@ module Studio
|
|
|
83
86
|
# end
|
|
84
87
|
mattr_accessor :sidebar_sections, default: []
|
|
85
88
|
|
|
89
|
+
# ---- The shared profile page (/profile) ----
|
|
90
|
+
# The rows that make up /profile. `nil` — the default — means "the engine's
|
|
91
|
+
# standard page", NOT "no rows": a brand-new app with an empty initializer gets
|
|
92
|
+
# a working profile page, which is the entire point of standardizing it.
|
|
93
|
+
#
|
|
94
|
+
# Declare an Array (or a callable receiving the view) to customize. Compose
|
|
95
|
+
# against Studio.default_profile_sections rather than a literal, so a later
|
|
96
|
+
# engine release that adds a standard row delivers it to you:
|
|
97
|
+
#
|
|
98
|
+
# Studio.configure do |config|
|
|
99
|
+
# config.profile_sections = ->(view) {
|
|
100
|
+
# Studio.default_profile_sections +
|
|
101
|
+
# [ { key: :wallet, title: "Identities", partial: "profiles/wallet" } ]
|
|
102
|
+
# }
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
# Drop a standard row by key instead of restating the list:
|
|
106
|
+
#
|
|
107
|
+
# config.profile_sections = Studio.default_profile_sections.reject { |s| s[:key] == :avatar }
|
|
108
|
+
#
|
|
109
|
+
# Shape and resolution rules: lib/studio/profile_sections.rb. A row may declare
|
|
110
|
+
# `requires:` — an attribute the current user must respond to — and is dropped
|
|
111
|
+
# when this host's model does not have it, so the page tolerates the fact that
|
|
112
|
+
# the consuming apps' users tables genuinely disagree.
|
|
113
|
+
mattr_accessor :profile_sections, default: nil
|
|
114
|
+
|
|
115
|
+
# Whether Studio.routes draws /profile (Studio::ProfilesController).
|
|
116
|
+
#
|
|
117
|
+
# ON BY DEFAULT, and it is worth saying why this one can be when
|
|
118
|
+
# draw_admin_emails_routes and draw_onboarding_routes could not: those two
|
|
119
|
+
# claimed helper names a consumer ALREADY OWNED, so drawing them raised
|
|
120
|
+
# `Invalid route name, already in use` while that app's routes.rb loaded and
|
|
121
|
+
# took down its entire route set. `profile` is claimed by none of the five
|
|
122
|
+
# consumers — mcritchie-studio, mcritchie-industries, turf-monster, moms-app
|
|
123
|
+
# and acquisition-studio were each checked (2026-08-14) — and turf-monster's
|
|
124
|
+
# nearest names are complete_profile_account_path / save_profile_account_path,
|
|
125
|
+
# which do not collide.
|
|
126
|
+
#
|
|
127
|
+
# That is exactly why the shared page is /profile and not /account: turf owns
|
|
128
|
+
# account_path, and a shared /account could never be default-on. An app that
|
|
129
|
+
# wants the page gone still sets this false.
|
|
130
|
+
mattr_accessor :draw_profile_routes, default: true
|
|
131
|
+
|
|
86
132
|
# How long a freshly minted magic link stays live.
|
|
87
133
|
mattr_accessor :magic_link_ttl, default: 15.minutes
|
|
88
134
|
|
|
@@ -179,6 +225,17 @@ module Studio
|
|
|
179
225
|
# later session may ask again. That is the whole reason this is not a column.
|
|
180
226
|
FIRST_NAME_SKIP_SESSION_KEY = :onboarding_skipped_first_name
|
|
181
227
|
|
|
228
|
+
# How long a first name may be. ONE constant because users.first_name is
|
|
229
|
+
# written from TWO surfaces — the onboarding step (seconds after signup) and
|
|
230
|
+
# /profile (any time after) — and rendered by a third, the profile form's
|
|
231
|
+
# maxlength. Two independently-correct caps that disagreed would let onboarding
|
|
232
|
+
# accept a name /profile then refused to save, a bug with no obvious owner.
|
|
233
|
+
#
|
|
234
|
+
# Keeping it here rather than on either controller also keeps the VIEW off a
|
|
235
|
+
# controller constant: the form needs the number, and a view reaching into
|
|
236
|
+
# Studio::ProfilesController to get it would couple the two for no reason.
|
|
237
|
+
FIRST_NAME_MAX_LENGTH = 40
|
|
238
|
+
|
|
182
239
|
# The shared rule for "does this account still owe us a first name?" — the one
|
|
183
240
|
# piece of onboarding logic every app agrees on. Hosts compose it into their own
|
|
184
241
|
# flow rather than re-deriving it (turf's OnboardingFlow calls straight through).
|
|
@@ -545,6 +602,21 @@ module Studio
|
|
|
545
602
|
SidebarSections.resolve(sidebar_sections, view)
|
|
546
603
|
end
|
|
547
604
|
|
|
605
|
+
# The engine's standard /profile rows. Hosts compose against this rather than
|
|
606
|
+
# restating a literal list, so a later release that adds a standard row
|
|
607
|
+
# delivers it to every app that used the seam as intended.
|
|
608
|
+
def self.default_profile_sections
|
|
609
|
+
ProfileSections.defaults
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
# Profile rows resolved for a view context: a callable config is called with
|
|
613
|
+
# the view, keys symbolize, admin-only rows drop for non-admin viewers, and
|
|
614
|
+
# rows this host's user model cannot serve drop entirely. `nil` config resolves
|
|
615
|
+
# to the standard page.
|
|
616
|
+
def self.profile_sections_for(view)
|
|
617
|
+
ProfileSections.resolve(profile_sections, view)
|
|
618
|
+
end
|
|
619
|
+
|
|
548
620
|
def self.env_truthy?(value)
|
|
549
621
|
%w[1 true yes on].include?(value.to_s.strip.downcase)
|
|
550
622
|
end
|
|
@@ -602,6 +674,23 @@ module Studio
|
|
|
602
674
|
post "auth/solana/verify", to: "solana_sessions#verify", as: :solana_verify
|
|
603
675
|
end
|
|
604
676
|
|
|
677
|
+
# The shared profile page. ON by default — unlike /admin/emails and the
|
|
678
|
+
# onboarding pair, `profile` is claimed by no consumer, so drawing it
|
|
679
|
+
# cannot take an app's route set down. See Studio.draw_profile_routes.
|
|
680
|
+
#
|
|
681
|
+
# Avatar is its own PATCH rather than a field on #update: an attachment
|
|
682
|
+
# param submitted empty PURGES the attachment, so a single form carrying
|
|
683
|
+
# both would delete someone's photo every time they edited their name.
|
|
684
|
+
if Studio.draw_profile_routes
|
|
685
|
+
get "profile", to: "studio/profiles#show", as: :profile
|
|
686
|
+
patch "profile", to: "studio/profiles#update"
|
|
687
|
+
patch "profile/avatar", to: "studio/profiles#avatar", as: :profile_avatar
|
|
688
|
+
# DELETE, because unlinking removes an identity. Linking is not drawn
|
|
689
|
+
# here: it is OmniAuth's own /auth/:provider, which the middleware owns.
|
|
690
|
+
delete "profile/google", to: "studio/profiles#unlink_google",
|
|
691
|
+
as: :profile_unlink_google
|
|
692
|
+
end
|
|
693
|
+
|
|
605
694
|
resources :error_logs, only: [:index, :show]
|
|
606
695
|
|
|
607
696
|
# Admin
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.48.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -204,6 +204,7 @@ files:
|
|
|
204
204
|
- app/controllers/studio/local_reviews_controller.rb
|
|
205
205
|
- app/controllers/studio/models_controller.rb
|
|
206
206
|
- app/controllers/studio/onboarding_controller.rb
|
|
207
|
+
- app/controllers/studio/profiles_controller.rb
|
|
207
208
|
- app/controllers/style_controller.rb
|
|
208
209
|
- app/controllers/theme_settings_controller.rb
|
|
209
210
|
- app/helpers/studio/admin_models_table_helper.rb
|
|
@@ -219,6 +220,7 @@ files:
|
|
|
219
220
|
- app/models/concerns/sluggable.rb
|
|
220
221
|
- app/models/concerns/studio/board/rankable.rb
|
|
221
222
|
- app/models/concerns/studio/broadcastable.rb
|
|
223
|
+
- app/models/concerns/studio/user_profile.rb
|
|
222
224
|
- app/models/current.rb
|
|
223
225
|
- app/models/error_log.rb
|
|
224
226
|
- app/models/image_cache.rb
|
|
@@ -333,6 +335,10 @@ files:
|
|
|
333
335
|
- app/views/studio/models/show.html.erb
|
|
334
336
|
- app/views/studio/newsletter_mailer/subscribed.html.erb
|
|
335
337
|
- app/views/studio/newsletter_mailer/subscribed.text.erb
|
|
338
|
+
- app/views/studio/profiles/_avatar_section.html.erb
|
|
339
|
+
- app/views/studio/profiles/_first_name_section.html.erb
|
|
340
|
+
- app/views/studio/profiles/_google_section.html.erb
|
|
341
|
+
- app/views/studio/profiles/show.html.erb
|
|
336
342
|
- app/views/style/_modal_specimen.html.erb
|
|
337
343
|
- app/views/style/_modals.html.erb
|
|
338
344
|
- app/views/style/_specimen.html.erb
|
|
@@ -373,6 +379,9 @@ files:
|
|
|
373
379
|
- lib/studio/link_token.rb
|
|
374
380
|
- lib/studio/log_rotation.rb
|
|
375
381
|
- lib/studio/mail_transport.rb
|
|
382
|
+
- lib/studio/oauth_identity.rb
|
|
383
|
+
- lib/studio/profile_image.rb
|
|
384
|
+
- lib/studio/profile_sections.rb
|
|
376
385
|
- lib/studio/redis.rb
|
|
377
386
|
- lib/studio/s3.rb
|
|
378
387
|
- lib/studio/sidebar_sections.rb
|