Changes made using the ember-native-class-codemod, plus some manual tweaks
Also ensures our implicit injections applied to the prototype immediately. Without this, they will only be applied on the next `.extend()` call, which is now later than the first native-class extension.
When the tooltip items are tooltipItem = [{parsed: {y:12} }, {parsed: {y:10} } ], reducing without a initial value as a number would result in Javascript thinking it is a string. Thanks, Javascript!
There are no tests here yet since this makes use of an external library Chart.js.
Why all the manual setting of `width` and `height`? Without it all ace editors were 0x0, invisible.
Why didn't it affect the tests before? ace-editor tests only confirms that there is an element, theme-settings-editor tests were effectively unit tests before 7317b5a, fk-code test avoids interacting with ace.
Makes it easier to reach the group from the category security
tab, and moves the trash button to the right to avoid misclicks.
Also converts the category permission row to gjs
This commit changes the cutoff number for the admins and mods lists on the new /about page from 12 to 6. If the admins or mods lists are bigger than 6, the about page will display the 6 most recently seen admins/mods, and tuck the rest away behind a "view more" button.
Given we are a single-page-app, we hijack the browser's default behavior
for keyboard shortcuts like End because on long topics (20+ posts) we
need to load the last post before really reaching the end of the page.
However, when the last post is already rendered, the End shortcut
currently does nothing, it takes the user to the start of the last post.
If that post is too long, the user will have to scroll down manually.
This change ensures that if the last post of a topic is already rendered
(whether it is in the viewport or not), pressing End will take the user
to the very bottom of the page.
Note for the reviewer: the test added here is for the general case, it
is too hard to test the case where the last post is already rendered,
that isn't covered here.
In FormKit you can add error on an existing field existing in the DOM, but you can also set an arbitrary error on a virtual field not existing in the DOM.
When revalidating existing data, we are only resetting real fields. This commit adds `removeError(name)` to allow you to manually manage virtual fields. `removeError` is available in the same helpers where `addError` is available.
<!-- NOTE: All pull requests should have tests (rspec in Ruby, qunit in JavaScript). If your code does not include test coverage, please include an explanation of why it was omitted. -->
Prior to this fix using:
- `replaceText(...)`
- `selectText(..., ..., { scroll: true})`
Wouldn't have the expected behaviour as the scroll from selectText will attempt to save the scroll position and restore it AFTER the replacement happened. This commit allows scroll to be a Boolean or a Number, when a number, it will be used to restore the scrollTop position.
I tried to write tests for this specific behavior but couldn't reproduce the issue in tests.
A follow-up to dd1abf91ef.
Because `ember-cli-deprecation-workflow` relied on `@ember/string` 3.1.1, that was the effective version used in the app.
Turns out there's a compatiblity issue with 4.0.0. Let's lock it to 3.x so we can easily update deprecation-workflow and then we can look into the ember/string issue.
All those addons import it but didn't have it in their package.jsons. And because the only thing that had it as a dependency (ember-cli-deprecation-workflow) removed it in the latest version, the build breaks.
File it under: a problem we wouldn't have with pnpm :P
In GroupRequestsController, request_count is incorrectly written as
user_count, which causes group member requests to be loaded infinitely
when user_count is greater than request_count.
When searching for site texts for admin using the english
version of the text, previously we would show the english
version in the results _even if_ there was another locale
translated version available when a locale was selected
from the dropdown.
This commit adds a "Only show results in selected locale"
checkbox option which will instead make it so the results
shown are in the target locale, making it easier for translators
to tell when there is actually translations vs. missing tranlsations.
If there is a codec issue or something trying to process a video file
for thumbnail generation, uploads could hang indefinitely. This fix
ensures that we continue the upload process even if we encounter an
error trying to generate a thumbnail for it.
If you’re viewing a tag and you switch to a different tag via the sidebar or the tags dropdown, after expanding the info section of the tag page via the wrench button, the info section keeps showing the previous tag's details instead of the new one.
This happens because the tag info section makes an ajax request to load the tag's details, and this request is made inside the `didInsertElement` hook which is only fired once when the component is rendered. To fix this, we need to set the result from the ajax request to null and add a `didUpdateAttrs` hook to trigger another request to load the info of the new tag.
Internal topic: t/134809.
This change is preventing the "is dirty check" from happening when clicking delete on this form. This was not good UX and was also causing bugs by leaving the form in a unexpected state.
Previous to this change there is no clean way to apply keyboard shortcuts
to things such as "add poll" and other hidden options in the toolbar
This allows shortcuts to be specified similar to how they are on the toolbar
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
If we don't get a `videoWidth` back for a video don't try and generate a
thumbnail for it.
Also as part of this change I switched getImageData, the function
throwing the error, to use video.videoWidth instead of canvas.width
because it's very likely we were setting canvas.width too early before
the width could be read. Now that we are reading the value inside of the
setTimeout hopefully we will actually have a width. Just incase we don't
detect a width we will now exit early instead of throwing an error.
We only need to check for `0` and not null because the value is an
integer and will always return a 0 if it can't be read. https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/videoWidth
See https://meta.discourse.org/t/322363
This lays the groundwork for converting SelectKit subclasses to native class syntax. This commit is designed to be entirely backwards-compatible, so it should not affect any existing subclasses.
Of interest:
- Any properties which are designed to be overridden by subclasses are implemented using a local `@protoProp` decorator. That means they are applied to the prototype, so that they can be overridden in subclasses by both legacy `.extend()` prototype extensions, and by modern native-class fields.
- New class decorators are introduced: `@selectKitOptions` and `@pluginApiIdentifiers`. These are native class versions of the legacy `concatenatedProperties` system. This follows the pattern Ember has introduced for `@className`, `@classNameBindings`, etc.
This would cause the infamous error:
```
index.js:118 Uncaught (in promise)
Error: Assertion Failed: You attempted to update `href` on `<ExtraNavItem:ember384>`, but it had already been used previously in the same computation.
```
Moreover, set didnt seem necessary here.
Followup 1446596089
The link to inform admins that restore is disabled
was not correct. This fixes it and also changes it
to go to /admin/backups/settings
This commit changes the custom flags admin config area
to use the AdminPageHeader component and conforms
to the new admin UI guidelines. Also makes some slight
modifications to the reusable components to handle using
a @route for an action button.
This commit implements 2 new metrics/stats in the /about page for the _estimated_ numbers of unique visitors from the EU and the rest of the world. This new feature is currently off by default, but it can be enabled by turning on the hidden `display_eu_visitor_stats` site settings via the rails console.
There are a number of assumptions that we're making here in order to estimate the number of unique visitors, specifically:
1. we're assuming that the average of page views per anonymous visitor is similar to the average number of page views that a logged-in visitor makes, and
2. we're assuming that the ratio of logged in visitors from the EU is similar to the ratio of anonymous visitors from the EU
Discourse keeps track of the number of both logged-in and anonymous page views, and also the number of unique logged-in visitors and where they're from. So with those numbers and the assumptions above, we can estimate the number of unique anonymous visitors from the EU and the rest of the world.
Internal topic: t/128480.
This commit introduces a new frontend API to add custom items to the "Site activity" section in the new /about page. The new API is called `addAboutPageActivity` and it works along side the `register_stat` serve-side API which serializes the data that the frontend API consumes. More details of how the two APIs work together is in the JSDoc comment above the API function definition.
Internal topic: t/128545/9.
This commit improves the hilight-ing of mentions in posts and chat messages.
- `@here` and `@all` will generate a `<a class="mention --wide">`
- bots will generate a `<a class="mention --bot">`
- current user will generate a `<a class="mention --current">`
To achieve this change the following value transformer has been added: "mentions-class". It will be run in posts and chat messages after the mention is rendered.
A bug were bots were not considered in mentioned users has also been fixed as part of this PR.
This commit fixes a bug where the silence button is incorrectly displayed on the admin page of a staff user. It's not actually possible to silence a staff user because the backend correctly prevents it, but the frontend isn't checking if the button should be displayed.
Another small bug that this commit fixes is the similar users list not showing up inside the silence/suspend modals due to also a bug in the frontend.
I've also changed the way similar users are loaded so that they're not returned by the `admin/users#show` endpoint anymore and moved them into a new endpoint that the penalize modals (suspend and silence) can call directly to retrieve the list of users. This is done because the similar users list is never shown on the admin user page (`/admin/users/:user_id/:username`); they're only needed when the suspend or silence modals are opened.
Internal topic: t/130014.
This is a variation on bc3e8a9963cf9a64d114ec751c875025af169690, which was reverted due to issues on iOS. Safari's "in response to user action" check cannot follow the `runAfterFramePaint` chain of interaction -> requestAnimationFrame -> messageChannel, and so some sensitive browser APIs (e.g. clipboard, upload, etc.) were blocked.
This commit is similar, but uses `next()` instead of `runAfterFramePaint()`. The result seems the same, but doesn't have the same issue on iOS.
The chat-emoji-picker change was required to resolve a test failure. The emoji picker has never closed-on-scroll on desktop, so there is no user-facing change in behavior.
This commit converts the Backups page in the admin interface
to follow our new admin interface guidelines.
As part of this work, I've also made `AdminPageHeader` and `AdminPageSubheader`
components that can be reused on any admin page for consistency, that handle
the title and action buttons and also breadcrumbs.
Also renamed `AdminPluginFilteredSiteSettings` to `AdminFilteredSiteSettings` since
it can be used generally to show a subset of filtered site settings, not only
settings for a plugin. Not sure if it's ideal to have to define a new route for this
for every config area, but not sure how else to do it right now.
This commit adds a blue dot next to the "What's New"
link in the admin sidebar if the user has not seen the
new features yet, as a followup to 3e5976f843
which removed the tab on the dashboard that had this same
functionality.
When the admin visits the "What's New" page they count
as having seen all the features straight away. This could
be something we want to change, but for now this keeps the
same functionality.
`routeAction` is testing at runtime that a route exists when in debug mode. However in the case of components tested in isolation there's no existing route which was causing an exception, this commit prevents this check in this case as it's irrelevant.
Long code is considered 1000 characters atm. 10000+ characters would take seconds due to how highlightjs will consider every installed lang before choosing the best candidate.
Move it into the preceding `dl` element and remove the `div` wrapper.
The two plugins that use this outlet:
* d-follow - actually jumps through hacky hoops to get its elements into that `dl` anyway
* d-gamification - would look better if its element was in that `dl`
We used to show New Features in a tab on the dashboard,
but this could get pushed down the page especially on
our hosting. In 043117ca13
we made a separate What's New page, so this commit removes
the dashboard tab and changes the admin notification to
send the admin to /admin/whats-new instead of the dashboard
tab.
This commit fixes a number of bugs in `file_size_restriction` settings and does a little of refactoring to reduce duplicated code in site setting types (the refactoring is necessary to fix one of the bugs).
The bugs in `file_size_restriction` settings that are fixed in this commit:
1. Save/cancel buttons next to a `file_size_restriction` setting are shown upon navigating to the settings page without changes being made to the setting
2. Cancel button that discards changes made to the setting doesn't work
3. Reset button that resets the setting to its default doesn't work
4. Validation error message isn't cleared when resetting/cancelling changes
To repro those bugs, navigate to `/admin/site_settings/category/files` and observe the top 2 settings in the page (`max image size kb` and `max attachment size kb`).
Internal topic: t/134726.
Desktop chat notification sounds have stopped working on most desktop browsers.
This is due to Notifications API being disabled when Push Notifications are supported in the browser, which means that we never iterate on the desktopNotificationHandlers and trigger the callback since we return early.
When browsing through a sidebar with this feature enabled (i.e. admin, or docs), it's weird to have the scroll jump around when you click an item. This commit adds a check, so that we only `scrollIntoView` for items which are not already in the viewport.
Followup to b7cce1a0dc
We changed the design of the member access wizard step to use toggle groups instead of switches. To support existing designs for notices, we need another plugin outlet.
0 is falsy in JavaScript, so the original code would treat 0 as if it
were not input. This unique exception was added to prevent 0 from being
treated as empty input.
This commit fixes a bug in the redesigned about page where if there's no banner image configured for the page, the top of the page where the banner goes is occupied with large white space. Additionally, this commit also fixes a related bug in the admin config area for the /about page where it's not possible to remove the uploaded banner image.
`defaultCategoryLinkRenderer` is using a fake category object which doesn’t have access to the functions and getters of category model.
This had been incorrectly set in c197daa04c
As we don't get a real category object, we have to call the transformers manually and also pass the fake category object as context, this is not ideal as people might try to access properties in the transformer which are not available on the category object given they will be different based on the context. Hopefully one day this helper and all the chain can be refactored to use a real category model.
This commit also adds tests for these two properties in the category-link helper.
<!-- NOTE: All pull requests should have tests (rspec in Ruby, qunit in JavaScript). If your code does not include test coverage, please include an explanation of why it was omitted. -->
This commit forces the textarea to check if the list is inside a codefence and won't continue the list if it's the case.
Note this commit also uses the message param of qunit assertions to make them more explicit. It has no impact on behavior.
This commit continues on work laid out by 6039b513fe to redesign the /about page. In this commit, we add sections for showing the site admins and moderators.
The lists of admins and moderators display the 10 most recently seen admins/moderators, with a button to display the rest of admins or moderators. Admins or moderators that have not logged in to the site in the last year will not be shown. Clicking on an admin's or moderator's name/avatar will show their user card.
e.g.
```
WARNING: Binding style attributes may introduce cross-site scripting vulnerabilities; please ensure that values being bound are properly escaped. For more information, including how to disable this warning, see https://deprecations.emberjs.com/v1.x/#toc_binding-style-attributes. Style affected: \"height: 60px\"
```
This message indicates broken behavior, so it should be an error rather than a warning.
An early-return is added, so that we don't even attempt to make the modification. This will make the behavior consistent, and easier to understand.
Also updates the normalization logic to use the resolver's own logic. This will handle all sorts of normalization in addition to our deprecations.
In development, Ember raises an error when previously-used values are updated during a render. This is to avoid 'backtracking', where parts of templates have to be re-rendered multiple times. In general, this kind of pattern should be avoided, and Ember's warning helps us do that.
However, for the deprecation warning banner, it is quite reasonable for some rendering to trigger a deprecation, and thereby require the global-notice to be re-rendered. We can use our `DeferredTrackedSet` to achieve that. Its `.add` method will delay adding an item to the Set until after the current render has completed.
e.g. we map `controller:composer` to `service:composer` in resolver lookups. So, when doing the cache check in modifyClass, we need to check against the normalized name, not the deprecated name.
Very similar to move up/down flag problem fixed here - https://github.com/discourse/discourse/pull/28272
Those are the steps to toggle the flag:
1. click toggle - `saving` CSS class is added;
2. request to backend;
3. `saving` CSS class is removed.
And check if the flag was toggle was:
```ruby
def has_saved_flag?(key)
has_css?(".admin-flag-item.#{key}.saving")
has_no_css?(".admin-flag-item.#{key}.saving")
end
```
If the save action is very fast, then the saving class is removed before the first check.
Therefore I decided to invert it, and once action is finished add `saved` CSS class.
Then we can have a quick positive check:
```ruby
def has_saved_flag?(key)
has_css?(".admin-flag-item.#{key}.saved")
end
```
This commit adds two new getters to the category model:
- `displayName`
- `descriptionText`
These getters are used instead of `name` and `description_text` where appropriate.
On top of this two transformers have been added to allow plugins to alter these getters:
```javascript
api.registerValueTransformer(
"category-display-name",
({ value, context }) =>
value + "-" + context.category.id + "-transformed"
);
```
```javascript
api.registerValueTransformer(
"category-description-text",
({ value, context }) =>
value + "-" + context.category.id + "-transformed"
);
```
Those are the steps to move the flag:
1. open menu;
2. click move up - `saving` CSS class is added;
3. request to backend;
4. `saving` CSS class is removed.
To check if the action was finished we are using this method:
```
def move_up(key)
open_flag_menu(key)
find(".admin-flag-item__move-up").click
has_saved_flag?(key)
self
end
def has_saved_flag?(key)
has_css?(".admin-flag-item.#{key}.saving")
has_no_css?(".admin-flag-item.#{key}.saving")
end
```
However, sometimes specs were failing with `expected to find CSS ".admin-flag-item.spam.saving" but there were no matches`
I think that the problem is with those 2 lines:
```
find(".admin-flag-item__move-up").click
has_closed_flag_menu?
```
If the save action is very fast, then the `saving` class is removed before the first check.
Therefore, to determine that the move action is finished, I am checking if the menu is closed.
This resolves issues when a mix of callback-based modifications and Ember-reopen-based modifications are used on the same target. In summary:
- Fixes `pluginId` exception logic for callback-based modifications
- Moves `pluginId` storage to a WeakMap so it doesn't pollute the target's descriptors
- When applying a legacy modifyClass, we will temporarily rollback any modern callback-based modifications. This means all of Ember's reopen calls apply to un-prepended classes, and then we add our modern prepends on top.
- Calls `.proto()` on CoreObject descendants before prepending, to ensure that pending Ember mixins have been applied
This commit continues on work laid out by 6039b513fe to redesign the /about page. In this commit, we add the site age and a section on the right hand side to show site activities/statistics such as topics, posts, sign-ups, likes etc.
- Added `addLogSearchLinkClickedCallbacks` which allows plugins/TCs to register a callback when a search link is clicked and before a search log is created
During our refactoring of admin badges we decided to link to:
`adminSiteText.edit locale=locale`
Instead of:
`adminSiteText q=key`
After feedback from the community we are reverting this change.
A recent change in FormKit has changed the syntax of this specific component. It's also better to use `<CheckboxGroup />` for this use case too.
Im mixed on writing tests for labels, it's a lot of tests to write for a rather low value.
This commit also slightly tweaks the width of the icon picker, from medium to small.
Admin can create up to 50 custom flags. It is limited for performance reasons.
When the limit is reached "Add button" is disabled and backend is protected by guardian.
The Safari 15 bugfix has been rolled into @babel/preset-env in the most recent version, so we no longer need to carry our vendored copy.
This commit updates @babel/preset-env, runs npx yarn-deduplicate yarn.lock, and removes the vendored transform.
This commit also refactors our theme transpiler to use @babel/preset-env, with the same list of target browsers as our ember-cli build uses. This means we no longer need to maintain a separate list of babel transforms for themes.
Followup to f70a65ea02
1. Update a second regex in `routeTo` to avoid stripping domain/protocol from middle of string
2. Update `URL.handleURL` to strip double-slashes in paths, before calling the ember router. This mimics what Ember does on initial page-load
Additional tests are added for both
This change is mainly a refactor of the desktop notifications service to improve readability and have standardised values for tracking state for current user in regards to the Notification API and Push API.
Also improves readability when handling push notification jobs, especially in scenarios where the push_notification_time_window_mins site setting is set to 0, which will allow sending push notifications instantly.
To achieve this, a new notifications service is set up with an `isInDoNotDisturb` tracked property. While a user is in do-not-disturb mode, it runs a regular timer until do-not-disturb is over.
We were writing theme-transpiler JS files to the filesystem on a per-process basis, and then immediately reading them back in. Plus, there was no cleanup mechanism, so the tmp directory would grow indefinitely.
This commit refactors things so that the `build.js` script outputs the theme-transpiler source to stdout. That way, we can read it directly into the process, and then into mini-racer, without needing to go via the filesystem. No cleanup required!
In production, the theme-transpiler is still cached in a file during `assets:precompile`
In the formkit conversion in 2ca06ba236
we missed setting a type for the UppyImageUploader for badges. Also,
we were not passing down the `image_url` as form data, so when we used
`data.image` for that field the badge was not updating in the UI after
page loads and the image URL was not loading for preview.
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
This commit introduces the `behaviorTransformer` API to safely override behaviors defined in Discourse.
Two new plugin APIs are introduced:
- `addBehaviorTransformerName` which allows plugins and theme-components to add a new valid transformer name if they want to provide overridable behaviors;
- `registerBehaviorTransformer` to register a transformer to override behaviors.
It also introduces the function `applyBehaviorTransformer` which can be imported from `discourse/lib/transformer`. This is used to mark a callback containing the desired behavior as overridable and applies the transformer logic.
How does it work?
## Marking a behavior as overridable:
To mark a behavior as overridable, in Discourse core, first the transformer name must be added to `app/assets/javascripts/discourse/app/lib/transformer/registry.js`. For plugins and theme-components, use the plugin API `addBehaviorTransformerName` instead.
Then, in your component or class, use the function `applyBehaviorTransformer` to mark the Behavior as overridable and handle the logic:
- example:
```js
...
@action
loadMore() {
applyBehaviorTransformer(
"discovery-topic-list-load-more",
() => {
this.documentTitle.updateContextCount(0);
return this.model
.loadMore()
.then(({ moreTopicsUrl, newTopics } = {}) => {
if (
newTopics &&
newTopics.length &&
this.bulkSelectHelper?.bulkSelectEnabled
) {
this.bulkSelectHelper.addTopics(newTopics);
}
if (moreTopicsUrl && $(window).height() >= $(document).height()) {
this.send("loadMore");
}
});
},
{ model: this.model }
);
},
...
```
## Overriding a behavior in plugins or themes
To override a behavior in plugins, themes, or TCs use the plugin API `registerBehaviorTransformer`:
- Example:
```js
withPluginApi("1.35.0", (api) => {
api.registerBehaviorTransformer("example-transformer", ({ context, next }) => {
console.log('we can introduce new behavior here instead', context);
next(); // call next to execute the expected behavior
});
});
```
Ember's legacy mixin system does not support native-class syntax, so we have to use the non-decorator syntaxes for `action()` and `computed()`.
Eventually, we will need to refactor things to remove these mixins... but today is not that day.
* SECURITY: Update default allowed iframes list
Change the default iframe url list to all include 3 slashes.
* SECURITY: limit group tag's name length
Limit the size of a group tag's name to 100 characters.
Internal ref - t/130059
* SECURITY: Improve sanitization of SVGs in Onebox
---------
Co-authored-by: Blake Erickson <o.blakeerickson@gmail.com>
Co-authored-by: Régis Hanol <regis@hanol.fr>
Co-authored-by: David Taylor <david@taylorhq.com>
Followup 4aea12fdcb
In certain config areas (like About) we want to be able
to fetch specific site settings by name. In this case,
sometimes we need to be able to fetch hidden settings,
in cases where a config area is still experimental.
Splitting out a different endpoint for this purpose
allows us to be stricter with what we return for config
areas without affecting the main site settings UI, revealing
hidden settings before they are ready.
`addCommunitySectionLink` API function accepts secondary argument to determine if the link should be added to the primary or secondary (more) section. There was a bug and all links were mounted in the secondary section.
In this case, there is no 'nearPost' param in the URL. Instead, the server preloads a post-stream with whichever page of posts is requested. We can check for that situation using `postStream.firstPostPresent`.
Also updates the widget-header version to fetch a value from the service on initial render, instead of relying on the observer triggering.
Followup to bdec564d14
Currently, if MF definitions are missing (typically because there’s a
compilation error), `I18n.messageFormat` will try to access
`I18n._mfMessages.hasMessage` resulting in a crash that will in turn
crash Ember.
This patch addresses the issue by using the optional chaining operator
making the `I18n.messageFormat` method return a "Missing Key" message.
MF strings won’t be rendered properly, but the site will stay usable.
By default, the swc minifier seems to unwrap 'unneeded' IIFE. That means it was undoing the 'bugfix' transformation we have for class fields in Safari 15. Disabling the 'inline' and 'reduce_funcs' options seems to stop this behavior.
When we show user tips, we immediately send an AJAX request to mark the
tiup as seen. This is done in the background. However, when system tests
are run, sometimes that request is not completed before the test ends.
This causes the test to be flakey.
One way to fix this is to force the system test run to wait for the AJAX
request to complete. However, this is not ideal because it makes the
test suite slower on each run.
Instead, this commit removes the flakey assertion and adds an alternative
assertion in the frontend tests that ensures the background request is
sent when the user tip is shown.
Form Kit is our new form library/framework for unifying the way forms look across Discourse. The admin config area for the /about page is a new form that isn't currently used, so it makes sense for it to be one of the first forms to be migrated to Form Kit to test the library.
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
* `@ember/owner` instead of `@ember/application`
* `discourse-i18n` instead of `I18n`
* `{ service } from "@ember/service"` instead of `inject as service`
In an attempt to improve build performance, 9db5eafb mistakenly removed minimization for some of our JS assets, leading to a significant increase in the size of some files.
This commit restores minimization to those files. To avoid regressing on the build time improvements, this commit switches to using the `webpack-terser-plugin`'s "swcMinify" option. On an entry-level 1CPU/1GB-ram/2GB-swap DO droplet, this commit increases build time from ~16 minutes to ~18 minutes.
Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
* FIX: Add post id to the anchor to prevent two identical anchors
We generate anchors for headings in posts. This works fine if there is
only one post in a topic with anchors. The problem comes when you have
two or more posts with the same heading. PrettyText generates anchors
based on the heading text using the raw context of each post, so it is
entirely possible to generate the same anchor for two posts in the same
topic, especially for topics with template replies
Post1:
# heading
context
Post2:
# heading
context
When both posts are on the page at the same time, the anchor will only
work for the first post, according to the [HTML specification](https://html.spec.whatwg.org/multipage/browsing-the-web.html#scroll-to-the-fragment-identifier).
> If there is an a element in the document tree whose root is document
> that has a name attribute whose value is equal to fragment, then
> return the *first* such element in tree order.
This bug is particularly serious in forums with non-Latin languages,
such as Chinese. We do not generate slugs for Chinese, which results in
the heading anchors being completely dependent on their order.
```ruby
[2] pry(main)> PrettyText.cook("# 中文")
=> "<h1><a name=\"h-1\" class=\"anchor\" href=\"#h-1\"></a>中文</h1>"
```
Therefore, the anchors in the two posts must be in exactly the same by
order, causing almost all of the anchors in the second post to be
invalid.
This commit solves this problem by adding the `post_id` to the anchor.
The new anchor generation method will add `p-{post_id}` as a prefix when
post_id is available:
```ruby
[3] pry(main)> PrettyText.cook("# 中文", post_id: 1234)
=> "<h1><a name=\"p-1234-h-1\" class=\"anchor\" href=\"#p-1234-h-1\"></a>中文</h1>"
```
This way we can ensure that each anchor name only appears once on the
same topic. Using post id also prevents the potential possibility of the
same anchor name when splitting/merging topics.
Disabling webpack minimize is a bug we are working to resolve but we
have to consider self-hosters that deploy on low cost hardware
and reenabling this for them drastically increases the build time.
For now, add a `DISCOURSE_WEBPACK_MINIMIZE` env to allow sites to opt
back in.
This commit also attempts to promote more declarative patterns. The route history logic has been replaced by using the history-store service.
---------
Co-authored-by: Jarek Radosz <jarek@cvx.dev>
Co-authored-by: David Taylor <david@taylorhq.com>
This commit changes the group SMTP settings form (at
`/g/:name/manage/email`) to use
FormKit, our magical new form component system ✨
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
- Ensure main title is set as 'not visible' when removed from DOM
- `deactivate` -> `willTransition` to ensure proper behavior when navigating between multiple topics
Followup to bdec564d14
- Move topic-title on-screen detection to intersection-observer (via new modifier), and add a boolean to header service which indicates whether it's on-screen
- Move scroll-direction from Mixin to dedicated service. Teach it to pause scroll monitoring while transitions are in progress, to avoid reporting false changes in scroll direction. Also resets to a 'neutral' state after each navigation, which indicates the the user has not yet scrolled
- When entering a topic view, notify the header service which post is being targeted. It can then make an educated guess about whether the topic title is likely to be in-view
- Update header service `topicInfoVisible` to be a declarative getter, based on the three refactored sources of truth mentioned above
- Update legacy widget header to use the header service for topic info
All of these changes mean that the header no longer 'flickers' when navigating into topics on mobile. As well as the improved UX, this should also improve our Cumulative Layout Shift (CLS) web vital metrics.
Followup dd30463276
We missed the explicit `return` when we changed to
async/await, so the model ends up being null on admin
backups.
This means we also have no tests for the backup UI, that
will be fixed in a subsequent PR.
This commit promotes the new topic bulk action
menu introduced in 89883b2f51
to the main method of bulk selecting and performing
actions on topics. The site setting flag gating this
feature is deleted, and the old bulk select code is
deleted as well.
The new modal shows a loading spinner while operations
are taking place, allows selecting the action from a dropdown
instead of having a 2-step modal flow,
and also supports additional options for some operations, e.g.
allowing Close silently.
Replaces the existing topic map with the experimental-topic-map made by @awesomerobot.
---------
Co-authored-by: awesomerobot <kris.aubuchon@discourse.org>
This commit introduces the foundation for a new design for the /about page that we're currently working on. The current version will remain available and still be the default until we finish the new version and are ready to roll out. To opt into the new version right now, add one or more group to the `experimental_redesigned_about_page_groups` site setting and members in those groups will get the new version.
Internal topic: t/128545.
If a user has a required action, e.g. adding a 2FA method or filling in new required fields, we disable client-side routing except to allowed pages.
This led to a situation where a user might navigate away from e.g. the profile page to look at the new ToS, and then being "stuck" due to not knowing how to get back to accept the new terms.
This PR makes it so that if you click any restricted link, instead of doing nothing we transition the user back to the page where they can take the required action.
User actions can trigger functions that render changes to the screen within the same cycle (e.g. pressing the reply button will cause the login modal to pop up), potentially impacting performance and causing some jank on slower devices.
This change inserts runAfterFramePaint where certain actions are triggered. Below are some screenshots indicating an improved INP for some of the buttons affected on controls with the highest INPs. The two places where this is added help with several actions, e.g. user + group cards, generic button action usage.
Usage:
```
@validation="integer"
```
This commit also adds a default for rules. By default a rule will now be `ruleName: {}`, this avoids all the boilerplate in validation-parser.js.
We'd implemented the deprecation by overriding `get parentView`, and storing the real value on `_parentView`. Unfortunately that meant people could access `_parentView` directly, thereby bypassing the deprecation message.
This commit moves the internal storage to a private field, which cannot be accessed from outside the class. A deprecated getter for `_parentView` is introduced to avoid immediate breakage for any code using this workaround.
This is a convenience for when you have multiple properties to set in form kit.
```
// before
set("foo", 1);
set("bar", 2);
//after
setProperties({foo: 1, bar: 2});
```
Our old group SMTP SSL option was a checkbox,
but this was not ideal because there are actually
3 different ways SSL can be used when sending
SMTP:
* None
* SSL/TLS
* STARTTLS
We got around this before with specific overrides
for Gmail, but it's not flexible enough and now people
want to use other providers. It's best to be clear,
though it is a technical detail. We provide a way
to test the SMTP settings before saving them so there
should be little chance of messing this up.
This commit also converts GroupEmailSettings to a glimmer
component.
Allow admin to create custom flag which requires an additional message.
I decided to rename the old `custom_flag` into `require_message` as it is more descriptive.
- removes unused css code
- improves password control sizing
- adds more spacing between collection items
- correct a typo in collection class
---------
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
This PR introduces FormKit, a component-based form library designed to simplify form creation and management. This library provides a single `Form` component, various field components, controls, validation mechanisms, and customization options. Additionally, it includes helpers to facilitate testing and writing specifications for forms.
1. **Form Component**:
- The main component that encapsulates form logic and structure.
- Yields various utilities like `Field`, `Submit`, `Alert`, etc.
**Example Usage**:
```gjs
import Form from "discourse/form";
<template>
<Form as |form|>
<form.Field
@name="username"
@title="Username"
@validation="required"
as |field|
>
<field.Input />
</form.Field>
<form.Field @name="age" @title="Age" as |field|>
<field.Input @type="number" />
</form.Field>
<form.Submit />
</Form>
</template>
```
2. **Validation**:
- Built-in validation rules such as `required`, `number`, `length`, and `url`.
- Custom validation callbacks for more complex validation logic.
**Example Usage**:
```javascript
validateUsername(name, value, data, { addError }) {
if (data.bar / 2 === value) {
addError(name, "That's not how maths work.");
}
}
```
```hbs
<form.Field @name="username" @validate={{this.validateUsername}} />
```
3. **Customization**:
- Plugin outlets for extending form functionality.
- Styling capabilities through propagated attributes.
- Custom controls with properties provided by `form` and `field`.
**Example Usage**:
```hbs
<Form class="my-form" as |form|>
<form.Field class="my-field" as |field|>
<MyCustomControl id={{field.id}} @onChange={{field.set}} />
</form.Field>
</Form>
```
4. **Helpers for Testing**:
- Test assertions for form and field validation.
**Example usage**:
```javascript
assert.form().hasErrors("the form shows errors");
assert.form().field("foo").hasValue("bar", "user has set the value");
```
- Helper for interacting with he form
**Example usage**:
```javascript
await formKit().field("foo").fillIn("bar");
```
5. **Page Object for System Specs**:
- Page objects for interacting with forms in system specs.
- Methods for submitting forms, checking alerts, and interacting with fields.
**Example Usage**:
```ruby
form = PageObjects::Components::FormKit.new(".my-form")
form.submit
expect(form).to have_an_alert("message")
```
**Field Interactions**:
```ruby
field = form.field("foo")
expect(field).to have_value("bar")
field.fill_in("bar")
```
6. **Collections handling**:
- A specific component to handle array of objects
**Example Usage**:
```gjs
<Form @data={{hash foo=(array (hash bar=1) (hash bar=2))}} as |form|>
<form.Collection @name="foo" as |collection|>
<collection.Field @name="bar" @title="Bar" as |field|>
<field.Input />
</collection.Field>
</form.Collection>
</Form>
```
In order to facilitate discourse-tag-icons and discourse-category-icons to render icons for post content, we need to provide an additional slug parameter here
Followup 560e8aff75
GitHub auth tokens cannot be made with permissions to
access multiple organisations. This is quite limiting.
This commit changes the site setting to be a "secret list"
type, which allows for a key/value mapping where the value
is treated like a password in the UI.
Now when a GitHub URL is requested for oneboxing, the
org name from the URL is used to determine which token
to use for the request.
Just in case anyone used the old site setting already,
there is a migration to create a `default` entry
with that token in the new list setting, and for
a period of time we will consider that token valid to
use for all GitHub oneboxes as well.
Allow admin to create custom flag which requires an additional message.
I decided to rename the old `custom_flag` into `require_message` as it is more descriptive.
This tightens things up to reduce the number of initializers which need to be wrapped in an IIFE.
Mirrors the changes made in https://github.com/babel/babel/pull/16569
This needs to run before any component files are `import`'d. In traditional resolver-based tests, this was working previously because component files would only be loaded 'at runtime'. However, in gjs-based tests (e.g. those introduced in the formkit PR), component files are imported before the application is booted.
This reverts commit d05f8285e7 and 727acfee6a. This bump introduced a new deprecation message which is very noisy for us. We'll resolve it before merging again.
Followup db993cf8fd
Since in the above commit we converted integer site settings
to actual integers then set that as the new `buffered.value`,
the overridden indicator technically thinks the value has changed,
even if the user sets it back to the default:
```
overridden: propertyNotEqual("setting.default", "buffered.value"),
```
We can fix this by converting the parsed integer back to a string
before setting the buffered setting value.
- set in constructor so they're guaranteed to be present, even if async-import hasn't finished yet
- ensure they're all cleaned up properly
- combine two cleanup methods into one
This patch upgrades the MessageFormat library to version 3.3.0 from
0.1.5.
Our `I18n.messageFormat` method signature is unchanged, and now uses the
new API under the hood.
We don’t need dedicated locale files for handling pluralization rules
anymore as everything is now included by the library itself.
The compilation of the messages now happens through our
`messageformat-wrapper` gem. It then outputs an ES module that includes
all its needed dependencies.
Most of the changes happen in `JsLocaleHelper` and in the `ExtraLocales`
controller.
A new method called `.output_MF` has been introduced in
`JsLocaleHelper`. It handles all the fetching, compiling and
transpiling to generate the proper MF messages in JS. Overrides and
fallbacks are also handled directly in this method.
The other main change is that now the MF translations are served through
the `ExtraLocales` controller instead of being statically compiled in a
JS file, then having to patch the messages using overrides and
fallbacks. Now the MF translations are just another bundle that is
created on the fly and cached by the client.
On the review page, once you select a category to filter by, while you can still change the category, you can not clear it.
After this commit, we pass the "clearable" select-kit option through.
Followup 0434112aa7,
we introduced HideApplicationHeaderButtons there
but didn't validate the buttons passed to it. With this
commit we do, and send an error to the browser console
if an invalid one is used.
When a user action is required and enforced, such as filling up newly added required fields or adding a 2FA method, we disable routing on the client-side. However, this could be bypassed by first loading an always allowed page, such as /faq and then client-side routing away from there.
This commit fixes that by 1) moving the logic for checking if routing is restricted and if a given path is allowed into a service and 2) hoisting the willTransition hook into the application router and use the newly created service to check whether to abort transitions or not.
Last week I disabled smart lists in Firefox in 2ab4913d13.
This week the same issue presented itself in Chrome. Turns out,
the list modification was still not firing at the right time
in the event chain. I investigated and it looks as though
`beforeinput` is a better fit, since:
> This allows web apps to override text edit behavior before the browser
modifies the DOM tree, and provides more control over input events to
improve performance.
c.f. https://developer.mozilla.org/en-US/docs/Web/API/Element/beforeinput_event
and https://webkit.org/blog/7358/enhanced-editing-with-input-events/
and https://www.w3.org/TR/uievents/#events-keyboard-event-order
The order of keyboard events is `keydown` -> `beforeinput` -> `input` -> `keyup`
I changed to detect the event type of `insertLineBreak` which is
not always consistently true in `input` events. If it's true when
`beforeinput` is fired then we go ahead with the smart list when
`input` fires.
Handles the cases where the sections titles are Unicode only strings, allowing them to be expanded separately if the Unicode string contains letters.
Also prevents a sidebar section with the header hidden to be displayed collapsed.
- Delete vendored copy
- Create a JS entrypoint under `static/` which imports all the modes/themes/extensions we need
- Create an async `load-ace-editor` entrypoint
- Update `<AceEditor` component to use the new entrypoint
- De-jquery-ify `<AceEditor`
- Bump `v1.4.13` -> `v1.35.2`
Background:
In order to redrive failed webhook events, an operator has to go through and click on each. This PR is adding a mechanism to retry all failed events to help resolve issues quickly once the underlying failure has been resolved.
What is the change?:
Previously, we had to redeliver each webhook event. This merge is adding a 'Redeliver Failed' button next to the webhook event filter to redeliver all failed events. If there is no failed webhook events to redeliver, 'Redeliver Failed' gets disabled. If you click it, a window pops up to confirm the operator. Failed webhook events will be added to the queue and webhook event list will show the redelivering progress. Every minute, a job will be ran to go through 20 events to redeliver. Every hour, a job will cleanup the redelivering events which have been stored more than 8 hours.
Example:
```hbs
<DBreadcrumbItem
@path="/admin/plugins/{{@plugin.name}}"
@label={{@plugin.nameTitleized}}
/>
```
Using `@path` instead of `@route`+`@model` combo makes it impossible to pass temporarily unresolvable routes.
This fixes a bug with navigating from a model-based route to a parent route.
Adds experimental_flags_admin_page_enabled_groups (default "")
to remove the Moderation Flags link from the admin sidebar for now,
there are still a few bugfixes that need to be done before we
are comfortable with turning this on more widely. This is
a _temporary_ flag, we will be removing this once the feature
is more stable.
The featured topics have not been rendered correctly since 2190c9b and
it has been fixed for desktop recently in commit d2a52c3. This commit
implements similar changes that initialize Category and Topic object
instances from the serialized data.
The AdminPlugin JS model uses a similar pattern to chat models,
where it is a plain JS class manually converting provided
snake_case attributes from the serializer to JS camelCase.
However this doesn't work when it comes to using `add_to_serializer`
in plugins since core does not know about these new attributes.
Instead, we can use a JS function to convert snake_case to camelCase
and use that when initializing AdminPlugin. This commit also moves
similar functions to a new case-converter.js file in
discourse-common/lib.
Followup to e113eff663
We previously sanitized input for integer site settings
on the server side only, which was a bit confusing when
users would enter e.g. 100.5 and end up with 1005, and
not see this reflected in the UI.
Now that we are using native number inputs for these settings,
we can improve the experience a bit by not allowing `.` or `,`
in the input, because it should be whole numbers only, and
add a step size of 1. All other characters are already prevented
in this native number input.
The old implementation used unnecessary `\r\n` and caused the table generator to incorrectly add extra empty lines.
This commit replaces it with `\n`, fixing the bug
Instead of the deprecated 'min trust level to allow ignore' in order to reduce the number of deprecation notices in the logs.
This tweaks a few serializers so that the 'can_ignore_users?` property is always coming from the server and properly used on the client-side.
* FIX: Table Builder editor eradicates column alignment specification
Currently, when you use the table builder to edit an existing table, the table builder does not observe the text-align property of the original table. This results in the original table alignment being lost after editing and reset to no alignment.
This commit fixed this issue
related meta topic: https://meta.discourse.org/t/table-builder-editor-eradicates-column-alignment-specification/299577
Before checking if flags were reordered on the topic page, we need to ensure that the reorder action was finished. To achieve it "saving" CSS is added and removed when AJAX call is completed.
Followup 2f2da72747
This commit moves topic view tracking from happening
every time a Topic is requested, which is susceptible
to inflating numbers of views from web crawlers, to
our request tracker middleware.
In this new location, topic views are only tracked when
the following headers are sent:
* HTTP_DISCOURSE_TRACK_VIEW - This is sent on every page navigation when
clicking around the ember app. We count these as browser page views
because we know it comes from the AJAX call in our app. The topic ID
is extracted from HTTP_DISCOURSE_TRACK_VIEW_TOPIC_ID
* HTTP_DISCOURSE_DEFERRED_TRACK_VIEW - Sent when MessageBus initializes
after first loading the page to count the initial page load view. The
topic ID is extracted from HTTP_DISCOURSE_DEFERRED_TRACK_VIEW.
This will bring topic views more in line with the change we
made to page views in the referenced commit and result in
more realistic topic view counts.
Adds warnings for:
- `discourse.plugin-outlet-tag-name`
- `discourse.plugin-outlet-parent-view`
Also updates the ID list to be strings rather than regex (so that `.` is not treated as a wildcard).
Firefox is having a lot of inconsistent issues with this
feature introduced in 30fdd7738e,
disabling it there for now until further investigation can
be done.
Followup 30fdd7738e,
The issue with keyup is that it happens too late. maybeContinueList
itself runs in about 1 or 2 ms. But we show the linebreak in the
textarea on keydown and we handle it in keyup, which causes the “lag”.
The fix here is “hacking” itsatrap and textarea behavior to allow us to handle
it right away after the linebreak is inserted.
Full credit to Joffrey Jaffeux for this fix, I am making him
"co-author" below.
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
I am changing many of these to notes or resolving them as is,
most of these I have not actively worked on in years so someone
else can work on them when we get to these areas again.
This commit continues work laid out by ffec8163b0 for the admin config page for the /about page. The last commit set up the user interface, and this one sets up all the wiring needed to make the input fields and save buttons actually work.
Internal topic: t/128544.
While creating a new category if the user didn't specify a value for `minimum_required_tags` input but clicked it then it returned the "PG::NotNullViolation: null value in column 'minimum_required_tags'" error.
This improves the way we sanitize `iframe` and correctly strips them from the "raw" before cooking it.
Otherwise, we would show an empty iframe box.
Internal ref - t/131430
When visiting a user profile, and then opening the search, there's an option to filter down by posts made by that user.
When clicking that option, it used to pre-fill the "search bar" with "@<username>" to filter down the search.
This restore this behaviour and add a system spec to ensure it doesn't regress.
Context - https://meta.discourse.org/t/in-posts-by-search-option-does-not-work-when-clicked/312916
If the `enforce_second_factor_on_external_auth` setting
is disabled and a user logs in with an OAuth method,
they don't automatically get redirected to /preferences/second-factor
on login. However, they can get there manually, and once there
they cannot leave.
This commit fixes the issue and allows them to leave
and also does some refactors to indicate to the client
what login method is used as a followup to
0e1102b332
We want to allow admins to make new required fields apply to existing users. In order for this to work we need to have a way to make those users fill up the fields on their next page load. This is very similar to how adding a 2FA requirement post-fact works. Users will be redirected to a page where they can fill up the remaining required fields, and until they do that they won't be able to do anything else.
We didn't escape the "user status" before inserting in in the title of the "user status badge" next to the current user avatar.
This only affects the current user.
Internal ref - t/130332
In 53b3d2f0dc we introduced a stricter BBCode Tag parser. It prevents having "values" with spaces when they're not surrounded by a valid pair of quotes.
The `[details=` BBCode Tag is popular enough that it's worth adding a special case for it (especially since it doesn't support other parameters).
This also adds the Finnish pair of quotes.
Context - https://meta.discourse.org/t/details-accepts-only-one-word-as-summary/313019
Adds a checkbox to filter untranslated text strings in the admin UI, behind a hidden and default `false` site setting `admin_allow_filter_untranslated_text`.
* Removed the link from the title, so the settings can only be accessed via the settings button on the right
* Added an icon to the "Learn more" link to indicate that it opens a new window
* Made various styling adjustments
…to avoid re-evaulation right before destroying.
With `DeferredTrackedSet` we delay both adding and removing elements from the set. That means when you're transitioning between routes, and breadcrumbs change, both old and new breadcrumbs are rendered (briefly, in a first render pass)
And since the arguments for the old breadcrumbs can be (and often are) destroyed - it would blow up the renderer. By caching the template it will reuse it in that first pass.
---
No test because I couldn't figure out a synthetic test setup where you have breadcrumbs in a deeply nested route and where you navigate from that route to one of the parent routes.
This commit introduces behaviour similar to sites
like GitHub, Notion, and others where, if you
are already typing a list and press enter in the composer,
we continue the list on the next line.
Then, if you press enter again on the next line with
an empty list item, we remove that item on that last line.
This works with the following list types:
* star bullet
- dash bullet
* [] star and dash bullet with checkbox
1. numbered
This also works if you are in the middle of a list, and
with indented sub-lists.
With the numbered lists, we continue with the next number
in the sequence, and if you start a new line in the middle
of the list, we renumber the rest of the list.
Previously filter route was not setting topic list, this meant that
keyboard navigation using "G" "J" was not functioning.
This amends it by ensuring the list is set after looking up the model.