1
0
mirror of https://github.com/flarum/core.git synced 2025-08-26 01:34:16 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Daniël Klabbers
34079108c8 update version to v0.1.0-beta.8.2 2019-06-12 16:12:01 +02:00
Daniël Klabbers
f7a8b76fa8 fixes font awesome issues in flarum 0.1.0-beta.8.1 2019-06-12 16:11:00 +02:00
334 changed files with 4109 additions and 6876 deletions

BIN
.deploy.enc Normal file

Binary file not shown.

View File

@@ -3,6 +3,9 @@ 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

@@ -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 `composer test`).
- [ ] Backend changes: tests are green (run `php vendor/bin/phpunit`).
**Required changes:**

13
.github/SECURITY.md vendored
View File

@@ -1,13 +0,0 @@
# Security Policy
## Supported Versions
During the beta phase, we will only patch security vulnerabilities in the latest beta release.
## Reporting a Vulnerability
If you discover a security vulnerability within Flarum, please send an email to security@flarum.org so we can address it promptly.
We will get back to you as time allows.
Discussions may commence internally, so you may not hear back immediately.
When reporting a vulnerability, please provide your GitHub username (if available), so that we can invite you to collaborate on a [security advisory on GitHub](https://help.github.com/en/articles/about-maintainer-security-advisories).

View File

@@ -1,16 +0,0 @@
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 }}

View File

@@ -1,80 +0,0 @@
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

2
.gitignore vendored
View File

@@ -4,6 +4,6 @@ composer.phar
node_modules
.DS_Store
Thumbs.db
/tests/integration/tmp
/tests/tmp
.vagrant
.idea/*

46
.travis.yml Normal file
View File

@@ -0,0 +1,46 @@
language: php
cache:
directories:
- $HOME/.composer/cache
- $HOME/.npm
install:
- composer install
- mysql -e 'CREATE DATABASE flarum;'
script:
- vendor/bin/phpunit --coverage-clover=coverage.xml
after_success:
- bash <(curl -s https://codecov.io/bash)
jobs:
include:
- php: 7.1
env: DB=mysql
- php: 7.2
env: DB=mysql
- php: 7.2
env: DB=mysql PREFIX=forum_
- php: 7.1
addons:
mariadb: '10.2'
env: DB=mariadb
- php: 7.2
addons:
mariadb: '10.2'
env: DB=mariadb
- 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

33
.travis/build.sh Executable file
View File

@@ -0,0 +1,33 @@
#!/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,85 +1,12 @@
# 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
- New `hasPermission()` helper method for `Group` objects ([9684fbc](https://github.com/flarum/core/commit/9684fbc4da07d32aa322d9228302a23418412cb9))
- Expose supported mail drivers in IoC container ([208bad3](https://github.com/flarum/core/commit/208bad393f37bfdb76007afcddfa4b7451563e9d))
- More test for some API endpoints ([1670590](https://github.com/flarum/core/commit/167059027e5a066d618599c90164ef1b5a509148))
- The `Formatter\Rendering` event now receives the HTTP request instance as well ([0ab9fac](https://github.com/flarum/core/commit/0ab9facc4bd59a260575e6fc650793c663e5866a))
- More and better validation in installer UIs
- Check and enforce minimum MariaDB ([7ff9a90](https://github.com/flarum/core/commit/7ff9a90204923293adc520d3c02dc984845d4f9f))
- Revert publication of assets when installation fails ([ed9591c](https://github.com/flarum/core/commit/ed9591c16fb2ea7a4be3387b805d855a53e0a7d5))
- Benefit from Laravel's database reconnection logic in long-running tasks ([e0becd0](https://github.com/flarum/core/commit/e0becd0c7bda939048923c1f86648793feee78d5))
- The "vendor path" (where Composer dependencies can be found) can now be configured ([5e1680c](https://github.com/flarum/core/commit/5e1680c458cd3ba274faeb92de3ac2053789131e))
### Changed
- Performance: Actually cache translations on disk ([0d16fac](https://github.com/flarum/core/commit/0d16fac001bb735ee66e82871183516aeac269b7))
- Allow per-site extenders to override extension extenders ([ba594de](https://github.com/flarum/core/commit/ba594de13a033480834d53d73f747b05fe9796f8))
- Do not resolve objects from the IoC container (in service providers and extenders) until they are actually used
- Replace event subscribers (that resolve objects from the IoC container) with listeners (that resolve lazily)
- Use custom service provider for Mail component ([ac5e26a](https://github.com/flarum/core/commit/ac5e26a254d89e21bd4c115b6cbd40338e2e4b4b))
- Update to Laravel 5.7, revert custom logic for building database index names
- Refactored installer, extracted Installation class and pipeline for reuse in CLI and web installers ([790d5be](https://github.com/flarum/core/commit/790d5beee5e283178716bc8f9901c758d9e5b6a0))
- Use whitelist for enabling pre-installed extensions during installation ([4585f03](https://github.com/flarum/core/commit/4585f03ee356c92942fbc2ae8c683c651b473954))
- Update minimum MySQL version ([7ff9a90](https://github.com/flarum/core/commit/7ff9a90204923293adc520d3c02dc984845d4f9f))
### Fixed
- Signing up via OAuth providers was broken ([67f9375](https://github.com/flarum/core/commit/67f9375d4745add194ae3249d526197c32fd5461))
- Group badges were overlapping ([16eb1fa](https://github.com/flarum/core/commit/16eb1fa63b6d7b80ec30c24c0e406a2b7ab09934))
- API: Endpoint for uninstalling extensions returned an error ([c761802](https://github.com/flarum/core/commit/c76180290056ddbab67baf5ede814fcedf1dcf14))
- Documentation links in installer were outdated ([b58380e](https://github.com/flarum/core/commit/b58380e224ee54abdade3d0a4cc107ef5c91c9a9))
- Event posts where counted when aggregating user posts ([671fdec](https://github.com/flarum/core/commit/671fdec8d0a092ccceb5d4d5f657d0f4287fc4c7))
- Admins could not reset user passwords ([c67fb2d](https://github.com/flarum/core/commit/c67fb2d4b6a128c71d65dc6703310c0b62f91be2))
- Several down migrations were invalid
- Validation errors on reset password page resulted in HTTP 404 ([4611abe](https://github.com/flarum/core/commit/4611abe5db8b94ca3dc7bf9c447fca7c67358ee3))
- `is:unread` gambit generated an invalid query ([e17bb0b](https://github.com/flarum/core/commit/e17bb0b4331f2c92459292195c6b7db8cde1f9f3))
- Entire forum was breaking when the `custom_less` setting was missing from the database ([bf2c5a5](https://github.com/flarum/core/commit/bf2c5a5564dff3f5ef13efe7a8d69f2617570ce6))
- Dropdown icon was not showing in user card when on user page ([12fdfc9](https://github.com/flarum/core/commit/12fdfc9b544a27f6fe59c82ad6bddd3420cc0181))
- Requests were missing the `original*` attributes, which broke installations in subfolders ([56fde28](https://github.com/flarum/core/commit/56fde28e436f52fee0c03c538f0a6049bc584b53))
- Special characters such as `%` and `_` could return incorrect results ([ee3640e](https://github.com/flarum/core/commit/ee3640e1605ff67fef4b3d5cd0596f14a6ae73c9))
- FontAwesome component package changed paths in version 5.9.0 ([5eb69e1](https://github.com/flarum/core/commit/5eb69e1f59fa73fdfd5badbf41a05a6a040e7426))
- Some server environments had problems accessing the system-wide tmp path for storing JS file maps ([54660eb](https://github.com/flarum/core/commit/54660ebd6311f9ea142f1b573263d0d907400786))
- Content length of posts.content was not migrated to mediumText in 2017 ([590b311](https://github.com/flarum/core/commit/590b3115708bf94a9c7f169d98c6126380c7056e))
- An error occurred when going to the previous route if there was no previous route found ([985b87da](https://github.com/flarum/core/commit/985b87da6c9942c568a1a192e2fdcfde72e030ee))
### Removed
- `php flarum install --defaults` - this was meant to be used in our old development VM ([44c9109](https://github.com/flarum/core/commit/44c91099cd77138bb5fc29f14fb1e81a9781272d))
- Obsolete `id` attributes in JSON-API responses ([ecc3b5e](https://github.com/flarum/core/commit/ecc3b5e2271f8d9b38d52cd54476d86995dbe32e) and [7a44086](https://github.com/flarum/core/commit/7a44086bf3a0e3ba907dceb13d07ac695eca05ea))
## [0.1.0-beta.8.1](https://github.com/flarum/core/compare/v0.1.0-beta.8...v0.1.0-beta.8.1)
### Fixed
- Fix live output in `migrate:reset` command ([f591585](https://github.com/flarum/core/commit/f591585d02f8c4ff0211c5bf4413dd6baa724c05))
- Fix search with database prefix ([7705a2b](https://github.com/flarum/core/commit/7705a2b7d751943ef9d0c7379ec34f8530b99310))
- Fix invalid join time of admin user created by installer ([57f73c9](https://github.com/flarum/core/commit/57f73c9638eeb825f9e336ed3c443afccfd8995e))
- Ensure InnoDB engine is used for all tables ([fb6b51b](https://github.com/flarum/core/commit/fb6b51b1cfef0af399607fe038603c8240800b2b), [6370f7e](https://github.com/flarum/core/commit/6370f7ecffa9ea7d5fb64d9551400edbc63318db))
- Ensure InnoDB engine is used for all tables ([fb6b51b](https://github.com/flarum/core/commit/fb6b51b1cfef0af399607fe038603c8240800b2b))
- Fix dropping foreign keys in `down` migrations ([57d5846](https://github.com/flarum/core/commit/57d5846b647881009d9e60f9ffca20b1bb77776e))
- Fix discussion list scroll position not being maintained when hero is not visible ([40dc6ac](https://github.com/flarum/core/commit/40dc6ac604c2a0973356b38217aa8d09352daae5))
- Fix empty meta description tag ([88e43cc](https://github.com/flarum/core/commit/88e43cc6940ee30d6529e9ce659471ec4fb1c474))

3
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,3 @@
# Contributing to Flarum
Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://flarum.org/docs/contributing.html)** to learn how you can help.

View File

@@ -12,10 +12,6 @@
{
"name": "Franz Liedke",
"email": "franz@develophp.org"
},
{
"name": "Daniel Klabbers",
"email": "daniel@klabbers.email"
}
],
"support": {
@@ -30,21 +26,20 @@
"dflydev/fig-cookies": "^1.0.2",
"doctrine/dbal": "^2.7",
"franzl/whoops-middleware": "^0.4.0",
"illuminate/bus": "5.7.*",
"illuminate/cache": "5.7.*",
"illuminate/config": "5.7.*",
"illuminate/container": "5.7.*",
"illuminate/contracts": "5.7.*",
"illuminate/database": "5.7.*",
"illuminate/events": "5.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.*",
"illuminate/view": "5.7.*",
"illuminate/bus": "5.5.*",
"illuminate/cache": "5.5.*",
"illuminate/config": "5.5.*",
"illuminate/container": "5.5.*",
"illuminate/contracts": "5.5.*",
"illuminate/database": "5.5.*",
"illuminate/events": "5.5.*",
"illuminate/filesystem": "5.5.*",
"illuminate/hashing": "5.5.*",
"illuminate/mail": "5.5.*",
"illuminate/session": "5.5.*",
"illuminate/support": "5.5.*",
"illuminate/validation": "5.5.*",
"illuminate/view": "5.5.*",
"intervention/image": "^2.3.0",
"league/flysystem": "^1.0.11",
"matthiasmullie/minify": "^1.3",
@@ -59,8 +54,8 @@
"psr/http-server-middleware": "^1.0",
"s9e/text-formatter": "^1.2.0",
"symfony/config": "^3.3",
"symfony/console": "^4.2",
"symfony/event-dispatcher": "^4.3.2",
"symfony/console": "^3.3",
"symfony/http-foundation": "^3.3",
"symfony/translation": "^3.3",
"symfony/yaml": "^3.3",
"tobscure/json-api": "^0.3.0",
@@ -69,8 +64,8 @@
"zendframework/zend-stratigility": "^3.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0"
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^6.0"
},
"autoload": {
"psr-4": {
@@ -92,20 +87,5 @@
"branch-alias": {
"dev-master": "0.1.x-dev"
}
},
"scripts": {
"test": [
"@test:unit",
"@test:integration"
],
"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."
}
}

22
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

24
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

905
js/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,14 +2,14 @@
"private": true,
"name": "@flarum/core",
"dependencies": {
"bootstrap": "^3.4.1",
"bootstrap": "^3.3.7",
"classnames": "^2.2.5",
"color-thief-browser": "^2.0.2",
"expose-loader": "^0.7.5",
"flarum-webpack-config": "0.1.0-beta.10",
"jquery": "^3.4.1",
"jquery": "^3.3.1",
"jquery.hotkeys": "^0.1.0",
"lodash-es": "^4.17.14",
"lodash-es": "^4.17.11",
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
"mithril": "^0.2.8",
"moment": "^2.22.2",

View File

@@ -84,21 +84,17 @@ export default class EditGroupModal extends Modal {
return items;
}
submitData() {
return {
nameSingular: this.nameSingular(),
namePlural: this.namePlural(),
color: this.color(),
icon: this.icon()
};
}
onsubmit(e) {
e.preventDefault();
this.loading = true;
this.group.save(this.submitData(), {errorHandler: this.onerror.bind(this)})
this.group.save({
nameSingular: this.nameSingular(),
namePlural: this.namePlural(),
color: this.color(),
icon: this.icon()
}, {errorHandler: this.onerror.bind(this)})
.then(this.hide.bind(this))
.catch(() => {
this.loading = false;

View File

@@ -2,55 +2,36 @@ import Page from './Page';
import FieldSet from '../../common/components/FieldSet';
import Button from '../../common/components/Button';
import Alert from '../../common/components/Alert';
import Select from '../../common/components/Select';
import LoadingIndicator from '../../common/components/LoadingIndicator';
import saveSettings from '../utils/saveSettings';
export default class MailPage extends Page {
init() {
super.init();
this.loading = true;
this.saving = false;
this.loading = false;
this.driverFields = {};
this.fields = ['mail_driver', 'mail_from'];
this.fields = [
'mail_driver',
'mail_host',
'mail_from',
'mail_port',
'mail_username',
'mail_password',
'mail_encryption'
];
this.values = {};
const settings = app.data.settings;
this.fields.forEach(key => this.values[key] = m.prop(settings[key]));
app.request({
method: 'GET',
url: app.forum.attribute('apiUrl') + '/mail-drivers'
}).then(response => {
this.driverFields = response['data'].reduce(
(hash, driver) => ({...hash, [driver['id']]: driver['attributes']['fields']}),
{}
);
Object.keys(this.driverFields).flatMap(key => this.driverFields[key]).forEach(
key => {
this.fields.push(key);
this.values[key] = m.prop(settings[key]);
}
);
this.loading = false;
m.redraw();
});
this.localeOptions = {};
const locales = app.locales;
for (const i in locales) {
this.localeOptions[i] = `${locales[i]} (${i})`;
}
}
view() {
if (this.loading) {
return (
<div className="MailPage">
<div className="container">
<LoadingIndicator />
</div>
</div>
);
}
return (
<div className="MailPage">
<div className="container">
@@ -60,6 +41,36 @@ export default class MailPage extends Page {
{app.translator.trans('core.admin.email.text')}
</div>
{FieldSet.component({
label: app.translator.trans('core.admin.email.server_heading'),
className: 'MailPage-MailSettings',
children: [
<div className="MailPage-MailSettings-input">
<label>{app.translator.trans('core.admin.email.driver_label')}</label>
<input className="FormControl" value={this.values.mail_driver() || ''} oninput={m.withAttr('value', this.values.mail_driver)} />
<label>{app.translator.trans('core.admin.email.host_label')}</label>
<input className="FormControl" value={this.values.mail_host() || ''} oninput={m.withAttr('value', this.values.mail_host)} />
<label>{app.translator.trans('core.admin.email.port_label')}</label>
<input className="FormControl" value={this.values.mail_port() || ''} oninput={m.withAttr('value', this.values.mail_port)} />
<label>{app.translator.trans('core.admin.email.encryption_label')}</label>
<input className="FormControl" value={this.values.mail_encryption() || ''} oninput={m.withAttr('value', this.values.mail_encryption)} />
</div>
]
})}
{FieldSet.component({
label: app.translator.trans('core.admin.email.account_heading'),
className: 'MailPage-MailSettings',
children: [
<div className="MailPage-MailSettings-input">
<label>{app.translator.trans('core.admin.email.username_label')}</label>
<input className="FormControl" value={this.values.mail_username() || ''} oninput={m.withAttr('value', this.values.mail_username)} />
<label>{app.translator.trans('core.admin.email.password_label')}</label>
<input className="FormControl" value={this.values.mail_password() || ''} oninput={m.withAttr('value', this.values.mail_password)} />
</div>
]
})}
{FieldSet.component({
label: app.translator.trans('core.admin.email.addresses_heading'),
className: 'MailPage-MailSettings',
@@ -71,35 +82,11 @@ export default class MailPage extends Page {
]
})}
{FieldSet.component({
label: app.translator.trans('core.admin.email.driver_heading'),
className: 'MailPage-MailSettings',
children: [
<div className="MailPage-MailSettings-input">
<label>{app.translator.trans('core.admin.email.driver_label')}</label>
<Select value={this.values.mail_driver()} options={Object.keys(this.driverFields).reduce((memo, val) => ({...memo, [val]: val}), {})} onchange={this.values.mail_driver} />
</div>
]
})}
{Object.keys(this.driverFields[this.values.mail_driver()]).length > 0 && FieldSet.component({
label: app.translator.trans(`core.admin.email.${this.values.mail_driver()}_heading`),
className: 'MailPage-MailSettings',
children: [
<div className="MailPage-MailSettings-input">
{this.driverFields[this.values.mail_driver()].flatMap(field => [
<label>{app.translator.trans(`core.admin.email.${field}_label`)}</label>,
<input className="FormControl" value={this.values[field]() || ''} oninput={m.withAttr('value', this.values[field])} />
])}
</div>
]
})}
{Button.component({
type: 'submit',
className: 'Button Button--primary',
children: app.translator.trans('core.admin.email.submit_button'),
loading: this.saving,
loading: this.loading,
disabled: !this.changed()
})}
</form>
@@ -115,9 +102,9 @@ export default class MailPage extends Page {
onsubmit(e) {
e.preventDefault();
if (this.saving) return;
if (this.loading) return;
this.saving = true;
this.loading = true;
app.alerts.dismiss(this.successAlert);
const settings = {};
@@ -130,7 +117,7 @@ export default class MailPage extends Page {
})
.catch(() => {})
.then(() => {
this.saving = false;
this.loading = false;
m.redraw();
});
}

View File

@@ -228,12 +228,6 @@ 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,12 +29,6 @@ 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,7 +91,6 @@ Object.assign(User.prototype, {
user.freshness = new Date();
m.redraw();
};
image.crossOrigin = 'anonymous';
image.src = this.avatarUrl();
},

View File

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

View File

@@ -77,7 +77,7 @@ export default class EditUserModal extends Modal {
<label>{app.translator.trans('core.forum.edit_user.password_heading')}</label>
<div>
<label className="checkbox">
<input type="checkbox" onchange={e => {
<input type="checkbox" checked={this.setPassword()} onChange={e => {
this.setPassword(e.target.checked);
m.redraw(true);
if (e.target.checked) this.$('[name=password]').select();

View File

@@ -13,7 +13,7 @@ export default class LogInButton extends Button {
props.className = (props.className || '') + ' LogInButton';
props.onclick = function() {
const width = 580;
const width = 600;
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=yes,resizable=no');
'status=no,scrollbars=no,resizable=no');
};
super.initProps(props);

View File

@@ -2,7 +2,6 @@ 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.
@@ -27,17 +26,6 @@ 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,6 +2,7 @@ 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';
@@ -585,7 +586,7 @@ class PostStream extends Component {
*/
unpause() {
this.paused = false;
this.scrollListener.update();
this.scrollListener.update(true);
this.trigger('unpaused');
}
}

View File

@@ -2,6 +2,7 @@ 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';
/**
@@ -364,7 +365,7 @@ export default class PostStreamScrubber extends Component {
}
onresize() {
this.scrollListener.update();
this.scrollListener.update(true);
// Adjust the height of the scrollbar so that it fills the height of
// the sidebar and doesn't overlap the footer.

View File

@@ -122,8 +122,6 @@ export default class TextEditor extends Component {
setSelectionRange(start, end) {
const $textarea = this.$('textarea');
if (!$textarea.length) return;
$textarea[0].setSelectionRange(start, end);
$textarea.focus();
}
@@ -136,8 +134,6 @@ 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

@@ -86,13 +86,9 @@ export default class History {
* @public
*/
back() {
if (! this.canGoBack()) {
return this.home();
}
this.stack.pop();
this.canGoBack() ? m.route(this.getCurrent().url) : this.home();
m.route(this.getCurrent().url);
}
/**

View File

@@ -1,4 +1,3 @@
import Alert from '../../common/components/Alert';
import Button from '../../common/components/Button';
import Separator from '../../common/components/Separator';
import EditUserModal from '../components/EditUserModal';
@@ -62,7 +61,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(this, user)
onclick: this.editAction.bind(user)
}));
}
@@ -85,7 +84,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(this, user)
onclick: this.deleteAction.bind(user)
}));
}
@@ -94,51 +93,23 @@ export default {
/**
* Delete the user.
*
* @param {User} user
*/
deleteAction(user) {
if (!confirm(app.translator.trans('core.forum.user_controls.delete_confirmation'))) {
return;
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();
}
});
}
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(user) {
app.modal.show(new EditUserModal({ user }));
editAction() {
app.modal.show(new EditUserModal({user: this}));
}
};

View File

@@ -29,10 +29,6 @@
margin-bottom: 7px;
}
.Select {
display: block;
}
:last-child {
margin-bottom: 0;
}

View File

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

View File

@@ -87,10 +87,6 @@
&:hover {
text-decoration: none;
background: @control-bg;
.Notification-action {
display: block;
}
}
.Avatar {
.Avatar--size(24px);
@@ -102,27 +98,6 @@
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

@@ -78,7 +78,6 @@
&, > li {
display: inline-block;
margin-right: 5px
}
}
.UserCard-info {

View File

@@ -1,5 +1,10 @@
.UserPage {
.UserHero .Dropdown-toggle .Button-icon {
display: none;
.UserCard-controls {
float: right;
.Dropdown-toggle .Button-icon {
display: none;
}
}
}

View File

@@ -12,17 +12,16 @@
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
// https://github.com/doctrine/dbal/issues/2566#issuecomment-480217999
return [
'up' => function (Builder $schema) {
$schema->table('posts', function (Blueprint $table) {
$table->mediumText('content')->comment(' ')->change();
$table->mediumText('content')->change();
});
},
'down' => function (Builder $schema) {
$schema->table('posts', function (Blueprint $table) {
$table->text('content')->comment('')->change();
$table->text('content')->change();
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -23,14 +24,18 @@ return [
})
->delete();
$schema->table('access_tokens', function (Blueprint $table) {
$schema->table('access_tokens', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('access_tokens', function (Blueprint $table) {
$schema->table('access_tokens', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,18 +9,21 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('api_keys', function (Blueprint $table) {
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
$table->dropPrimary(['id']);
$table->renameColumn('id', 'key');
$table->unique('key');
Migration::fixIndexNames($schema, $table);
});
$schema->table('api_keys', function (Blueprint $table) {
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
$table->increments('id');
$table->string('allowed_ips')->nullable();
$table->string('scopes')->nullable();
@@ -29,19 +32,25 @@ return [
$table->dateTime('last_activity_at')->nullable();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('api_keys', function (Blueprint $table) {
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
$table->dropColumn('id', 'allowed_ips', 'user_id', 'scopes', 'created_at');
Migration::fixIndexNames($schema, $table);
});
$schema->table('api_keys', function (Blueprint $table) {
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
$table->dropUnique(['key']);
$table->renameColumn('key', 'id');
$table->primary('id');
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -33,22 +34,26 @@ return [
'last_post_id' => $selectId('posts', 'last_post_id'),
]);
$schema->table('discussions', function (Blueprint $table) {
$schema->table('discussions', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
$table->foreign('last_posted_user_id')->references('id')->on('users')->onDelete('set null');
$table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
$table->foreign('first_post_id')->references('id')->on('posts')->onDelete('set null');
$table->foreign('last_post_id')->references('id')->on('posts')->onDelete('set null');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('discussions', function (Blueprint $table) {
$schema->table('discussions', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
$table->dropForeign(['last_posted_user_id']);
$table->dropForeign(['hidden_user_id']);
$table->dropForeign(['first_post_id']);
$table->dropForeign(['last_post_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -26,16 +27,20 @@ return [
})
->delete();
$schema->table('discussion_user', function (Blueprint $table) {
$schema->table('discussion_user', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('discussion_id')->references('id')->on('discussions')->onDelete('cascade');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('discussion_user', function (Blueprint $table) {
$schema->table('discussion_user', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
$table->dropForeign(['discussion_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -23,14 +24,18 @@ return [
})
->delete();
$schema->table('email_tokens', function (Blueprint $table) {
$schema->table('email_tokens', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('email_tokens', function (Blueprint $table) {
$schema->table('email_tokens', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -23,14 +24,18 @@ return [
})
->delete();
$schema->table('group_permission', function (Blueprint $table) {
$schema->table('group_permission', function (Blueprint $table) use ($schema) {
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('group_permission', function (Blueprint $table) {
$schema->table('group_permission', function (Blueprint $table) use ($schema) {
$table->dropForeign(['group_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -26,16 +27,20 @@ return [
})
->delete();
$schema->table('group_user', function (Blueprint $table) {
$schema->table('group_user', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('group_user', function (Blueprint $table) {
$schema->table('group_user', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
$table->dropForeign(['group_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -30,16 +31,20 @@ return [
})
->update(['from_user_id' => null]);
$schema->table('notifications', function (Blueprint $table) {
$schema->table('notifications', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('from_user_id')->references('id')->on('users')->onDelete('set null');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('notifications', function (Blueprint $table) {
$schema->table('notifications', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
$table->dropForeign(['from_user_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -23,14 +24,18 @@ return [
})
->delete();
$schema->table('password_tokens', function (Blueprint $table) {
$schema->table('password_tokens', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('password_tokens', function (Blueprint $table) {
$schema->table('password_tokens', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
@@ -31,18 +32,23 @@ return [
'hidden_user_id' => $selectId('users', 'hidden_user_id'),
]);
$schema->table('posts', function (Blueprint $table) {
$schema->table('posts', function (Blueprint $table) use ($schema) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
$table->foreign('edited_user_id')->references('id')->on('users')->onDelete('set null');
$table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('posts', function (Blueprint $table) {
$schema->table('posts', function (Blueprint $table) use ($schema) {
$table->dropForeign(['user_id']);
$table->dropForeign(['discussion_id']);
$table->dropForeign(['edited_user_id']);
$table->dropForeign(['hidden_user_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,25 +9,30 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$schema->table('users', function (Blueprint $table) use ($schema) {
$table->index('joined_at');
$table->index('last_seen_at');
$table->index('discussion_count');
$table->index('comment_count');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$schema->table('users', function (Blueprint $table) use ($schema) {
$table->dropIndex(['joined_at']);
$table->dropIndex(['last_seen_at']);
$table->dropIndex(['discussion_count']);
$table->dropIndex(['comment_count']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,12 +9,13 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('discussions', function (Blueprint $table) {
$schema->table('discussions', function (Blueprint $table) use ($schema) {
$table->index('last_posted_at');
$table->index('last_posted_user_id');
$table->index('created_at');
@@ -22,11 +23,13 @@ return [
$table->index('comment_count');
$table->index('participant_count');
$table->index('hidden_at');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('discussions', function (Blueprint $table) {
$schema->table('discussions', function (Blueprint $table) use ($schema) {
$table->dropIndex(['last_posted_at']);
$table->dropIndex(['last_posted_user_id']);
$table->dropIndex(['created_at']);
@@ -34,6 +37,8 @@ return [
$table->dropIndex(['comment_count']);
$table->dropIndex(['participant_count']);
$table->dropIndex(['hidden_at']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,19 +9,24 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('notifications', function (Blueprint $table) {
$schema->table('notifications', function (Blueprint $table) use ($schema) {
$table->index('user_id');
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('notifications', function (Blueprint $table) {
$schema->table('notifications', function (Blueprint $table) use ($schema) {
$table->dropIndex(['user_id']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -9,23 +9,28 @@
* file that was distributed with this source code.
*/
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('posts', function (Blueprint $table) {
$schema->table('posts', function (Blueprint $table) use ($schema) {
$table->index(['discussion_id', 'number']);
$table->index(['discussion_id', 'created_at']);
$table->index(['user_id', 'created_at']);
Migration::fixIndexNames($schema, $table);
});
},
'down' => function (Builder $schema) {
$schema->table('posts', function (Blueprint $table) {
$schema->table('posts', function (Blueprint $table) use ($schema) {
$table->dropIndex(['discussion_id', 'number']);
$table->dropIndex(['discussion_id', 'created_at']);
$table->dropIndex(['user_id', 'created_at']);
Migration::fixIndexNames($schema, $table);
});
}
];

View File

@@ -24,7 +24,7 @@ return [
},
'down' => function (Builder $schema) {
$schema->table('registration_tokens', function (Blueprint $table) {
$schema->table('auth_tokens', function (Blueprint $table) {
$table->dropColumn('provider', 'identifier', 'user_attributes');
$table->string('payload', 150)->change();

View File

@@ -7,11 +7,16 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
stopOnFailure="false"
syntaxCheck="false">
<testsuites>
<testsuite name="Flarum Integration Tests">
<directory suffix="Test.php">./integration</directory>
<testsuite name="installation">
<directory suffix="Test.php">./tests/Install</directory>
</testsuite>
<testsuite name="all">
<directory suffix="Test.php">./tests</directory>
<exclude>./tests/Install</exclude>
</testsuite>
</testsuites>
<filter>

View File

@@ -12,15 +12,8 @@
namespace Flarum\Admin;
use Flarum\Event\ConfigureMiddleware;
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;
use Flarum\Frontend\Compiler\Source\SourceCollector;
@@ -29,8 +22,6 @@ use Flarum\Http\Middleware as HttpMiddleware;
use Flarum\Http\RouteCollection;
use Flarum\Http\RouteHandlerFactory;
use Flarum\Http\UrlGenerator;
use Flarum\Locale\LocaleManager;
use Flarum\Settings\Event\Saved;
use Zend\Stratigility\MiddlewarePipe;
class AdminServiceProvider extends AbstractServiceProvider
@@ -45,27 +36,23 @@ class AdminServiceProvider extends AbstractServiceProvider
});
$this->app->singleton('flarum.admin.routes', function () {
$routes = new RouteCollection;
$this->populateRoutes($routes);
return $routes;
return new RouteCollection;
});
$this->app->singleton('flarum.admin.middleware', function (Application $app) {
$pipe = new MiddlewarePipe;
// All requests should first be piped through our global error handler
$pipe->pipe(new HttpMiddleware\HandleErrors(
$app->make(Registry::class),
$app->inDebugMode() ? $app->make(WhoopsFormatter::class) : $app->make(ViewFormatter::class),
$app->tagged(Reporter::class)
));
if ($app->inDebugMode()) {
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithWhoops::class));
} else {
$pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithView::class));
}
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
$pipe->pipe($app->make(Middleware\RequireAdministrateAbility::class));
@@ -111,30 +98,15 @@ class AdminServiceProvider extends AbstractServiceProvider
*/
public function boot()
{
$this->populateRoutes($this->app->make('flarum.admin.routes'));
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum.admin');
$events = $this->app->make('events');
$events->listen(
[Enabled::class, Disabled::class, ClearingCache::class],
function () {
$recompile = new RecompileFrontendAssets(
$this->app->make('flarum.assets.admin'),
$this->app->make(LocaleManager::class)
);
$recompile->flush();
}
);
$events->listen(
Saved::class,
function (Saved $event) {
$recompile = new RecompileFrontendAssets(
$this->app->make('flarum.assets.admin'),
$this->app->make(LocaleManager::class)
);
$recompile->whenSettingsSaved($event);
}
$this->app->make('events')->subscribe(
new RecompileFrontendAssets(
$this->app->make('flarum.assets.admin'),
$this->app->make('flarum.locales')
)
);
}

View File

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

View File

@@ -20,13 +20,12 @@ 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
@@ -41,20 +40,13 @@ class ApiServiceProvider extends AbstractServiceProvider
});
$this->app->singleton('flarum.api.routes', function () {
$routes = new RouteCollection;
$this->populateRoutes($routes);
return $routes;
return new RouteCollection;
});
$this->app->singleton('flarum.api.middleware', function (Application $app) {
$pipe = new MiddlewarePipe;
$pipe->pipe(new HttpMiddleware\HandleErrors(
$app->make(Registry::class),
new JsonApiFormatter($app->inDebugMode()),
$app->tagged(Reporter::class)
));
$pipe->pipe($app->make(Middleware\HandleErrors::class));
$pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class));
$pipe->pipe($app->make(Middleware\FakeHttpMethods::class));
@@ -62,7 +54,6 @@ class ApiServiceProvider extends AbstractServiceProvider
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithHeader::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
event(new ConfigureMiddleware($pipe, 'api'));
@@ -73,6 +64,25 @@ 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;
});
}
/**
@@ -80,6 +90,8 @@ class ApiServiceProvider extends AbstractServiceProvider
*/
public function boot()
{
$this->populateRoutes($this->app->make('flarum.api.routes'));
$this->registerNotificationSerializers();
AbstractSerializeController::setContainer($this->app);

View File

@@ -12,14 +12,11 @@
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
@@ -30,18 +27,18 @@ class Client
protected $container;
/**
* @var Registry
* @var ErrorHandler
*/
protected $registry;
protected $errorHandler;
/**
* @param Container $container
* @param Registry $registry
* @param ErrorHandler $errorHandler
*/
public function __construct(Container $container, Registry $registry)
public function __construct(Container $container, ErrorHandler $errorHandler = null)
{
$this->container = $container;
$this->registry = $registry;
$this->errorHandler = $errorHandler;
}
/**
@@ -72,14 +69,23 @@ class Client
try {
return $controller->handle($request);
} catch (Throwable $e) {
$error = $this->registry->handle($e);
if ($error->shouldBeReported()) {
} catch (Exception $e) {
if (! $this->errorHandler) {
throw $e;
}
return (new JsonApiFormatter)->format($error, $request);
return $this->errorHandler->handle($e);
}
}
/**
* @param ErrorHandler $errorHandler
* @return Client
*/
public function setErrorHandler(?ErrorHandler $errorHandler): self
{
$this->errorHandler = $errorHandler;
return $this;
}
}

View File

@@ -102,7 +102,7 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
);
$serializer = static::$container->make($this->serializer);
$serializer->setRequest($request);
$serializer->setActor($request->getAttribute('actor'));
$element = $this->createElement($data, $serializer)
->with($this->extractInclude($request))

View File

@@ -16,7 +16,6 @@ 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;
@@ -64,14 +63,14 @@ class CreateDiscussionController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$ipAddress = Arr::get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1');
$ipAddress = array_get($request->getServerParams(), 'REMOTE_ADDR', '127.0.0.1');
if (! $request->getAttribute('bypassFloodgate')) {
$this->floodgate->assertNotFlooding($actor);
}
$discussion = $this->bus->dispatch(
new StartDiscussion($actor, Arr::get($request->getParsedBody(), 'data', []), $ipAddress)
new StartDiscussion($actor, array_get($request->getParsedBody(), 'data', []), $ipAddress)
);
// After creating the discussion, we assume that the user has seen all

View File

@@ -14,7 +14,6 @@ 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;
@@ -44,7 +43,7 @@ class CreateGroupController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new CreateGroup($request->getAttribute('actor'), Arr::get($request->getParsedBody(), 'data', []))
new CreateGroup($request->getAttribute('actor'), array_get($request->getParsedBody(), 'data', []))
);
}
}

View File

@@ -16,7 +16,6 @@ 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;
@@ -63,9 +62,9 @@ class CreatePostController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$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');
$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');
if (! $request->getAttribute('bypassFloodgate')) {
$this->floodgate->assertNotFlooding($actor);

View File

@@ -12,11 +12,10 @@
namespace Flarum\Api\Controller;
use Flarum\Http\AccessToken;
use Flarum\User\Exception\NotAuthenticatedException;
use Flarum\User\Exception\PermissionDeniedException;
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;
@@ -58,14 +57,14 @@ class CreateTokenController implements RequestHandlerInterface
{
$body = $request->getParsedBody();
$identification = Arr::get($body, 'identification');
$password = Arr::get($body, 'password');
$lifetime = Arr::get($body, 'lifetime', 3600);
$identification = array_get($body, 'identification');
$password = array_get($body, 'password');
$lifetime = array_get($body, 'lifetime', 3600);
$user = $this->users->findByIdentification($identification);
if (! $user || ! $user->checkPassword($password)) {
throw new NotAuthenticatedException;
throw new PermissionDeniedException;
}
$token = AccessToken::generate($user->id, $lifetime);

View File

@@ -14,7 +14,6 @@ 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;
@@ -44,7 +43,7 @@ class CreateUserController extends AbstractCreateController
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new RegisterUser($request->getAttribute('actor'), Arr::get($request->getParsedBody(), 'data', []))
new RegisterUser($request->getAttribute('actor'), array_get($request->getParsedBody(), 'data', []))
);
}
}

View File

@@ -14,7 +14,6 @@ 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;
@@ -44,7 +43,7 @@ class DeleteAvatarController extends AbstractShowController
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new DeleteAvatar(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeleteAvatar(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -13,7 +13,6 @@ 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
@@ -36,7 +35,7 @@ class DeleteDiscussionController extends AbstractDeleteController
*/
protected function delete(ServerRequestInterface $request)
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$input = $request->getParsedBody();

View File

@@ -13,7 +13,6 @@ 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
@@ -37,7 +36,7 @@ class DeleteGroupController extends AbstractDeleteController
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeleteGroup(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeleteGroup(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -13,7 +13,6 @@ 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
@@ -37,7 +36,7 @@ class DeletePostController extends AbstractDeleteController
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeletePost(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeletePost(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -13,7 +13,6 @@ 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
@@ -37,7 +36,7 @@ class DeleteUserController extends AbstractDeleteController
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeleteUser(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
new DeleteUser(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -14,7 +14,6 @@ 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;
@@ -47,7 +46,7 @@ class ForgotPasswordController implements RequestHandlerInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$email = Arr::get($request->getParsedBody(), 'email');
$email = array_get($request->getParsedBody(), 'email');
$this->bus->dispatch(
new RequestPasswordReset($email)

View File

@@ -16,7 +16,6 @@ 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;
@@ -76,7 +75,7 @@ class ListDiscussionsController extends AbstractListController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$query = Arr::get($this->extractFilter($request), 'q');
$query = array_get($this->extractFilter($request), 'q');
$sort = $this->extractSort($request);
$criteria = new SearchCriteria($actor, $query, $sort);

View File

@@ -1,45 +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\Controller;
use Flarum\Api\Serializer\MailDriverSerializer;
use Flarum\User\AssertPermissionTrait;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListMailDriversController extends AbstractListController
{
use AssertPermissionTrait;
/**
* {@inheritdoc}
*/
public $serializer = MailDriverSerializer::class;
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$this->assertAdmin($request->getAttribute('actor'));
$drivers = self::$container->make('mail.supported_drivers');
array_walk($drivers, function (&$driver, $key) {
$driver = [
'id' => $key,
'driver' => self::$container->make($driver),
];
});
return $drivers;
}
}

View File

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

View File

@@ -15,8 +15,6 @@ 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;
@@ -66,7 +64,7 @@ class ListPostsController extends AbstractListController
$filter = $this->extractFilter($request);
$include = $this->extractInclude($request);
if ($postIds = Arr::get($filter, 'id')) {
if ($postIds = array_get($filter, 'id')) {
$postIds = explode(',', $postIds);
} else {
$postIds = $this->getPostIds($request);
@@ -88,7 +86,7 @@ class ListPostsController extends AbstractListController
$limit = $this->extractLimit($request);
$filter = $this->extractFilter($request);
if (($near = Arr::get($queryParams, 'page.near')) > 1) {
if (($near = array_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'
@@ -122,7 +120,7 @@ class ListPostsController extends AbstractListController
$query->skip($offset)->take($limit);
foreach ((array) $sort as $field => $order) {
$query->orderBy(Str::snake($field), $order);
$query->orderBy(snake_case($field), $order);
}
return $query->pluck('id')->all();
@@ -134,19 +132,19 @@ class ListPostsController extends AbstractListController
*/
private function applyFilters(Builder $query, array $filter)
{
if ($discussionId = Arr::get($filter, 'discussion')) {
if ($discussionId = array_get($filter, 'discussion')) {
$query->where('discussion_id', $discussionId);
}
if ($number = Arr::get($filter, 'number')) {
if ($number = array_get($filter, 'number')) {
$query->where('number', $number);
}
if ($userId = Arr::get($filter, 'user')) {
if ($userId = array_get($filter, 'user')) {
$query->where('user_id', $userId);
}
if ($type = Arr::get($filter, 'type')) {
if ($type = array_get($filter, 'type')) {
$query->where('type', $type);
}

View File

@@ -14,16 +14,13 @@ namespace Flarum\Api\Controller;
use Flarum\Api\Serializer\UserSerializer;
use Flarum\Http\UrlGenerator;
use Flarum\Search\SearchCriteria;
use Flarum\User\AssertPermissionTrait;
use Flarum\User\Exception\PermissionDeniedException;
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}
*/
@@ -72,9 +69,11 @@ class ListUsersController extends AbstractListController
{
$actor = $request->getAttribute('actor');
$this->assertCan($actor, 'viewUserList');
if ($actor->cannot('viewUserList')) {
throw new PermissionDeniedException;
}
$query = Arr::get($this->extractFilter($request), 'q');
$query = array_get($this->extractFilter($request), 'q');
$sort = $this->extractSort($request);
$criteria = new SearchCriteria($actor, $query, $sort);

View File

@@ -18,7 +18,6 @@ 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;
@@ -68,7 +67,7 @@ class SendConfirmationEmailController implements RequestHandlerInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$this->assertRegistered($actor);

View File

@@ -13,7 +13,6 @@ 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;
@@ -31,8 +30,8 @@ class SetPermissionController implements RequestHandlerInterface
$this->assertAdmin($request->getAttribute('actor'));
$body = $request->getParsedBody();
$permission = Arr::get($body, 'permission');
$groupIds = Arr::get($body, 'groupIds');
$permission = array_get($body, 'permission');
$groupIds = array_get($body, 'groupIds');
Permission::where('permission', $permission)->delete();

View File

@@ -16,8 +16,6 @@ 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;
@@ -75,7 +73,7 @@ class ShowDiscussionController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$discussionId = Arr::get($request->getQueryParams(), 'id');
$discussionId = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$include = $this->extractInclude($request);
@@ -88,7 +86,7 @@ class ShowDiscussionController extends AbstractShowController
}
$discussion->load(array_filter($include, function ($relationship) {
return ! Str::startsWith($relationship, 'posts');
return ! starts_with($relationship, 'posts');
}));
return $discussion;
@@ -152,7 +150,7 @@ class ShowDiscussionController extends AbstractShowController
$queryParams = $request->getQueryParams();
$actor = $request->getAttribute('actor');
if (($near = Arr::get($queryParams, 'page.near')) > 1) {
if (($near = array_get($queryParams, 'page.near')) > 1) {
$offset = $this->posts->getIndexForNumber($discussion->id, $near, $actor);
$offset = max(0, $offset - $limit / 2);
} else {

View File

@@ -13,7 +13,6 @@ 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;
@@ -53,6 +52,6 @@ class ShowPostController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->posts->findOrFail(Arr::get($request->getQueryParams(), 'id'), $request->getAttribute('actor'));
return $this->posts->findOrFail(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'));
}
}

View File

@@ -14,7 +14,6 @@ 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;
@@ -48,7 +47,7 @@ class ShowUserController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
if (! is_numeric($id)) {
$id = $this->users->getIdForUsername($id);

View File

@@ -13,7 +13,6 @@ 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
@@ -37,12 +36,11 @@ class UninstallExtensionController extends AbstractDeleteController
{
$this->assertAdmin($request->getAttribute('actor'));
$name = Arr::get($request->getQueryParams(), 'name');
$name = array_get($request->getQueryParams(), 'name');
if ($this->extensions->getExtension($name) == null) {
return;
}
$extension = $this->extensions->getExtension($name);
$this->extensions->uninstall($name);
$this->extensions->disable($extension);
$this->extensions->uninstall($extension);
}
}

View File

@@ -16,7 +16,6 @@ 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;
@@ -46,8 +45,8 @@ class UpdateDiscussionController extends AbstractShowController
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$discussionId = Arr::get($request->getQueryParams(), 'id');
$data = Arr::get($request->getParsedBody(), 'data', []);
$discussionId = array_get($request->getQueryParams(), 'id');
$data = array_get($request->getParsedBody(), 'data', []);
$discussion = $this->bus->dispatch(
new EditDiscussion($discussionId, $actor, $data)
@@ -55,7 +54,7 @@ class UpdateDiscussionController extends AbstractShowController
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
// That's what extensions will do anyway.
if ($readNumber = Arr::get($data, 'attributes.lastReadPostNumber')) {
if ($readNumber = array_get($data, 'attributes.lastReadPostNumber')) {
$state = $this->bus->dispatch(
new ReadDiscussion($discussionId, $actor, $readNumber)
);

View File

@@ -13,7 +13,6 @@ 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;
@@ -43,8 +42,8 @@ class UpdateExtensionController implements RequestHandlerInterface
{
$this->assertAdmin($request->getAttribute('actor'));
$enabled = Arr::get($request->getParsedBody(), 'enabled');
$name = Arr::get($request->getQueryParams(), 'name');
$enabled = array_get($request->getParsedBody(), 'enabled');
$name = array_get($request->getQueryParams(), 'name');
if ($enabled === true) {
$this->extensions->enable($name);

View File

@@ -14,7 +14,6 @@ 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;
@@ -43,9 +42,9 @@ class UpdateGroupController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = Arr::get($request->getParsedBody(), 'data', []);
$data = array_get($request->getParsedBody(), 'data', []);
return $this->bus->dispatch(
new EditGroup($id, $actor, $data)

View File

@@ -14,7 +14,6 @@ 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;
@@ -43,7 +42,7 @@ class UpdateNotificationController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
return $this->bus->dispatch(

View File

@@ -14,7 +14,6 @@ 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;
@@ -51,9 +50,9 @@ class UpdatePostController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = Arr::get($request->getParsedBody(), 'data', []);
$data = array_get($request->getParsedBody(), 'data', []);
return $this->bus->dispatch(
new EditPost($id, $actor, $data)

View File

@@ -16,7 +16,6 @@ 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;
@@ -50,9 +49,9 @@ class UpdateUserController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = Arr::get($request->getParsedBody(), 'data', []);
$data = array_get($request->getParsedBody(), 'data', []);
if ($actor->id == $id) {
$this->serializer = CurrentUserSerializer::class;
@@ -61,7 +60,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 = Arr::get($request->getParsedBody(), 'meta.password');
$password = array_get($request->getParsedBody(), 'meta.password');
if (! $actor->checkPassword($password)) {
throw new PermissionDeniedException;

View File

@@ -14,7 +14,6 @@ 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;
@@ -43,9 +42,9 @@ class UploadAvatarController extends AbstractShowController
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = Arr::get($request->getQueryParams(), 'id');
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$file = Arr::get($request->getUploadedFiles(), 'avatar');
$file = array_get($request->getUploadedFiles(), 'avatar');
return $this->bus->dispatch(
new UploadAvatar($id, $file, $actor)

View File

@@ -14,7 +14,6 @@ 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;
@@ -53,7 +52,7 @@ class UploadFaviconController extends ShowForumController
{
$this->assertAdmin($request->getAttribute('actor'));
$file = Arr::get($request->getUploadedFiles(), 'favicon');
$file = array_get($request->getUploadedFiles(), 'favicon');
$tmpFile = tempnam($this->app->storagePath().'/tmp', 'favicon');
$file->moveTo($tmpFile);

View File

@@ -14,7 +14,6 @@ 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;
@@ -53,7 +52,7 @@ class UploadLogoController extends ShowForumController
{
$this->assertAdmin($request->getAttribute('actor'));
$file = Arr::get($request->getUploadedFiles(), 'logo');
$file = array_get($request->getUploadedFiles(), 'logo');
$tmpFile = tempnam($this->app->storagePath().'/tmp', 'logo');
$file->moveTo($tmpFile);

51
src/Api/ErrorHandler.php Normal file
View File

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

View File

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

View File

@@ -0,0 +1,77 @@
<?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

@@ -0,0 +1,42 @@
<?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

@@ -0,0 +1,58 @@
<?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

@@ -0,0 +1,42 @@
<?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

@@ -0,0 +1,42 @@
<?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

@@ -0,0 +1,42 @@
<?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]);
}
}

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