* transliterate when generating attribute names to avoid emptiness
If you enter a non-ascii (non `\w` matching) string as the profile
field label, the attribute name is the empty string.
This causes problems outlined in #16391
To avoid losing user provided data, transliterate (Sterile is the same
tool we're using for Article#title_to_slug) before matching against
the word regex.
If we persist an empty string, or persist a non-`\w` name, the
coordinating regex in Profile::ATTRIBUTE_NAME_REGEX will leave a nil
match and raise NoMethodError (the method missing is for
match[:attribute_name] when match was nil, not Profile - that's the
next commit.
* Guard against nil matches
If an attribute name doesn't match the regex, the match is nil, and
trying to access (nil)[:attribute_name] raises a NoMethodError.
If there was no match, assume profile does not respond to the
selector, and don't handle it in method missing.
* Ensure generated attribute name is valid before saving
This raises a validation error if the generated name (from the label)
would be empty.
It's not an optimal error message (since the user can't see the
internal attribute name) but it prevents persisting broken/empty data
* actually raise error when validating
validate/valid? only return true or false (and set errors on the
object). In order to reject the creation, we need to raise a
validation error, not only call validate. I think this is because the
execution of before_create hooks happens after validation (which is
why the validation was only checked on update, not create).
* Generate an attribute name completely independent of the field label
This prevents mistakenly labeling a field "Class" or "Association" or
one of the other hundred public methods an AR model like Profile
exposes. Since attribute_name will be passed to `profile.public_send`
we really shouldn't build selectors from user supplied inputs.
* Use the admin supplied label in the sidebar
The profile decorator is used in the Users#show page to populate the
sidebar fields. Don't use the attribute name (which we mangled during
creation, and now generate randomly) as the label, use the label.
* Make the label lookup null safe, and filter attributes more
* Update data update script to not expect predictable labels
This is low impact since it ran in july, but we no longer know what
attribute name a label will create.
* Fix moderator spec
"Test Field" label no longer predictably generates :test_field as an
attribute name. Ask the field what it's name is before asking profile
about it.
* Fix profile preview card request spec
Remove the assumption that profile responds to a method name based on
the label for Work and Education fields.
* Update old DUS and its test
This can probably be archived
* Update profile spec to use fields generated attribute names
We used to "know" how attributes were generated from labels. Now we don't.
* update e2e seeds for profile field change
* Don't expect attribute name to be based on the label
* expect created profile fields respond to their attribute name
* Update system test
The label, not the attribute name, is shown on the profile form (the
field has an id related to the attribute name, but the view shows the
mutable/human-readable label).
* Keep the field title lowercase when sending the json preview card
The userMetadata component expects "work" and "education" to be
attributes of the metadata, but the ui_attributes_for() method was
titlizing these (for display).
Ideally we wouldn't have "special purposed" these two field names, but
they're there.
* update profile field by attribute name, not based on label
* Update profile field removal assumptions
We don't know what the method selectors will be, we have to ask.
* remove old test
* Update translations for Education and Work
Since the ui_attributes_for(area:) now gives the label, not the
attribute, we need to match the label of the profile field.
Note to self: this exposes an issue in localizing the custom profile
fields (probably a bigger problem for large, international communities
like DEV than some others, but trying to match static translation
files against user-modifiable database records seems like a problem
we'll see again).
* Empty commit to retrigger buildkite
* Remove profile field migration update scripts
Cloned the specs from the other "remove unused scripts" script.
* Remove unused scripts
The data update script removes the entry from the table (recording
that these have run) - we also want to remove the files (preventing
them from running again).
* remove unneeded spec for removed file
* when translation for header area field not found, use the title
Only Work and Education already have keys in the yml translation file,
and there's not a great (or easy?) way to make multiple translations
on these fields right now.
Since an admin can create a new field, and assign it to the header
area, we can't assume the code has a configured translation key for
this field.
Fallback to the title (we do this in another context already) if
there's no translation.
* PR feedback: Avoid n+1 query for labels
the original implementation of "label_for_attribute" had an n+1 query
looping over each matched key.
Follow suggested improvement and pull labels and attributes at once
from the db and modify the returned hash.
* Downcase title before looking for translation key
This avoids putting "odd" capitalized keys into the yml translation
file
Revert addition of "Work" and "Education" to the users files.
* use a let binding for duplicated test data
* Update app/models/profile_field.rb
prefer SecureRandom.hex for a dashless uuid (instead of removing the dashes).
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
* Ensuring confirmation and flash message for spaces
This change delivers an accessible form, and while it doesn't remove the
Save button nor produce a modal it does deliver the minimum viable
functionality.
In conversations with Suzanne, we spent an hour looking at how we might
make an accessible modal with the checkbox and state management
required as we as removing save buttons.
There are two paths:
1. Extend the current, yet deprecated, stimulus modal controller.
2. Extend the Preact modal work done for the Members Detail View.
We spent about 15 minutes pursuing the stimulus modal controller route,
and realized how much cruft it added to the system. Not ideal and very
opaque in it's interaction.
The second one we talked about, but for our alloted time was inadequate
to begin further work. To deliver on that generalized preact modal will
require at least an afternoon of work.
Closesforem/forem#17032
* Update config/locales/controllers/admin/fr.yml
Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
* Update app/views/admin/spaces/index.html.erb
Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
While reviewing the authorization system, I saw a curious instance
variable and wanted to know more about it. Turns out, it's no longer
referenced.
`$ rg "on_comments_page"` exits status 1 (e.g. no matches)
Prior to this commit, when we change attributes for a user we might
resave each article. The purpose of the resave is to update some of the
cached attributes of an article.
This removes some redundant code. The following removed code has
an `article.save` call. Which, the callbacks in the article (see below)
already clear the cache.
```ruby
def resave_articles
articles.find_each do |article|
if article.path
cache_bust = EdgeCache::Bust.new
cache_bust.call(article.path)
cache_bust.call("#{article.path}?i=i")
end
article.save
end
end
```
8e6981aac5/app/models/article.rb (L164)8e6981aac5/app/models/article.rb (L881-L888)
This was discovered in triaging forem/forem#17041
There are three major things occurring in this pull request:
1. Renaming `Article#update_cached_user` to `Article#set_cached_entities`.
2. Reducing an organization's direct knowledge of which of the org's
attributes an article caches.
3. Removing duplicate calls to update the article associated with the
organization.
For renaming to `Article#set_cached_entities`, the prior method implied
we were updating the persistence layer. However, we were not making any
save nor update calls. This rename should clarify intention.
For reducing knowledge, the comments for
`Article::ATTRIBUTES_CACHED_FOR_RELATED_ENTITY` should explain the details.
And last, removing the duplicate calls; we had three methods that were
attempting to build and update the `Article#cached_organization`'s
value.
Closesforem/forem#17041
* Added a way to sort comments on an article
* Sorting of comments now uses crayons-dropwn. Some minor fixes as per comments on PR
* Add and fix test cases for sorting comments functionality
* Changes in code for sort comments. Code is more aligned with forem's conventions
* Added cyperss tests for sort comments on an article. Cleaned up code to better follow forem conventions
* Get fresh handle of triggerButton everytime clickOustideListener is clicked. Fix Cypress test cases to reflect the earlier.
Prior to this commit, we determined the 404 status based on the final
scoped query of the articles. That scoped query would limit to a
timeframe.
However, what we want is to not render a 404 status if we have an
"established" tag. An established tag means that we have at least one
published article (regardless of when we published).
Fixesforem/forem#16932
* Award badges for github commits with various milestones
* Fixes on award badges to github commits
* Add data update script to add badges
* Disable award_multi_commit_contributors
* Revert "Disable award_multi_commit_contributors"
This reverts commit 1852f34fd45bd23b716cf80ebbd9ce2c1879a819.
* Remove DUS
* Change badge name
* Only use award_contributors
* Simplify specs
* Revert "Simplify specs"
This reverts commit 230514ccea68445b057991d3d536fbb2a590a3b1.
* Revert "Only use award_contributors"
This reverts commit a92735a079f43610aedc805f74f032466d23f1cf.
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
There's an existing data update to introduce the connect feature flag,
and it's been disabled. However, since all the code relying on this is
now gone, it makes sense to remove the flag.
* Fixed hiding 'Click to edit' link for drafts
* Improve "click to edit" link spec
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
* Make the expectation more clear
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
* Adding some docs for ApplicationHelper
This is the happy byproduct of my waiting for Heroku's git server to
come back online.
What follows is my attempt to provide inline documentation for
application helper methods. I haven't done all of them, but this is my
effort to inch forward the state of our internal documents.
Along the way, I've discovered a discrepency (and noted it but am not
going to proceed with any refactor).
* Update app/helpers/application_helper.rb
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
In helping track down forem/forem#17041 I was looking at our cache
busting logic. We were looking up a constant via the `.const_get` call
from a string already defined inline. This refactor short-circuits
naming a string, capitalizing it, then looking in the class's registered
constants.
There's also a subtle bug in the original; When `provider` equals
"another_cache", the `#capitalize` method would return
`"Another_cache"`, whereas `#classify` will return `"AnotherCache"`.
Below are some benchmarks for the change.
```ruby
require "benchmark"
module EdgeCache
class Bust
def by_class
Fastly
end
def by_const_get
self.class.const_get("fastly".classify)
end
end
end
Benchmark.bmbm do |x|
x.report("by_class") do
1000.times do
EdgeCache::Bust.new.by_class
end
end
x.report("by_const_get") do
1000.times do
EdgeCache::Bust.new.by_const_get
end
end
end
```
```shell
> bin/rails runner /Users/jfriesen/git/forem/bench.rb
Rehearsal ------------------------------------------------
by_class 0.001239 0.000186 0.001425 ( 0.001342)
by_const_get 0.011398 0.000136 0.011534 ( 0.011538)
--------------------------------------- total: 0.012959sec
user system total real
by_class 0.000973 0.000030 0.001003 ( 0.000969)
by_const_get 0.011102 0.000032 0.011134 ( 0.011104)
```
* Trigger Build
* WIP - use turbo to render html for create post button
todos and discussion topics
[] add tests (probably gonna be a lot of cypress 😅😭)
[] nominclature & file structure for authorization controller/routes
[] is this "authorizations" namespace approach the best?
[] authorization strategy relies on current user and caching doesn't
like `current_user`
* Use URL helper and not change the policy just yet
* put the turbo frame behind a feature flag
* Trigger Build
* Use URL helper and not change the policy just yet
* :)
* Add some test coverage and use same feature flag for consistency
* rubocop
* more rubocop
* trigger build
* adding a small sleep to see if specs pass 😩
* remove sleep
* disable Turbo session drive
* WIP
* Revert "WIP"
This reverts commit ce838672069fd703122156a0d82e5e6ad5398e0a.
* Moving spec from dashboard to root
With forem/forem#16913 the underlying test broke because we were
redirecting on the dashboard.
Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>
* initial work to show the expand/collapse search and filter on member index
* replace icon with actual button
* simplify logic in pack
* add cypress tests
* make sure both fields part of same form
* add an indicator in mobile view when field has a value
* make sure empty params treated same as missing params
* make sure indicators stay in step with current user input
* partials for inputs
* rename pack file
* Add toggle button to 'New Forem Secret' form
* Display messages using i18n
* Fix from snake case to kebab case
* Fix a bug when the specified class did not exist