* feat: update all the paths as a first order of business
* feat: update all the folders to no longer appear under users
* feat: update specs
* feat: update specs
* feat: remove User namespace
* feat: remove route namespace
* feat: path change
* feat: update url
* remove name from invite user flow
* remove name from invitation instructions
* update invitations spec
* update specs and invitation actions overflow menu name
* allow users to set name when accepting invite
* Remove sustaining member newsletter, and its settings
This removes the concept of sustaining memberships from the system,
and logic related to or dependent on it.
This does not remove the monthly_dues column from the users table (todo).
* Remove unused newsletter setting
Since nothing accesses the mailchimp_sustaining_members_id setting,
it's safe to remove.
* Use destroy rather than delete to ensure settings cache is cleared
We have a callback in Settings::Base to clear the cache after commit,
I assume it's useful to trigger that. This requires destroy, not
delete, to be called.
* Prefer compact_blank! to delete_if blank
Rubocop suggested this. Why would I argue.
* When sort_direction is anything other than asc or desc, remove it
There is initializer code in the search/query classes that handles nil
sort_direction by adding a default value, so deleting the sort
direction is reasonable here.
* Set a default sort direction for articles of :desc
* Only sort if sort_direction and sort_by are present
* Add test case for article search with invalid parameter
As I was looking into the implementation details of the `/t/:tag_name`
relevancy feed I noticed an instance variable that we do not need.
Below are the results of looking for the instance_variable
`@article_index` or a potential `article_index` local variable or method name.
```shell
❯ rg "@?article_index"
app/controllers/stories_controller.rb
28: @article_index = true
131: @article_index = true
162: @organization_article_index = true
app/controllers/stories/tagged_articles_controller.rb
18: @article_index = true
app/controllers/stories/articles_search_controller.rb
7: @article_index = true
app/views/articles/_single_story.html.erb
26: <% if story.cached_organization && !@organization_article_index %>
32: <a href="/<%= story.cached_user.username %>" class="crayons-avatar <% if story.cached_organization && !@organization_article_index %> crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted <% else %> crayons-avatar--l <% end %> ">
79: <% if story.cached_organization && !@organization_article_index %>
```
* Allowing VariantQuery for Feed Generation
Apologies for the breadth of this pull request, I had considered many
small commits, but felt that would've been more effort for the value
provided.
This commit includes the following:
- Documentation updates to the feed variant (though not the final pass)
- Renaming and adding RelevancyLevers that help differentiate
- Adding RelevancyLever#range to provide documentation
- Reducing redundent controller logic by making a
`Articles::Feeds.feed_for` method.
- Adding some configuration validation for RelevancyLevers
- Adding constants for better clarification
- Testing unhappy paths for feed configuration
- Adjusting the module namespace of some objects
- Exposing top-level configurations for variants (along with their
defaults)
- Creating the VariantyQuery that at present inherits from the
`Articles::Feeds::WeightedQueryStrategy`
As implemented, we can deploy this code to production without using the
new VariantQuery. Once we toggle on the
`:feed_uses_variant_query_feature` FeatureFlag, it will switch to using
the VariantQuery. The VariantQuery's two variants and the
internal configuration of `Articles::Feeds::WeightedQueryStrategy`
produce the same query.
The goal of this factor is to allow for a quick on and off toggle of the
feed query; to ensure that what we introduce remains performant.
- Closesforem/forem#17272
- Closesforem/forem#17276
- Closesforem/forem#17216
In addition, I will be recording a code-walkthrough and linking that
recording to the pull request.
* Apply suggestions from code review
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
Co-authored-by: Mac Siri <krairit.siri@gmail.com>
There are two problems engrained in this:
1. The undocumented expectation of `/new` needed to other sites linking
with prefill information.
2. The prefill parameters being lost during the authentication process.
The first bug was introduced as part forem/forem#16536. The second is
likely introduced as part of a Rails upgrade (namely `request.path` most
likely once included the query parameters but with our current
implementation it does not).
Closesforem/forem#17292
* Adds internationalization to Admin::UsersController success messages
* Adds a new line at the end of en.yml and fr.yml
* Removes perriod from update_success message
* feat: add the export route
* feat: base export csv
* refactor: move the svg into its own file
* feat: add the export partial
* feat: export teh correct fields etc. for the csv
* user status helper
* feat: ensure that we format the time
* feat: add a spec for the CSV
* remove space
* remove puts
* chore: remove blank space
* feat: update traits
* Update app/views/admin/users/export.csv.erb
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
* Update spec/requests/admin/users/users_export_spec.rb
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
* Update spec/requests/admin/users/users_export_spec.rb
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
* fix: export should not error for unregistered users
Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
* Revert "conditionally render create post button for admins #16490 (#16606)"
This reverts commit 1cb45995cb.
* Adding conditional create post rendering
This pull request does two things:
- Reverts the forem/forem#16606
- Replaces the conditional rendering with the approach from forem/forem#17076
What to consider:
- How does this impact Cumulative Layout Shift (CLS). Prior to this
commit, we didn't "flicker" the "Create Post" button into view if the
site didn't have the conditional.
- In reverting forem/forem#16606 we remove a dependency on Turbo.
- We reduce one network call, relying instead on the async_info to carry
the visible/hidden logic.
If the "flicker" is a problem we could add a conditional in the page for
if the site has enabled that feature. However, that's a place holder as
we're looking at possible other reasons for allowing/disabling the
create a post.
This is a non-blocking PR, as in we can merge or not merge this and
proceed with what we have. Instead the main goal is to "unify" our approach.
* Update cypress/integration/seededFlows/policyFlows/limitPostCreationToAdmins.spec.js
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Leveraging change from forem/forem#17143
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
This commit adds instruction as to what's happening when a person
toggles on this feature. So as to not repeat knowlege, I added a
`NUMBER_OF_MINUTES_FOR_CACHE_EXPIRY` constant so the UI can reference that
instead of dropping a hard-coded 15 in the UI.
Closesforem/forem#17097
This commit does three things:
1. Documents a method
3. Implies the question: "Do we want to use class_attribute in Forem's codebase?"
2. Switches from an inferrence to an explicit (and configurable)
In my experience, I want to favor "explicit" declarations instead of
inferring what they should be. In this case, the inferrence is perhaps
adequate. But as I look to `ApplicationController::PUBLIC_CONTROLLERS`,
I think that is a prime case for a `class_attribute`. (The `api_action`
happened to be the lowest hanging fruit to begin the conversation.)
We still need some clarity into the `verify_private_forem` method as it
looks like it's doing a few different things.
There is precedence for using `class_attribute` found in
[`UniqueCrossModelSlugValidator.model_and_attribute_name_for_uniqueness_test`][1] (also
introduced by me).
[1]:https://github.com/forem/forem/blob/main/app/validators/unique_cross_model_slug_validator.rb
* Re-arranging method to be less surprising
Prior to this commit we hand an unless code block with a return then we
set an instance variable.
On a quick scan I didn't notice the return but saw the render followed
later by the instance variable.
This change is logically the same, my hope is that it's just a bit more legible.
* Bump for travis
* WIP - Conditional rendering of "Create Post" link
This PR builds on the conversation from forem/forem#17056 and moves in a
slightly different direction.
Important in all of this is that the ability to create a post is
enforced on the server. If the "Create Post" button were to be visible
but the user couldn't create a post when they clicked the button, they
would get an authorization error (or some such response).
This PR posits a different and perhaps competing approach to
forem/forem#16606. This PR provides a general approach in which we add
class attributes in our HTML erb files.
Note: I have not included Cypress tests as I don't want to yet commit
that time. I'm also wondering if this is the "right" thing to do. I
definitely think we want to add some JS tests. But could we do JS and
unit tests? (How do we reach consensus regarding our test approach?)
Again, thank you for the conversation and expect an even more "complete"
PR after we have our discussion.
* Extracting AsyncInfo model to ease testing
* Renaming forbidden to visible
* Adding cypress test to assert no 'Create Post'
These are always treacherous. What happens when we rename the button?
The test will continue to work.
* Apply suggestions from code review
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
* Switching to pack file for policy
Follows on [Suzanne's comment](https://github.com/forem/forem/pull/17076#issuecomment-1088567286)
* Update app/javascript/packs/applyApplicationPolicyToggles.js
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
Co-authored-by: Suzanne Aitchison <suzanne@forem.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)
* 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
* 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>
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
There are two existing listeners for the `Audit::Logger`: `:moderator`
and `:internal`. (Note: during tests we ignore the :moderator and
:internal logs as defined in [config/initializers/audit_events.rb][1].)
Using `rg "Audit::Logger\.log\(:internal," --files-with-matches`, the
`:internal` listener is found in:
- app/controllers/admin/secrets_controller.rb
- app/controllers/admin/settings/base_controller.rb
- app/controllers/admin/settings/general_settings_controller.rb
Using `rg "Audit::Logger\.log\(:moderator," --files-with-matches`, the
`:moderator` listener is used in:
- app/controllers/rating_votes_controller.rb
- app/controllers/comments_controller.rb
- app/controllers/stories/pinned_articles_controller.rb
- app/controllers/admin/response_templates_controller.rb
- app/controllers/admin/tags_controller.rb
- app/controllers/admin/articles_controller.rb
- app/controllers/admin/users_controller.rb
- app/controllers/admin/reactions_controller.rb
- app/controllers/admin/tags/moderators_controller.rb
- app/controllers/tag_adjustments_controller.rb
- app/controllers/reactions_controller.rb
The `admin/spaces#update` action is most similar to the `admin#settings`
actions, which is why I chose `:internal`. I am looking for further
guidance on documenting this little area of the application (in
particular providing a data dictionary of :internal and :moderator).
Closesforem/forem#16957
[1]:https://github.com/forem/forem/blob/main/config/initializers/audit_events.rb#L9-L11
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
We only have one reference to the UnauthorizedError, which is shadows
the ApplicationPolicy::NotAuthorizedError. This commit removes the
exception.
Related to forem/forem#16985 but only barely
As I'm looking at the dashboard, there's lots of small duplication.
This refactor is a way to help me collect my thoughts regarding how to
approach the larger issue at hand.
There is no reason to create an instance variable as we don't pass this
to the view. Further, by adding this as a before_action there's a
disconnect in logic.
This commit attempts to address those issues.
What follows is a three-fold change:
1. Removing the before action (let's just call the method)
2. Reworking the method to reduce, just a bit, the method cost
3. Removing an unused instance variable
Here are the benchmarks. Note the "follows_limit" as written below is
the "Proposed" route.
```ruby
require 'benchmark'
def follows_limit_original(params:, default: 80, max: 1000)
per_page = (params[:per_page] || default).to_i
@follows_limit = [per_page, max].min
end
def follows_limit(params:, default: 80, max: 1000)
return default unless params.key?(:per_page)
per_page = params[:per_page].to_i
return max
per_page
end
def follows_limit_alt(params:, default: 80, max: 1000)
per_page = params.fetch(:per_page, default).to_i
return max if per_page > max
per_page
end
TIMES = 10_000
Benchmark.bmbm do |b|
b.report("Original no params") { 1000.times { follows_limit_original(params: {}) } }
b.report("Original less than max") { 1000.times { follows_limit_original(params: {per_page: 90 }) } }
b.report("Original greater than max") { 1000.times { follows_limit_original(params: {per_page: 9000 }) } }
b.report("Proposed no params") { 1000.times { follows_limit(params: {}) } }
b.report("Proposed less than max") { 1000.times { follows_limit(params: {per_page: 90 }) } }
b.report("Proposed greater than max") { 1000.times { follows_limit(params: {per_page: 9000 }) } }
b.report("Alt no params") { 1000.times { follows_limit_alt(params: {}) } }
b.report("Alt less than max") { 1000.times { follows_limit_alt(params: {per_page: 90 }) } }
b.report("Alt greater than max") { 1000.times { follows_limit_alt(params: {per_page: 9000 }) } }
end
```
```shell
$ ruby /Users/jfriesen/git/forem/bench.rb
Rehearsal -------------------------------------------------------------
Original no params 0.000403 0.000002 0.000405 ( 0.000405)
Original less than max 0.000109 0.000005 0.000114 ( 0.000114)
Original greater than max 0.000161 0.000006 0.000167 ( 0.000166)
Proposed no params 0.000076 0.000001 0.000077 ( 0.000076)
Proposed less than max 0.000114 0.000009 0.000123 ( 0.000126)
Proposed greater than max 0.000112 0.000011 0.000123 ( 0.000123)
Alt no params 0.000104 0.000007 0.000111 ( 0.000114)
Alt less than max 0.000113 0.000009 0.000122 ( 0.000122)
Alt greater than max 0.000111 0.000001 0.000112 ( 0.000115)
---------------------------------------------------- total: 0.001354sec
user system total real
Original no params 0.000108 0.000000 0.000108 ( 0.000110)
Original less than max 0.000109 0.000001 0.000110 ( 0.000111)
Original greater than max 0.000109 0.000000 0.000109 ( 0.000109)
Proposed no params 0.000071 0.000000 0.000071 ( 0.000071)
Proposed less than max 0.000103 0.000001 0.000104 ( 0.000103)
Proposed greater than max 0.000102 0.000000 0.000102 ( 0.000103)
Alt no params 0.000093 0.000000 0.000093 ( 0.000093)
Alt less than max 0.000103 0.000000 0.000103 ( 0.000104)
Alt greater than max 0.000102 0.000000 0.000102 ( 0.000103)
```
There is an incredible amount of conditionals in play within this
controller. I'm working to disentangle the logic so we can introduce
unified authorization policies.
The first step is removing the quasi-opaque "before_action" behavior
related to authorization. My strong preference is to make that explicit
and in doing so begin to see how to adjust the policy enforcement/creation.
This relates to forem/forem#16913
While exploring the DashboardsController, I came across method calls to
`not_found`. Idiomatically, I assumed that these methods were returning
a value. However, in looking at the code, it raises an exception.
_Note: I excpect methods that raise exceptions, especially as the only
thing they do, to end in a `!`._
By adding the documentation my "IntelliSense" provides insight into the
expected behavior of this function (e.g. "Raises an exception").
Without the documentation, I don't see any useful information.
* 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>
* 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>