* Overhaul the way model visibility scoping works
- Previously post visibility scoping required concrete knowledge of the
parent discussion, ie. you needed a Discussion model on which you
would call `postsVisibleTo($actor)`. This meant that to fetch posts
from different discussions (eg. when listing user posts), it was a
convoluted process, ultimately causing #1333.
Now posts behave like any other model in terms of visibility scoping,
and you simply call `whereVisibleTo($actor)` on a Post query. This
scope will automatically apply a WHERE EXISTS clause that scopes the
query to only include posts whose discussions are visible too. Thus,
fetching posts from multiple discussions can now be done in a single
query, simplifying things greatly and fixing #1333.
- As such, the ScopePostVisibility event has been removed. Also, the
rest of the "Scope" events have been consolidated into a single event,
ScopeModelVisibility. This event is called whenever a user must have
a certain $ability in order to see a set of discussions. Typically
this ability is just "view". But in the case of discussions which have
been marked as `is_private`, it is "viewPrivate". And in the case of
discussions which have been hidden, it is "hide". etc.
The relevant API on AbstractPolicy has been refined, now providing
`find`, `findPrivate`, `findEmpty`, and `findWithPermission` methods.
This could probably do with further refinement and we can re-address
it once we get around to implementing more Extenders.
- An additional change is that Discussion::comments() (the relation
used to calculate the cached number of replies) now yields "comments
that are not private", where before it meant "comments that are
visible to Guests". This was flawed because eg. comments in non-public
tags are technically not visible to Guests.
Consequently, the Approval extension must adopt usage of `is_private`,
so that posts which are not approved are not included in the replies
count. Fundamentally, `is_private` now indicates that a discussion/
post should be hidden by default and should only be visible if it
meets certain criteria. This is in comparison to non-is_private
entities, which are visible by default and may be hidden if they don't
meet certain criteria.
Note that these changes have not been extensively tested, but I have
been over the logic multiple times and it seems to check out.
* Add event to determine whether a discussion `is_private`
See https://github.com/flarum/core/pull/1153#issuecomment-292693624
* Don't include hidden posts in the comments count
* Apply fixes from StyleCI (#1350)
* Add Custom Footer HTML
Straight copy from Custom Header HTML
* Move Custom Footer HTML to exactly before `</body>` tag.
* Fix invalid class name
* Append CustomFooterHTML when preparing the view.
* Some consistency in placing the variable
ref #1025#1238. This should prevent the frontend from crashing when
opening the notifications menu, but we still need to make sure
notifications are deleted properly when subjects are deleted.
The various middleware can be registered in the service provider,
and the rest of the logic can all go through one single front
controller (index.php in flarum/flarum, and Flarum\Http\Server in
flarum/core).
This will also simplify the necessary server setup, as only one
rewrite rule remains.
They will probably be refactored away at a later stage (when we get
rid of the command bus). Until then, this lets us remove the
Flarum\Core namespace and actually feels quite clean.
* Introduce user display names
It is not uncommon for forums to be intergrated with sites where users
don't have a unique "handle" - they might just have their first name,
or a full name, which is not guaranteed to be unique.
This commit introduces the concept of "display names" for users. By
default display names are the same as usernames, but extensions may
override this and set them to something different. The important thing
is that all code should use `display_name` whenever intending to output
a human-readable name - `username` is reserved for cases where you want
to output a unique identifier (which may or may not be human-friendly).
The new "GetDisplayName" API is probably sub-optimal, but I didn't worry
too much because we can come up with something better in `next-back`.
ref #557
* Apply fixes from StyleCI
[ci skip] [skip ci]
Currently all of a post's replies are loaded in full whenever the post
is loaded, which is kind of overkill - we really just need to know that
they exist (and who posted them) in order to render the "X replied to
this" line.
* Update Client.php
Now forwarding exceptions from client to page in case debug mode is on. Fixes#1120.
* Update Client.php
Satisfying .. the unsatisfiable.
* Update Client.php
Satisfying again.
* Fix ListPostsController::applyFilters not receiving array if argument not present
* Whoops! Use `[]` instead of `array()`
* Update AbstractSerializeController.php
* Update ListPostsController.php
* Added option to hide the language selector in the header
* Added `hide_language_selector` Switch to BasicsPage
* Added `hideLanguageSelector` property to ForumSerializer
* Apparently fixed the "Add Extension" button locale.... someone must not have compiled their changes :P
* Changed hideLanguageSelector (and such) to showLanguageSelector
* Change `core.admin.basics.show_language_selector_heading` to be `_label`
* Change showLanguageSelector in ForumSerializer to be boolean, default: true
* Ooops! Remove console.log 🤦♂️
* Fix dependency version constraint. (Reverts #1066.)
* Allow exceptions to be raised when dispatching middleware.
* Fix our error handler middleware (do not implement Stratigility's
error handler interface, catch exceptions instead).
See https://docs.zendframework.com/zend-stratigility/migration/to-v2/.
Closes#1069.
Since extensions may add nested includes, we need to make sure they are eager-loaded to avoid excessive queries. For example, when the tags extension adds "tags" and "tags.state".
Closes#268. Not going to bother with a preview SVG or anything fancy for now – we can think about that as part of #746. Right now it's just good to finally get this functionality in!
Also need to think about apple-touch-icon, msTile stuff, and social sharing image. Not sure if this is all too much for core, but it's definitely too much for the current Appearance page layout. Again, something to think about as part of #746.
Code is a bit rough around the edges, but figured there's not much point in using the command bus properly since #870.
- All custom JS variables are now preloaded into the `app.data` object, rather than directly on the `app` object. This means that admin settings are available in `app.data.settings` rather than `app.settings`, etc.
- Cleaner route handler generation
- Renamed ConfigureClientView to ConfigureWebApp, though the former still exists and is deprecated
- Partial fix for #881 (strips ?nojs=1 from URL if possible, so that refreshing will attempt to load JS version again)
If the authorization header is stripped by CGI wrap,
the server can be configured to send the value along
in an environment variable. If the server admin sticks
to this convention, Flarum can now use this variable.
This is supposed to take care of #384.
- Make sure is_activated is serialized to a bool (otherwise "0" will evaluate to true)
- Remove "error" class from message so it's more friendly
- Make the alert more prominent by mounting it into a new div at the top of the page
- Add loading UX to the resend button
It became apparent in https://github.com/flarum/core/issues/319#issuecomment-170558573 that there was no way for extensions to add filter parameters to the /api/posts endpoint (e.g. /api/posts?filter[mentioned]=1). Simply adding an event to modify the `$where` array severely limits how much can be done with the query. This commit refactors the controller so that filters are applied directly to the query Builder, and exposes the Builder in a new `ConfigurePostsQuery` event.
This means that flood control can be disabled depending on the nature of the request (i.e. when authenticated using a master API key). The particular use case for this is to allow using the API to migrate data from an old forum.
Because extensions can have dependencies injected, a RouteCollection could potentially be instantiated, and thus the ConfigureRoutes event would be called before extensions have had a chance to subscribe to it. Instead, we instantiate the RouteCollection on demand, but only populate it when the application boots.
- Use Symfony's Session component to work with sessions, instead of a custom database model. Separate the concept of access tokens from sessions once again.
- Extract common session/remember cookie logic into SessionAuthenticator and Rememberer classes.
- Extract AuthenticateUserTrait into a new AuthenticationResponseFactory class.
- Fix forgot password process.
- Use cookies + CSRF token for API authentication in the default client. This mitigates potential XSS attacks by making the token unavailable to JavaScript. The Authorization header is still supported, but not used by default.
- Make sensitive/destructive actions (editing a user, permanently deleting anything, visiting the admin CP) require the user to re-enter their password if they haven't entered it in the last 30 minutes.
- Refactor and clean up the authentication middleware.
- Add an `onhide` hook to the Modal component. (+1 squashed commit)
Specifically, the crash would occur when the first notification had a subject without a discussion relationship (e.g. the Subscriptions extension's newPost notification, where the subject itself was a discussion). Instead of simply eager loading the nested subject.discussion relationship, we load discussions manually instead.
This means that the "Start a Discussion" button will still be enabled if the user is not allowed to start globally, but only in certain tags.
Also add some other stuff to the changelog.
closes#640