1
0
mirror of https://github.com/flarum/core.git synced 2025-08-13 20:04:24 +02:00

Compare commits

..

131 Commits

Author SHA1 Message Date
David Sevilla Martin
4242a82b80 Use composer.json instead of composer.lock 2019-11-06 19:25:30 -05:00
David Sevilla Martin
cee4cb6840 Cache composer dependencies, skip installing dependencies if cache was used 2019-11-06 19:24:03 -05:00
David Sevilla Martin
9c0d921f49 Fix Modal width on <768px screens not occupying the whole page 2019-11-06 17:48:00 -05:00
flarum-bot
d7bdc173a4 Bundled output for commit 937354512b [skip ci] 2019-10-28 15:37:34 +00:00
Daniël Klabbers
937354512b Update User.js
Use recommended `anonymous`, see https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin
2019-10-28 16:35:45 +01:00
J.C.Ködel
2dedfe4b92 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)
2019-10-28 16:35:45 +01:00
Franz Liedke
9f6ec80432 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.
2019-10-26 15:41:39 +02:00
Daniël Klabbers
aa31b8307d improve queue error handling 2019-10-18 13:13:30 +02:00
Daniël Klabbers
dc06d5b5c9 added return type hint to memory cache 2019-10-08 15:51:19 +02:00
Daniël Klabbers
2c867d2292 add type hinting to settings repository 2019-10-08 15:39:01 +02:00
luceos
b09ac3f3f8 Apply fixes from StyleCI
[ci skip] [skip ci]
2019-10-07 09:56:10 +02:00
Daniël Klabbers
21f54c5562 added ability to re-use existing error handling stack 2019-10-07 09:56:10 +02:00
David Sevilla Martin
a0ace316e8 Alias 'flarum.queue.connection' to Queue contract 2019-10-05 16:14:27 -04:00
Tariq Hussein
6c96c932e0 Fixes #1877 Replace getIdsForUsername() with subquery instead. (#1878) 2019-10-02 01:04:01 +02:00
flarum-bot
522e41aa71 Bundled output for commit bbd891965f [skip ci] 2019-10-01 21:50:25 +00:00
Madalin Tache
bbd891965f 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.
2019-10-01 23:48:54 +02:00
flarum-bot
0f43445a90 Bundled output for commit 7a684660e9 [skip ci] 2019-10-01 21:39:07 +00:00
David Sevilla Martín
7a684660e9 Enable scrollbars in login button popups (#1900)
Fixes #1716
2019-10-01 23:37:42 +02:00
Daniël Klabbers
12dc4fff57 works towards #1789 by allowing event subscribing (#1810) 2019-10-01 11:12:46 +02:00
Franz Liedke
1b5a200781 Amend the existing rel attribute of links
...instead of overwriting. This will play more nicely with extensions.

Refs #859.
2019-09-26 23:02:39 +02:00
Franz Liedke
1bdf7764a9 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.
2019-09-26 23:01:24 +02:00
Franz Liedke
3417c0cbee 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
2019-09-24 01:00:22 +02:00
Stefan Totev
738ca405fe 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
2019-09-24 00:26:51 +02:00
Matteo Contrini
09609a9f20 Change rel for external links to nofollow ugc (#1884) 2019-09-23 23:37:49 +02:00
Daniël Klabbers
fb0a875c6d added author Daniel Klabbers 2019-09-16 10:37:51 +02:00
David Sevilla Martín
74b6b9935b Prepare beta.10 release (#1885)
* Update Application version string to beta 10
* Add beta.10 changelog
2019-09-16 09:28:17 +02:00
Franz Liedke
3b5691ee28 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.
2019-09-14 21:32:00 +02:00
Franz Liedke
18593e0d7d 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.
2019-09-14 21:30:09 +02:00
Franz Liedke
40e1b61fe6 Apply fixes from StyleCI
[ci skip] [skip ci]
2019-09-14 18:57:28 +00:00
Franz Liedke
95dcb45d65 Convert more controller tests to feature tests 2019-09-14 13:09:56 +02:00
Franz Liedke
bd989df769 Update vulnerable JS dependencies 2019-09-13 15:26:10 +02:00
Franz Liedke
538136153c 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".
2019-09-13 15:03:03 +02:00
Franz Liedke
c330662241 Convert another controller test to feature test
Decouple from implementation, test closer to HTTP...
2019-09-13 14:58:45 +02:00
flarum-bot
588cbaee2d Bundled output for commit a9557c399a [skip ci] 2019-09-12 22:36:12 +00:00
David Sevilla Martín
a9557c399a 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
2019-09-13 00:34:05 +02:00
Daniël Klabbers
14e7bc73ee moved the artisan binary override and commented some of the bindings for queue 2019-09-12 09:11:12 +02:00
flarum-bot
edc579fa6f Bundled output for commit 119831e51c [skip ci] 2019-09-11 22:16:20 +00:00
David Sevilla Martin
119831e51c Fixes an issue where deleting a nonexistent model would error instead of resolving gracefully 2019-09-11 18:14:37 -04:00
Daniël Klabbers
2aee020c14 prevent constant to be duplicated during tests 2019-09-11 12:20:35 +02:00
Daniël Klabbers
f20696210e Merge branch 'master' of github.com:flarum/core 2019-09-11 11:59:10 +02:00
Daniël Klabbers
ea84fc4836 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.
2019-09-11 11:58:27 +02:00
luceos
5ff04d0c68 Apply fixes from StyleCI
[ci skip] [skip ci]
2019-09-11 09:43:46 +00:00
Daniël Klabbers
e2ec52c28c Fixes the queue listen command. We might need to rectify this implementation before stable. 2019-09-11 11:42:52 +02:00
Daniël Klabbers
6196081bdf 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.
2019-09-10 12:33:25 +02:00
Franz Liedke
6d8e6583c8 Fix instructions in PR template 2019-09-10 00:18:04 +02:00
flarum-bot
c2b0060852 Bundled output for commit 24964b94bf [skip ci] 2019-09-09 21:37:54 +00:00
David Sevilla Martín
24964b94bf Mark notification as read without visiting discussion (#1874) 2019-09-09 23:36:06 +02:00
flarum-bot
2b624c935d Bundled output for commit 2e647cdda8 [skip ci] 2019-09-09 21:07:00 +00:00
David Sevilla Martín
2e647cdda8 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
2019-09-09 17:05:11 -04:00
Daniël Klabbers
ba175144f4 listen and restart currently fail in the queue, see #1879 2019-09-09 15:47:56 +02:00
flarum-bot
e9af36ab47 Bundled output for commit 8b3913339a [skip ci] 2019-09-08 17:33:39 +00:00
Matthew Kilgore
8b3913339a Fix the new edit user permission label (#1870) 2019-09-08 13:31:57 -04:00
David Sevilla Martín
3cced4156f 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.
2019-09-08 13:28:39 -04:00
David Sevilla Martín
e88a9394ed Add back defaults for language and direction attributes (#1860) 2019-09-05 08:28:52 +02:00
flarum-bot
ba73c59601 Bundled output for commit 0191babb05 [skip ci] 2019-09-05 00:34:59 +00:00
Franz Liedke
0191babb05 Optimize ScrollListener performance
Listen to "scroll" event and throttle callback executions instead
of actively polling for changes to the scroll position.

Fixes #1222.
2019-09-05 02:17:09 +02:00
Franz Liedke
ed51f9ff0a Fix failing test 2019-09-05 00:07:40 +02:00
Franz Liedke
0a2bdbaa09 Debug mode: Include stacktrace in JSON-API errors
Refs #1843, #1865.
2019-09-04 23:35:32 +02:00
Franz Liedke
26229db1fd Refactor JSON-API error formatter 2019-09-04 23:30:22 +02:00
Franz Liedke
1aef3162be Apply fixes from StyleCI (#1867)
[ci skip] [skip ci]
2019-09-04 01:44:59 +02:00
Franz Liedke
dcf88df0c7 Restore error details in JSON-API error formatter
Fixes #1865. Refs #1843.
2019-09-04 01:44:22 +02:00
Franz Liedke
3eb28dfb16 Convert controller test to request test
This further decouples these tests from the implementation (i.e. which
controller are we calling?).
2019-09-04 01:27:24 +02:00
Matteo Contrini
1d43371fa9 Allow formatting post content without a request (#1848) 2019-09-04 00:12:28 +02:00
Matthew Kilgore
4df455cf04 Add Edit User permission to permissions grid (#1859) 2019-09-03 23:54:38 +02:00
Franz Liedke
2c43ccf66c Merge pull request #1854 from flarum/fl/1641-fix-status-codes
Error handling: Fix status codes
2019-09-02 16:33:48 +02:00
dependabot[bot]
1d010efbca 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>
2019-08-28 09:11:25 +02:00
dependabot[bot]
2135d5908e 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>
2019-08-28 09:06:38 +02:00
Franz Liedke
9640dd6419 Remove unnecessary dependency
Refs #1773.
2019-08-22 10:04:38 +02:00
Franz Liedke
98464a8a33 Remove superfluous ForbiddenException
It has the same effect as the PermissionDeniedException, so let's
just use that.

Refs #1641.
2019-08-22 00:06:26 +02:00
Franz Liedke
2b6535525b When signups are prohibited, respond with HTTP 403 2019-08-21 23:48:24 +02:00
Franz Liedke
b60617b849 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.
2019-08-21 23:48:03 +02:00
Franz Liedke
0836d99e83 Remove unnecessary indirection 2019-08-21 00:06:32 +02:00
Franz Liedke
279c7df9b9 Document permission check methods 2019-08-21 00:06:31 +02:00
Franz Liedke
04bcf1eef6 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.
2019-08-21 00:06:31 +02:00
Franz Liedke
70e98f810c Travis: Remove deploy key 2019-08-21 00:06:16 +02:00
David Sevilla Martín
3851d805f7 Move to GitHub Actions (#1853) 2019-08-21 00:05:04 +02:00
Franz Liedke
085468382a Error handling: Document another interface 2019-08-20 22:20:11 +02:00
Franz Liedke
7dbdd8c024 Rename method 2019-08-20 20:08:01 +02:00
Franz Liedke
ad25307e68 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".
2019-08-20 20:07:56 +02:00
Franz Liedke
6c454b8279 Error handling: Document classes and interfaces 2019-08-20 20:07:52 +02:00
Franz Liedke
9f15e9ba86 Error handling: Rename renderers to formatters
Refs #1641.
2019-08-20 20:07:47 +02:00
Franz Liedke
41009dba74 Remove obsolete queue config 2019-08-19 22:33:32 +02:00
Daniël Klabbers
a045f8bef9 Queue support (#1773)
Implementation of clean queue handling, by default sync is used
2019-08-19 21:44:06 +02:00
Franz Liedke
689d767f82 Don't fail when extend.php doesn't return an array
Refs #1607.
2019-08-16 12:29:31 +02:00
Franz Liedke
77fff9fde8 #1607: Show more details when catching boot errors 2019-08-16 12:13:47 +02:00
Franz Liedke
c6c1ae32e6 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.
2019-08-14 19:47:56 +02:00
Franz Liedke
bdac88b573 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.
2019-08-14 19:47:56 +02:00
flarum-bot
31ee65be93 Bundled output for commit 29df6b60be [skip ci] 2019-08-14 06:41:45 +00:00
Franz Liedke
29df6b60be Tweak translation keys, always use full keys
Makes them easier to grep when editing / removing.

Refs #1750, #1788.
2019-08-14 08:34:36 +02:00
Franz Liedke
1e6f175379 Extract real method
Refs #1750, #1788.
2019-08-14 08:34:14 +02:00
flarum-bot
065ff3456f Bundled output for commit 37e0a5579b [skip ci] 2019-08-13 21:02:10 +00:00
Tobias Karlsson
37e0a5579b Improve feedback on user deletion
Fixes #1750, #1777
2019-08-13 22:56:24 +02:00
Franz Liedke
cd9aa0096e Merge pull request #1843 from flarum/fl/1641-exception-handling
Implement new error handling stack
2019-08-13 22:45:46 +02:00
Franz Liedke
d06493c61e 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.
2019-08-10 11:04:12 +02:00
Franz Liedke
9f71e2c3cb Remove old error handler, middleware and tests 2019-08-10 00:26:24 +02:00
Franz Liedke
81a8736ba9 API Client: Use new error handling mechanism 2019-08-10 00:26:24 +02:00
Franz Liedke
57ce25301d Use new error handler middleware 2019-08-10 00:26:24 +02:00
Franz Liedke
cfbaa84fbc Wire up new error handling stack 2019-08-10 00:26:23 +02:00
Franz Liedke
3417f5a77e Make existing extensions compatible with new stack 2019-08-10 00:26:22 +02:00
Franz Liedke
1035636d0f 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.
2019-08-10 00:26:22 +02:00
Franz Liedke
d00fc2c49d Remove obsolete constructor parameter
This was removed in commit 484c6d2e.
2019-08-10 00:16:21 +02:00
flarum-bot
f3b889a665 Bundled output for commit c5122bf5d5 [skip ci] 2019-08-08 23:12:56 +00:00
Franz Liedke
c5122bf5d5 a11y: Try to make screenreaders read tooltips
Refs #1835.
2019-08-08 22:40:30 +02:00
David Sevilla Martín
5ed55195e1 Add canonical URL to discussion list (#1814) 2019-08-08 00:01:25 +02:00
Franz Liedke
8604ea3020 Bypass CSRF token check when using access tokens
Fixes #1828.
2019-08-01 22:53:31 +02:00
Franz Liedke
2648e960a7 Make exception message dynamic as well 2019-08-01 22:25:28 +02:00
Daniël Klabbers
f0dff95d62 Merge branch 'master' of github.com:flarum/core 2019-07-31 17:10:47 +02:00
Daniël Klabbers
894db01ad8 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.
2019-07-31 17:10:13 +02:00
Franz Liedke
bd04023359 Determine default route after extensions
Fixes #1819.
2019-07-30 00:56:29 +02:00
Franz Liedke
f357434a72 PHPUnit: Get rid of deprecated annotation
Refs #1795.
2019-07-30 00:09:10 +02:00
Daniël Klabbers
c2586586c4 fixes #1695, take into consideration is_private with counts on User stats 2019-07-28 20:59:12 +02:00
Daniël Klabbers
06cd062a1b fixed ci, make green again; mysql service wasnt booted 2019-07-27 22:24:39 +02:00
Franz Liedke
1502fc98d8 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.
2019-07-23 23:55:06 +02:00
Franz Liedke
ed97989ca2 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.
2019-07-14 22:22:06 +02:00
Franz Liedke
7f1048352d 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.
2019-07-11 22:35:19 +02:00
dependabot[bot]
d2700961ba 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>
2019-07-11 09:21:17 +02:00
Daniël Klabbers
b2dbb0439c fixed class property hint for event post content attribute 2019-07-09 08:31:48 +02:00
Daniël Klabbers
085c924a07 fix return type hint for event post content attribute 2019-07-09 08:30:10 +02:00
David Sevilla Martín
f31f02d4cc Set Whoops middleware HTTP status to error code (#1648)
* Use error code for HTTP status, defaults to 500
* Use logic from HandleErrorsWithView, make sure status is valid
2019-07-07 14:57:40 +02:00
Daniël Klabbers
797f6eea50 moved GetDisplayName event to User namespace (#1768) 2019-07-06 19:27:44 +02:00
Franz Liedke
9fb3a31b51 Get rid of unnecessary method 2019-07-06 02:25:06 +02:00
Franz Liedke
f8061bbca1 Remove deprecated bootstrap.php fallback
Closes #1557.
2019-07-06 02:11:09 +02:00
Franz Liedke
de67927ef2 Travis: Allow PHP 7.4 to fail for now
We are currently experiencing issues due to Mockery not yet being 100%
compatible with PHP.

See https://github.com/mockery/mockery/pull/980.
2019-07-06 01:56:54 +02:00
Franz Liedke
8c841c3266 Update test libraries 2019-07-06 01:49:55 +02:00
Franz Liedke
2f656146a7 Travis: Test against the upcoming PHP 7.4 2019-07-06 01:30:59 +02:00
Franz Liedke
d66d2aa26e Convert more helpers in tests 2019-07-06 01:30:59 +02:00
Franz Liedke
f4c0d4ba87 Type hint contract, not implementation 2019-07-06 01:30:58 +02:00
Franz Liedke
646bd40bca Use Laravel's class-based Str and Arr helpers
Starting with version 5.9, the global funtions will be deprecated.

* https://laravel-news.com/laravel-5-8-deprecates-string-and-array-helpers
* https://github.com/laravel/framework/pull/26898
2019-07-06 01:30:58 +02:00
Franz Liedke
307b912019 Issue templates: Remove vulnerability information
GitHub now automatically displays this information (or rather, links to
the Security Policy) at the issue type selection page.
2019-07-06 00:08:55 +02:00
Franz Liedke
cbc896eba7 Use class constant instead of strings 2019-07-06 00:03:25 +02:00
Franz Liedke
cc4e4a068b Add descriptions to custom Composer scripts 2019-07-05 23:34:23 +02:00
212 changed files with 2743 additions and 2089 deletions

Binary file not shown.

View File

@@ -3,9 +3,6 @@ name: "🐛 Bug Report"
about: "If something isn't working as expected"
---
<!--
IMPORTANT: If you discover a security vulnerability within Flarum, please send an email to [security@flarum.org](mailto:security@flarum.org) instead. We will address these with the utmost urgency and it will prevent vulnerabilities, which may be abused, from popping up on our issue tracker.
-->
## Bug Report
**Current Behavior**

View File

@@ -1,8 +0,0 @@
---
name: "🔒 Security Vulnerability"
about: "When you discover a security issue"
---
If you discover a security vulnerability within Flarum, please send an email to [security@flarum.org](mailto:security@flarum.org) instead.
**DO NOT open an issue on this repository.**
We will address these with the utmost urgency and it will prevent vulnerabilities, which may be abused, from popping up on our issue tracker.

View File

@@ -16,7 +16,7 @@ IMPORTANT: We applaud pull requests, they excite us every single time. As we hav
**Confirmed**
- [ ] Frontend changes: tested on a local Flarum installation.
- [ ] Backend changes: tests are green (run `php vendor/bin/phpunit`).
- [ ] Backend changes: tests are green (run `composer test`).
**Required changes:**

16
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: JavaScript
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: flarum/action-build@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

80
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: [7.1, 7.2, 7.3]
service: ['mysql:5.7', mariadb]
prefix: ['', flarum_]
include:
- service: 'mysql:5.7'
db: MySQL
- service: mariadb
db: MariaDB
- prefix: flarum_
prefixStr: (prefix)
exclude:
- php: 7.1
service: 'mysql:5.7'
prefix: flarum_
- php: 7.1
service: mariadb
prefix: flarum_
- php: 7.2
service: 'mysql:5.7'
prefix: flarum_
- php: 7.2
service: mariadb
prefix: flarum_
services:
mysql:
image: ${{ matrix.service }}
ports:
- 13306:3306
name: 'PHP ${{ matrix.php }} / ${{ matrix.db }} ${{ matrix.prefixStr }}'
steps:
- name: Checkout repository
uses: actions/checkout@v1
- name: Get Composer Cache Directory
id: composer-cache
run: |
echo "::set-output name=dir::$(composer config cache-files-dir)"
- uses: actions/cache@v1
id: cache
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Select PHP version
run: sudo update-alternatives --set php $(which php${{ matrix.php }})
- name: Create MySQL Database
run: mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
- name: Install Composer dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: composer install
- name: Setup Composer tests
run: composer test:setup
env:
DB_PORT: 13306
DB_PASSWORD: root
DB_PREFIX: ${{ matrix.prefix }}
- name: Run Composer tests
run: composer test

View File

@@ -1,55 +0,0 @@
language: php
cache:
directories:
- $HOME/.composer/cache
- $HOME/.npm
install:
- composer install
- mysql -e 'CREATE DATABASE flarum_test;'
before_script:
- echo 'error_reporting = E_ALL' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
script:
- composer test:setup
- composer test
jobs:
include:
- php: 7.1
env: DB=mysql
- php: 7.2
env: DB=mysql
- php: 7.3
env: DB=mysql
- php: 7.1
addons:
mariadb: '10.2'
env: DB=mariadb
- php: 7.2
addons:
mariadb: '10.2'
env: DB=mariadb
- php: 7.3
addons:
mariadb: '10.2'
env: DB=mariadb
- php: 7.2
env: DB=mysql PREFIX=forum_
- stage: build
language: generic
if: branch = master AND type = push
install: skip
script: bash .travis/build.sh
-k $encrypted_678139e2bc67_key
-i $encrypted_678139e2bc67_iv
after_success: skip

View File

@@ -1,33 +0,0 @@
#!/bin/bash
main() {
while getopts ":k:i:" opt; do
case $opt in
k) encrypted_key="$OPTARG"
;;
i) encrypted_iv="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
;;
esac
done
git checkout -f $TRAVIS_BRANCH
git config user.name "flarum-bot"
git config user.email "bot@flarum.org"
cd js
npm i -g npm@6.1.0
npm ci
npm run build
git add dist/* -f
git commit -m "Bundled output for commit $TRAVIS_COMMIT [skip ci]"
eval `ssh-agent -s`
openssl aes-256-cbc -K $encrypted_key -iv $encrypted_iv -in ../.deploy.enc -d | ssh-add -
git push git@github.com:$TRAVIS_REPO_SLUG.git $TRAVIS_BRANCH
}
main "$@"

View File

@@ -1,5 +1,31 @@
# Changelog
## [0.1.0-beta.10](https://github.com/flarum/core/compare/v0.1.0-beta.9...v0.1.0-beta.10)
### Added
- Initial queue support: Infrastructure for offloading long-running tasks (e.g. email sending) to background workers (#1773)
- Notifications can now be marked as read without visiting a discussion (#151)
- SEO: The discussion list now has a `rel="canonical"` meta tag, preventing duplicate content (#1134, #1814)
- The "Edit User" permission can now be edited in the UI (#1845)
- New status message and redirect after user deletion (#1750, #1777)
- Errors in Flarum's boot process are now presented with more detailed information (#1607)
### Changed
- Better, more detailed and extensible error handling (#1641, #1843)
- Error pages in debug mode now return the same HTTP status codes as in production (#1648)
- Tweak HTTP status codes for authentication / authorization errors (#1854)
- Already-used links from account activation emails now show a better error message (#1337)
### Fixed
- Security vulnerabilities in dependencies
- Performance: High CPU usage when scrolling in a discussion (#1222)
- Special characters crashed the search (#1498)
- Missing declarations for language and text direction in HTML output (#1772)
- Private messages were counted in user post counts (#1695)
- Extensions could not change the forum's default page (#1819)
- API requests authenticated using access tokens needed to provide a CSRF token (#1828)
- Accessibility: Screenreaders did not read the "Back to discussion list" link (#1835)
## [0.1.0-beta.9](https://github.com/flarum/core/compare/v0.1.0-beta.8.2...v0.1.0-beta.9)
### Added

View File

@@ -12,6 +12,10 @@
{
"name": "Franz Liedke",
"email": "franz@develophp.org"
},
{
"name": "Daniel Klabbers",
"email": "daniel@klabbers.email"
}
],
"support": {
@@ -36,6 +40,7 @@
"illuminate/filesystem": "5.7.*",
"illuminate/hashing": "5.7.*",
"illuminate/mail": "5.7.*",
"illuminate/queue": "5.7.*",
"illuminate/session": "5.7.*",
"illuminate/support": "5.7.*",
"illuminate/validation": "5.7.*",
@@ -55,6 +60,7 @@
"s9e/text-formatter": "^1.2.0",
"symfony/config": "^3.3",
"symfony/console": "^4.2",
"symfony/event-dispatcher": "^4.3.2",
"symfony/translation": "^3.3",
"symfony/yaml": "^3.3",
"tobscure/json-api": "^0.3.0",
@@ -63,8 +69,8 @@
"zendframework/zend-stratigility": "^3.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^6.0"
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0"
},
"autoload": {
"psr-4": {
@@ -95,5 +101,11 @@
"test:unit": "phpunit -c tests/phpunit.unit.xml",
"test:integration": "phpunit -c tests/phpunit.integration.xml",
"test:setup": "@php tests/integration/setup.php"
},
"scripts-descriptions": {
"test": "Runs all tests.",
"test:unit": "Runs all unit tests.",
"test:integration": "Runs all integration tests.",
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
}
}

4
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

6
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

204
js/package-lock.json generated
View File

@@ -1098,6 +1098,16 @@
"requires": {
"micromatch": "^3.1.4",
"normalize-path": "^2.1.1"
},
"dependencies": {
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"requires": {
"remove-trailing-separator": "^1.0.1"
}
}
}
},
"aproba": {
@@ -1164,9 +1174,9 @@
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
},
"async-each": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
"integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ=="
},
"atob": {
"version": "2.1.2",
@@ -1250,9 +1260,9 @@
"integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q=="
},
"binary-extensions": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz",
"integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg=="
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
},
"bluebird": {
"version": "3.5.3",
@@ -1468,23 +1478,22 @@
}
},
"chokidar": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
"integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.0",
"braces": "^2.3.0",
"fsevents": "^1.2.2",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.1",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"lodash.debounce": "^4.0.8",
"normalize-path": "^2.1.1",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.0.0",
"upath": "^1.0.5"
"readdirp": "^2.2.1",
"upath": "^1.1.1"
}
},
"chownr": {
@@ -2204,13 +2213,13 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
"integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
"integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
"optional": true,
"requires": {
"nan": "^2.9.2",
"node-pre-gyp": "^0.10.0"
"nan": "^2.12.1",
"node-pre-gyp": "^0.12.0"
},
"dependencies": {
"abbrev": {
@@ -2229,7 +2238,7 @@
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"version": "1.1.5",
"bundled": true,
"optional": true,
"requires": {
@@ -2252,7 +2261,7 @@
}
},
"chownr": {
"version": "1.0.1",
"version": "1.1.1",
"bundled": true,
"optional": true
},
@@ -2277,15 +2286,15 @@
"optional": true
},
"debug": {
"version": "2.6.9",
"version": "4.1.1",
"bundled": true,
"optional": true,
"requires": {
"ms": "2.0.0"
"ms": "^2.1.1"
}
},
"deep-extend": {
"version": "0.5.1",
"version": "0.6.0",
"bundled": true,
"optional": true
},
@@ -2328,7 +2337,7 @@
}
},
"glob": {
"version": "7.1.2",
"version": "7.1.3",
"bundled": true,
"optional": true,
"requires": {
@@ -2346,11 +2355,11 @@
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
"version": "0.4.24",
"bundled": true,
"optional": true,
"requires": {
"safer-buffer": "^2.1.0"
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
@@ -2407,16 +2416,16 @@
"optional": true
},
"minipass": {
"version": "2.2.4",
"version": "2.3.5",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
"version": "1.1.0",
"version": "1.2.1",
"bundled": true,
"optional": true,
"requires": {
@@ -2432,32 +2441,32 @@
}
},
"ms": {
"version": "2.0.0",
"version": "2.1.1",
"bundled": true,
"optional": true
},
"needle": {
"version": "2.2.0",
"version": "2.3.0",
"bundled": true,
"optional": true,
"requires": {
"debug": "^2.1.2",
"debug": "^4.1.0",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
}
},
"node-pre-gyp": {
"version": "0.10.0",
"version": "0.12.0",
"bundled": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.0",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.1.7",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
@@ -2473,12 +2482,12 @@
}
},
"npm-bundled": {
"version": "1.0.3",
"version": "1.0.6",
"bundled": true,
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
"version": "1.4.1",
"bundled": true,
"optional": true,
"requires": {
@@ -2545,11 +2554,11 @@
"optional": true
},
"rc": {
"version": "1.2.7",
"version": "1.2.8",
"bundled": true,
"optional": true,
"requires": {
"deep-extend": "^0.5.1",
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
@@ -2577,15 +2586,15 @@
}
},
"rimraf": {
"version": "2.6.2",
"version": "2.6.3",
"bundled": true,
"optional": true,
"requires": {
"glob": "^7.0.5"
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.1.1",
"version": "5.1.2",
"bundled": true,
"optional": true
},
@@ -2600,7 +2609,7 @@
"optional": true
},
"semver": {
"version": "5.5.0",
"version": "5.7.0",
"bundled": true,
"optional": true
},
@@ -2646,16 +2655,16 @@
"optional": true
},
"tar": {
"version": "4.4.1",
"version": "4.4.8",
"bundled": true,
"optional": true,
"requires": {
"chownr": "^1.0.1",
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.2.4",
"minizlib": "^1.1.0",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
@@ -2665,11 +2674,11 @@
"optional": true
},
"wide-align": {
"version": "1.1.2",
"version": "1.1.3",
"bundled": true,
"optional": true,
"requires": {
"string-width": "^1.0.2"
"string-width": "^1.0.2 || 2"
}
},
"wrappy": {
@@ -2678,7 +2687,7 @@
"optional": true
},
"yallist": {
"version": "3.0.2",
"version": "3.0.3",
"bundled": true,
"optional": true
}
@@ -3032,9 +3041,9 @@
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-glob": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
"integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"requires": {
"is-extglob": "^2.1.1"
}
@@ -3184,19 +3193,14 @@
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"lodash-es": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz",
"integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q=="
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
"version": "4.17.14",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.14.tgz",
"integrity": "sha512-7zchRrGa8UZXjD/4ivUWP1867jDkhzTG2c/uj739utSd7O/pFFdxspCemIFKEEjErbcqRzn8nKnGsi7mvTgRPA=="
},
"loose-envify": {
"version": "1.4.0",
@@ -3357,9 +3361,9 @@
"integrity": "sha512-9XuGnVmS2OyFexUuP/CcJFFJjHLM+RGYBxyVRNyQ6khbMfDJIF/xyZ4zq18ZRfPagpFmWUFpjHd5ZqPULGZyNg=="
},
"mixin-deep": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
"integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
"integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
"requires": {
"for-in": "^1.0.2",
"is-extendable": "^1.0.1"
@@ -3407,9 +3411,9 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"nan": {
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==",
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
"optional": true
},
"nanomatch": {
@@ -3486,12 +3490,9 @@
}
},
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"requires": {
"remove-trailing-separator": "^1.0.1"
}
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"npm-run-path": {
"version": "2.0.2",
@@ -4036,9 +4037,9 @@
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"set-value": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
"integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
"integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
"requires": {
"extend-shallow": "^2.0.1",
"is-extendable": "^0.1.1",
@@ -4562,35 +4563,14 @@
"integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg=="
},
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
"integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
"integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
"requires": {
"arr-union": "^3.1.0",
"get-value": "^2.0.6",
"is-extendable": "^0.1.1",
"set-value": "^0.4.3"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"requires": {
"is-extendable": "^0.1.0"
}
},
"set-value": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
"integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
"requires": {
"extend-shallow": "^2.0.1",
"is-extendable": "^0.1.1",
"is-plain-object": "^2.0.1",
"to-object-path": "^0.3.0"
}
}
"set-value": "^2.0.1"
}
},
"unique-filename": {
@@ -4646,9 +4626,9 @@
}
},
"upath": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
"integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw=="
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="
},
"uri-js": {
"version": "4.2.2",

View File

@@ -9,7 +9,7 @@
"flarum-webpack-config": "0.1.0-beta.10",
"jquery": "^3.4.1",
"jquery.hotkeys": "^0.1.0",
"lodash-es": "^4.17.11",
"lodash-es": "^4.17.14",
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
"mithril": "^0.2.8",
"moment": "^2.22.2",

View File

@@ -228,6 +228,12 @@ export default class PermissionGrid extends Component {
permission: 'discussion.deletePosts'
}, 60);
items.add('userEdit', {
icon: 'fas fa-user-cog',
label: app.translator.trans('core.admin.permissions.edit_users_label'),
permission: 'user.edit'
}, 60);
return items;
}

View File

@@ -190,7 +190,7 @@ export default class Model {
* @public
*/
delete(data, options = {}) {
if (!this.exists) return m.deferred.resolve().promise;
if (!this.exists) return m.deferred().resolve().promise;
return app.request(Object.assign({
method: 'DELETE',

View File

@@ -29,6 +29,12 @@ export default class Button extends Component {
attrs.className = attrs.className || '';
attrs.type = attrs.type || 'button';
// If a tooltip was provided for buttons without additional content, we also
// use this tooltip as text for screen readers
if (attrs.title && !this.props.children) {
attrs['aria-label'] = attrs.title;
}
// If nothing else is provided, we use the textual button content as tooltip
if (!attrs.title && this.props.children) {
attrs.title = extractText(this.props.children);

View File

@@ -91,6 +91,7 @@ Object.assign(User.prototype, {
user.freshness = new Date();
m.redraw();
};
image.crossOrigin = 'anonymous';
image.src = this.avatarUrl();
},

View File

@@ -1,4 +1,4 @@
const scroll = window.requestAnimationFrame ||
const later = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
@@ -17,7 +17,7 @@ export default class ScrollListener {
*/
constructor(callback) {
this.callback = callback;
this.lastTop = -1;
this.ticking = false;
}
/**
@@ -27,27 +27,27 @@ export default class ScrollListener {
* @protected
*/
loop() {
if (!this.active) return;
// THROTTLE: If the callback is still running (or hasn't yet run), we ignore
// further scroll events.
if (this.ticking) return;
this.update();
// Schedule the callback to be executed soon (TM), and stop throttling once
// the callback is done.
later(() => {
this.update();
this.ticking = false;
});
scroll(this.loop.bind(this));
this.ticking = true;
}
/**
* Check if the scroll position has changed; if it has, run the handler.
* Run the callback, whether there was a scroll event or not.
*
* @param {Boolean} [force=false] Whether or not to force the handler to be
* run, even if the scroll position hasn't changed.
* @public
*/
update(force) {
const top = window.pageYOffset;
if (this.lastTop !== top || force) {
this.callback(top);
this.lastTop = top;
}
update() {
this.callback(window.pageYOffset);
}
/**
@@ -57,8 +57,10 @@ export default class ScrollListener {
*/
start() {
if (!this.active) {
this.active = true;
this.loop();
window.addEventListener(
'scroll',
this.active = this.loop.bind(this)
);
}
}
@@ -68,6 +70,8 @@ export default class ScrollListener {
* @public
*/
stop() {
this.active = false;
window.removeEventListener('scroll', this.active);
this.active = null;
}
}

View File

@@ -13,7 +13,7 @@ export default class LogInButton extends Button {
props.className = (props.className || '') + ' LogInButton';
props.onclick = function() {
const width = 600;
const width = 580;
const height = 400;
const $window = $(window);
@@ -22,7 +22,7 @@ export default class LogInButton extends Button {
`height=${height},` +
`top=${$window.height() / 2 - height / 2},` +
`left=${$window.width() / 2 - width / 2},` +
'status=no,scrollbars=no,resizable=no');
'status=no,scrollbars=yes,resizable=no');
};
super.initProps(props);

View File

@@ -2,6 +2,7 @@ import Component from '../../common/Component';
import avatar from '../../common/helpers/avatar';
import icon from '../../common/helpers/icon';
import humanTime from '../../common/helpers/humanTime';
import Button from '../../common/components/Button';
/**
* The `Notification` component abstract displays a single notification.
@@ -26,6 +27,17 @@ export default class Notification extends Component {
if (!isInitialized) $(element).click(this.markAsRead.bind(this));
}}>
{!notification.isRead() && Button.component({
className: 'Notification-action Button Button--icon Button--link',
icon: 'fas fa-check',
title: app.translator.trans('core.forum.notifications.mark_as_read_tooltip'),
onclick: e => {
e.preventDefault();
e.stopPropagation();
this.markAsRead();
}
})}
{avatar(notification.fromUser())}
{icon(this.icon(), {className: 'Notification-icon'})}
<span className="Notification-content">{this.content()}</span>

View File

@@ -2,7 +2,6 @@ import Component from '../../common/Component';
import ScrollListener from '../../common/utils/ScrollListener';
import PostLoading from './LoadingPost';
import anchorScroll from '../../common/utils/anchorScroll';
import mixin from '../../common/utils/mixin';
import evented from '../../common/utils/evented';
import ReplyPlaceholder from './ReplyPlaceholder';
import Button from '../../common/components/Button';
@@ -586,7 +585,7 @@ class PostStream extends Component {
*/
unpause() {
this.paused = false;
this.scrollListener.update(true);
this.scrollListener.update();
this.trigger('unpaused');
}
}

View File

@@ -2,7 +2,6 @@ import Component from '../../common/Component';
import icon from '../../common/helpers/icon';
import ScrollListener from '../../common/utils/ScrollListener';
import SubtreeRetainer from '../../common/utils/SubtreeRetainer';
import computed from '../../common/utils/computed';
import formatNumber from '../../common/utils/formatNumber';
/**
@@ -365,7 +364,7 @@ export default class PostStreamScrubber extends Component {
}
onresize() {
this.scrollListener.update(true);
this.scrollListener.update();
// Adjust the height of the scrollbar so that it fills the height of
// the sidebar and doesn't overlap the footer.

View File

@@ -122,6 +122,8 @@ export default class TextEditor extends Component {
setSelectionRange(start, end) {
const $textarea = this.$('textarea');
if (!$textarea.length) return;
$textarea[0].setSelectionRange(start, end);
$textarea.focus();
}
@@ -134,6 +136,8 @@ export default class TextEditor extends Component {
getSelectionRange() {
const $textarea = this.$('textarea');
if (!$textarea.length) return [0, 0];
return [$textarea[0].selectionStart, $textarea[0].selectionEnd];
}

View File

@@ -92,7 +92,7 @@ export default class History {
this.stack.pop();
m.route(this.getCurrent().url);
this.canGoBack() ? m.route(this.getCurrent().url) : this.home();
}
/**

View File

@@ -1,3 +1,4 @@
import Alert from '../../common/components/Alert';
import Button from '../../common/components/Button';
import Separator from '../../common/components/Separator';
import EditUserModal from '../components/EditUserModal';
@@ -61,7 +62,7 @@ export default {
items.add('edit', Button.component({
icon: 'fas fa-pencil-alt',
children: app.translator.trans('core.forum.user_controls.edit_button'),
onclick: this.editAction.bind(user)
onclick: this.editAction.bind(this, user)
}));
}
@@ -84,7 +85,7 @@ export default {
items.add('delete', Button.component({
icon: 'fas fa-times',
children: app.translator.trans('core.forum.user_controls.delete_button'),
onclick: this.deleteAction.bind(user)
onclick: this.deleteAction.bind(this, user)
}));
}
@@ -93,23 +94,51 @@ export default {
/**
* Delete the user.
*
* @param {User} user
*/
deleteAction() {
if (confirm(app.translator.trans('core.forum.user_controls.delete_confirmation'))) {
this.delete().then(() => {
if (app.current instanceof UserPage && app.current.user === this) {
app.history.back();
} else {
window.location.reload();
}
});
deleteAction(user) {
if (!confirm(app.translator.trans('core.forum.user_controls.delete_confirmation'))) {
return;
}
user.delete().then(() => {
this.showDeletionAlert(user, 'success');
if (app.current instanceof UserPage && app.current.user === user) {
app.history.back();
} else {
window.location.reload();
}
}).catch(() => this.showDeletionAlert(user, 'error'));
},
/**
* Show deletion alert of user.
*
* @param {User} user
* @param {string} type
*/
showDeletionAlert(user, type) {
const { username, email } = user.data.attributes;
const message = {
success: 'core.forum.user_controls.delete_success_message',
error: 'core.forum.user_controls.delete_error_message',
}[type];
app.alerts.show(new Alert({
type,
children: app.translator.trans(
message, { username, email }
)
}));
},
/**
* Edit the user.
*
* @param {User} user
*/
editAction() {
app.modal.show(new EditUserModal({user: this}));
editAction(user) {
app.modal.show(new EditUserModal({ user }));
}
};

View File

@@ -156,6 +156,7 @@
}
}
.Modal {
max-width: 100%;
margin: 0;
-webkit-transform: none !important;
transform: none !important;

View File

@@ -87,6 +87,10 @@
&:hover {
text-decoration: none;
background: @control-bg;
.Notification-action {
display: block;
}
}
.Avatar {
.Avatar--size(24px);
@@ -98,6 +102,27 @@
font-weight: bold;
text-transform: uppercase;
}
.Notification-action {
float: right;
display: none;
margin-top: -7px;
margin-right: -10px;
line-height: inherit;
padding: 5px 0;
& when (@config-colored-header = true) {
.Button--color(@control-color, @control-bg);
&:hover {
color: @link-color;
}
}
.icon {
font-size: 12px;
}
}
}
.Notification-icon {
float: left;

View File

@@ -16,6 +16,10 @@ use Flarum\Extension\Event\Disabled;
use Flarum\Extension\Event\Enabled;
use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Foundation\Application;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Flarum\Foundation\ErrorHandling\ViewFormatter;
use Flarum\Foundation\ErrorHandling\WhoopsFormatter;
use Flarum\Foundation\Event\ClearingCache;
use Flarum\Frontend\AddLocaleAssets;
use Flarum\Frontend\AddTranslations;
@@ -51,11 +55,11 @@ class AdminServiceProvider extends AbstractServiceProvider
$pipe = new MiddlewarePipe;
// All requests should first be piped through our global error handler
if ($app->inDebugMode()) {
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithWhoops::class));
} else {
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithView::class));
}
$pipe->pipe(new HttpMiddleware\HandleErrors(
$app->make(Registry::class),
$app->inDebugMode() ? $app->make(WhoopsFormatter::class) : $app->make(ViewFormatter::class),
$app->tagged(Reporter::class)
));
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api;
use Carbon\Carbon;
use Flarum\Database\AbstractModel;
use Flarum\User\User;
use Illuminate\Support\Str;
/**
* @property int $id
@@ -38,7 +39,7 @@ class ApiKey extends AbstractModel
{
$key = new static;
$key->key = str_random(40);
$key->key = Str::random(40);
return $key;
}

View File

@@ -20,12 +20,13 @@ use Flarum\Event\ConfigureMiddleware;
use Flarum\Event\ConfigureNotificationTypes;
use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Foundation\Application;
use Flarum\Foundation\ErrorHandling\JsonApiFormatter;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Flarum\Http\Middleware as HttpMiddleware;
use Flarum\Http\RouteCollection;
use Flarum\Http\RouteHandlerFactory;
use Flarum\Http\UrlGenerator;
use Tobscure\JsonApi\ErrorHandler;
use Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler;
use Zend\Stratigility\MiddlewarePipe;
class ApiServiceProvider extends AbstractServiceProvider
@@ -49,7 +50,11 @@ class ApiServiceProvider extends AbstractServiceProvider
$this->app->singleton('flarum.api.middleware', function (Application $app) {
$pipe = new MiddlewarePipe;
$pipe->pipe($app->make(Middleware\HandleErrors::class));
$pipe->pipe(new HttpMiddleware\HandleErrors(
$app->make(Registry::class),
new JsonApiFormatter($app->inDebugMode()),
$app->tagged(Reporter::class)
));
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(Middleware\FakeHttpMethods::class));
@@ -68,25 +73,6 @@ class ApiServiceProvider extends AbstractServiceProvider
$this->app->afterResolving('flarum.api.middleware', function (MiddlewarePipe $pipe) {
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.api.routes')));
});
$this->app->singleton(ErrorHandler::class, function () {
$handler = new ErrorHandler;
$handler->registerHandler(new ExceptionHandler\FloodingExceptionHandler);
$handler->registerHandler(new ExceptionHandler\IlluminateValidationExceptionHandler);
$handler->registerHandler(new ExceptionHandler\InvalidAccessTokenExceptionHandler);
$handler->registerHandler(new ExceptionHandler\InvalidConfirmationTokenExceptionHandler);
$handler->registerHandler(new ExceptionHandler\MethodNotAllowedExceptionHandler);
$handler->registerHandler(new ExceptionHandler\ModelNotFoundExceptionHandler);
$handler->registerHandler(new ExceptionHandler\PermissionDeniedExceptionHandler);
$handler->registerHandler(new ExceptionHandler\RouteNotFoundExceptionHandler);
$handler->registerHandler(new ExceptionHandler\TokenMismatchExceptionHandler);
$handler->registerHandler(new ExceptionHandler\ValidationExceptionHandler);
$handler->registerHandler(new InvalidParameterExceptionHandler);
$handler->registerHandler(new ExceptionHandler\FallbackExceptionHandler($this->app->inDebugMode(), $this->app->make('log')));
return $handler;
});
}
/**

View File

@@ -12,11 +12,14 @@
namespace Flarum\Api;
use Exception;
use Flarum\Foundation\ErrorHandling\JsonApiFormatter;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\User\User;
use Illuminate\Contracts\Container\Container;
use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Throwable;
use Zend\Diactoros\ServerRequestFactory;
class Client
@@ -27,18 +30,18 @@ class Client
protected $container;
/**
* @var ErrorHandler
* @var Registry
*/
protected $errorHandler;
protected $registry;
/**
* @param Container $container
* @param ErrorHandler $errorHandler
* @param Registry $registry
*/
public function __construct(Container $container, ErrorHandler $errorHandler = null)
public function __construct(Container $container, Registry $registry)
{
$this->container = $container;
$this->errorHandler = $errorHandler;
$this->registry = $registry;
}
/**
@@ -69,23 +72,14 @@ class Client
try {
return $controller->handle($request);
} catch (Exception $e) {
if (! $this->errorHandler) {
} catch (Throwable $e) {
$error = $this->registry->handle($e);
if ($error->shouldBeReported()) {
throw $e;
}
return $this->errorHandler->handle($e);
return (new JsonApiFormatter)->format($error, $request);
}
}
/**
* @param ErrorHandler $errorHandler
* @return Client
*/
public function setErrorHandler(?ErrorHandler $errorHandler): self
{
$this->errorHandler = $errorHandler;
return $this;
}
}

View File

@@ -16,6 +16,7 @@ use Flarum\Discussion\Command\ReadDiscussion;
use Flarum\Discussion\Command\StartDiscussion;
use Flarum\Post\Floodgate;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -63,14 +64,14 @@ class CreateDiscussionController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$ipAddress = array_get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1');
$ipAddress = Arr::get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1');
if (! $request->getAttribute('bypassFloodgate')) {
$this->floodgate->assertNotFlooding($actor);
}
$discussion = $this->bus->dispatch(
new StartDiscussion($actor, array_get($request->getParsedBody(), 'data', []), $ipAddress)
new StartDiscussion($actor, Arr::get($request->getParsedBody(), 'data', []), $ipAddress)
);
// After creating the discussion, we assume that the user has seen all

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\GroupSerializer;
use Flarum\Group\Command\CreateGroup;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -43,7 +44,7 @@ class CreateGroupController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new CreateGroup($request->getAttribute('actor'), array_get($request->getParsedBody(), 'data', []))
new CreateGroup($request->getAttribute('actor'), Arr::get($request->getParsedBody(), 'data', []))
);
}
}

View File

@@ -16,6 +16,7 @@ use Flarum\Discussion\Command\ReadDiscussion;
use Flarum\Post\Command\PostReply;
use Flarum\Post\Floodgate;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -62,9 +63,9 @@ class CreatePostController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data', []);
$discussionId = array_get($data, 'relationships.discussion.data.id');
$ipAddress = array_get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1');
$data = Arr::get($request->getParsedBody(), 'data', []);
$discussionId = Arr::get($data, 'relationships.discussion.data.id');
$ipAddress = Arr::get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1');
if (! $request->getAttribute('bypassFloodgate')) {
$this->floodgate->assertNotFlooding($actor);

View File

@@ -12,10 +12,11 @@
namespace Flarum\Api\Controller;
use Flarum\Http\AccessToken;
use Flarum\User\Exception\PermissionDeniedException;
use Flarum\User\Exception\NotAuthenticatedException;
use Flarum\User\UserRepository;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
@@ -57,14 +58,14 @@ class CreateTokenController implements RequestHandlerInterface
{
$body = $request->getParsedBody();
$identification = array_get($body, 'identification');
$password = array_get($body, 'password');
$lifetime = array_get($body, 'lifetime', 3600);
$identification = Arr::get($body, 'identification');
$password = Arr::get($body, 'password');
$lifetime = Arr::get($body, 'lifetime', 3600);
$user = $this->users->findByIdentification($identification);
if (! $user || ! $user->checkPassword($password)) {
throw new PermissionDeniedException;
throw new NotAuthenticatedException;
}
$token = AccessToken::generate($user->id, $lifetime);

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\CurrentUserSerializer;
use Flarum\User\Command\RegisterUser;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -43,7 +44,7 @@ class CreateUserController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new RegisterUser($request->getAttribute('actor'), array_get($request->getParsedBody(), 'data', []))
new RegisterUser($request->getAttribute('actor'), Arr::get($request->getParsedBody(), 'data', []))
);
}
}

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\UserSerializer;
use Flarum\User\Command\DeleteAvatar;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -43,7 +44,7 @@ class DeleteAvatarController extends AbstractShowController
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new DeleteAvatar(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeleteAvatar(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\Discussion\Command\DeleteDiscussion;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
class DeleteDiscussionController extends AbstractDeleteController
@@ -35,7 +36,7 @@ class DeleteDiscussionController extends AbstractDeleteController
*/
protected function delete(ServerRequestInterface $request)
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$input = $request->getParsedBody();

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\Group\Command\DeleteGroup;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
class DeleteGroupController extends AbstractDeleteController
@@ -36,7 +37,7 @@ class DeleteGroupController extends AbstractDeleteController
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeleteGroup(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeleteGroup(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\Post\Command\DeletePost;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
class DeletePostController extends AbstractDeleteController
@@ -36,7 +37,7 @@ class DeletePostController extends AbstractDeleteController
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeletePost(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeletePost(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\User\Command\DeleteUser;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
class DeleteUserController extends AbstractDeleteController
@@ -36,7 +37,7 @@ class DeleteUserController extends AbstractDeleteController
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeleteUser(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeleteUser(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\User\Command\RequestPasswordReset;
use Flarum\User\UserRepository;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
@@ -46,7 +47,7 @@ class ForgotPasswordController implements RequestHandlerInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$email = array_get($request->getParsedBody(), 'email');
$email = Arr::get($request->getParsedBody(), 'email');
$this->bus->dispatch(
new RequestPasswordReset($email)

View File

@@ -16,6 +16,7 @@ use Flarum\Discussion\Discussion;
use Flarum\Discussion\Search\DiscussionSearcher;
use Flarum\Http\UrlGenerator;
use Flarum\Search\SearchCriteria;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -75,7 +76,7 @@ class ListDiscussionsController extends AbstractListController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$query = array_get($this->extractFilter($request), 'q');
$query = Arr::get($this->extractFilter($request), 'q');
$sort = $this->extractSort($request);
$criteria = new SearchCriteria($actor, $query, $sort);

View File

@@ -15,12 +15,14 @@ use Flarum\Api\Serializer\NotificationSerializer;
use Flarum\Discussion\Discussion;
use Flarum\Http\UrlGenerator;
use Flarum\Notification\NotificationRepository;
use Flarum\User\Exception\PermissionDeniedException;
use Flarum\User\AssertPermissionTrait;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListNotificationsController extends AbstractListController
{
use AssertPermissionTrait;
/**
* {@inheritdoc}
*/
@@ -67,9 +69,7 @@ class ListNotificationsController extends AbstractListController
{
$actor = $request->getAttribute('actor');
if ($actor->isGuest()) {
throw new PermissionDeniedException;
}
$this->assertRegistered($actor);
$actor->markNotificationsAsRead()->save();

View File

@@ -15,6 +15,8 @@ use Flarum\Api\Serializer\PostSerializer;
use Flarum\Event\ConfigurePostsQuery;
use Flarum\Post\PostRepository;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
use Tobscure\JsonApi\Exception\InvalidParameterException;
@@ -64,7 +66,7 @@ class ListPostsController extends AbstractListController
$filter = $this->extractFilter($request);
$include = $this->extractInclude($request);
if ($postIds = array_get($filter, 'id')) {
if ($postIds = Arr::get($filter, 'id')) {
$postIds = explode(',', $postIds);
} else {
$postIds = $this->getPostIds($request);
@@ -86,7 +88,7 @@ class ListPostsController extends AbstractListController
$limit = $this->extractLimit($request);
$filter = $this->extractFilter($request);
if (($near = array_get($queryParams, 'page.near')) > 1) {
if (($near = Arr::get($queryParams, 'page.near')) > 1) {
if (count($filter) > 1 || ! isset($filter['discussion']) || $sort) {
throw new InvalidParameterException(
'You can only use page[near] with filter[discussion] and the default sort order'
@@ -120,7 +122,7 @@ class ListPostsController extends AbstractListController
$query->skip($offset)->take($limit);
foreach ((array) $sort as $field => $order) {
$query->orderBy(snake_case($field), $order);
$query->orderBy(Str::snake($field), $order);
}
return $query->pluck('id')->all();
@@ -132,19 +134,19 @@ class ListPostsController extends AbstractListController
*/
private function applyFilters(Builder $query, array $filter)
{
if ($discussionId = array_get($filter, 'discussion')) {
if ($discussionId = Arr::get($filter, 'discussion')) {
$query->where('discussion_id', $discussionId);
}
if ($number = array_get($filter, 'number')) {
if ($number = Arr::get($filter, 'number')) {
$query->where('number', $number);
}
if ($userId = array_get($filter, 'user')) {
if ($userId = Arr::get($filter, 'user')) {
$query->where('user_id', $userId);
}
if ($type = array_get($filter, 'type')) {
if ($type = Arr::get($filter, 'type')) {
$query->where('type', $type);
}

View File

@@ -14,13 +14,16 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\UserSerializer;
use Flarum\Http\UrlGenerator;
use Flarum\Search\SearchCriteria;
use Flarum\User\Exception\PermissionDeniedException;
use Flarum\User\AssertPermissionTrait;
use Flarum\User\Search\UserSearcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListUsersController extends AbstractListController
{
use AssertPermissionTrait;
/**
* {@inheritdoc}
*/
@@ -69,11 +72,9 @@ class ListUsersController extends AbstractListController
{
$actor = $request->getAttribute('actor');
if ($actor->cannot('viewUserList')) {
throw new PermissionDeniedException;
}
$this->assertCan($actor, 'viewUserList');
$query = array_get($this->extractFilter($request), 'q');
$query = Arr::get($this->extractFilter($request), 'q');
$sort = $this->extractSort($request);
$criteria = new SearchCriteria($actor, $query, $sort);

View File

@@ -18,6 +18,7 @@ use Flarum\User\EmailToken;
use Flarum\User\Exception\PermissionDeniedException;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Mail\Message;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
@@ -67,7 +68,7 @@ class SendConfirmationEmailController implements RequestHandlerInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$this->assertRegistered($actor);

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\Group\Permission;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
@@ -30,8 +31,8 @@ class SetPermissionController implements RequestHandlerInterface
$this->assertAdmin($request->getAttribute('actor'));
$body = $request->getParsedBody();
$permission = array_get($body, 'permission');
$groupIds = array_get($body, 'groupIds');
$permission = Arr::get($body, 'permission');
$groupIds = Arr::get($body, 'groupIds');
Permission::where('permission', $permission)->delete();

View File

@@ -16,6 +16,8 @@ use Flarum\Discussion\Discussion;
use Flarum\Discussion\DiscussionRepository;
use Flarum\Post\PostRepository;
use Flarum\User\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -73,7 +75,7 @@ class ShowDiscussionController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$discussionId = array_get($request->getQueryParams(), 'id');
$discussionId = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$include = $this->extractInclude($request);
@@ -86,7 +88,7 @@ class ShowDiscussionController extends AbstractShowController
}
$discussion->load(array_filter($include, function ($relationship) {
return ! starts_with($relationship, 'posts');
return ! Str::startsWith($relationship, 'posts');
}));
return $discussion;
@@ -150,7 +152,7 @@ class ShowDiscussionController extends AbstractShowController
$queryParams = $request->getQueryParams();
$actor = $request->getAttribute('actor');
if (($near = array_get($queryParams, 'page.near')) > 1) {
if (($near = Arr::get($queryParams, 'page.near')) > 1) {
$offset = $this->posts->getIndexForNumber($discussion->id, $near, $actor);
$offset = max(0, $offset - $limit / 2);
} else {

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Post\PostRepository;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -52,6 +53,6 @@ class ShowPostController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->posts->findOrFail(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'));
return $this->posts->findOrFail(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'));
}
}

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\CurrentUserSerializer;
use Flarum\Api\Serializer\UserSerializer;
use Flarum\User\UserRepository;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -47,7 +48,7 @@ class ShowUserController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
if (! is_numeric($id)) {
$id = $this->users->getIdForUsername($id);

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\Extension\ExtensionManager;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
class UninstallExtensionController extends AbstractDeleteController
@@ -36,7 +37,7 @@ class UninstallExtensionController extends AbstractDeleteController
{
$this->assertAdmin($request->getAttribute('actor'));
$name = array_get($request->getQueryParams(), 'name');
$name = Arr::get($request->getQueryParams(), 'name');
if ($this->extensions->getExtension($name) == null) {
return;

View File

@@ -16,6 +16,7 @@ use Flarum\Discussion\Command\EditDiscussion;
use Flarum\Discussion\Command\ReadDiscussion;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -45,8 +46,8 @@ class UpdateDiscussionController extends AbstractShowController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$discussionId = array_get($request->getQueryParams(), 'id');
$data = array_get($request->getParsedBody(), 'data', []);
$discussionId = Arr::get($request->getQueryParams(), 'id');
$data = Arr::get($request->getParsedBody(), 'data', []);
$discussion = $this->bus->dispatch(
new EditDiscussion($discussionId, $actor, $data)
@@ -54,7 +55,7 @@ class UpdateDiscussionController extends AbstractShowController
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
// That's what extensions will do anyway.
if ($readNumber = array_get($data, 'attributes.lastReadPostNumber')) {
if ($readNumber = Arr::get($data, 'attributes.lastReadPostNumber')) {
$state = $this->bus->dispatch(
new ReadDiscussion($discussionId, $actor, $readNumber)
);

View File

@@ -13,6 +13,7 @@ namespace Flarum\Api\Controller;
use Flarum\Extension\ExtensionManager;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
@@ -42,8 +43,8 @@ class UpdateExtensionController implements RequestHandlerInterface
{
$this->assertAdmin($request->getAttribute('actor'));
$enabled = array_get($request->getParsedBody(), 'enabled');
$name = array_get($request->getQueryParams(), 'name');
$enabled = Arr::get($request->getParsedBody(), 'enabled');
$name = Arr::get($request->getQueryParams(), 'name');
if ($enabled === true) {
$this->extensions->enable($name);

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\GroupSerializer;
use Flarum\Group\Command\EditGroup;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -42,9 +43,9 @@ class UpdateGroupController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data', []);
$data = Arr::get($request->getParsedBody(), 'data', []);
return $this->bus->dispatch(
new EditGroup($id, $actor, $data)

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\NotificationSerializer;
use Flarum\Notification\Command\ReadNotification;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -42,7 +43,7 @@ class UpdateNotificationController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
return $this->bus->dispatch(

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Post\Command\EditPost;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -50,9 +51,9 @@ class UpdatePostController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data', []);
$data = Arr::get($request->getParsedBody(), 'data', []);
return $this->bus->dispatch(
new EditPost($id, $actor, $data)

View File

@@ -16,6 +16,7 @@ use Flarum\Api\Serializer\UserSerializer;
use Flarum\User\Command\EditUser;
use Flarum\User\Exception\PermissionDeniedException;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -49,9 +50,9 @@ class UpdateUserController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data', []);
$data = Arr::get($request->getParsedBody(), 'data', []);
if ($actor->id == $id) {
$this->serializer = CurrentUserSerializer::class;
@@ -60,7 +61,7 @@ class UpdateUserController extends AbstractShowController
// Require the user's current password if they are attempting to change
// their own email address.
if (isset($data['attributes']['email']) && $actor->id == $id) {
$password = array_get($request->getParsedBody(), 'meta.password');
$password = Arr::get($request->getParsedBody(), 'meta.password');
if (! $actor->checkPassword($password)) {
throw new PermissionDeniedException;

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\UserSerializer;
use Flarum\User\Command\UploadAvatar;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
@@ -42,9 +43,9 @@ class UploadAvatarController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$id = Arr::get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$file = array_get($request->getUploadedFiles(), 'avatar');
$file = Arr::get($request->getUploadedFiles(), 'avatar');
return $this->bus->dispatch(
new UploadAvatar($id, $file, $actor)

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Foundation\Application;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Intervention\Image\ImageManager;
use League\Flysystem\Adapter\Local;
@@ -52,7 +53,7 @@ class UploadFaviconController extends ShowForumController
{
$this->assertAdmin($request->getAttribute('actor'));
$file = array_get($request->getUploadedFiles(), 'favicon');
$file = Arr::get($request->getUploadedFiles(), 'favicon');
$tmpFile = tempnam($this->app->storagePath().'/tmp', 'favicon');
$file->moveTo($tmpFile);

View File

@@ -14,6 +14,7 @@ namespace Flarum\Api\Controller;
use Flarum\Foundation\Application;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Intervention\Image\ImageManager;
use League\Flysystem\Adapter\Local;
@@ -52,7 +53,7 @@ class UploadLogoController extends ShowForumController
{
$this->assertAdmin($request->getAttribute('actor'));
$file = array_get($request->getUploadedFiles(), 'logo');
$file = Arr::get($request->getUploadedFiles(), 'logo');
$tmpFile = tempnam($this->app->storagePath().'/tmp', 'logo');
$file->moveTo($tmpFile);

View File

@@ -1,51 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api;
use Exception;
use Throwable;
use Tobscure\JsonApi\Document;
use Tobscure\JsonApi\ErrorHandler as JsonApiErrorHandler;
class ErrorHandler
{
/**
* @var JsonApiErrorHandler
*/
protected $errorHandler;
/**
* @param JsonApiErrorHandler $errorHandler
*/
public function __construct(JsonApiErrorHandler $errorHandler)
{
$this->errorHandler = $errorHandler;
}
/**
* @param Exception $e
* @return JsonApiResponse
*/
public function handle(Throwable $e)
{
if (! $e instanceof Exception) {
$e = new Exception($e->getMessage(), $e->getCode(), $e);
}
$response = $this->errorHandler->handle($e);
$document = new Document;
$document->setErrors($response->getErrors());
return new JsonApiResponse($document, $response->getStatus());
}
}

View File

@@ -12,6 +12,7 @@
namespace Flarum\Api\Event;
use Flarum\Api\Controller\AbstractSerializeController;
use Illuminate\Support\Arr;
class WillGetData
{
@@ -64,7 +65,7 @@ class WillGetData
*/
public function removeInclude($name)
{
array_forget($this->controller->include, $name);
Arr::forget($this->controller->include, $name);
}
/**
@@ -84,7 +85,7 @@ class WillGetData
*/
public function removeOptionalInclude($name)
{
array_forget($this->controller->optionalInclude, $name);
Arr::forget($this->controller->optionalInclude, $name);
}
/**
@@ -124,7 +125,7 @@ class WillGetData
*/
public function removeSortField($field)
{
array_forget($this->controller->sortFields, $field);
Arr::forget($this->controller->sortFields, $field);
}
/**

View File

@@ -12,7 +12,12 @@
namespace Flarum\Api\Exception;
use Exception;
use Flarum\Foundation\KnownError;
class InvalidAccessTokenException extends Exception
class InvalidAccessTokenException extends Exception implements KnownError
{
public function getType(): string
{
return 'invalid_access_token';
}
}

View File

@@ -1,77 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Psr\Log\LoggerInterface;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class FallbackExceptionHandler implements ExceptionHandlerInterface
{
/**
* @var bool
*/
protected $debug;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @param bool $debug
* @param LoggerInterface $logger
*/
public function __construct($debug, LoggerInterface $logger)
{
$this->debug = $debug;
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return true;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 500;
$error = $this->constructError($e, $status);
$this->logger->error($e);
return new ResponseBag($status, [$error]);
}
/**
* @param Exception $e
* @param $status
* @return array
*/
private function constructError(Exception $e, $status)
{
$error = ['code' => $status, 'title' => 'Internal server error'];
if ($this->debug) {
$error['detail'] = (string) $e;
}
return $error;
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\Post\Exception\FloodingException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class FloodingExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof FloodingException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 429;
$error = [
'status' => (string) $status,
'code' => 'too_many_requests'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,58 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Illuminate\Validation\ValidationException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class IlluminateValidationExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof ValidationException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 422;
$errors = $this->formatErrors($e->errors());
return new ResponseBag($status, $errors);
}
/**
* @param array $errors
* @return array
*/
protected function formatErrors(array $errors)
{
$errors = array_map(function ($field, $messages) {
return [
'status' => '422',
'code' => 'validation_error',
'detail' => implode("\n", $messages),
'source' => ['pointer' => "/data/attributes/$field"]
];
}, array_keys($errors), $errors);
return $errors;
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\Api\Exception\InvalidAccessTokenException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class InvalidAccessTokenExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof InvalidAccessTokenException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 401;
$error = [
'status' => (string) $status,
'code' => 'invalid_access_token'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\User\Exception\InvalidConfirmationTokenException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class InvalidConfirmationTokenExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof InvalidConfirmationTokenException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 403;
$error = [
'status' => (string) $status,
'code' => 'invalid_confirmation_token'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\Http\Exception\MethodNotAllowedException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class MethodNotAllowedExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof MethodNotAllowedException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 405;
$error = [
'status' => (string) $status,
'code' => 'method_not_allowed'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class ModelNotFoundExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof ModelNotFoundException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 404;
$error = [
'status' => (string) $status,
'code' => 'resource_not_found'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\User\Exception\PermissionDeniedException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class PermissionDeniedExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof PermissionDeniedException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 401;
$error = [
'status' => (string) $status,
'code' => 'permission_denied'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\Http\Exception\RouteNotFoundException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class RouteNotFoundExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof RouteNotFoundException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 404;
$error = [
'status' => (string) $status,
'code' => 'route_not_found'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,42 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\Http\Exception\TokenMismatchException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class TokenMismatchExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof TokenMismatchException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$status = 400;
$error = [
'status' => (string) $status,
'code' => 'csrf_token_mismatch'
];
return new ResponseBag($status, [$error]);
}
}

View File

@@ -1,53 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\ExceptionHandler;
use Exception;
use Flarum\Foundation\ValidationException;
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
class ValidationExceptionHandler implements ExceptionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function manages(Exception $e)
{
return $e instanceof ValidationException;
}
/**
* {@inheritdoc}
*/
public function handle(Exception $e)
{
$errors = array_merge(
$this->buildErrors($e->getAttributes(), '/data/attributes'),
$this->buildErrors($e->getRelationships(), '/data/relationships')
);
return new ResponseBag(422, $errors);
}
private function buildErrors(array $messages, $pointer)
{
return array_map(function ($path, $detail) use ($pointer) {
return [
'status' => '422',
'code' => 'validation_error',
'detail' => $detail,
'source' => ['pointer' => $pointer.'/'.$path]
];
}, array_keys($messages), $messages);
}
}

View File

@@ -1,47 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Middleware;
use Flarum\Api\ErrorHandler;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface as Middleware;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Throwable;
class HandleErrors implements Middleware
{
/**
* @var ErrorHandler
*/
protected $errorHandler;
/**
* @param ErrorHandler $errorHandler
*/
public function __construct(ErrorHandler $errorHandler)
{
$this->errorHandler = $errorHandler;
}
/**
* Catch all errors that happen during further middleware execution.
*/
public function process(Request $request, Handler $handler): Response
{
try {
return $handler->handle($request);
} catch (Throwable $e) {
return $this->errorHandler->handle($e);
}
}
}

View File

@@ -33,7 +33,7 @@ class Configuring
public $console;
/**
* @param Application $app
* @param Application $app
* @param ConsoleApplication $console
*/
public function __construct(Application $app, ConsoleApplication $console)

View File

@@ -13,9 +13,14 @@ namespace Flarum\Console;
use Flarum\Console\Event\Configuring;
use Flarum\Foundation\Application;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Flarum\Foundation\SiteInterface;
use Illuminate\Contracts\Events\Dispatcher;
use Symfony\Component\Console\Application as ConsoleApplication;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
class Server
{
@@ -45,7 +50,33 @@ class Server
{
$app = Application::getInstance();
$this->handleErrors($app, $console);
$events = $app->make(Dispatcher::class);
$events->fire(new Configuring($app, $console));
}
private function handleErrors(Application $app, ConsoleApplication $console)
{
$dispatcher = new EventDispatcher();
$dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event) use ($app) {
/** @var Registry $registry */
$registry = $app->make(Registry::class);
$error = $registry->handle($event->getError());
/** @var Reporter[] $reporters */
$reporters = $app->tagged(Reporter::class);
if ($error->shouldBeReported()) {
foreach ($reporters as $reporter) {
$reporter->report($error->getException());
}
}
});
$console->setDispatcher($dispatcher);
}
}

View File

@@ -12,7 +12,12 @@
namespace Flarum\Database\Console;
use Flarum\Console\AbstractCommand;
use Flarum\Database\Migrator;
use Flarum\Extension\ExtensionManager;
use Flarum\Foundation\Application;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Schema\Builder;
class MigrateCommand extends AbstractCommand
{
@@ -55,16 +60,16 @@ class MigrateCommand extends AbstractCommand
public function upgrade()
{
$this->app->bind('Illuminate\Database\Schema\Builder', function ($app) {
return $app->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
$this->app->bind(Builder::class, function ($app) {
return $app->make(ConnectionInterface::class)->getSchemaBuilder();
});
$migrator = $this->app->make('Flarum\Database\Migrator');
$migrator = $this->app->make(Migrator::class);
$migrator->setOutput($this->output);
$migrator->run(__DIR__.'/../../../migrations');
$extensions = $this->app->make('Flarum\Extension\ExtensionManager');
$extensions = $this->app->make(ExtensionManager::class);
$extensions->getMigrator()->setOutput($this->output);
foreach ($extensions->getEnabledExtensions() as $name => $extension) {
@@ -75,7 +80,7 @@ class MigrateCommand extends AbstractCommand
}
}
$this->app->make('Flarum\Settings\SettingsRepositoryInterface')->set('version', $this->app->version());
$this->app->make(SettingsRepositoryInterface::class)->set('version', $this->app->version());
$this->info('Publishing assets...');

View File

@@ -17,6 +17,7 @@ use Flarum\Discussion\Event\Saving;
use Flarum\Foundation\DispatchEventsTrait;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Arr;
class EditDiscussionHandler
{
@@ -54,7 +55,7 @@ class EditDiscussionHandler
{
$actor = $command->actor;
$data = $command->data;
$attributes = array_get($data, 'attributes', []);
$attributes = Arr::get($data, 'attributes', []);
$discussion = $this->discussions->findOrFail($command->discussionId, $actor);

View File

@@ -20,6 +20,7 @@ use Flarum\Post\Command\PostReply;
use Flarum\User\AssertPermissionTrait;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
use Illuminate\Support\Arr;
class StartDiscussionHandler
{
@@ -66,7 +67,7 @@ class StartDiscussionHandler
// an opportunity to alter the discussion entity based on data in the
// command they may have passed through in the controller.
$discussion = Discussion::start(
array_get($data, 'attributes.title'),
Arr::get($data, 'attributes.title'),
$actor
);

View File

@@ -29,9 +29,10 @@ class FulltextGambit implements GambitInterface
throw new LogicException('This gambit can only be applied on a DiscussionSearch');
}
// The @ character crashes fulltext searches on InnoDB tables.
// See https://bugs.mysql.com/bug.php?id=74042
$bit = str_replace('@', '*', $bit);
// Replace all non-word characters with spaces.
// We do this to prevent MySQL fulltext search boolean mode from taking
// effect: https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
$bit = preg_replace('/[^\p{L}\p{N}_]+/u', ' ', $bit);
$query = $search->getQuery();
$grammar = $query->getGrammar();
@@ -52,7 +53,7 @@ class FulltextGambit implements GambitInterface
// discussions that have a relevant title or that contain relevant posts.
$query
->addSelect('posts_ft.most_relevant_post_id')
->leftJoin(
->join(
new Expression('('.$subquery->toSql().') '.$grammar->wrapTable('posts_ft')),
'posts_ft.discussion_id', '=', 'discussions.id'
)

View File

@@ -13,6 +13,7 @@ namespace Flarum\Event;
use DirectoryIterator;
use Flarum\Locale\LocaleManager;
use Illuminate\Support\Arr;
use RuntimeException;
/**
@@ -49,8 +50,8 @@ class ConfigureLocales
throw new RuntimeException("Error parsing composer.json in $name: ".json_last_error_msg());
}
$locale = array_get($json, 'extra.flarum-locale.code');
$title = array_get($json, 'extra.flarum-locale.title', $title);
$locale = Arr::get($json, 'extra.flarum-locale.code');
$title = Arr::get($json, 'extra.flarum-locale.title', $title);
}
if (! isset($locale)) {

View File

@@ -20,6 +20,18 @@ use RuntimeException;
class LanguagePack implements ExtenderInterface, LifecycleInterface
{
private $path;
/**
* LanguagePack constructor.
*
* @param string|null $path Path to yaml language files.
*/
public function __construct(string $path = '/locale')
{
$this->path = $path;
}
public function extend(Container $container, Extension $extension = null)
{
if (is_null($extension)) {
@@ -49,11 +61,11 @@ class LanguagePack implements ExtenderInterface, LifecycleInterface
{
$locales->addLocale($locale, $title);
$directory = $extension->getPath().'/locale';
$directory = $extension->getPath().$this->path;
if (! is_dir($directory)) {
throw new RuntimeException(
'Language packs must have a "locale" subdirectory.'
'Expected to find "'.$this->path.'" directory in language pack.'
);
}

View File

@@ -12,8 +12,9 @@
namespace Flarum\Extension;
use Flarum\Extension\Event\Disabling;
use Flarum\Http\Exception\ForbiddenException;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\Exception\PermissionDeniedException;
use Illuminate\Support\Arr;
class DefaultLanguagePackGuard
{
@@ -34,10 +35,10 @@ class DefaultLanguagePackGuard
}
$defaultLocale = $this->settings->get('default_locale');
$locale = array_get($event->extension->extra, 'flarum-locale.code');
$locale = Arr::get($event->extension->extra, 'flarum-locale.code');
if ($locale === $defaultLocale) {
throw new ForbiddenException('You cannot disable the default language pack!');
throw new PermissionDeniedException('You cannot disable the default language pack!');
}
}
}

View File

@@ -21,6 +21,8 @@ use Flarum\Foundation\Application;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Schema\Builder;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
@@ -227,8 +229,8 @@ class ExtensionManager
*/
public function migrate(Extension $extension, $direction = 'up')
{
$this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
$this->app->bind(Builder::class, function ($container) {
return $container->make(ConnectionInterface::class)->getSchemaBuilder();
});
$extension->migrate($this->migrator, $direction);

View File

@@ -139,8 +139,8 @@ class Formatter
$dom = $configurator->tags['URL']->template->asDOM();
foreach ($dom->getElementsByTagName('a') as $a) {
$a->setAttribute('target', '_blank');
$a->setAttribute('rel', 'nofollow');
$rel = $a->getAttribute('rel');
$a->setAttribute('rel', "$rel nofollow ugc");
}
$dom->saveChanges();

View File

@@ -12,6 +12,7 @@
namespace Flarum\Formatter;
use Flarum\Foundation\AbstractServiceProvider;
use Illuminate\Cache\Repository;
use Illuminate\Contracts\Container\Container;
class FormatterServiceProvider extends AbstractServiceProvider
@@ -23,7 +24,7 @@ class FormatterServiceProvider extends AbstractServiceProvider
{
$this->app->singleton('flarum.formatter', function (Container $container) {
return new Formatter(
$container->make('cache.store'),
new Repository($container->make('cache.filestore')),
$container->make('events'),
$this->app->storagePath().'/formatter'
);

View File

@@ -15,6 +15,7 @@ use Flarum\Http\Rememberer;
use Flarum\User\LoginProvider;
use Flarum\User\RegistrationToken;
use Flarum\User\User;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\HtmlResponse;
@@ -43,7 +44,7 @@ class ResponseFactory
$provided = $registration->getProvided();
if (! empty($provided['email']) && $user = User::where(array_only($provided, 'email'))->first()) {
if (! empty($provided['email']) && $user = User::where(Arr::only($provided, 'email'))->first()) {
$user->loginProviders()->create(compact('provider', 'identifier'));
return $this->makeLoggedInResponse($user);

View File

@@ -17,6 +17,7 @@ use Flarum\Http\Exception\RouteNotFoundException;
use Flarum\Http\UrlGenerator;
use Flarum\User\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface as Request;
class Discussion
@@ -51,12 +52,12 @@ class Discussion
public function __invoke(Document $document, Request $request)
{
$queryParams = $request->getQueryParams();
$page = max(1, array_get($queryParams, 'page'));
$page = max(1, Arr::get($queryParams, 'page'));
$params = [
'id' => (int) array_get($queryParams, 'id'),
'id' => (int) Arr::get($queryParams, 'id'),
'page' => [
'near' => array_get($queryParams, 'near'),
'near' => Arr::get($queryParams, 'near'),
'offset' => ($page - 1) * 20,
'limit' => 20
]
@@ -65,7 +66,7 @@ class Discussion
$apiDocument = $this->getApiDocument($request->getAttribute('actor'), $params);
$getResource = function ($link) use ($apiDocument) {
return array_first($apiDocument->included, function ($value) use ($link) {
return Arr::first($apiDocument->included, function ($value) use ($link) {
return $value->type === $link->type && $value->id === $link->id;
});
};

View File

@@ -14,8 +14,11 @@ namespace Flarum\Forum\Content;
use Flarum\Api\Client;
use Flarum\Api\Controller\ListDiscussionsController;
use Flarum\Frontend\Document;
use Flarum\Http\UrlGenerator;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface as Request;
class Index
@@ -30,23 +33,37 @@ class Index
*/
protected $view;
/**
* @var SettingsRepositoryInterface
*/
protected $settings;
/**
* @var UrlGenerator
*/
protected $url;
/**
* @param Client $api
* @param Factory $view
* @param SettingsRepositoryInterface $settings
* @param UrlGenerator $url
*/
public function __construct(Client $api, Factory $view)
public function __construct(Client $api, Factory $view, SettingsRepositoryInterface $settings, UrlGenerator $url)
{
$this->api = $api;
$this->view = $view;
$this->settings = $settings;
$this->url = $url;
}
public function __invoke(Document $document, Request $request)
{
$queryParams = $request->getQueryParams();
$sort = array_pull($queryParams, 'sort');
$q = array_pull($queryParams, 'q');
$page = array_pull($queryParams, 'page', 1);
$sort = Arr::pull($queryParams, 'sort');
$q = Arr::pull($queryParams, 'q');
$page = Arr::pull($queryParams, 'page', 1);
$sortMap = $this->getSortMap();
@@ -57,9 +74,11 @@ class Index
];
$apiDocument = $this->getApiDocument($request->getAttribute('actor'), $params);
$defaultRoute = $this->settings->get('default_route');
$document->content = $this->view->make('flarum.forum::frontend.content.index', compact('apiDocument', 'page'));
$document->payload['apiDocument'] = $apiDocument;
$document->canonicalUrl = $defaultRoute === '/all' ? $this->url->to('forum')->base() : $request->getUri()->withQuery('');
return $document;
}

View File

@@ -14,12 +14,11 @@ namespace Flarum\Forum\Controller;
use Flarum\Foundation\Application;
use Flarum\Http\SessionAuthenticator;
use Flarum\User\Command\ConfirmEmail;
use Flarum\User\Exception\InvalidConfirmationTokenException;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Arr;
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 ConfirmEmailController implements RequestHandlerInterface
@@ -57,15 +56,11 @@ class ConfirmEmailController implements RequestHandlerInterface
*/
public function handle(Request $request): ResponseInterface
{
try {
$token = array_get($request->getQueryParams(), 'token');
$token = Arr::get($request->getQueryParams(), 'token');
$user = $this->bus->dispatch(
new ConfirmEmail($token)
);
} catch (InvalidConfirmationTokenException $e) {
return new HtmlResponse('Invalid confirmation token');
}
$user = $this->bus->dispatch(
new ConfirmEmail($token)
);
$session = $request->getAttribute('session');
$this->authenticator->logIn($session, $user->id);

View File

@@ -18,6 +18,7 @@ use Flarum\Http\Rememberer;
use Flarum\Http\SessionAuthenticator;
use Flarum\User\Event\LoggedIn;
use Flarum\User\UserRepository;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
@@ -65,7 +66,7 @@ class LogInController implements RequestHandlerInterface
{
$actor = $request->getAttribute('actor');
$body = $request->getParsedBody();
$params = array_only($body, ['identification', 'password']);
$params = Arr::only($body, ['identification', 'password']);
$response = $this->apiClient->send(CreateTokenController::class, $actor, [], $params);
@@ -79,7 +80,7 @@ class LogInController implements RequestHandlerInterface
event(new LoggedIn($this->users->findOrFail($data->userId), $token));
if (array_get($body, 'remember')) {
if (Arr::get($body, 'remember')) {
$response = $this->rememberer->remember($response, $token);
}
}

View File

@@ -20,6 +20,7 @@ use Flarum\User\AssertPermissionTrait;
use Flarum\User\Event\LoggedOut;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
@@ -94,7 +95,7 @@ class LogOutController implements RequestHandlerInterface
$session = $request->getAttribute('session');
$actor = $request->getAttribute('actor');
$url = array_get($request->getQueryParams(), 'return', $this->app->url());
$url = Arr::get($request->getQueryParams(), 'return', $this->app->url());
// If there is no user logged in, return to the index.
if ($actor->isGuest()) {
@@ -105,8 +106,8 @@ class LogOutController implements RequestHandlerInterface
// allow the user to press a button to complete the log out process.
$csrfToken = $session->token();
if (array_get($request->getQueryParams(), 'token') !== $csrfToken) {
$return = array_get($request->getQueryParams(), 'return');
if (Arr::get($request->getQueryParams(), 'token') !== $csrfToken) {
$return = Arr::get($request->getQueryParams(), 'return');
$view = $this->view->make('flarum.forum::log-out')
->with('url', $this->url->to('forum')->route('logout').'?token='.$csrfToken.($return ? '&return='.urlencode($return) : ''));

View File

@@ -16,6 +16,7 @@ use Flarum\Http\Controller\AbstractHtmlController;
use Flarum\User\Exception\InvalidConfirmationTokenException;
use Flarum\User\PasswordToken;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface as Request;
class ResetPasswordController extends AbstractHtmlController
@@ -40,7 +41,7 @@ class ResetPasswordController extends AbstractHtmlController
*/
public function render(Request $request)
{
$token = array_get($request->getQueryParams(), 'token');
$token = Arr::get($request->getQueryParams(), 'token');
$token = PasswordToken::findOrFail($token);

View File

@@ -18,6 +18,7 @@ use Flarum\User\PasswordToken;
use Flarum\User\UserValidator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Validation\Factory;
use Illuminate\Support\Arr;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationException;
use Psr\Http\Message\ResponseInterface;
@@ -72,9 +73,9 @@ class SavePasswordController implements RequestHandlerInterface
{
$input = $request->getParsedBody();
$token = PasswordToken::findOrFail(array_get($input, 'passwordToken'));
$token = PasswordToken::findOrFail(Arr::get($input, 'passwordToken'));
$password = array_get($input, 'password');
$password = Arr::get($input, 'password');
try {
// todo: probably shouldn't use the user validator for this,

View File

@@ -18,6 +18,10 @@ use Flarum\Extension\Event\Enabled;
use Flarum\Formatter\Formatter;
use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Foundation\Application;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Flarum\Foundation\ErrorHandling\ViewFormatter;
use Flarum\Foundation\ErrorHandling\WhoopsFormatter;
use Flarum\Foundation\Event\ClearingCache;
use Flarum\Frontend\AddLocaleAssets;
use Flarum\Frontend\AddTranslations;
@@ -53,15 +57,19 @@ class ForumServiceProvider extends AbstractServiceProvider
return $routes;
});
$this->app->afterResolving('flarum.forum.routes', function (RouteCollection $routes) {
$this->setDefaultRoute($routes);
});
$this->app->singleton('flarum.forum.middleware', function (Application $app) {
$pipe = new MiddlewarePipe;
// All requests should first be piped through our global error handler
if ($app->inDebugMode()) {
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithWhoops::class));
} else {
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithView::class));
}
$pipe->pipe(new HttpMiddleware\HandleErrors(
$app->make(Registry::class),
$app->inDebugMode() ? $app->make(WhoopsFormatter::class) : $app->make(ViewFormatter::class),
$app->tagged(Reporter::class)
));
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(HttpMiddleware\CollectGarbage::class));
@@ -181,7 +189,16 @@ class ForumServiceProvider extends AbstractServiceProvider
$this->app->make('events')->fire(
new ConfigureForumRoutes($routes, $factory)
);
}
/**
* Determine the default route.
*
* @param RouteCollection $routes
*/
protected function setDefaultRoute(RouteCollection $routes)
{
$factory = $this->app->make(RouteHandlerFactory::class);
$defaultRoute = $this->app->make('flarum.settings')->get('default_route');
if (isset($routes->getRouteData()[0]['GET'][$defaultRoute])) {

Some files were not shown because too many files have changed in this diff Show More