- Return all discussion post IDs from API requests which add/remove
posts, so the post stream updates appropriately. Related to #146
- Always unload posts that are two pages away, no matter how fast
you’re scrolling
- Retrieve posts from cache instead of reloading them
- Fix various bugs. Maybe #152, needs confirmation
- Use contextual namespaces within Flarum\Core
- Clean up and docblock everything
- Refactor Activity/Notification blueprint stuff
- Refactor Formatter stuff
- Refactor Search stuff
- Upgrade to JSON-API 1.0
- Removed “addedPosts” and “removedPosts” relationships from discussion
API. This was used for adding/removing event posts after renaming a
discussion etc. Instead we should make an additional request to get all
new posts
Todo:
- Fix Extenders and extensions
- Get rid of repository interfaces
- Fix other bugs I’ve inevitably introduced
A good start I think, but still some work to do. If we go ahead with
https://github.com/flarum/core/issues/132#issuecomment-117507974 (which
I am in favour of), we can extract the entity-related stuff into some
smaller service providers (e.g. discussion repo, an event listener,
permissions, and gambits stuff could all go in
Flarum\Core\Discussions\DiscussionsServiceProvider).
Type hinting User should take place in the callbacks. Theoretically
these traits could be used for another project now, where something
else has permissions (like a Sheep class, or a number)
Also disallow the first post in a discussion to be deleted or hidden
(thus preventing discussions with zero posts)
closesflarum/core#90closesflarum/core#92
We need to fire the PostWasDeleted event for every post when a
discussion is deleted. This means deleting big discussions will be an
intensive process, but that’s OK because it’s very rare.
All routes are now stored in a RouteCollection, which is then used
for dispatching by the (reusable) RouterMiddleware.
This change also entails moving all routes to the service providers.
This may be changed again later, and is done for convenience reasons
right now.
Get rid of Permissible - too complex and inefficient. Replace with:
- a “Locked” trait which works similarly but only evaluates logic on
hydrated models.
- a “VisibleScope” trait which also works similarly but only scopes
queries
This is all we need, Permissible is overkill. There is only one
instance where we have to duplicate some logic
(Discussion::scopeVisiblePosts and Post::allow(‘view’, …)) but it’s
barely anything.
Haven’t decoupled for now, we can definitely look at doing that later.
Permissions table seeder slightly updated.
Also did a bit of a query audit, there’s still a lot to be done but
it’s much better than it was. Some relatively low-hanging fruit
detailed in EloquentPostRepository.
Ditched the idea of having language packs as extensions. Reasoning:
1. Because we use machine keys for translations (rather than English
keys), extensions need to be able to define default translations. If
English translations are to be included in extensions and not in a
language pack extension, then it doesn’t make sense to have other
languages as language pack extensions. Inconsistency → complexity.
2. Translations should maintain version parity with their respective
extensions. There’s no way to do this if extension translations are
external to the extension.
Instead, localisation will be a core effort, as well as a per-extension
effort. Translators will be encouraged to send PRs to core + extensions.
In core, each locale has a directory containing three files:
- translations.yml
- config.js: contains pluralisation logic for the JS app, as well as
moment.js localisation if necessary
- config.php: contains pluralisation logic for the PHP app
Extensions can use the Flarum\Extend\Locale extender to add/override
translations/config to a locale.
Asset compilation has been completely refactored with a better
architecture. Translations + config.js are compiled and cached for the
currently active locale.
It works but it’s not the most pretty thing in the world. @franzliedke
Would be great if you could take a look at the whole formatting API and
work your magic on it sometime… my brain is fried!
After a morning of searching, it seems there is no PHP Markdown library
that has built-in XSS/sanitization support. The recommended solution is
to use HTMLPurifier.
This actually works out OK, though, as it’s probably a good idea to
enforce sanitization regardless of which formatters are enabled, and to
not leave them with the responsibility of sanitization (it’s a big
responsibility). Since we cache rendered posts, the slow speed of
HTMLPurifier isn’t a concern.
Note that HTMLPurifier requires a file to be loaded by Composer, but
Studio does not yet support this, so for now I have included it
manually.
Originally the user activity feed was implemented using UNIONs. I was
looking at make an API to add activity “sources”, or extra UNION
queries (select from posts, mentions, etc.) but quickly realised that
this is too slow and there’s no way to make it scale.
So I’ve implemented an API which is very similar to how notifications
work (see previous commit). The `activity` table is an aggregation of
stuff that happens, and it’s kept in sync by an ActivitySyncer which is
used whenever a post it created/edited/deleted, a user is
mentioned/unmentioned, etc.
Again, the API is very simple (see Core\Activity\PostedActivity +
Core\Handlers\Events\UserActivitySyncer)
It turns out that the idea of “sending” a notification is flawed. (What
happens if the notification subject is deleted shortly after? The
notified user would end up with a dud notification which would be
confusing. What about if a post is edited to mention an extra user? If
you sent out notifications, the users who’ve already been mentioned
would get a duplicate notification.)
Instead, I’ve introduced the idea of notification “syncing”. Whenever a
change is made to a piece of data (e.g. a post is created, edited, or
deleted), you make a common notification and “sync” it to a set of
users. The users who’ve received this notification before won’t get it
again. It will be sent out to new users, and hidden from users who’ve
received it before but are no longer recipients (e.g. users who’ve been
“unmentioned” in a post).
To keep track of this, we use the existing notifications database
table, with an added `is_deleted` column. The syncing/diffing is
handled all behind the scenes; the API is extremely simple (see
Core\Notifications\DiscussionRenamedNotification +
Core\Events\Handlers\DiscussionRenamedNotifier)
For now I’ve chucked it on Flarum\Core as a static method, but
ultimately I think we will need a ConfigRepository abstraction (whether
it replaces or sits underneath the Flarum\Core static method I’m not
sure).
Also starting to think about multisite scenarios, I think this is
important. The Forum model could actually end up with a database table
behind it, and each forum would have its own config settings? Haven’t
really thought about it too hard yet…
- The recipient(s) are the concern of the notifier/sender, not the
notification itself
- Allow “retraction” of notifications (e.g. if a discussion is
stickied, but then it is unstickied)
- Misc. cleanup
Perhaps also in user activity stream. They are used in the mentions
extension.
In order to generate the excerpt, each formatter can implement a
“strip” method which basically converts block formatting into inline
formatting.
For example: when you rename a discussion, DiscussionRenamedPost is
created. If you rename it again immediately afterwards, then a new
DiscussionRenamedPost can be merged into the old one. This will either
result in the old one being updated with the new title, or it being
deleted all together if it was renamed back to the old title.
Also make some tweaks:
- Merge SerializeAction::$include and
SerializeAction::$includeAvailable into a keyed boolean array
- Set defaults for SerializeAction::$limit and $limitMax
- Rename SerializeAction::$sortAvailable to $sortFields