* 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>
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>
* 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>
* Avoid using robohash for article cover images
If their service slows down, loading the cover image times out in
system tests.
Use a locally served image file instead.
* Specify expected image format for view object test
We were relying on the internals of Faker to return a robohash.org url
and checking that we included that in the cloudinary url.
Rather than relying on the default behavior, explicitly pass a
robohash url in the main image for this spec.
Prior to this commit the following situation existed:
> The path /dashboard/analytics/org/:id requires user
> authentication (e.g. signed in). However, it does not enforce
> authorization. Anyone can see this page. The page, however, uses
> javascript to populate the data. So no information, aside from the org
> name associated with the :id leaks out. The javascript API end point
> enforces organization membership.
>
> I would expect that the authorization in the HTML rendering would be
> the same as the javascript API end point.
This commit ensures that the dashboards#analytics end point uses the
same policy logic as the API analytics end points. Further, it keeps
folks who aren't org members out of the base HTML page for other orgs.
Closes forem/forem/#16985
This commit provides two things:
1. Some notes related to my analysis regarding the dashboard
2. Conditional redirects and rendering based on article policies
The code comments say most of what I want to say, but to reiterate:
When a user can't create articles nor do they already have published
articles, then we don't want to avoid showing them stats related to
articles.
Closesforem/forem#16913
Related to forem/forem#16908 and forem/forem#16931
* Removing a JS message about connect
Related to forem/forem#14734
* Remvoing another reference
I ran `rg Connect[^i]` to see about any remaining references to
Connect. This one seems to be the last.
This menu item will show up once you have added the
`:limit_post_creation_to_admins` feature flag (regardless of whether
it is enabled or not).
If you wish to add the menu item:
```console
rails console runner "FeatureFlag.add(:limit_post_creation_to_admins)"
```
In "adding" the item, it will default to disabled. However, as an
administrator you should now be able to toggle on and off the
authorization enforcement.
If you wish to remove the menu item:
```console
rails console runner "FeatureFlag.remove(:limit_post_creation_to_admins)"
```
Future plans for this will be to remove the `FeatureFlag.exist?`
conditional so that the menu item shows up. A major consideration is
that we'll assume that all Forem's have a "default space" in which
folks (by default) can post.
Builds on forem/forem#16897Closesforem/forem#16842
* Adding ArticlePolicy#has_existing_articles_or_can_create_new_ones?
As part of our aspirations to only show users what is relevant to them
and "hiding" what is not, this method will help us with the edge case of
"should we show the user a dashboard listing of posts?"
Related to forem/forem#16837
* Adding further documentation
* Adding clarifying comment
* Always show the browse section regardless of featured
* Add tests for /pod
* Use safe operator if there are no episodes to show
* Fix test for new podcast page
* Fix test again for podcast page
* prefer case to multiple if branches
case klass works fine on inheritance chains (we don't need to match on
inheritance explicitly).
* Verify case on error class behaves as before
* Fix tests
I don't know how I committed tests that were failing, but don't do that
As I was looking to implement the new Spaces feature (see
forem/forem#16842), I began looking at how to adjust the admin menu. I
found the Menu and AdminMenu. I wanted to add the "spaces" item under
the `:content_manager` scope. Looking at the implementation, I saw I
would need some additional branching logic.
To get an understanding of the implementation, I chose factor towards
classes.
This refactor does a few things:
1) Clarifies a method name (e.g. "children?" becomes
"has_multiple_children?")
2) Adds some tests of the menu implementation.
3) Removes deeply nested hashs in favor of first class objects.
4) Removes a complex method for determining menu visibility, instead
favoring a method that either takes a boolean OR a lambda.
There are further refactors to consider, especially in regards to the
helper methods and some of the coercion of strings into different
formats. But this refactor gets me the part I most am interested in:
Making it easier to specify a menu item as visible or not.
* Penciling in a Default Spaces section
This delivers two primary things:
1. Extracting shared policy examples
2. Providing a functioning skeleton for toggling on the
"limit_post_creation_to_admins" feature.
Important is that once merged, it would be possible for this code to
"leak" out. But how that leaks out is also how you can test that the
feature works.
First, to orient, there is a constraint for a new route. That
constraint checks if the feature flag exists (e.g. has been explicitly
enabled or explicitly disabled). In other words, at this point, you
can't accidentally enable the feature via the UI. But once you have
enabled the feature, you can then toggle the feature on and off.
So, to test this in the browser:
1. Pull down this branch
2. Run `rails runner "FeatureFlag.disable(:limit_post_creation_to_admins)"`
3. Startup your the web server.
4. Login as an administrator
5. Goto /admin/content_manager/spaces
a. Bask in the glory of a plain HTML form
6. With another browser, login as another non-admin user
7. Go to /settings/extensions, you should see a section Publishing from RSS.
8. Now with the admin's session, update the form to turn on
"limit_post_creation_to_admins"
9. Back to the non-admin browser, refresh /settings/extensions, you
should no longer see the section Publishing from RSS.
Note: I have not included an admin menu item as that is related to and
dependent on some refactors I'm working on (see forem/forem#16888 and
forem/forem#16847). So a bit of "security through obsurity"
Note: In the future, once we resolveforem/forem#16490, we'll start
toggling the "Create a Post" button.
Note: I am not including Cypress tests nor request tests for this
feature because the implementation details related to the testing via
that approach are a little too volitale.
Related to forem/forem#16842
* Updating copy based on forem/forem#16893
* styles
* styles
* dark styles
Co-authored-by: Paweł Ludwiczak <ludwiczakpawel@gmail.com>
* complete IG implementation of profile embed, refactors, update specs
* Follow redirects and check them for validity via recursion
* working out redirects
* add IG test links with params
Co-authored-by: Dwight Scott <dwight@forem.com>
* Adding docs and specs to AdminMenu.nested_menu_items
This relates to PR forem/forem#16847 which addresses issue
forem/forem#16842.
The goal of this PR is to help me develop an understanding of the
AdminMenu so I can further extend it and better understand it.
There's also a bit of knowledge sharing that I'm looking for, and the
comments and tests are there to help "confirm" that knowledge. There
might be more that I'd consider regarding a refactor, but I want to make
sure I'm on the right path before I go further.
tl;dr - this is the smallest commit I can make to begin to tease out an
understanding of the method I put under test.
* Update app/models/admin_menu.rb
Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
* Adding more documentation
* Update app/models/admin_menu.rb
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
* feat: add the feature flag route and controller
* feat: add a ff page
* feat: update to a post with a success banner
* feat: test the feature
* feat: update the styling
* feat: change feature flag to extension witha few more improvements like adding a model not backed by a table
* feat: update the text
* refactor: if you enable a FF, it gets added under the hood as well
* feat: accessibility name for label
* use keyword args
* use locales
* Update app/views/admin/extensions/index.html.erb
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Update app/views/admin/extensions/index.html.erb
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Update app/views/admin/extensions/index.html.erb
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Update config/locales/controllers/admin/fr.yml
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Update app/controllers/admin/extensions_controller.rb
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* fix: rubucop
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Removes admin_member_view feature flags and refactors conditionals
* Removes unused admin/users/_email_tools partial and user_email_tools_spec
* Actually remove the admin/user_email_tools_spec this time
* Removes the edit route from admin.rb users
* Adds a DUS (and spec) to disable and remove admin_member_view flag
* Fixes admin/users_spec.rb failures by updating spec with Member Detail changes
* Fixes spec failures within admin_bans_or_warns_user_spec.rb
* Refactors Admin::UsersController#credit_params due to failing specs
* Removes last traces of edit_admin_user_path and fixes buttons, specs, etc.
* solution
* fixing specs
* complete specs
* nudge Travis
* resolve database issues
* nudge Travis
* Check that the settings table exists before loading the class
This maybe fixes an issue in test-console-check accessing a missing
table `users` (because we're requiring
settings during the initializer, and we're initializing the app during
a schema load on a newly created db).
* refactor check
* privatize and rename
* add self
* reposition private class method
* Prevent accidentally deploying to DEV
* Skip tests to try deploying to benhalpern
* Allow PR to deploy to benhalpern
* Allow other branches to deploy
* Oops
* Revert changes to travis.yml
Co-authored-by: Dan Uber <dan@forem.com>
Co-authored-by: Andy Z <17884966+Zhao-Andy@users.noreply.github.com>
* Normalize article titles
* Change method name to match what it does
* Expand on allowed emoji ranges
This required a few changes to be in compliance with Rubocop because
that regex is a beast.
* Limit feeds import fanout to users with feeds
This filtering for feed settings occurs in
Feeds::Import#filter_users_from already, so the enqueued ForUser
jobs were mostly no-op, but the queue latency spiked as hundreds of
thousands of jobs were added to default.
Prescreen users to avoid enqueuing a noop job every hour for every single
user without a feed.
* Update tests to check only jobs for users with feeds are enqueued
* spec cleanup
Only include alice when we check no job is enqueued for her.
Assert no job is enqueued for alice.
Rename bob to user when alice isnt there to contrast.
* Update spec
Use update_columns to bypass setting validation (checking feed is
valid/reachable)
Remove unneeded timecop block when passing a set time
Remove sidekiq: fake and allow Import.call to be received
* Empty commit to trigger CI rebuild
We had been (since the beginning) disabling webmock in this
test (which means vcr was _not_ intercepting requests and we were
making them live). This appears to have been a work-around for an
issue where the captured URL in the cassette pointed to cloudinary,
while the code was fetching from the source directly, and an unhandled
request error was raised when running the spec.
However, the upstream source (https://via.placeholder.com/350x150)
activated cloudflare bot detection, and rather than a png image
response, we were getting an unprocessable html gateway page "Checking
your browser..." from cloudflare, and the test was failing.
Point the vcr cassette at the same url the test checks, and remove the
local webmock disable.
* Fan out Feeds::ImportArticlesWorker
Doing all that work within a single Sidekiq job has begun taking over an
hour on DEV. Regardless of the reasons we did it that way originally, we
should be able to handle this concurrently. If we cannot, we need to
investigate why and handle it properly rather than consigning it to
sequential work.
* Fix specs
The specs make assumptions about how the code under test is implemented.
This commit does not change that, as much as I would like to. Instead,
it just aligns the assumptions with the new implementation.
The previous tests didn't actually represent reality though, since we
can't get a Time or ActiveSupport::TimeWithZone instance inside of the
`perform` method while running it through Sidekiq. Instead, the specs
seem to be relying on the fact that the time instance gets serialized to
ISO-8601/RFC3339 format and that that format is understood by Postgres.
Otherwise, I'm not sure how it would work in production as written.
The new specs reflect reality more closely. The `earlier_than` value
will be converted into an ISO8601/RFC3339 string when passed through
Sidekiq.
* Add parens to perform_bulk call
Turns out, we actually do this pretty consistently. I could've sworn I
saw a bunch of these calls without parens. ¯\_(ツ)_/¯
* Improve variable naming
This is not a list of ids, it's a list of lists of arguments for Sidekiq
jobs, the inner of which contains an id, but that's not the only thing
it contains.
* Don't suggest users for an anonymous visitor to follow
The sidebar raises an error when rendering suggested users for a tag
when the user is signed out.
https://app.honeybadger.io/projects/66984/faults/79342962/
* Assert empty response returned for anonymous requests
This commit does three things:
1) Adds and tests the ArticlePolicy#moderate? feature
2) Refactors the spec helpers to be a bit more flexible
3) Adds a few more edge case tests around ArticlePolicy
My apologies for conflating these changes, as it makes this commit
more challenging to review.
The goal is to adequate demonstrate the logic around users who can or
cannot moderate. At present, the rules for who can moderate is here:
841491c6ee/app/javascript/packs/articleModerationTools.js (L17-L27)
```js
if (user?.trusted) {
if (user?.id !== articleAuthorId && !isModerationPage()) {
initializeActionsPanel(user, path);
initializeFlagUserModal(articleAuthorId);
// "/mod" page
} else if (isModerationPage()) {
initializeActionsPanel(user, path);
initializeFlagUserModal(articleAuthorId);
}
}
```
- Related to forem/forem#16783
- Closesforem/forem#16784
Videos are a conceptual subset of Articles (see
[VideosController#create][1] for supporting "evidence"). As such, we
want to ensure they conform to the expectations of the ArticlePolicy (as
described in forem/forem#16483).
This feature enhancement does a few things:
1. Renames swaps the `VideoPolicy#new?` and `VideoPolicy#create?`
declarations (reversing the alias direction)
2. Moves methods out of the concrete ArticlesPolicy into the
ApplicationPolicy.
3. Exposes these user oriented methods for external
consumers (e.g. making future refactoring work easier). The happy
benefit is that this already removes a bit of duplicated logic.
4. Highlights that while yes the Policy is correct, we also may be
flirting with the concept of some sort of User "properties as it relates
to authorization" logic (good standing users, established users,
etc.)
5. Reworked the timing of a guard clause and setting of instance variables.
[1]:
33e9bac0f7/app/controllers/videos_controller.rb (L18-L22))
Relates to #16483Closesforem/forem#16728
To test this, I'm relying on the existing test suite. I envision that
shifting from returning false for suspended users to raising an
exception might cause some false positive errors. Note, in the
[AppliciationController][2] we handle the responses. Paired with
[config/appliciation.rb][3], we handle the policy exceptions.
[2]:33e9bac0f7/app/controllers/application_controller.rb (L32)
[3]:33e9bac0f7/config/application.rb (L71-L81)
In looking at forem/forem#16787, it felt like we would benefit from a
similar approach as we adopted for nullifying blank strings.
This refactor helps pave the way for a "normalize_text_for" method (or
some such thing; naming things is hard).
This change also involved introducing a `describe` block into the
associated spec.
* Fix broken documentation link
When sending an invitation, and smtp isn't configured, we show a link
to the old smtp-settings page, which has been renamed
email-server-settings in the admin documentation site.
* Remove explicit link target, assert link text is present
This spec failed when I changed the link target to match the
documentation. Rather than moving the target, I'm just asserting we
show a link to the docs (not where the docs are located).
* Only fetch RSS of users who can create articles
This commit leverages changes from forem/forem#16732 and
forem/forem#16763 to ensure that we're only fetching RSS feeds for
folks who have permission to create articles.
In addition, I chose to rename the variable `user_scope` to
`users_scope` to bring consistency in the variable names.
I also chose to refactor a private method so that it wasn't setting two
instance variables but was instead returning a value that could be used
to set an instance variable.
Closesforem/forem#16487
* Amending method name