1
0
mirror of https://github.com/flarum/core.git synced 2025-08-27 10:05:47 +02:00

Compare commits

..

201 Commits

Author SHA1 Message Date
Franz Liedke
4eaf9ae23f New extender for error handling (#1970)
This extender implements several methods for extending the new error
handling stack implemented in #1843.

Most use-cases should be covered, but I expect some challenges for more
complex setups. We can tackle those once they come up, though. Basic
use-cases should be covered.

Fixes #1781.
2020-02-04 22:59:06 +01:00
flarum-bot
4d80700e97 Bundled output for commit 8877bf97c4 [skip ci] 2020-02-04 22:59:06 +01:00
Franz Liedke
8ad95a4dfb Clarify the use-case of the JS slug helper 2020-02-04 22:59:06 +01:00
Franz Liedke
03faaaedef Use Laravel's slugger for basic transliteration
This is better than the current system, as it adds transliteration rules
for special characters, rather than just throwing all of them away.

For languages that cannot be transliterated to ASCII in a reasonable
manner, more possible improvements are outlined in #194.
2020-02-04 22:59:06 +01:00
flarum-bot
5c3a0e3e6e Bundled output for commit 02ceed4fed [skip ci] 2020-02-04 22:59:06 +01:00
Clark Winkelmann
588f7498e1 Fix the "reply posted" alert empty body 2020-02-04 22:59:06 +01:00
Franz Liedke
8d1240559b Remove unnecessary use statement 2020-02-04 22:59:06 +01:00
ozzzzzzzam
7ad8eb7544 Remove forum title from confirmation email subject (#1613)
The forum title is already used as the display name for the sender email address, so having it in the subject is just a duplication and waste of space.
2020-02-04 22:59:06 +01:00
Matthew Kilgore
ebd2c69c8d Additional functionality for Middleware extender
Implements the remove, insertBefore, insertAfter and replace
functionality for middlewares.

The IoC container now holds one array of middleware (bindings) per
frontend stack - the extender operates on that array, before it is
wrapped in a middleware "pipe".

Fixes #1957, closes #1971.
2020-02-04 22:59:06 +01:00
flarum-bot
637ae1624c Bundled output for commit 02899d4f68 [skip ci] 2020-02-04 22:59:06 +01:00
David Sevilla Martín
0c9bcba3a6 Add Content for User page, preload user & throw 404 accordingly (#1901) 2020-02-04 22:59:06 +01:00
Franz Liedke
5b3acfc0d9 Convert another test
Test the request, not a controller (implementation detail). This also
focuses on the observable behavior instead of hacking our way into the
middleware pipeline in order to observe internal behavior.

The authenticated user is now determined by looking at the API response
to compare permissions and (non-)existing JSON keys.
2020-02-04 22:59:06 +01:00
David Sevilla Martín
7adfb5bd7e Initial template for Stale bot configuration (#1841) 2020-02-04 22:59:06 +01:00
Julian Berger
6b916065e9 Get translations from fallback catalogues (#1961) 2020-02-04 22:59:06 +01:00
Franz Liedke
f99f48b155 Add backwards compatibility layer for mail drivers
Support the old format (a simple list of available fields), in addition
to the new format (a map from field names to their types + metadata).

This will be removed after beta.12 is released.
2020-02-04 22:59:06 +01:00
Franz Liedke
d2c345c834 Document changes in mail driver interface 2020-02-04 22:59:06 +01:00
flarum-bot
966a093911 Bundled output for commit 4c89e2eb77 [skip ci] 2020-02-04 22:59:06 +01:00
Vladimir Vinogradov
0560238945 Add Mailgun region setting
Fixes #1834.
2020-02-04 22:59:05 +01:00
Franz Liedke
63801484fa Ensure page parameters are always integers 2020-02-04 22:59:05 +01:00
Matt Kilgore
f8d92edc9a Change Zend namespace to Laminas (#1963)
Also ensure backwards compatibility for extensions that use the Zend framework but don't explicitly require it.
2020-02-04 22:59:05 +01:00
Daniël Klabbers
e1dbfa7d68 Update LICENSE 2020-02-04 22:59:05 +01:00
Matt Kilgore
ef7623e4ff Middleware extender (#1952) 2020-02-04 22:59:05 +01:00
flarum-bot
d7fd076220 Bundled output for commit c1878fe29b [skip ci] 2020-02-04 22:59:05 +01:00
Franz Liedke
30a2421749 Update Webpack 2020-02-04 22:59:05 +01:00
Franz Liedke
1bf6e79b32 Catch more exceptions during boot process
This extends our boot exception handling block to also catch and format
all exceptions that could be thrown while building our request handler,
i.e. the middleware stack handling requests.

The only exceptions that would now not be handled in this way could be
raised by Zend's `RequestHandlerRunner` and its delegates, which we
should be able to rely on.

Exceptions on request execution will be handled by the error handler in
the middleware stack.

Fixes #1607.
2020-02-04 22:59:05 +01:00
w-4
8aaa39bd4e Fix update page with custom base path (#1947)
Calling UpdateHandler causes RouteNotFoundException when basepath is not /.
2020-02-04 22:59:05 +01:00
Franz Liedke
b1e11830d1 Link to security policy from README 2020-02-04 22:59:05 +01:00
Franz Liedke
3375f283eb FUNDING.yml does not inherit 2020-02-04 22:59:05 +01:00
Franz Liedke
df4c193ab7 Add a custom FUNDING.yml file for this repository
Let's hope GitHub inherits the lines from our default community health
files at https://github.com/flarum/.github.
2020-02-04 22:59:05 +01:00
Daniël Klabbers
5a8326f442 Update CHANGELOG.md 2020-02-04 22:59:05 +01:00
Daniel Klabbers
d8dd870efe releasing beta 11.1 2020-02-04 22:59:05 +01:00
Franz Liedke
60572b93fb Fix implementations of settings repo interface 2020-02-04 22:59:05 +01:00
Daniel Klabbers
810bfdc28f Revert "7.4 release, forcing tests to work with them"
This reverts commit da5628d125.
2020-02-04 22:59:05 +01:00
Daniel Klabbers
9c4a24b258 7.4 release, forcing tests to work with them 2020-02-04 22:59:05 +01:00
David Sevilla Martín
18462c079f Update Application version string to beta 11 2020-02-04 22:59:05 +01:00
Franz Liedke
10d6e653cb Apply fixes from StyleCI
[ci skip] [skip ci]
2020-02-04 22:59:05 +01:00
Franz Liedke
4e1f753f59 Update copyright claims in LICENSE 2020-02-04 22:59:04 +01:00
Daniel Klabbers
3c2dd23765 preparing the changelog for beta 11, part 2 2020-02-04 22:59:04 +01:00
Daniel Klabbers
aebc23cba2 preparing the changelog for beta 11 2020-02-04 22:59:04 +01:00
Clark Winkelmann
f6d03771cb Fix tests to include expectation count and run user saving events 2020-02-04 22:59:04 +01:00
Clark Winkelmann
54be9573ac Add unit test for AvatarUploader 2020-02-04 22:59:04 +01:00
Clark Winkelmann
bf46ea3840 Fix avatar files not being deleted. Fixes #1918 2020-02-04 22:59:04 +01:00
flarum-bot
bab49650e6 Bundled output for commit 17c86b82bf [skip ci] 2020-02-04 22:59:04 +01:00
w-4
a789c6b4e9 history back function fix
it shouldn't check for canGoBack again after the array pop()
2020-02-04 22:59:04 +01:00
Daniel Klabbers
3b3459ad3d incorrect ability used, drop prefix discussion. 2020-02-04 22:59:04 +01:00
Daniel Klabbers
223f4d93d4 test only on the hidePosts policy ability 2020-02-04 22:59:04 +01:00
Daniel Klabbers
5d1fe9b815 resume chain in query builder 2020-02-04 22:59:04 +01:00
Daniël Klabbers
521834f5da [review] using orWhere to allow any where to follow in extensions 2020-02-04 22:59:04 +01:00
Daniël Klabbers
622e2a6644 fixes #1827
- set default statement to block access
- added tests to confirm all scenarios work as intended
2020-02-04 22:59:04 +01:00
Franz Liedke
c5e38a5b1f Automatically set up Mockery for unit tests
- Use provided PhpUnit listener to enforce verification of expectations.
- Include Mockery's trait to auto-close Mockery after each test.
2020-02-04 22:59:04 +01:00
Franz Liedke
9d2595d531 Actually return null
Nullable return types require an explicit null return value; not
returning or returning without value is the "void" type.
2020-02-04 22:59:04 +01:00
David Sevilla Martin
3526083320 Add test for discussion posts being deleted on discussion delete from DB 2020-02-04 22:59:04 +01:00
David Sevilla Martin
82562294b7 Fix failing tests 2020-02-04 22:59:04 +01:00
datitisev
51ae92f841 Apply fixes from StyleCI
[ci skip] [skip ci]
2020-02-04 22:59:04 +01:00
David Sevilla Martin
6448babaa5 Remove 'or' from 'orWhereNotExists' 2020-02-04 22:59:04 +01:00
David Sevilla Martin
f7feea496d Add discussion_id foreign key to posts table 2020-02-04 22:59:04 +01:00
flarum-bot
75e624d7ca Bundled output for commit 6d2b50722a [skip ci] 2020-02-04 22:59:04 +01:00
Clark Winkelmann
5a01b63c99 Pass event to KeyboardNavigatable whenCallback (#1922)
This way the callback can know which key is pressed.
2020-02-04 22:59:04 +01:00
Daniël Klabbers
cae4a6eb45 Fix the queue:restart command (#1932)
Adding a proxy callStatic on our simple implementation of the Manager class allows passing through calls like `forever()` to the underlying cache driver instance.
2020-02-04 22:59:04 +01:00
Franz Liedke
36d6d79011 Add a docblock 2020-02-04 22:59:04 +01:00
Daniël Klabbers
22c599b283 only show queue commands if using another driver than sync 2020-02-04 22:59:04 +01:00
flarum-bot
456f5095da Bundled output for commit 1ba4a0b87e [skip ci] 2020-02-04 22:59:04 +01:00
Daniël Klabbers
0b3ce2e7d0 Fix existing Post component classes being dropped 2020-02-04 22:59:04 +01:00
flarum-bot
607eeb530d Bundled output for commit 1f2566c32c [skip ci] 2020-02-04 22:59:04 +01:00
Daniël Klabbers
5f4efe3c66 Improved naming of class for post by actor.
Made class list for post extensible by using a separate method.
2020-02-04 22:59:04 +01:00
flarum-bot
fac61b5bce Bundled output for commit 19ecd968c6 [skip ci] 2020-02-04 22:59:04 +01:00
Matthew Kilgore
ea64de5952 Removed LESS changes 2020-02-04 22:59:03 +01:00
Matthew Kilgore
ff6c407e53 Set border to left side only 2020-02-04 22:59:03 +01:00
Matthew Kilgore
c4ba13f608 Added border around post made by active user 2020-02-04 22:59:03 +01:00
flarum-bot
cef46ec357 Bundled output for commit 54c5c09693 [skip ci] 2020-02-04 22:59:03 +01:00
David Sevilla Martin
5788c7373e Cleanup some code and fix alert dismiss not working 2020-02-04 22:59:03 +01:00
Moritz Stueckler
8fdd8a1089 feat: re-add debug button/modal
Fixes #1687
2020-02-04 22:59:03 +01:00
David Sevilla Martin
dc31a0a076 Fix Modal width on <768px screens not occupying the whole page 2020-02-04 22:59:03 +01:00
flarum-bot
29eb233dd1 Bundled output for commit 937354512b [skip ci] 2020-02-04 22:59:03 +01:00
Daniël Klabbers
f6e48fa054 Update User.js
Use recommended `anonymous`, see https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin
2020-02-04 22:59:03 +01:00
J.C.Ködel
1b5e8f221a Fix Color Thief cross origin bug
When users have external avatar urls (for instance: in a SSO environment where the avatar is provided by another domain), color thief fails to get the avatar dominant color because the canvas would be tainted. 

Following the instructions here (https://lokeshdhakar.com/projects/color-thief/ on the "Does it work if the image is hosted on another domain?"), adding an `image.crossOrigin = 'Anonymous';` solves the issue.

Tested on my forum which before suffered from a JS error and works fine (without this fix, the canvas remain in the `body` while an script error is thrown by color thief)
2020-02-04 22:59:03 +01:00
Franz Liedke
f903487ef3 Revert search performance regression
We decided it is better to have a less intelligent search (that does not
match search terms in titles) for some people than a bad-performing
search for everyone.

We will revisit the search performance topic in the next release cycle,
possibly with larger changes around indexing.

Refs #1738, #1741, #1764.
2020-02-04 22:59:03 +01:00
Daniël Klabbers
1da4b72eac improve queue error handling 2020-02-04 22:59:03 +01:00
Daniël Klabbers
55bdad55fc added return type hint to memory cache 2020-02-04 22:59:03 +01:00
Daniël Klabbers
b603c7b336 add type hinting to settings repository 2020-02-04 22:59:03 +01:00
luceos
a530c52fb4 Apply fixes from StyleCI
[ci skip] [skip ci]
2020-02-04 22:59:03 +01:00
Daniël Klabbers
f7dc716042 added ability to re-use existing error handling stack 2020-02-04 22:59:03 +01:00
David Sevilla Martin
a72f87d7ee Alias 'flarum.queue.connection' to Queue contract 2020-02-04 22:59:03 +01:00
Tariq Hussein
5dbe97630c Fixes #1877 Replace getIdsForUsername() with subquery instead. (#1878) 2020-02-04 22:59:03 +01:00
flarum-bot
23709a77a2 Bundled output for commit bbd891965f [skip ci] 2020-02-04 22:59:03 +01:00
Madalin Tache
29a2c247a1 Update window size (#1894)
This small change attempts to fix #1727, as i just got my eye on it and figured i could simply fix it while seeing it.
2020-02-04 22:59:03 +01:00
flarum-bot
a7f97c14ec Bundled output for commit 7a684660e9 [skip ci] 2020-02-04 22:59:03 +01:00
David Sevilla Martín
8fcd62955b Enable scrollbars in login button popups (#1900)
Fixes #1716
2020-02-04 22:59:02 +01:00
Daniël Klabbers
618e91805f works towards #1789 by allowing event subscribing (#1810) 2020-02-04 22:59:02 +01:00
Franz Liedke
431ab9f3e8 Amend the existing rel attribute of links
...instead of overwriting. This will play more nicely with extensions.

Refs #859.
2020-02-04 22:59:02 +01:00
Franz Liedke
4f06133d75 Stop opening external links in new tabs
We accept that this may be desired by forum owners and will offer an
extension to enable this feature. By default, we will not make any
assumptions and simply adopt the web's and browsers' default behavior.

Fixes #859.
2020-02-04 22:59:02 +01:00
Franz Liedke
bf79f2474c Cleanup code from #1876
- Extract a method for email address generation
- Consistent types
- No docblocks for types where superfluous
- Tweak console output
- Don't inherit from integration test's base class in unit test
2020-02-04 22:59:02 +01:00
Stefan Totev
1b74e43cb9 Normalize Base URL during installation
- Fix base url when is appended with a script filename
- Add default base url http://flarum.local when CLI wizard used
- Remove some code duplication
- Add minor improvement to the UX when CLI wizard used
- Add tests
- Extract base url normalisation into its own value object
2020-02-04 22:59:02 +01:00
Matteo Contrini
3643e2010b Change rel for external links to nofollow ugc (#1884) 2020-02-04 22:59:02 +01:00
Daniël Klabbers
58299edc20 added author Daniel Klabbers 2020-02-04 22:59:02 +01:00
David Sevilla Martín
18774e0b10 Prepare beta.10 release (#1885)
* Update Application version string to beta 10
* Add beta.10 changelog
2020-02-04 22:59:02 +01:00
Franz Liedke
86d890d043 Restore beta.9 behavior of assertCan()
In flarum/core#1854, I changed the implementation of `assertCan()` to be
more aware of the user's log-in status. I came across this when unifying
our API's response status code when actors are not authenticated or not
authorized to do something.

@luceos rightfully had to tweak this again in ea84fc4, because the
behavior changed for one of the few API endpoints that checked for a
permission that even guests can have.

It turns out having this complex behavior in `assertCan()` is quite
misleading, because the name suggests a simple permission check and
nothing more.

Where we actually want to differ between HTTP 401 and 403, we can do
this using two method calls, and enforce it with our tests.

If this turns out to be problematic or extremely common, we can revisit
this and introduce a method with a different, better name in the future.

This commit restores the method's behavior in the last release, so we
also avoid another breaking change for extensions.
2020-02-04 22:59:02 +01:00
Franz Liedke
ab0ba707e7 Add a test for viewUserList guest permission
This test would have failed without commit ea84fc4. Next, I will revert
that commit and most of my PR #1854, so we need this test to ensure the
API continues to behave as desired.
2020-02-04 22:59:02 +01:00
Franz Liedke
04b2cf4462 Apply fixes from StyleCI
[ci skip] [skip ci]
2020-02-04 22:59:02 +01:00
Franz Liedke
28e3ec4014 Convert more controller tests to feature tests 2020-02-04 22:59:02 +01:00
Franz Liedke
a6decb2350 Update vulnerable JS dependencies 2020-02-04 22:59:02 +01:00
Franz Liedke
1e55361539 Send a HTTP 401 for incorrect login credentials
This fixes a regression from #1843 and #1854. Now, the frontend again
shows the proper "Incorrect login details" message instead of "You
do not have permission to do that".
2020-02-04 22:59:02 +01:00
Franz Liedke
e80f5429d0 Convert another controller test to feature test
Decouple from implementation, test closer to HTTP...
2020-02-04 22:59:02 +01:00
flarum-bot
108a23c1eb Bundled output for commit a9557c399a [skip ci] 2020-02-04 22:58:49 +01:00
David Sevilla Martín
1dd329982a Fix errors caused by deletion alert when deleting users (#1883)
Refs #1788

TypeError: t.showDeletionAlert is not a function
  at onSuccess(./src/forum/utils/UserControls.js:104:12)

Also, don't override 'this' param with user object for editAction
2020-02-04 22:58:49 +01:00
Daniël Klabbers
e0c2ef5e64 moved the artisan binary override and commented some of the bindings for queue 2020-02-04 22:58:49 +01:00
flarum-bot
d654517c91 Bundled output for commit 119831e51c [skip ci] 2020-02-04 22:58:49 +01:00
David Sevilla Martin
0232d949e9 Fixes an issue where deleting a nonexistent model would error instead of resolving gracefully 2020-02-04 22:58:49 +01:00
Daniël Klabbers
6363753d0f prevent constant to be duplicated during tests 2020-02-04 22:58:49 +01:00
luceos
0918b04fe2 Apply fixes from StyleCI
[ci skip] [skip ci]
2020-02-04 22:58:49 +01:00
Daniël Klabbers
929d7b87c1 Fixes an issue where permission checks aren't made for guest users,
due to the gate being accessed after the check whether the user
is registered/signed in.
2020-02-04 22:58:49 +01:00
Daniël Klabbers
544f687cf4 Fixes the queue listen command. We might need to rectify this implementation before stable. 2020-02-04 22:58:49 +01:00
Daniël Klabbers
a7ed625d16 Fixes an issue where a different cache driver is used and Formatter
attempts to load the s9e Renderer from the wrong cache. It has
to be saved locally so that it can be properly loaded using
the spl auto register functionality.
2020-02-04 22:58:49 +01:00
Franz Liedke
a67eca0c9e Fix instructions in PR template 2020-02-04 22:58:49 +01:00
flarum-bot
855dd2445a Bundled output for commit 24964b94bf [skip ci] 2020-02-04 22:58:49 +01:00
David Sevilla Martín
1a3d955b4f Mark notification as read without visiting discussion (#1874) 2020-02-04 22:58:48 +01:00
flarum-bot
8db91e3395 Bundled output for commit 2e647cdda8 [skip ci] 2020-02-04 22:58:48 +01:00
David Sevilla Martín
d725012a84 Fix error thrown if textarea doesn't exist in TextEditor (#1852)
* Prevent textarea not existing from causing errors to be thrown

* Replace [0] with .length
2020-02-04 22:58:48 +01:00
Daniël Klabbers
5a03cd865a listen and restart currently fail in the queue, see #1879 2020-02-04 22:58:48 +01:00
flarum-bot
0a32a96207 Bundled output for commit 8b3913339a [skip ci] 2020-02-04 22:58:48 +01:00
Matthew Kilgore
1587d48e59 Fix the new edit user permission label (#1870) 2020-02-04 22:58:48 +01:00
David Sevilla Martín
b750554011 Add DB prefix to PHP tests (#1855)
* Add test job with PHP 7.3, MySQL & custom prefix

* Add prefix MariaDB test

* Add PHP 7.4 to tests

* Remove PHP 7.4 from tests

This reverts commit 270cba2f5f.
2020-02-04 22:58:48 +01:00
David Sevilla Martín
db7e28d316 Add back defaults for language and direction attributes (#1860) 2020-02-04 22:58:48 +01:00
flarum-bot
14e89546ca Bundled output for commit 0191babb05 [skip ci] 2020-02-04 22:58:48 +01:00
Franz Liedke
92642519d4 Optimize ScrollListener performance
Listen to "scroll" event and throttle callback executions instead
of actively polling for changes to the scroll position.

Fixes #1222.
2020-02-04 22:58:48 +01:00
Franz Liedke
f779f4d092 Fix failing test 2020-02-04 22:58:48 +01:00
Franz Liedke
7b73036441 Debug mode: Include stacktrace in JSON-API errors
Refs #1843, #1865.
2020-02-04 22:58:48 +01:00
Franz Liedke
8b628be507 Refactor JSON-API error formatter 2020-02-04 22:58:48 +01:00
Franz Liedke
51f4bcdcb0 Apply fixes from StyleCI (#1867)
[ci skip] [skip ci]
2020-02-04 22:58:48 +01:00
Franz Liedke
47a528305b Restore error details in JSON-API error formatter
Fixes #1865. Refs #1843.
2020-02-04 22:58:48 +01:00
Franz Liedke
6121229c6f Convert controller test to request test
This further decouples these tests from the implementation (i.e. which
controller are we calling?).
2020-02-04 22:58:48 +01:00
Matteo Contrini
df7f1291a7 Allow formatting post content without a request (#1848) 2020-02-04 22:58:28 +01:00
Matthew Kilgore
52e73b2481 Add Edit User permission to permissions grid (#1859) 2020-02-04 22:58:28 +01:00
Franz Liedke
d08f851c0b When signups are prohibited, respond with HTTP 403 2020-02-04 22:58:28 +01:00
Franz Liedke
22b32bd601 Move authentication check into assertCan() method
This will cause the right error (HTTP 401) to be thrown whenever
we're checking for a specific permission, but the user is not even
logged in. Authenticated users will still get HTTP 403.
2020-02-04 22:58:28 +01:00
Franz Liedke
6797770c75 Remove unnecessary indirection 2020-02-04 22:58:28 +01:00
Franz Liedke
4cab48c0fd Document permission check methods 2020-02-04 22:58:28 +01:00
Franz Liedke
f7222d7e20 Fix inconsistent status codes
HTTP 401 should be used when logging in (i.e. authenticating) would make
a difference; HTTP 403 is reserved for requests that fail because the
already authenticated user is not authorized (i.e. lacking permissions)
to do something.
2020-02-04 22:58:28 +01:00
dependabot[bot]
53c728b184 Bump lodash from 4.17.11 to 4.17.15 in /js (#1863)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.15)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-04 22:58:28 +01:00
dependabot[bot]
1d525d0a78 Bump mixin-deep from 1.3.1 to 1.3.2 in /js (#1862)
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-04 22:58:28 +01:00
Franz Liedke
301e571772 Remove unnecessary dependency
Refs #1773.
2020-02-04 22:58:28 +01:00
Franz Liedke
e7c12ce928 Remove superfluous ForbiddenException
It has the same effect as the PermissionDeniedException, so let's
just use that.

Refs #1641.
2020-02-04 22:58:28 +01:00
Franz Liedke
5d5ebc088e Travis: Remove deploy key 2020-02-04 22:55:25 +01:00
David Sevilla Martín
6e62240153 Move to GitHub Actions (#1853) 2020-02-04 22:55:25 +01:00
Franz Liedke
17d1942c5c Error handling: Document another interface 2020-02-04 22:55:25 +01:00
Franz Liedke
e786e297ef Rename method 2020-02-04 22:55:25 +01:00
Franz Liedke
2829618814 Error handling: Tweak Reporter interface
Because reporters are used for exceptions we were not able to handle, it
makes sense to simply pass the exception, not the "handled error".
2020-02-04 22:55:25 +01:00
Franz Liedke
5875b31fd5 Error handling: Document classes and interfaces 2020-02-04 22:55:25 +01:00
Franz Liedke
ae59bf549f Error handling: Rename renderers to formatters
Refs #1641.
2020-02-04 22:55:25 +01:00
Franz Liedke
d45bf04341 Remove obsolete queue config 2020-02-04 22:55:25 +01:00
Daniël Klabbers
7f9588af62 Queue support (#1773)
Implementation of clean queue handling, by default sync is used
2020-02-04 22:55:25 +01:00
Franz Liedke
17dfb58590 Don't fail when extend.php doesn't return an array
Refs #1607.
2020-02-04 22:55:25 +01:00
Franz Liedke
c5e3e26d07 #1607: Show more details when catching boot errors 2020-02-04 22:55:25 +01:00
Franz Liedke
5d768db6d2 Bubble up exception for invalid confirmation token
This way, the error handler can simply be amended to deal with this
exception type with a dedicated error message or page.

Refs #1337.
Closes #1528.
2020-02-04 22:55:25 +01:00
Franz Liedke
6e089c12d4 Determine error view and message based on type
...not based on status code.

To simplify this logic, we now use the same error "type" both when
routes are not found and specific models are not found. One exception is
ours, one is from Laravel, but for the purposes of error handling they
should be treated the same.

Fixes flarum/core#1641.
2020-02-04 22:55:25 +01:00
flarum-bot
5ddb843eb2 Bundled output for commit 29df6b60be [skip ci] 2020-02-04 22:55:25 +01:00
Franz Liedke
bbeacc0299 Tweak translation keys, always use full keys
Makes them easier to grep when editing / removing.

Refs #1750, #1788.
2020-02-04 22:55:25 +01:00
Franz Liedke
82480457ce Extract real method
Refs #1750, #1788.
2020-02-04 22:55:25 +01:00
flarum-bot
685459c0bc Bundled output for commit 37e0a5579b [skip ci] 2020-02-04 22:55:25 +01:00
Tobias Karlsson
347edcf2cd Improve feedback on user deletion
Fixes #1750, #1777
2020-02-04 22:55:25 +01:00
Franz Liedke
731a038f29 Support multiple error reporters
The error handling middleware now expects an array of reporters.
Extensions can register new reporters in the container like this:

    use Flarum\Foundation\ErrorHandling\Reporter;

    $container->tag(NewReporter::class, Reporter::class);

Note that this is just an implementation detail and will be hidden
behind an extender.
2020-02-04 22:55:25 +01:00
Franz Liedke
af5113eb7b Remove old error handler, middleware and tests 2020-02-04 22:55:25 +01:00
Franz Liedke
ddfb2c1ec1 API Client: Use new error handling mechanism 2020-02-04 22:37:25 +01:00
Franz Liedke
6cf3c1088d Use new error handler middleware 2020-02-04 22:37:24 +01:00
Franz Liedke
2f174edfd0 Wire up new error handling stack 2020-02-04 22:37:24 +01:00
Franz Liedke
2c231aa475 Make existing extensions compatible with new stack 2020-02-04 22:37:24 +01:00
Franz Liedke
1e5c7e54ee Implement new error handling stack
This separates the error registry (mapping exception types to status
codes) from actual handling (the middleware) as well as error formatting
(Whoops, pretty error pages or JSON-API?) and reporting (log? Sentry?).

The components can be reused in different places (e.g. the API client
and the error handler middleware both need the registry to understand
all the exceptions Flarum knows how to handle), while still allowing to
change only the parts that need to change (the API stack always uses the
JSON-API formatter, and the forum stack switches between Whoops and
pretty error pages based on debug mode).

Finally, this paves the way for some planned features and extensibility:
- A console error handler can build on top of the registry.
- Extensions can register new exceptions and how to handle them.
- Extensions can change how we report exceptions (e.g. Sentry).
- We can build more pretty error pages, even different ones for
  exceptions having the same status code.
2020-02-04 22:37:24 +01:00
Franz Liedke
408043a203 Remove obsolete constructor parameter
This was removed in commit 484c6d2e.
2020-02-04 22:37:24 +01:00
flarum-bot
9b449386d6 Bundled output for commit c5122bf5d5 [skip ci] 2020-02-04 22:37:24 +01:00
Franz Liedke
f1d9753aee a11y: Try to make screenreaders read tooltips
Refs #1835.
2020-02-04 22:37:24 +01:00
David Sevilla Martín
54f733ca80 Add canonical URL to discussion list (#1814) 2020-02-04 22:37:24 +01:00
Franz Liedke
a737b98e7f Bypass CSRF token check when using access tokens
Fixes #1828.
2020-02-04 22:37:24 +01:00
Franz Liedke
80546b9ed7 Make exception message dynamic as well 2020-02-04 22:37:24 +01:00
Franz Liedke
9758dfac47 Determine default route after extensions
Fixes #1819.
2020-02-04 22:37:24 +01:00
Franz Liedke
970c0f5604 PHPUnit: Get rid of deprecated annotation
Refs #1795.
2020-02-04 22:37:24 +01:00
Daniël Klabbers
42a7f2f586 Allows configuration of where the language files live. So that
language packs can optionally decide for themselves if they want
to use a different directory.
2020-02-04 22:37:24 +01:00
Daniël Klabbers
3611fa1bb9 fixes #1695, take into consideration is_private with counts on User stats 2020-02-04 22:37:24 +01:00
Daniël Klabbers
c881f9f633 fixed ci, make green again; mysql service wasnt booted 2020-02-04 22:37:24 +01:00
Franz Liedke
0a22a66189 Prevent MySQL search operators from taking effect
We do not want to inherit MySQL's fulltext query language, so let's
just drop all non-word characters from the search term.

Fixes #1498.
2020-02-04 22:37:24 +01:00
Franz Liedke
64b53fb0ac Revert "Remove deprecated bootstrap.php fallback"
This reverts commit f8061bbca1.

We will keep this fallback in place, to avoid unnecessary breakage of
backwards compatibility for extension authors.

Removal is planned for the final 0.1 release.
2020-02-04 22:37:24 +01:00
Franz Liedke
627724839d Clean up database query
- Use existing `selectRaw()` method to avoid using the global `app()`
  helper as a service locator, which hides dependencies.
- Do the same for the join.
- The `Expression` is necessary to prevent the aliased column from being
  prefixed with the database table prefix, if configured.
2020-02-04 22:37:24 +01:00
dependabot[bot]
fa3915fa53 Bump lodash-es from 4.17.11 to 4.17.14 in /js (#1818)
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.11 to 4.17.14.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.14)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-04 22:37:24 +01:00
luceos
1948b7e6f4 Apply fixes from StyleCI
[ci skip] [skip ci]
2020-02-04 21:11:08 +00:00
Daniël Klabbers
4465e7648b move deprecated methods to trait for cleaner code until we remove it 2020-02-04 22:10:39 +01:00
luceos
aef56c055a Apply fixes from StyleCI
[ci skip] [skip ci]
2019-11-19 11:38:56 +00:00
Daniël Klabbers
dd3a4173a1 noop instead of exception 2019-11-19 12:38:45 +01:00
Daniël Klabbers
7c204c82ab attempt to be more decisive on forcing the new user preferences 2019-11-13 14:35:42 +01:00
Daniël Klabbers
12fff33763 started refactoring the User class to the Notification Preference class 2019-10-28 10:27:38 +01:00
Daniël Klabbers
603367a41a added followAfterReply to core 2019-10-25 22:38:30 +02:00
luceos
6bdebfbf3c Apply fixes from StyleCI
[ci skip] [skip ci]
2019-10-25 20:33:57 +00:00
Daniël Klabbers
58ab6052ad reordered migrations 2019-09-28 21:02:45 +02:00
luceos
3737ce8146 Apply fixes from StyleCI
[ci skip] [skip ci]
2019-07-09 20:17:16 +00:00
Daniël Klabbers
ca5404db76 Merge branch '1236-user-preferences' of github.com:flarum/core into 1236-user-preferences 2019-07-09 22:17:00 +02:00
Daniël Klabbers
d6fc3a91a6 removed references to preferences column, now we need to refactor how notification ppreferences is integrated into the current app 2019-07-09 22:16:51 +02:00
luceos
31134ca16d Apply fixes from StyleCI
[ci skip] [skip ci]
2019-07-09 19:42:15 +00:00
Daniël Klabbers
6cfc9182f4 added the drop column statement for user.preferences and tested migrations 2019-07-09 21:41:57 +02:00
Daniël Klabbers
caa63107ad add migration to drop preferences column 2019-07-09 21:22:37 +02:00
Daniël Klabbers
0acab8f1c7 Merge branch 'master' into 1236-user-preferences 2019-07-09 21:19:41 +02:00
Daniël Klabbers
c19b2e99bd Merge branch 'master' into 1236-user-preferences 2019-03-08 22:07:54 +01:00
Daniël Klabbers
e7a5e1e5e2 Merge branch 'master' into 1236-user-preferences 2018-12-13 20:19:47 +01:00
Daniel Klabbers
209c4c6143 moved user preferences to new branch 2018-06-19 09:40:53 +02:00
73 changed files with 1359 additions and 809 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
github: flarum
open_collective: flarum
tidelift: packagist/flarum/core

26
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
daysUntilStale: 90
daysUntilClose: 30
staleLabel: stale
exemptLabels:
- org/keep
- type/bug
- type/regression
- critical
- security
exemptAssignees: true
exemptMilestones: true
exemptProjects: true
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. We do this
to keep the amount of open issues to a manageable minimum.
In any case, thanks for taking an interest in this software and contributing
by opening the issue in the first place!
closeComment: >
We are closing this issue as it seems to have grown stale. If you still
encounter this problem with the latest version, feel free to re-open it.

View File

@@ -1,6 +1,6 @@
# Changelog
## [0.1.0-beta.11](https://github.com/flarum/core/compare/v0.1.0-beta.11...v0.1.0-beta.11.1)
## [0.1.0-beta.11.1](https://github.com/flarum/core/compare/v0.1.0-beta.11...v0.1.0-beta.11.1)
### Fixed

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2019 Stichting Flarum (Flarum Foundation)
Copyright (c) 2019-2020 Stichting Flarum (Flarum Foundation)
Copyright (c) 2014-2019 Toby Zerner (toby.zerner@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@@ -27,7 +27,7 @@ Thank you for considering contributing to Flarum! Please read the **[Contributin
## Security Vulnerabilities
If you discover a security vulnerability within Flarum, please send an e-mail to [security@flarum.org](mailto:security@flarum.org). All security vulnerabilities will be promptly addressed.
If you discover a security vulnerability within Flarum, please send an e-mail to [security@flarum.org](mailto:security@flarum.org). All security vulnerabilities will be promptly addressed. More details can be found in our [security policy](https://github.com/flarum/core/security/policy).
## License

View File

@@ -46,6 +46,10 @@
"illuminate/validation": "5.7.*",
"illuminate/view": "5.7.*",
"intervention/image": "^2.3.0",
"laminas/laminas-diactoros": "^1.8.4",
"laminas/laminas-httphandlerrunner": "^1.0",
"laminas/laminas-stratigility": "^3.0",
"laminas/laminas-zendframework-bridge": "^1.0",
"league/flysystem": "^1.0.11",
"matthiasmullie/minify": "^1.3",
"middlewares/base-path": "^1.1",
@@ -63,10 +67,7 @@
"symfony/event-dispatcher": "^4.3.2",
"symfony/translation": "^3.3",
"symfony/yaml": "^3.3",
"tobscure/json-api": "^0.3.0",
"zendframework/zend-diactoros": "^1.8.4",
"zendframework/zend-httphandlerrunner": "^1.0",
"zendframework/zend-stratigility": "^3.0"
"tobscure/json-api": "^0.3.0"
},
"require-dev": {
"mockery/mockery": "^1.0",

20
js/dist/admin.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

22
js/dist/forum.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

777
js/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
"moment": "^2.22.2",
"punycode": "^2.1.1",
"spin.js": "^3.1.0",
"webpack": "^4.26.0",
"webpack": "^4.41.2",
"webpack-cli": "^3.1.2",
"webpack-merge": "^4.1.4"
},

View File

@@ -29,12 +29,13 @@ export default class MailPage extends Page {
{}
);
Object.keys(this.driverFields).flatMap(key => this.driverFields[key]).forEach(
key => {
this.fields.push(key);
this.values[key] = m.prop(settings[key]);
for (const driver in this.driverFields) {
for (const field in this.driverFields[driver]) {
this.fields.push(field);
this.values[field] = m.prop(settings[field]);
}
);
}
this.loading = false;
m.redraw();
});
@@ -87,9 +88,9 @@ export default class MailPage extends Page {
className: 'MailPage-MailSettings',
children: [
<div className="MailPage-MailSettings-input">
{this.driverFields[this.values.mail_driver()].flatMap(field => [
{Object.keys(this.driverFields[this.values.mail_driver()]).map(field => [
<label>{app.translator.trans(`core.admin.email.${field}_label`)}</label>,
<input className="FormControl" value={this.values[field]() || ''} oninput={m.withAttr('value', this.values[field])} />
this.renderField(field),
])}
</div>
]
@@ -108,6 +109,18 @@ export default class MailPage extends Page {
);
}
renderField(name) {
const driver = this.values.mail_driver();
const field = this.driverFields[driver][name];
const prop = this.values[name];
if (typeof field === 'string') {
return <input className="FormControl" value={prop() || ''} oninput={m.withAttr('value', prop)}/>;
} else {
return <Select value={prop()} options={field} onchange={prop} />;
}
}
changed() {
return this.fields.some(key => this.values[key]() !== app.data.settings[key]);
}

View File

@@ -16,6 +16,10 @@ export function truncate(string, length, start = 0) {
* Create a slug out of the given string. Non-alphanumeric characters are
* converted to hyphens.
*
* NOTE: This method does not use the comparably sophisticated transliteration
* mechanism that is employed in the backend. Therefore, it should only be used
* to *suggest* slugs that can be overridden by the user.
*
* @param {String} string
* @return {String}
*/

View File

@@ -102,7 +102,7 @@ export default class ReplyComposer extends ComposerBody {
app.alerts.show(
alert = new Alert({
type: 'success',
message: app.translator.trans('core.forum.composer_reply.posted_message'),
children: app.translator.trans('core.forum.composer_reply.posted_message'),
controls: [viewButton]
})
);

View File

@@ -88,8 +88,13 @@ export default class UserPage extends Page {
loadUser(username) {
const lowercaseUsername = username.toLowerCase();
// Load the preloaded user object, if any, into the global app store
// We don't use the output of the method because it returns raw JSON
// instead of the parsed models
app.preloadedApiDocument();
app.store.all('users').some(user => {
if (user.username().toLowerCase() === lowercaseUsername && user.joinTime()) {
if ((user.username().toLowerCase() === lowercaseUsername || user.id() === username) && user.joinTime()) {
this.show(user);
return true;
}

View File

@@ -7,9 +7,9 @@
* LICENSE file that was distributed with this source code.
*/
use Flarum\Util\Str;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
use Illuminate\Support\Str;
return [
'up' => function (Builder $schema) {

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->create('notification_preferences', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->string('type');
$table->string('channel');
$table->boolean('enabled')->default(false);
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
},
'down' => function (Builder $schema) {
$schema->drop('notification_preferences');
}
];

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$table->boolean('disclose_online')->default(false);
$table->string('locale')->nullable();
});
},
'down' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$table->dropColumn('disclose_online');
$table->dropColumn('locale');
});
}
];

View File

@@ -0,0 +1,52 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
use Illuminate\Database\Schema\Builder;
use Illuminate\Support\Str;
return [
'up' => function (Builder $builder) {
$db = $builder->getConnection();
$db->table('users')
->select(['id', 'preferences'])
->whereNotNull('preferences')
->orderBy('id')
->each(function ($user) use ($db) {
collect(json_decode($user->preferences ?? '{}'))
->each(function ($value, $key) use ($user, $db) {
if (in_array($key, ['discloseOnline', 'followAfterReply'])) {
$db->table('users')
->where('id', $user->id)
->update([Str::snake($key) => (bool) $value]);
}
if ($key === 'locale') {
$db->table('users')
->where('id', $user->id)
->update(['locale' => $value]);
}
if (preg_match('/^notify_(?<type>[^_]+)_(?<channel>.*)$/', $key, $matches)) {
$db->table('notification_preferences')
->insert([
'user_id' => $user->id,
'type' => $matches['type'],
'channel' => $matches['channel'],
'enabled' => (bool) $value
]);
}
});
});
},
'down' => function (Builder $builder) {
$db = $builder->getConnection();
$db->table('notification_preferences')->truncate();
}
];

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$table->dropColumn('preferences');
});
},
'down' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$table->binary('preferences')->nullable();
});
}
];

View File

@@ -29,7 +29,7 @@ use Flarum\Http\RouteHandlerFactory;
use Flarum\Http\UrlGenerator;
use Flarum\Locale\LocaleManager;
use Flarum\Settings\Event\Saved;
use Zend\Stratigility\MiddlewarePipe;
use Laminas\Stratigility\MiddlewarePipe;
class AdminServiceProvider extends AbstractServiceProvider
{
@@ -49,7 +49,19 @@ class AdminServiceProvider extends AbstractServiceProvider
return $routes;
});
$this->app->singleton('flarum.admin.middleware', function (Application $app) {
$this->app->singleton('flarum.admin.middleware', function () {
return [
HttpMiddleware\ParseJsonBody::class,
HttpMiddleware\StartSession::class,
HttpMiddleware\RememberFromCookie::class,
HttpMiddleware\AuthenticateWithSession::class,
HttpMiddleware\CheckCsrfToken::class,
HttpMiddleware\SetLocale::class,
Middleware\RequireAdministrateAbility::class,
];
});
$this->app->singleton('flarum.admin.handler', function (Application $app) {
$pipe = new MiddlewarePipe;
// All requests should first be piped through our global error handler
@@ -59,21 +71,15 @@ class AdminServiceProvider extends AbstractServiceProvider
$app->tagged(Reporter::class)
));
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
$pipe->pipe($app->make(Middleware\RequireAdministrateAbility::class));
foreach ($this->app->make('flarum.admin.middleware') as $middleware) {
$pipe->pipe($this->app->make($middleware));
}
event(new ConfigureMiddleware($pipe, 'admin'));
return $pipe;
});
$this->app->afterResolving('flarum.admin.middleware', function (MiddlewarePipe $pipe) {
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.admin.routes')));
return $pipe;
});
$this->app->bind('flarum.assets.admin', function () {

View File

@@ -25,7 +25,7 @@ use Flarum\Http\Middleware as HttpMiddleware;
use Flarum\Http\RouteCollection;
use Flarum\Http\RouteHandlerFactory;
use Flarum\Http\UrlGenerator;
use Zend\Stratigility\MiddlewarePipe;
use Laminas\Stratigility\MiddlewarePipe;
class ApiServiceProvider extends AbstractServiceProvider
{
@@ -45,7 +45,20 @@ class ApiServiceProvider extends AbstractServiceProvider
return $routes;
});
$this->app->singleton('flarum.api.middleware', function (Application $app) {
$this->app->singleton('flarum.api.middleware', function () {
return [
HttpMiddleware\ParseJsonBody::class,
Middleware\FakeHttpMethods::class,
HttpMiddleware\StartSession::class,
HttpMiddleware\RememberFromCookie::class,
HttpMiddleware\AuthenticateWithSession::class,
HttpMiddleware\AuthenticateWithHeader::class,
HttpMiddleware\CheckCsrfToken::class,
HttpMiddleware\SetLocale::class,
];
});
$this->app->singleton('flarum.api.handler', function (Application $app) {
$pipe = new MiddlewarePipe;
$pipe->pipe(new HttpMiddleware\HandleErrors(
@@ -54,22 +67,15 @@ class ApiServiceProvider extends AbstractServiceProvider
$app->tagged(Reporter::class)
));
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(Middleware\FakeHttpMethods::class));
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithHeader::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
foreach ($this->app->make('flarum.api.middleware') as $middleware) {
$pipe->pipe($this->app->make($middleware));
}
event(new ConfigureMiddleware($pipe, 'api'));
return $pipe;
});
$this->app->afterResolving('flarum.api.middleware', function (MiddlewarePipe $pipe) {
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.api.routes')));
return $pipe;
});
}

View File

@@ -15,10 +15,10 @@ use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\User\User;
use Illuminate\Contracts\Container\Container;
use InvalidArgumentException;
use Laminas\Diactoros\ServerRequestFactory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Throwable;
use Zend\Diactoros\ServerRequestFactory;
class Client
{

View File

@@ -9,10 +9,10 @@
namespace Flarum\Api\Controller;
use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\EmptyResponse;
abstract class AbstractDeleteController implements RequestHandlerInterface
{

View File

@@ -11,10 +11,10 @@ namespace Flarum\Api\Controller;
use Flarum\Foundation\Console\CacheClearCommand;
use Flarum\User\AssertPermissionTrait;
use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Zend\Diactoros\Response\EmptyResponse;
class ClearCacheController extends AbstractDeleteController
{

View File

@@ -15,10 +15,10 @@ use Flarum\User\UserRepository;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\JsonResponse;
class CreateTokenController implements RequestHandlerInterface
{

View File

@@ -12,10 +12,10 @@ namespace Flarum\Api\Controller;
use Flarum\Foundation\Application;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\AssertPermissionTrait;
use Laminas\Diactoros\Response\EmptyResponse;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\EmptyResponse;
class DeleteFaviconController extends AbstractDeleteController
{

View File

@@ -12,10 +12,10 @@ namespace Flarum\Api\Controller;
use Flarum\Foundation\Application;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\AssertPermissionTrait;
use Laminas\Diactoros\Response\EmptyResponse;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\EmptyResponse;
class DeleteLogoController extends AbstractDeleteController
{

View File

@@ -13,10 +13,10 @@ use Flarum\User\Command\RequestPasswordReset;
use Flarum\User\UserRepository;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\EmptyResponse;
class ForgotPasswordController implements RequestHandlerInterface
{

View File

@@ -17,11 +17,11 @@ use Flarum\User\Exception\PermissionDeniedException;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Mail\Message;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Zend\Diactoros\Response\EmptyResponse;
class SendConfirmationEmailController implements RequestHandlerInterface
{
@@ -86,9 +86,9 @@ class SendConfirmationEmailController implements RequestHandlerInterface
$body = $this->translator->trans('core.email.activate_account.body', $data);
$this->mailer->raw($body, function (Message $message) use ($actor, $data) {
$this->mailer->raw($body, function (Message $message) use ($actor) {
$message->to($actor->email);
$message->subject('['.$data['{forum}'].'] '.$this->translator->trans('core.email.activate_account.subject'));
$message->subject($this->translator->trans('core.email.activate_account.subject'));
});
return new EmptyResponse;

View File

@@ -12,10 +12,10 @@ namespace Flarum\Api\Controller;
use Flarum\Group\Permission;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\EmptyResponse;
class SetPermissionController implements RequestHandlerInterface
{

View File

@@ -13,10 +13,10 @@ use Flarum\Settings\Event;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Contracts\Events\Dispatcher;
use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\EmptyResponse;
class SetSettingsController implements RequestHandlerInterface
{

View File

@@ -12,10 +12,10 @@ namespace Flarum\Api\Controller;
use Flarum\Extension\ExtensionManager;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\EmptyResponse;
class UpdateExtensionController implements RequestHandlerInterface
{

View File

@@ -9,8 +9,8 @@
namespace Flarum\Api;
use Laminas\Diactoros\Response\JsonResponse;
use Tobscure\JsonApi\Document;
use Zend\Diactoros\Response\JsonResponse;
class JsonApiResponse extends JsonResponse
{

View File

@@ -33,10 +33,20 @@ class MailDriverSerializer extends AbstractSerializer
);
}
$driver = $driver['driver'];
$settings = $driver['driver']->availableSettings();
if (key($settings) === 0) {
// BACKWARDS COMPATIBILITY: Support a simple list of fields (without
// type or additional metadata).
// Turns ["f1", "f2"] into {"f1": "", "f2": ""}
// @deprecated since 0.1.0-beta.12
$settings = array_reduce($settings, function ($memo, $key) {
return [$key => ''] + $memo;
}, []);
}
return [
'fields' => $driver->availableSettings(),
'fields' => $settings,
];
}

View File

@@ -23,7 +23,7 @@ use Flarum\Notification\Notification;
use Flarum\Post\MergeableInterface;
use Flarum\Post\Post;
use Flarum\User\User;
use Flarum\Util\Str;
use Illuminate\Support\Str;
/**
* @property int $id

View File

@@ -9,7 +9,7 @@
namespace Flarum\Event;
use Zend\Stratigility\MiddlewarePipe;
use Laminas\Stratigility\MiddlewarePipe;
/**
* @deprecated

View File

@@ -0,0 +1,120 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Extend;
use Flarum\Extension\Extension;
use Flarum\Foundation\ErrorHandling\Reporter;
use Illuminate\Contracts\Container\Container;
class ErrorHandling implements ExtenderInterface
{
private $statuses = [];
private $types = [];
private $handlers = [];
private $reporters = [];
/**
* Define the corresponding HTTP status code for a well-known error type.
*
* This can be used to configure the status code (second parameter) to be
* used for the error response when encountering an exception with a certain
* type (first parameter). This type can be provided by the exception class
* itself (if it implements {@see \Flarum\Foundation\KnownError}), or
* explicitly defined by using the {@see type} method (useful for exception
* classes not under your control).
*/
public function status(string $errorType, int $httpStatus)
{
$this->statuses[$errorType] = $httpStatus;
return $this;
}
/**
* Define the internal error type for a specific exception class.
*
* If the exception class is under your control, you should prefer having
* the exception implement our {@see \Flarum\Fondation\KnownError} interface
* and define the type there. This method should only be used for third-
* party exceptions, e.g. when integrating another package that already
* defines its own exception classes.
*/
public function type(string $exceptionClass, string $errorType)
{
$this->types[$exceptionClass] = $errorType;
return $this;
}
/**
* Register a handler with custom error handling logic.
*
* When Flarum's default error handling is not enough for you, and the other
* methods of this extender don't help, this is the place where you can go
* wild! Using this method, you can define a handler class (second
* parameter) that will be responsible for exceptions of a certain type
* (first parameter).
*
* The handler class must implement a handle() method (surprise!), which
* returns a {@see \Flarum\Foundation\ErrorHandling\HandledError} instance.
* Besides the usual type and HTTP status code, such an object can also
* contain "details" - arbitrary data with more context for to the error.
*/
public function handler(string $exceptionClass, string $handlerClass)
{
$this->handlers[$exceptionClass] = $handlerClass;
return $this;
}
/**
* Register an error reporter.
*
* Reporters will be called whenever Flarum encounters an exception that it
* does not know how to handle (i.e. none of the well-known exceptions that
* have an associated error type). They can then e.g. write the exception to
* a log, or send it to some external service, so that developers and/or
* administrators are notified about the error.
*
* When passing in a reporter class, make sure that it implements the
* {@see \Flarum\Foundation\ErrorHandling\Reporter} interface.
*/
public function reporter(string $reporterClass)
{
$this->reporters[] = $reporterClass;
return $this;
}
public function extend(Container $container, Extension $extension = null)
{
if (count($this->statuses)) {
$container->extend('flarum.error.statuses', function ($statuses) {
return array_merge($statuses, $this->statuses);
});
}
if (count($this->types)) {
$container->extend('flarum.error.classes', function ($types) {
return array_merge($types, $this->types);
});
}
if (count($this->handlers)) {
$container->extend('flarum.error.handlers', function ($handlers) {
return array_merge($handlers, $this->handlers);
});
}
foreach ($this->reporters as $reporterClass) {
$container->tag($reporterClass, Reporter::class);
}
}
}

101
src/Extend/Middleware.php Normal file
View File

@@ -0,0 +1,101 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Extend;
use Flarum\Extension\Extension;
use Illuminate\Contracts\Container\Container;
class Middleware implements ExtenderInterface
{
protected $addMiddlewares = [];
protected $removeMiddlewares = [];
protected $replaceMiddlewares = [];
protected $insertBeforeMiddlewares = [];
protected $insertAfterMiddlewares = [];
protected $frontend;
public function __construct(string $frontend)
{
$this->frontend = $frontend;
}
public function add($middleware)
{
$this->addMiddlewares[] = $middleware;
return $this;
}
public function replace($originalMiddleware, $newMiddleware)
{
$this->replaceMiddlewares[$originalMiddleware] = $newMiddleware;
return $this;
}
public function remove($middleware)
{
$this->removeMiddlewares[] = $middleware;
return $this;
}
public function insertBefore($originalMiddleware, $newMiddleware)
{
$this->replaceMiddlewares[$originalMiddleware] = $newMiddleware;
return $this;
}
public function insertAfter($originalMiddleware, $newMiddleware)
{
$this->replaceMiddlewares[$originalMiddleware] = $newMiddleware;
return $this;
}
public function extend(Container $container, Extension $extension = null)
{
$container->extend("flarum.{$this->frontend}.middleware", function ($existingMiddleware) {
foreach ($this->addMiddlewares as $addMiddleware) {
$existingMiddleware[] = $addMiddleware;
}
foreach ($this->replaceMiddlewares as $originalMiddleware => $newMiddleware) {
$existingMiddleware = array_replace($existingMiddleware,
array_fill_keys(
array_keys($existingMiddleware, $originalMiddleware),
$newMiddleware
)
);
}
foreach ($this->insertBeforeMiddlewares as $originalMiddleware => $newMiddleware) {
array_splice($existingMiddleware,
array_search($originalMiddleware, $existingMiddleware),
0,
$newMiddleware
);
}
foreach ($this->insertAfterMiddlewares as $originalMiddleware => $newMiddleware) {
array_splice($existingMiddleware,
array_search($originalMiddleware, $existingMiddleware) + 1,
0,
$newMiddleware
);
}
$existingMiddleware = array_diff($existingMiddleware, $this->removeMiddlewares);
return $existingMiddleware;
});
}
}

View File

@@ -14,8 +14,8 @@ use Flarum\User\LoginProvider;
use Flarum\User\RegistrationToken;
use Flarum\User\User;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\HtmlResponse;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\HtmlResponse;
class ResponseFactory
{

View File

@@ -50,7 +50,7 @@ class Discussion
public function __invoke(Document $document, Request $request)
{
$queryParams = $request->getQueryParams();
$page = max(1, Arr::get($queryParams, 'page'));
$page = max(1, intval(Arr::get($queryParams, 'page')));
$params = [
'id' => (int) Arr::get($queryParams, 'id'),

View File

@@ -61,7 +61,7 @@ class Index
$sort = Arr::pull($queryParams, 'sort');
$q = Arr::pull($queryParams, 'q');
$page = Arr::pull($queryParams, 'page', 1);
$page = max(1, intval(Arr::pull($queryParams, 'page')));
$sortMap = $this->getSortMap();

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Forum\Content;
use Flarum\Api\Client;
use Flarum\Api\Controller\ShowUserController;
use Flarum\Frontend\Document;
use Flarum\Http\UrlGenerator;
use Flarum\User\User as FlarumUser;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface as Request;
class User
{
/**
* @var Client
*/
protected $api;
/**
* @var UrlGenerator
*/
protected $url;
/**
* @param Client $api
* @param UrlGenerator $url
*/
public function __construct(Client $api, UrlGenerator $url)
{
$this->api = $api;
$this->url = $url;
}
public function __invoke(Document $document, Request $request)
{
$queryParams = $request->getQueryParams();
$actor = $request->getAttribute('actor');
$userId = Arr::get($queryParams, 'username');
$params = [
'id' => $userId,
];
$apiDocument = $this->getApiDocument($actor, $params);
$user = $apiDocument->data->attributes;
$document->title = $user->displayName;
$document->canonicalUrl = $this->url->to('forum')->route('user', ['username' => $user->username]);
$document->payload['apiDocument'] = $apiDocument;
return $document;
}
/**
* Get the result of an API request to show a user.
*
* @param FlarumUser $actor
* @param array $params
* @return object
* @throws ModelNotFoundException
*/
protected function getApiDocument(FlarumUser $actor, array $params)
{
$response = $this->api->send(ShowUserController::class, $actor, $params);
$statusCode = $response->getStatusCode();
if ($statusCode === 404) {
throw new ModelNotFoundException;
}
return json_decode($response->getBody());
}
}

View File

@@ -14,10 +14,10 @@ use Flarum\Http\SessionAuthenticator;
use Flarum\User\Command\ConfirmEmail;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\RedirectResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\RedirectResponse;
class ConfirmEmailController implements RequestHandlerInterface
{

View File

@@ -19,11 +19,11 @@ use Flarum\User\Event\LoggedOut;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\HtmlResponse;
use Laminas\Diactoros\Response\RedirectResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\RedirectResponse;
class LogOutController implements RequestHandlerInterface
{

View File

@@ -19,10 +19,10 @@ use Illuminate\Contracts\Validation\Factory;
use Illuminate\Support\Arr;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationException;
use Laminas\Diactoros\Response\RedirectResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\RedirectResponse;
class SavePasswordController implements RequestHandlerInterface
{

View File

@@ -34,8 +34,8 @@ use Flarum\Locale\LocaleManager;
use Flarum\Settings\Event\Saved;
use Flarum\Settings\Event\Saving;
use Flarum\Settings\SettingsRepositoryInterface;
use Laminas\Stratigility\MiddlewarePipe;
use Symfony\Component\Translation\TranslatorInterface;
use Zend\Stratigility\MiddlewarePipe;
class ForumServiceProvider extends AbstractServiceProvider
{
@@ -59,7 +59,20 @@ class ForumServiceProvider extends AbstractServiceProvider
$this->setDefaultRoute($routes);
});
$this->app->singleton('flarum.forum.middleware', function (Application $app) {
$this->app->singleton('flarum.forum.middleware', function () {
return [
HttpMiddleware\ParseJsonBody::class,
HttpMiddleware\CollectGarbage::class,
HttpMiddleware\StartSession::class,
HttpMiddleware\RememberFromCookie::class,
HttpMiddleware\AuthenticateWithSession::class,
HttpMiddleware\CheckCsrfToken::class,
HttpMiddleware\SetLocale::class,
HttpMiddleware\ShareErrorsFromSession::class
];
});
$this->app->singleton('flarum.forum.handler', function (Application $app) {
$pipe = new MiddlewarePipe;
// All requests should first be piped through our global error handler
@@ -69,22 +82,15 @@ class ForumServiceProvider extends AbstractServiceProvider
$app->tagged(Reporter::class)
));
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(HttpMiddleware\CollectGarbage::class));
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
$pipe->pipe($app->make(HttpMiddleware\ShareErrorsFromSession::class));
foreach ($this->app->make('flarum.forum.middleware') as $middleware) {
$pipe->pipe($this->app->make($middleware));
}
event(new ConfigureMiddleware($pipe, 'forum'));
return $pipe;
});
$this->app->afterResolving('flarum.forum.middleware', function (MiddlewarePipe $pipe) {
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.forum.routes')));
return $pipe;
});
$this->app->bind('flarum.assets.forum', function () {

View File

@@ -28,7 +28,7 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
$map->get(
'/u/{username}[/{filter:[^/]*}]',
'user',
$route->toForum()
$route->toForum(Content\User::class)
);
$map->get(

View File

@@ -11,10 +11,10 @@ namespace Flarum\Foundation\ErrorHandling;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Laminas\Diactoros\Response\HtmlResponse;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Symfony\Component\Translation\TranslatorInterface;
use Zend\Diactoros\Response\HtmlResponse;
/**
* A formatter for turning caught exceptions into "pretty" HTML error pages.

View File

@@ -17,11 +17,11 @@ use Flarum\Foundation\Console\InfoCommand;
use Flarum\Http\Middleware\DispatchRoute;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Container\Container;
use Laminas\Stratigility\Middleware\OriginalMessages;
use Laminas\Stratigility\MiddlewarePipe;
use Middlewares\BasePath;
use Middlewares\BasePathRouter;
use Middlewares\RequestHandler;
use Zend\Stratigility\Middleware\OriginalMessages;
use Zend\Stratigility\MiddlewarePipe;
class InstalledApp implements AppInterface
{
@@ -63,9 +63,9 @@ class InstalledApp implements AppInterface
$pipe->pipe(new OriginalMessages);
$pipe->pipe(
new BasePathRouter([
$this->subPath('api') => 'flarum.api.middleware',
$this->subPath('admin') => 'flarum.admin.middleware',
'/' => 'flarum.forum.middleware',
$this->subPath('api') => 'flarum.api.handler',
$this->subPath('admin') => 'flarum.admin.handler',
'/' => 'flarum.forum.handler',
])
);
$pipe->pipe(new RequestHandler($this->container));
@@ -92,6 +92,7 @@ class InstalledApp implements AppInterface
private function getUpdaterHandler()
{
$pipe = new MiddlewarePipe;
$pipe->pipe(new BasePath($this->basePath()));
$pipe->pipe(
new DispatchRoute($this->container->make('flarum.update.routes'))
);

View File

@@ -10,12 +10,12 @@
namespace Flarum\Foundation;
use Illuminate\Support\Str;
use Laminas\Diactoros\Response\HtmlResponse;
use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Tobscure\JsonApi\Document;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\JsonResponse;
class MaintenanceModeHandler implements RequestHandlerInterface
{

View File

@@ -53,7 +53,12 @@ class AddTranslations
private function getTranslations(string $locale)
{
$translations = $this->locales->getTranslator()->getCatalogue($locale)->all('messages');
$catalogue = $this->locales->getTranslator()->getCatalogue($locale);
$translations = $catalogue->all('messages');
while ($catalogue = $catalogue->getFallbackCatalogue()) {
$translations = array_replace($catalogue->all('messages'), $translations);
}
return Arr::only(
$translations,

View File

@@ -9,10 +9,10 @@
namespace Flarum\Frontend;
use Laminas\Diactoros\Response\HtmlResponse;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\HtmlResponse;
class Controller implements RequestHandlerInterface
{

View File

@@ -10,10 +10,10 @@
namespace Flarum\Http\Controller;
use Illuminate\Contracts\Support\Renderable;
use Laminas\Diactoros\Response\HtmlResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\HtmlResponse;
abstract class AbstractHtmlController implements RequestHandlerInterface
{

View File

@@ -10,13 +10,13 @@
namespace Flarum\Http;
use Flarum\Foundation\SiteInterface;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use Laminas\HttpHandlerRunner\RequestHandlerRunner;
use Laminas\Stratigility\Middleware\ErrorResponseGenerator;
use Throwable;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\ServerRequestFactory;
use Zend\HttpHandlerRunner\Emitter\SapiEmitter;
use Zend\HttpHandlerRunner\RequestHandlerRunner;
use Zend\Stratigility\Middleware\ErrorResponseGenerator;
class Server
{
@@ -29,10 +29,8 @@ class Server
public function listen()
{
$app = $this->safelyBootApp();
$runner = new RequestHandlerRunner(
$app->getRequestHandler(),
$this->safelyBootAndGetHandler(),
new SapiEmitter,
[ServerRequestFactory::class, 'fromGlobals'],
function (Throwable $e) {
@@ -45,14 +43,17 @@ class Server
}
/**
* Try to boot Flarum, and prevent exceptions from exposing sensitive info.
* Try to boot Flarum, and retrieve the app's HTTP request handler.
*
* @return \Flarum\Foundation\AppInterface
* We catch all exceptions happening during this process and format them to
* prevent exposure of sensitive information.
*
* @return \Psr\Http\Server\RequestHandlerInterface
*/
private function safelyBootApp()
private function safelyBootAndGetHandler()
{
try {
return $this->site->bootApp();
return $this->site->bootApp()->getRequestHandler();
} catch (Throwable $e) {
exit($this->formatBootException($e));
}

View File

@@ -18,10 +18,10 @@ use Flarum\Install\StepFailed;
use Flarum\Install\ValidationFailed;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Laminas\Diactoros\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response;
class InstallController implements RequestHandlerInterface
{

View File

@@ -18,7 +18,7 @@ use Flarum\Http\Middleware\HandleErrors;
use Flarum\Http\Middleware\StartSession;
use Flarum\Install\Console\InstallCommand;
use Illuminate\Contracts\Container\Container;
use Zend\Stratigility\MiddlewarePipe;
use Laminas\Stratigility\MiddlewarePipe;
class Installer implements AppInterface
{

View File

@@ -24,6 +24,10 @@ interface DriverInterface
{
/**
* Provide a list of settings for this driver.
*
* The list must be an array of field names (keys) mapping to their type
* (the empty string "" for a text field; or an array of possible values for
* a dropdown field).
*/
public function availableSettings(): array;

View File

@@ -19,8 +19,12 @@ class MailgunDriver implements DriverInterface
public function availableSettings(): array
{
return [
'mail_mailgun_secret', // the secret key
'mail_mailgun_domain', // the API base URL
'mail_mailgun_secret' => '', // the secret key
'mail_mailgun_domain' => '', // the API base URL
'mail_mailgun_region' => [ // region's endpoint
'api.mailgun.net' => 'US',
'api.eu.mailgun.net' => 'EU',
],
];
}
@@ -29,7 +33,8 @@ class MailgunDriver implements DriverInterface
return new MailgunTransport(
new Client(['connect_timeout' => 60]),
$settings->get('mail_mailgun_secret'),
$settings->get('mail_mailgun_domain')
$settings->get('mail_mailgun_domain'),
$settings->get('mail_mailgun_region')
);
}
}

View File

@@ -19,7 +19,7 @@ class MandrillDriver implements DriverInterface
public function availableSettings(): array
{
return [
'mail_mandrill_secret',
'mail_mandrill_secret' => '',
];
}

View File

@@ -19,9 +19,9 @@ class SesDriver implements DriverInterface
public function availableSettings(): array
{
return [
'mail_ses_key',
'mail_ses_secret',
'mail_ses_region',
'mail_ses_key' => '',
'mail_ses_secret' => '',
'mail_ses_region' => '',
];
}

View File

@@ -18,11 +18,11 @@ class SmtpDriver implements DriverInterface
public function availableSettings(): array
{
return [
'mail_host', // a hostname, IPv4 address or IPv6 wrapped in []
'mail_port', // a number, defaults to 25
'mail_encryption', // "tls" or "ssl"
'mail_username', // required
'mail_password', // required
'mail_host' => '', // a hostname, IPv4 address or IPv6 wrapped in []
'mail_port' => '', // a number, defaults to 25
'mail_encryption' => '', // "tls" or "ssl"
'mail_username' => '', // required
'mail_password' => '', // required
];
}

View File

@@ -13,13 +13,13 @@ use Exception;
use Flarum\Database\Console\MigrateCommand;
use Flarum\Foundation\Application;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\Response\HtmlResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\StreamOutput;
use Zend\Diactoros\Response;
use Zend\Diactoros\Response\HtmlResponse;
class UpdateController implements RequestHandlerInterface
{

View File

@@ -0,0 +1,83 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\User\Concerns;
use Flarum\User\User;
use Illuminate\Support\Arr;
trait DeprecatedUserPreferences
{
/**
* Get the values of all registered preferences for this user, by
* transforming their stored preferences and merging them with the defaults.
*
* @param string $value
* @return array
* @deprecated 0.1.0-beta.11: `users.preferences` is no longer used.
*/
public function getPreferencesAttribute($value)
{
$defaults = array_map(function ($value) {
return $value['default'];
}, static::$preferences);
$user = Arr::only($this->notificationPreferences->toArray(), array_keys(static::$preferences));
return array_merge($defaults, $user);
}
/**
* Encode an array of preferences for storage in the database.
*
* @param mixed $value
* @deprecated 0.1.0-beta.11: `users.preferences` is no longer used.
*/
public function setPreferencesAttribute($value)
{
}
/**
* Get the value of a preference for this user.
*
* @param string $key
* @param mixed $default
* @return mixed
* @deprecated 0.1.0-beta.11: `users.preferences` is no longer used.
*/
public function getPreference($key, $default = null)
{
return Arr::get($this->preferences, $key, $default);
}
/**
* Set the value of a preference for this user.
*
* @param string $key
* @param mixed $value
* @return $this
* @deprecated 0.1.0-beta.11: `users.preferences` is no longer used.
*/
public function setPreference($key, $value)
{
}
/**
* Get the key for a preference which flags whether or not the user will
* receive a notification for $type via $method.
*
* @param string $type
* @param string $method
* @return string
* @deprecated 0.1.0-beta.11: `users.preferences` is no longer used, use NotificationPreference::getPreferenceKey.
*/
public static function getNotificationPreferenceKey($type, $method)
{
}
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\User;
use Flarum\Database\AbstractModel;
use Illuminate\Database\Eloquent\Builder;
use InvalidArgumentException;
/**
* @property int $user_id
* @property string $type
* @property string $channel
* @property bool $enabled
*/
class NotificationPreference extends AbstractModel
{
protected static $channels = [];
public function user()
{
return $this->belongsTo(User::class);
}
public static function addChannel(string $channel)
{
static::$channels[] = $channel;
}
public static function setNotificationPreference(User $user, string $type, string $channel, bool $enabled = true)
{
if (in_array($channel, static::$channels)) {
$attributes = [
'channel' => $channel,
'type' => $type
];
$user->notificationPreferences()->updateOrInsert($attributes, ['enabled' => $enabled]);
} else {
throw new InvalidArgumentException("Channel '$channel' is not registered.");
}
}
public function scopeShouldBeNotified(Builder $query, string $type, string $channel = null)
{
return $query
->where('enabled', true)
->where('type', $type)
->when($channel, function ($query, $channel) {
$query->where('channel', $channel);
});
}
}

View File

@@ -35,7 +35,6 @@ use Flarum\User\Event\Registered;
use Flarum\User\Event\Renamed;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Arr;
/**
* @property int $id
@@ -55,6 +54,7 @@ use Illuminate\Support\Arr;
*/
class User extends AbstractModel
{
use Concerns\DeprecatedUserPreferences;
use EventGeneratorTrait;
use ScopeVisibilityTrait;
@@ -446,32 +446,9 @@ class User extends AbstractModel
})->count();
}
/**
* Get the values of all registered preferences for this user, by
* transforming their stored preferences and merging them with the defaults.
*
* @param string $value
* @return array
*/
public function getPreferencesAttribute($value)
public function notificationPreferences()
{
$defaults = array_map(function ($value) {
return $value['default'];
}, static::$preferences);
$user = Arr::only((array) json_decode($value, true), array_keys(static::$preferences));
return array_merge($defaults, $user);
}
/**
* Encode an array of preferences for storage in the database.
*
* @param mixed $value
*/
public function setPreferencesAttribute($value)
{
$this->attributes['preferences'] = json_encode($value);
return $this->hasMany(NotificationPreference::class);
}
/**
@@ -481,9 +458,11 @@ class User extends AbstractModel
* @param string $type
* @return bool
*/
public function shouldAlert($type)
public function shouldAlert(string $type): bool
{
return (bool) $this->getPreference(static::getNotificationPreferenceKey($type, 'alert'));
return $this->notificationPreferences()
->shouldBeNotified($type, 'email')
->exists();
}
/**
@@ -493,45 +472,11 @@ class User extends AbstractModel
* @param string $type
* @return bool
*/
public function shouldEmail($type)
public function shouldEmail(string $type): bool
{
return (bool) $this->getPreference(static::getNotificationPreferenceKey($type, 'email'));
}
/**
* Get the value of a preference for this user.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function getPreference($key, $default = null)
{
return Arr::get($this->preferences, $key, $default);
}
/**
* Set the value of a preference for this user.
*
* @param string $key
* @param mixed $value
* @return $this
*/
public function setPreference($key, $value)
{
if (isset(static::$preferences[$key])) {
$preferences = $this->preferences;
if (! is_null($transformer = static::$preferences[$key]['transformer'])) {
$preferences[$key] = call_user_func($transformer, $value);
} else {
$preferences[$key] = $value;
}
$this->preferences = $preferences;
}
return $this;
return $this->notificationPreferences()
->shouldBeNotified($type, 'email')
->exists();
}
/**
@@ -735,19 +680,6 @@ class User extends AbstractModel
static::$preferences[$key] = compact('transformer', 'default');
}
/**
* Get the key for a preference which flags whether or not the user will
* receive a notification for $type via $method.
*
* @param string $type
* @param string $method
* @return string
*/
public static function getNotificationPreferenceKey($type, $method)
{
return 'notify_'.$type.'_'.$method;
}
/**
* Refresh the user's comments count.
*

View File

@@ -1,31 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Util;
class Str
{
/**
* Create a slug out of the given string.
*
* Non-alphanumeric characters are converted to hyphens.
*
* @param string $str
* @return string
*/
public static function slug($str)
{
$str = strtolower($str);
$str = preg_replace('/[^a-z0-9]/i', '-', $str);
$str = preg_replace('/-+/', '-', $str);
$str = preg_replace('/-$|^-/', '', $str);
return $str;
}
}

View File

@@ -12,10 +12,10 @@ namespace Flarum\Tests\integration;
use Dflydev\FigCookies\SetCookie;
use Flarum\Foundation\InstalledSite;
use Illuminate\Database\ConnectionInterface;
use Laminas\Diactoros\CallbackStream;
use Laminas\Diactoros\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\CallbackStream;
use Zend\Diactoros\ServerRequest;
abstract class TestCase extends \PHPUnit\Framework\TestCase
{

View File

@@ -1,166 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\api\Auth;
use Carbon\Carbon;
use Flarum\Api\ApiKey;
use Flarum\Api\Client;
use Flarum\Api\Controller\CreateGroupController;
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
use Flarum\Tests\integration\TestCase;
use Flarum\User\Guest;
use Flarum\User\User;
use Illuminate\Support\Str;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;
use Zend\Stratigility\MiddlewarePipe;
class AuthenticateWithApiKeyTest extends TestCase
{
use RetrievesAuthorizedUsers;
public function setUp()
{
parent::setUp();
$this->prepareDatabase([
'users' => [
$this->adminUser(),
$this->normalUser(),
],
]);
}
protected function key(int $user_id = null): ApiKey
{
return ApiKey::unguarded(function () use ($user_id) {
return ApiKey::query()->firstOrCreate([
'key' => Str::random(),
'user_id' => $user_id,
'created_at' => Carbon::now()
]);
});
}
/**
* @test
*/
public function cannot_authorize_without_key()
{
/** @var Client $api */
$api = $this->app()->getContainer()->make(Client::class);
$response = $api->send(CreateGroupController::class, new Guest);
$this->assertEquals(401, $response->getStatusCode());
}
/**
* @test
*/
public function master_token_can_authenticate_as_anyone()
{
$key = $this->key();
$request = ServerRequestFactory::fromGlobals()
->withAddedHeader('Authorization', "Token {$key->key}; userId=1");
$pipe = $this->injectAuthorizationPipeline();
$response = $pipe->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals(1, $response->getHeader('X-Authenticated-As')[0]);
$key = $key->refresh();
$this->assertNotNull($key->last_activity_at);
$key->delete();
}
/**
* @test
*/
public function personal_api_token_cannot_authenticate_as_anyone()
{
$user = User::find(2);
$key = $this->key($user->id);
$request = ServerRequestFactory::fromGlobals()
->withAddedHeader('Authorization', "Token {$key->key}; userId=1");
$pipe = $this->injectAuthorizationPipeline();
$response = $pipe->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals($user->id, $response->getHeader('X-Authenticated-As')[0]);
$key = $key->refresh();
$this->assertNotNull($key->last_activity_at);
$key->delete();
}
/**
* @test
*/
public function personal_api_token_authenticates_user()
{
$user = User::find(2);
$key = $this->key($user->id);
$request = ServerRequestFactory::fromGlobals()
->withAddedHeader('Authorization', "Token {$key->key}");
$pipe = $this->injectAuthorizationPipeline();
$response = $pipe->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals($user->id, $response->getHeader('X-Authenticated-As')[0]);
$key = $key->refresh();
$this->assertNotNull($key->last_activity_at);
$key->delete();
}
protected function injectAuthorizationPipeline(): MiddlewarePipe
{
app()->resolving('flarum.api.middleware', function ($pipeline) {
$pipeline->pipe(new class implements MiddlewareInterface {
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
if ($actor = $request->getAttribute('actor')) {
return new Response\EmptyResponse(200, [
'X-Authenticated-As' => $actor->id
]);
}
}
});
});
$pipe = app('flarum.api.middleware');
return $pipe;
}
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\api\authentication;
use Carbon\Carbon;
use Flarum\Api\ApiKey;
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
use Flarum\Tests\integration\TestCase;
use Illuminate\Support\Str;
class WithApiKeyTest extends TestCase
{
use RetrievesAuthorizedUsers;
public function setUp()
{
parent::setUp();
$this->prepareDatabase([
'users' => [
$this->adminUser(),
$this->normalUser(),
],
'api_keys' => [],
]);
}
protected function key(int $user_id = null): ApiKey
{
return ApiKey::unguarded(function () use ($user_id) {
return ApiKey::query()->firstOrCreate([
'key' => Str::random(),
'user_id' => $user_id,
'created_at' => Carbon::now()
]);
});
}
/**
* @test
*/
public function cannot_authorize_without_key()
{
$response = $this->send(
$this->request('GET', '/api')
);
$data = json_decode($response->getBody(), true);
$this->assertFalse($data['data']['attributes']['canViewUserList']);
}
/**
* @test
*/
public function master_token_can_authenticate_as_anyone()
{
$key = $this->key();
$response = $this->send(
$this->request('GET', '/api')
->withAddedHeader('Authorization', "Token {$key->key}; userId=1")
);
$data = json_decode($response->getBody(), true);
$this->assertTrue($data['data']['attributes']['canViewUserList']);
$this->assertArrayHasKey('adminUrl', $data['data']['attributes']);
$key->refresh();
$this->assertNotNull($key->last_activity_at);
}
/**
* @test
*/
public function personal_api_token_cannot_authenticate_as_anyone()
{
$key = $this->key(2);
$response = $this->send(
$this->request('GET', '/api')
->withAddedHeader('Authorization', "Token {$key->key}; userId=1")
);
$data = json_decode($response->getBody(), true);
$this->assertTrue($data['data']['attributes']['canViewUserList']);
$this->assertArrayNotHasKey('adminUrl', $data['data']['attributes']);
$key->refresh();
$this->assertNotNull($key->last_activity_at);
}
/**
* @test
*/
public function personal_api_token_authenticates_user()
{
$key = $this->key(2);
$response = $this->send(
$this->request('GET', '/api')
->withAddedHeader('Authorization', "Token {$key->key}")
);
$data = json_decode($response->getBody(), true);
$this->assertTrue($data['data']['attributes']['canViewUserList']);
$this->assertArrayNotHasKey('adminUrl', $data['data']['attributes']);
$key->refresh();
$this->assertNotNull($key->last_activity_at);
}
}

View File

@@ -11,7 +11,7 @@ namespace Flarum\Tests\unit\Install;
use Flarum\Install\BaseUrl;
use Flarum\Tests\unit\TestCase;
use Zend\Diactoros\Uri;
use Laminas\Diactoros\Uri;
class BaseUrlTest extends TestCase
{

View File

@@ -3,12 +3,12 @@
<p>These problems must be resolved before you can continue the installation. If you're having trouble, get help on the <a href="https://flarum.org/docs/install.html" target="_blank">Flarum website</a>.</p>
<div class="Problems">
<?php foreach ($problems as $problem): ?>
<?php foreach ($problems as $problem) { ?>
<div class="Problem">
<h3 class="Problem-message"><?php echo $problem['message']; ?></h3>
<?php if (! empty($problem['detail'])): ?>
<?php if (! empty($problem['detail'])) { ?>
<p class="Problem-detail"><?php echo $problem['detail']; ?></p>
<?php endif; ?>
<?php } ?>
</div>
<?php endforeach; ?>
<?php } ?>
</div>