mirror of
https://github.com/flarum/core.git
synced 2025-08-17 22:01:44 +02:00
Compare commits
2 Commits
as/monorep
...
v0.1.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
5bcf72dd49 | ||
|
0536b208e1 |
@@ -15,5 +15,5 @@ indent_size = 2
|
|||||||
[*.{diff,md}]
|
[*.{diff,md}]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
[*.{php,xml,json}]
|
[*.php]
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -1,8 +1,6 @@
|
|||||||
.gitattributes export-ignore
|
.gitattributes export-ignore
|
||||||
.gitignore export-ignore
|
.gitignore export-ignore
|
||||||
.gitmodules export-ignore
|
.gitmodules export-ignore
|
||||||
.github export-ignore
|
|
||||||
.travis export-ignore
|
|
||||||
.travis.yml export-ignore
|
.travis.yml export-ignore
|
||||||
.editorconfig export-ignore
|
.editorconfig export-ignore
|
||||||
.styleci.yml export-ignore
|
.styleci.yml export-ignore
|
||||||
@@ -10,10 +8,4 @@
|
|||||||
phpunit.xml export-ignore
|
phpunit.xml export-ignore
|
||||||
tests export-ignore
|
tests export-ignore
|
||||||
|
|
||||||
js/dist/* -diff
|
js/*/dist/*.js -diff
|
||||||
js/dist/* linguist-generated
|
|
||||||
js/dist-typings/* linguist-generated
|
|
||||||
js/yarn.lock -diff
|
|
||||||
js/package-lock.json -diff
|
|
||||||
|
|
||||||
* text=auto eol=lf
|
|
||||||
|
3
.github/CONTRIBUTING.md
vendored
Normal file
3
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Contributing to Flarum
|
||||||
|
|
||||||
|
Howdy! We're really excited that you are interested in contributing to Flarum. Before submitting your contribution, please take a moment and read through the [Contributing Guidelines](https://github.com/flarum/flarum/blob/master/CONTRIBUTING.md).
|
26
.github/ISSUE_TEMPLATE.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
> Issues on Github are meant for bug reporting. Please post feature requests on the [discussion forum](https://discuss.flarum.org/t/features).
|
||||||
|
---
|
||||||
|
> Try to complete the below form as far as you are able and are willing to share. Add a screenshot of the issue if you can.
|
||||||
|
|
||||||
|
## Bug report
|
||||||
|
- Version of Flarum: x.y.z
|
||||||
|
- Website URL where the bug is visible: http://example.com
|
||||||
|
- The webserver you are running: apache, nginx or something else
|
||||||
|
- PHP version: x.y.z
|
||||||
|
- Hosted environment: shared or vps
|
||||||
|
- Hosting provider: http://some-amazing-provider.com
|
||||||
|
|
||||||
|
## Flarum info
|
||||||
|
|
||||||
|
```
|
||||||
|
Output of "php flarum info", run this in terminal in your Flarum directory.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Additional comments
|
||||||
|
Some additional information you'd like to share, eg what have you tried so far.
|
||||||
|
|
||||||
|
## Log files
|
||||||
|
|
||||||
|
```
|
||||||
|
Put any relevant logs here.
|
||||||
|
```
|
39
.github/ISSUE_TEMPLATE/bug-report.md
vendored
39
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,39 +0,0 @@
|
|||||||
---
|
|
||||||
name: "🐛 Bug Report"
|
|
||||||
about: "If something isn't working as expected"
|
|
||||||
|
|
||||||
---
|
|
||||||
## Bug Report
|
|
||||||
|
|
||||||
**Current Behavior**
|
|
||||||
A clear and concise description of the behavior.
|
|
||||||
|
|
||||||
**Steps to Reproduce**
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected Behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Environment**
|
|
||||||
- Flarum version: x.y.z
|
|
||||||
- Website URL: http://example.com
|
|
||||||
- Webserver: [e.g. apache, nginx]
|
|
||||||
- Hosting environment: [e.g. shared, vps]
|
|
||||||
- PHP version: x.y.z
|
|
||||||
- Browser: [e.g. chrome 67, safari 11]
|
|
||||||
|
|
||||||
```
|
|
||||||
Output of "php flarum info", run this in terminal in your Flarum directory.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Possible Solution**
|
|
||||||
<!--- Only if you have suggestions or a fix for the bug -->
|
|
||||||
|
|
||||||
**Additional Context**
|
|
||||||
Add any other context about the problem here.
|
|
26
.github/ISSUE_TEMPLATE/feature-request.md
vendored
26
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
name: "🚀 Feature Request"
|
|
||||||
about: "I have a suggestion (and may want to implement it!)"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
IMPORTANT: Feature requests on this GitHub issue tracker are only accepted in case they have been approved by a core developer or contain extensive argumentation and directions for implementation. For all other feature requests, ideas and feedback please post in the Flarum Community: https://discuss.flarum.org/t/feedback.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Feature Request
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. eg. I have an issue when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A detailed description of your proposed solution. Include:
|
|
||||||
- How the feature would work/behave
|
|
||||||
- Any potential drawbacks
|
|
||||||
- Maybe a screenshot, design, or example code
|
|
||||||
|
|
||||||
**Justify why this feature belongs in Flarum's core, rather than in a third-party extension**
|
|
||||||
Consider who this change will be useful to – most Flarum forums, or just a few?
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
11
.github/ISSUE_TEMPLATE/support-question.md
vendored
11
.github/ISSUE_TEMPLATE/support-question.md
vendored
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
name: "🙋 Support Question"
|
|
||||||
about: "If you have a question, please check out our forum or Discord!"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
We primarily use GitHub as an issue tracker; for usage and support questions, please check out these resources below. Thanks!
|
|
||||||
|
|
||||||
* Flarum Community: https://discuss.flarum.org/
|
|
||||||
* Discord Chat: https://flarum.org/discord/
|
|
||||||
* Twitter: https://twitter.com/Flarum
|
|
26
.github/stale.yml
vendored
26
.github/stale.yml
vendored
@@ -1,26 +0,0 @@
|
|||||||
daysUntilStale: 90
|
|
||||||
daysUntilClose: 30
|
|
||||||
|
|
||||||
staleLabel: stale
|
|
||||||
|
|
||||||
exemptLabels:
|
|
||||||
- org/keep
|
|
||||||
- type/bug
|
|
||||||
- type/regression
|
|
||||||
- critical
|
|
||||||
- security
|
|
||||||
exemptAssignees: true
|
|
||||||
exemptMilestones: true
|
|
||||||
exemptProjects: true
|
|
||||||
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs. We do this
|
|
||||||
to keep the amount of open issues to a manageable minimum.
|
|
||||||
|
|
||||||
In any case, thanks for taking an interest in this software and contributing
|
|
||||||
by opening the issue in the first place!
|
|
||||||
|
|
||||||
closeComment: >
|
|
||||||
We are closing this issue as it seems to have grown stale. If you still
|
|
||||||
encounter this problem with the latest version, feel free to re-open it.
|
|
15
.github/workflows/backend.yml
vendored
15
.github/workflows/backend.yml
vendored
@@ -1,15 +0,0 @@
|
|||||||
name: Core PHP
|
|
||||||
|
|
||||||
on: [workflow_dispatch, push, pull_request]
|
|
||||||
|
|
||||||
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
|
|
||||||
# This will break your current script.
|
|
||||||
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
uses: flarum/.github/.github/workflows/REUSABLE_backend.yml@main
|
|
||||||
with:
|
|
||||||
enable_backend_testing: true
|
|
||||||
|
|
||||||
backend_directory: .
|
|
76
.github/workflows/codeql-analysis.yml
vendored
76
.github/workflows/codeql-analysis.yml
vendored
@@ -1,76 +0,0 @@
|
|||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
# Run on:
|
|
||||||
# - pushes to master, or
|
|
||||||
# - PRs with a base of `master`
|
|
||||||
# - which do not **only** consist of changes to .md or .less files
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
paths-ignore:
|
|
||||||
- '**/*.md'
|
|
||||||
- '**/*.less'
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
paths-ignore:
|
|
||||||
- '**/*.md'
|
|
||||||
- '**/*.less'
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * 1,3,5'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze / ${{ matrix.language }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'javascript' ]
|
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
|
||||||
# Learn more:
|
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v1
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v1
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 https://git.io/JvXDl
|
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
||||||
# and modify them (or add more) to build your code if your project
|
|
||||||
# uses a compiled language
|
|
||||||
|
|
||||||
#- run: |
|
|
||||||
# make bootstrap
|
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
23
.github/workflows/frontend.yml
vendored
23
.github/workflows/frontend.yml
vendored
@@ -1,23 +0,0 @@
|
|||||||
name: Core JS
|
|
||||||
|
|
||||||
on: [workflow_dispatch, push, pull_request]
|
|
||||||
|
|
||||||
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
|
|
||||||
# This will break your current script.
|
|
||||||
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
uses: flarum/.github/.github/workflows/REUSABLE_frontend.yml@main
|
|
||||||
with:
|
|
||||||
enable_bundlewatch: true
|
|
||||||
enable_prettier: true
|
|
||||||
enable_typescript: true
|
|
||||||
|
|
||||||
frontend_directory: ./js
|
|
||||||
backend_directory: .
|
|
||||||
js_package_manager: yarn
|
|
||||||
main_git_branch: master
|
|
||||||
|
|
||||||
secrets:
|
|
||||||
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
|
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,12 +1,7 @@
|
|||||||
/vendor
|
/vendor
|
||||||
composer.lock
|
|
||||||
composer.phar
|
composer.phar
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
tests/.phpunit.result.cache
|
tests/_output/*
|
||||||
/tests/integration/tmp
|
|
||||||
.vagrant
|
.vagrant
|
||||||
.idea/*
|
.idea/*
|
||||||
.vscode
|
|
||||||
js/coverage-ts
|
|
||||||
|
@@ -12,3 +12,7 @@ disabled:
|
|||||||
- phpdoc_order
|
- phpdoc_order
|
||||||
- phpdoc_separation
|
- phpdoc_separation
|
||||||
- phpdoc_types
|
- phpdoc_types
|
||||||
|
|
||||||
|
finder:
|
||||||
|
exclude:
|
||||||
|
- "stubs"
|
||||||
|
35
.travis.yml
Normal file
35
.travis.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
language: php
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.6
|
||||||
|
- 7.0
|
||||||
|
- 7.1
|
||||||
|
- hhvm
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- php: hhvm
|
||||||
|
fast_finish: true
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi;
|
||||||
|
- composer self-update
|
||||||
|
- composer install
|
||||||
|
|
||||||
|
script:
|
||||||
|
- vendor/bin/phpunit --coverage-clover=coverage.xml
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
on_failure: change
|
||||||
|
webhooks:
|
||||||
|
urls:
|
||||||
|
- https://webhooks.gitter.im/e/7b9e9827a03b44a16588
|
||||||
|
on_success: always
|
||||||
|
on_failure: always
|
||||||
|
on_start: false
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
|
sudo: false
|
713
CHANGELOG.md
713
CHANGELOG.md
@@ -1,713 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## [1.2.0](https://github.com/flarum/core/compare/v1.1.1...v1.2.0)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- View `README` documentation in extension pages (https://github.com/flarum/core/pull/3094).
|
|
||||||
- Declare & Use CSS Custom Properties (https://github.com/flarum/core/pull/3146).
|
|
||||||
- Lazy draw dropdowns to improve performance (https://github.com/flarum/core/pull/2925).
|
|
||||||
- Default Settings Extender (https://github.com/flarum/core/pull/3127).
|
|
||||||
- Add `textarea` setting type to admin pages (https://github.com/flarum/core/pull/3141).
|
|
||||||
- Allow registering settings as `Less` config vars through Settings Extender (https://github.com/flarum/core/pull/3011).
|
|
||||||
- Allow replacing of blade template namespaces via extender (https://github.com/flarum/core/pull/3167).
|
|
||||||
- Update to Webpack 5 (https://github.com/flarum/core/pull/3135).
|
|
||||||
- Introduce `Less` custom function extender with a `is-extension-enabled` function (https://github.com/flarum/core/pull/3190).
|
|
||||||
- Support for `few` in ICU Message syntax (https://github.com/flarum/core/pull/3122).
|
|
||||||
- ES6 local support for number formatting (https://github.com/flarum/core/pull/3099).
|
|
||||||
- Added dedicated endpoint for retrieving single groups (https://github.com/flarum/core/pull/3084).
|
|
||||||
- Callback `loadWhere` relation eager loading extender (https://github.com/flarum/core/pull/3116).
|
|
||||||
- Extensible document title driver implementation (https://github.com/flarum/core/pull/3109).
|
|
||||||
- Type checks, typescript coverage GH action (https://github.com/flarum/core/pull/3136).
|
|
||||||
- Add color indicator in appearance admin page instead of validating colors (https://github.com/flarum/core/pull/3140).
|
|
||||||
- Add typing files for our translator libraries (https://github.com/flarum/core/pull/3175).
|
|
||||||
- `StatusWidget` tools extensibility (https://github.com/flarum/core/pull/3189).
|
|
||||||
- Allow switching the `ImageManager` driver (https://github.com/flarum/core/pull/3195).
|
|
||||||
- Events for notification read/all read actions (https://github.com/flarum/core/pull/3203).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Testing with php8.1 (https://github.com/flarum/core/pull/3102).
|
|
||||||
- Migrate fully to Yarn (https://github.com/flarum/core/pull/3155).
|
|
||||||
- Handle post rendering errors to avoid crashes (https://github.com/flarum/core/pull/3061).
|
|
||||||
- Added basic filtering, sorting, and pagination to groups endpoint (https://github.com/flarum/core/pull/3084).
|
|
||||||
- Pass IP address to API Client pipeline (https://github.com/flarum/core/pull/3124).
|
|
||||||
- Rename Extension Page "Uninstall" to "Purge" (https://github.com/flarum/core/pull/3123).
|
|
||||||
- [A11Y] Improve accessibility for discussion reply count on post stream (https://github.com/flarum/core/pull/3090).
|
|
||||||
- Improved post loading support (https://github.com/flarum/core/pull/3100).
|
|
||||||
- Rewrite SubtreeRetainer into Typescript (https://github.com/flarum/core/pull/3137).
|
|
||||||
- Rewrite ModalManager and state to Typescript (https://github.com/flarum/core/pull/3007).
|
|
||||||
- Rewrite frontend application files to Typescript (https://github.com/flarum/core/pull/3006).
|
|
||||||
- Allow extensions to modify the minimum search length in the Search component (https://github.com/flarum/core/pull/3130).
|
|
||||||
- Allow use of any tag in `listItems` helper (https://github.com/flarum/core/pull/3147).
|
|
||||||
- Replace `for ... in` with `Array.reduce` (https://github.com/flarum/core/pull/3149).
|
|
||||||
- Page title format is now implemented through translations (https://github.com/flarum/core/pull/3077, https://github.com/flarum/core/pull/3228)
|
|
||||||
- Add `aria-label` attribute to the navigation drawer button (https://github.com/flarum/core/pull/3157).
|
|
||||||
- Convert extend util to TypeScript (https://github.com/flarum/core/pull/2928).
|
|
||||||
- Better typings for DiscussionListState (https://github.com/flarum/core/pull/3132).
|
|
||||||
- Rewrite ItemList, update `ItemList` typings (https://github.com/flarum/core/pull/3005).
|
|
||||||
- Add priority order to discussion page controls (https://github.com/flarum/core/pull/3165).
|
|
||||||
- Use `@php` in Blade templates (https://github.com/flarum/core/pull/3172).
|
|
||||||
- Convert some common classes/utils to TS (https://github.com/flarum/core/pull/2929).
|
|
||||||
- Convert routes to Typescript (https://github.com/flarum/core/pull/3177).
|
|
||||||
- Move admin `colorItems` to an `ItemList` (https://github.com/flarum/core/pull/3186).
|
|
||||||
- Centralize pagination/canonical meta URL generation in Document (https://github.com/flarum/core/pull/3077).
|
|
||||||
- Use revision versioner to allow custom asset versioning (https://github.com/flarum/core/pull/3183).
|
|
||||||
- Split up application error handling (https://github.com/flarum/core/pull/3184).
|
|
||||||
- Make SlugManager available to blade template (https://github.com/flarum/core/pull/3194).
|
|
||||||
- Convert models to TS (https://github.com/flarum/core/pull/3174).
|
|
||||||
- Allow loading relations in other discussion endpoints (https://github.com/flarum/core/pull/3191).
|
|
||||||
- Improve selected text stylization (https://github.com/flarum/core/pull/2961).
|
|
||||||
- Extract notification `primaryControl` items to an ItemList (https://github.com/flarum/core/pull/3204).
|
|
||||||
- Frontend code housekeeping (#3214, #3213).
|
|
||||||
- Only retain scroll position if coming from discussion (https://github.com/flarum/core/pull/3229).
|
|
||||||
- Use `aria-live` regions to focus screenreader attention on alerts as they appear (https://github.com/flarum/core/pull/3237).
|
|
||||||
- Prevent unwarranted `a11y` warnings on custom Button subclasses (https://github.com/flarum/core/pull/3238).
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Missing locale text in the user editing modal (https://github.com/flarum/core/pull/3093).
|
|
||||||
- Dashes in table prefix prevent installation (https://github.com/flarum/core/pull/3089).
|
|
||||||
- Missing autocomplete attributes to input fields (https://github.com/flarum/core/pull/3088).
|
|
||||||
- Missing route parameters throwing an error (https://github.com/flarum/core/pull/3118).
|
|
||||||
- Mail settings select component never used (https://github.com/flarum/core/pull/3120).
|
|
||||||
- White avatar image throws javascript errors on the profile page (https://github.com/flarum/core/pull/3119).
|
|
||||||
- Unformatted avatar upload validation errors (https://github.com/flarum/core/pull/2946).
|
|
||||||
- Webkit input clear button shows up with the custom one (https://github.com/flarum/core/pull/3128).
|
|
||||||
- Media query breakpoints conflict with Windows display scaling (https://github.com/flarum/core/pull/3139).
|
|
||||||
- `typeof this` not recognized by some IDEs (https://github.com/flarum/core/pull/3142).
|
|
||||||
- `Model.save()` cannot save `null` `hasOne` relationship (https://github.com/flarum/core/pull/3131).
|
|
||||||
- Edit post `until reply` policy broken on PHP 8 (https://github.com/flarum/core/pull/3145).
|
|
||||||
- Inaccurate `Component.component` argument typings (https://github.com/flarum/core/pull/3148).
|
|
||||||
- Scrolling notification list infinitely repeats (https://github.com/flarum/core/pull/3159).
|
|
||||||
- Argument for INFO constant was assigned to `maxfiles` argument incorrectly (bfd81a83cfd0fa8125395a147ff0c9ce622f38e3).
|
|
||||||
- `Activated` event is sent every time an email is confirmed instead of just once (https://github.com/flarum/core/pull/3163).
|
|
||||||
- [A11Y] Modal close button missing accessible label (https://github.com/flarum/core/pull/3161).
|
|
||||||
- [A11Y] Auth modal inputs missing accessible labels (https://github.com/flarum/core/pull/3207).
|
|
||||||
- [A11Y] Triggering click on drawer button can cause layered backdrops (https://github.com/flarum/core/pull/3018).
|
|
||||||
- [A11Y] Focus can leave open nav drawer on mobile (https://github.com/flarum/core/pull/3018).
|
|
||||||
- [A11Y] Post action items not showing when focus is within the post (https://github.com/flarum/core/pull/3173).
|
|
||||||
- [A11Y] Missing accessible label for alert dismiss button (https://github.com/flarum/core/pull/3237).
|
|
||||||
- Error accessing the forum after saving a setting with more than 65k characters (https://github.com/flarum/core/pull/3162).
|
|
||||||
- Cannot restart queue from within (https://github.com/flarum/core/pull/3166).
|
|
||||||
- `Post--by-actor` not showing when comparing user instances (https://github.com/flarum/core/pull/3170).
|
|
||||||
- Incorrect typings for Modal `hide()` method (https://github.com/flarum/core/pull/3180).
|
|
||||||
- Avatar Upload throws errors with correct mimetype and incorrect extension (https://github.com/flarum/core/pull/3181).
|
|
||||||
- Clicking the dropdown button on a post opens all dropdowns in `Post-actions` (https://github.com/flarum/core/pull/3185).
|
|
||||||
- `getPlainContent()` causes external content to be fetched (https://github.com/flarum/core/pull/3193).
|
|
||||||
- `listItems` not accepting all `Mithril.Children` (https://github.com/flarum/core/pull/3176).
|
|
||||||
- Notifications mark as read option updates all notifications including the read ones (https://github.com/flarum/core/pull/3202).
|
|
||||||
- Post meta permalink not properly generated (https://github.com/flarum/core/pull/3216).
|
|
||||||
- Broken contribution link in README (https://github.com/flarum/core/pull/3211).
|
|
||||||
- `WelcomeHero` is displayed when content is empty (https://github.com/flarum/core/pull/3219).
|
|
||||||
- `last_activity_at, last_seen_at` updated on all API requests (https://github.com/flarum/core/pull/3231).
|
|
||||||
- `RememberMe` access token updated twice in API requests (https://github.com/flarum/core/pull/3233).
|
|
||||||
- Error in `funding` item in `composer.json` bricks the frontend (https://github.com/flarum/core/pull/3239).
|
|
||||||
- Escaped quotes in window title (https://github.com/flarum/core/pull/3264)
|
|
||||||
- `schedule:list` command fails due to missing timezone configuration.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- Unused `evented` utility (https://github.com/flarum/core/pull/3125).
|
|
||||||
|
|
||||||
## [1.1.1](https://github.com/flarum/core/compare/v1.1.0...v1.1.1)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Performance issue with very large communities.
|
|
||||||
|
|
||||||
## [1.1.0](https://github.com/flarum/core/compare/v1.0.4...v1.1.0)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Info command now displays MySQL version, queue driver, mail driver (https://github.com/flarum/core/pull/2991)
|
|
||||||
- Use organization Prettier config (https://github.com/flarum/core/pull/2967)
|
|
||||||
- Support for global typings in extensions (https://github.com/flarum/core/pull/2992)
|
|
||||||
- Typings for class component state attribute (https://github.com/flarum/core/pull/2995)
|
|
||||||
- Custom colorising with CSS custom properties (https://github.com/flarum/core/pull/3001)
|
|
||||||
- Theme Extender to allow overriding LESS files (https://github.com/flarum/core/pull/3008)
|
|
||||||
- Update lastSeenAt when authenticating via API (https://github.com/flarum/core/pull/3058)
|
|
||||||
- NoJs Admin View (https://github.com/flarum/core/pull/3059)
|
|
||||||
- Preload FontAwesome, JS and CSS, and add `preload` extender (https://github.com/flarum/core/pull/3057)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Move Day.js plugin types import to global typings (https://github.com/flarum/core/pull/2954)
|
|
||||||
- Avoid resolving excluded middleware on each middleware items
|
|
||||||
- Allow extra attrs provided to `<Select>` to be passed through to the DOM element (https://github.com/flarum/core/pull/2959)
|
|
||||||
- Limit height of code blocks (https://github.com/flarum/core/pull/3012)
|
|
||||||
- Update normalize.css from v3.0.2 to v8.0.1 (https://github.com/flarum/core/pull/3015)
|
|
||||||
- Permission Grid: stick the headers to handle a lot of tags (https://github.com/flarum/core/pull/2887)
|
|
||||||
- Use `ItemList` for `DiscussionPage` content (https://github.com/flarum/core/pull/3004)
|
|
||||||
- Move email confirmation to POST request (https://github.com/flarum/core/pull/3038)
|
|
||||||
- Minor CSS code cleanup (https://github.com/flarum/core/pull/3026)
|
|
||||||
- Replace username with display name in more places (https://github.com/flarum/core/pull/3040)
|
|
||||||
- Rewrite Button to Typescript (https://github.com/flarum/core/pull/2984)
|
|
||||||
- Rewrite AdminPage abstract component into Typescript (https://github.com/flarum/core/pull/2996)
|
|
||||||
- Allow adding page parameters to PaginatedListState (https://github.com/flarum/core/pull/2935)
|
|
||||||
- Pass filter params to getApiDocument (https://github.com/flarum/core/pull/3037)
|
|
||||||
- Use author filter instead of gambit to get a user's discussions (https://github.com/flarum/core/pull/3068)
|
|
||||||
- [A11Y] Accessibility improvements for the Search component (https://github.com/flarum/core/pull/3017)
|
|
||||||
- Add determinsm to extension order resolution (https://github.com/flarum/core/pull/3076)
|
|
||||||
- Add cache control headers to the admin area (https://github.com/flarum/core/pull/3097)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- HLJS 11 new styles resulting in double padding (https://github.com/flarum/core/pull/2909)
|
|
||||||
- Internal API client attempting to load an uninstantiated session
|
|
||||||
- Empty post footer taking visual space (https://github.com/flarum/core/pull/2926)
|
|
||||||
- Unrecognized component class custom attribute typings (https://github.com/flarum/core/pull/2962)
|
|
||||||
- User edit groups permission not visually depending on view hidden groups permission (https://github.com/flarum/core/pull/2880)
|
|
||||||
- Event post excerpt preview triggers error (https://github.com/flarum/core/pull/2964)
|
|
||||||
- Missing settings defaults for display name driver and User slug driver (https://github.com/flarum/core/pull/2971)
|
|
||||||
- [A11Y] Icons not hidden from screenreaders (https://github.com/flarum/core/pull/3027)
|
|
||||||
- [A11Y] Checkboxes not focusable (https://github.com/flarum/core/pull/3014)
|
|
||||||
- Uploading ICO favicons resulting in server errors (https://github.com/flarum/core/pull/2949)
|
|
||||||
- Missing proper validation for large avatar upload payload (https://github.com/flarum/core/pull/3042)
|
|
||||||
- [A11Y] Missing focus rings in control elements (https://github.com/flarum/core/pull/3016)
|
|
||||||
- Unsanitised integer query parameters (https://github.com/flarum/core/pull/3064)
|
|
||||||
|
|
||||||
###### Code Contributors
|
|
||||||
@lhsazevedo, @Ornanovitch, @pierres, @the-turk, @iPurpl3x
|
|
||||||
|
|
||||||
###### Issue Reporters
|
|
||||||
@uamv, @dannyuk1982, @BurnNoticeSpy, @haarp, @peopleinside, @matteocontrini
|
|
||||||
|
|
||||||
## [1.0.4](https://github.com/flarum/core/compare/v1.0.3...v1.0.4)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Upgrade to v1.0 resets the "view" permission on all tags (https://github.com/flarum/core/pull/2941)
|
|
||||||
|
|
||||||
## [1.0.3](https://github.com/flarum/core/compare/v1.0.2...v1.0.3)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Removed [forum] prefix from Request Password and Email Confirmation emails ([a4a81c0](https://github.com/flarum/core/commit/a4a81c0ec237476cd6e7ca00c1ed9465493af476))
|
|
||||||
- Adopt huntr.dev for handling our security vulnerability reports (https://github.com/flarum/core/pull/2918)
|
|
||||||
- Maintenance handler can now be replaced through the service container (ioc) ([4acff91](https://github.com/flarum/core/commit/4acff91f8063fcced9bf8c9a76fbb510d06823c0))
|
|
||||||
- The colors on the auto generated avatars are now based on the Display Name of the user (https://github.com/flarum/core/pull/2873)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Avatar in notifications list are incorrectly aligned (https://github.com/flarum/core/pull/2906)
|
|
||||||
- FilesystemManager is not compatible with upstream Laravel implementation (https://github.com/flarum/core/pull/2936)
|
|
||||||
|
|
||||||
## [1.0.2](https://github.com/flarum/core/compare/v1.0.1...v1.0.2)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Critical XSS vulnerability
|
|
||||||
|
|
||||||
## [1.0.1](https://github.com/flarum/core/compare/v1.0.0...v1.0.1)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Installation fails on environments without proc_* functions enabled or mysql client binary (https://github.com/flarum/core/issues/2890)
|
|
||||||
|
|
||||||
## [1.0.0](https://github.com/flarum/core/compare/v0.1.0-beta.16...v1.0.0)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Task scheduling
|
|
||||||
- `load()` method on `ApiController` extender to allow eager loading of relations (https://github.com/flarum/core/pull/2724)
|
|
||||||
- Installation supports enabling a set of extensions (https://github.com/flarum/core/pull/2757)
|
|
||||||
- RequestUtil helper class added to abstract the logic of the actor, session, locale and route name from the request (https://github.com/flarum/core/pull/2449)
|
|
||||||
- Code scanning action with GitHub CodeQL (https://github.com/flarum/core/pull/2744)
|
|
||||||
- The Formatter extender now has an `unparse` method to allow extensions to hook into the unparsing of content (https://github.com/flarum/core/pull/2780)
|
|
||||||
- A Filesystem extender allows direct modification and addition of filesystem disks (https://github.com/flarum/core/pull/2732)
|
|
||||||
- A slug driver based on the User ID was introduced (https://github.com/flarum/core/pull/2787)
|
|
||||||
- An extensible users list was added to the admin area (https://github.com/flarum/core/pull/2626)
|
|
||||||
- Headers hardened by adding Referer Policy, Xss Protection and Content type (https://github.com/flarum/core/pull/2721)
|
|
||||||
- Tooltip component (https://github.com/flarum/core/pull/2843)
|
|
||||||
- Moved `insertText` and `styleSelectedText` from markdown to core (https://github.com/flarum/core/pull/2826)
|
|
||||||
- A squashed database schema install dump to speed up new installs (https://github.com/flarum/core/pull/2842)
|
|
||||||
- Pagination in the canonical URL for discussion pages (https://github.com/flarum/core/pull/2853)
|
|
||||||
- PaginatedListState for the DiscussionList and to support paginated lists in the frontend (https://github.com/flarum/core/pull/2781)
|
|
||||||
- Introduce the new webpack config and flarum-tsconfig for typehinting (https://github.com/flarum/core/pull/2856)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Now tracking bundle sizes to keep an eye on web performance (https://github.com/flarum/core/pull/2695)
|
|
||||||
- Eager load relations on ListPostsController to improve performance (https://github.com/flarum/core/pull/2717)
|
|
||||||
- Replace classList with clsx library (https://github.com/flarum/core/pull/2760)
|
|
||||||
- Replaced the javascript based loading spinner with a pure CSS version (https://github.com/flarum/core/pull/2764)
|
|
||||||
- Route names now have to be unique (https://github.com/flarum/core/pull/2771)
|
|
||||||
- ActorReference is now available from the error handler middleware (https://github.com/flarum/core/pull/2410)
|
|
||||||
- The `migrations` table now has an Auto Increment ID (https://github.com/flarum/core/pull/2794)
|
|
||||||
- Assets and avatars are now managed using Laravel filesystem disks (https://github.com/flarum/core/pull/2729)
|
|
||||||
- Extracted asset publishing (`php flarum assets:publish`) from migrating (https://github.com/flarum/core/pull/2731)
|
|
||||||
- Assets were compiled in the format `<asset>-<revision>.<js|css>`, this is now `<asset>.<js|css>?v=<revision>` (https://github.com/flarum/core/pull/2805)
|
|
||||||
- The powered by header can now be configured in the config under `headers` (https://github.com/flarum/core/pull/2777)
|
|
||||||
- Switched to the ICU format for translation files (https://github.com/flarum/core/pull/2759)
|
|
||||||
- Allow extend and override to apply to multiple methods in one call
|
|
||||||
- Notifications dropdown and list refactored (https://github.com/flarum/core/pull/2822)
|
|
||||||
- Updated validation locale strings based on Laravel 8 changes (https://github.com/flarum/core/pull/2829)
|
|
||||||
- Caching of permissions is now taken care of centrally, reducing code duplication (https://github.com/flarum/core/pull/2832)
|
|
||||||
- Replaced lodash-es by throttle-debounce to reduce bundle size (https://github.com/flarum/core/pull/2827)
|
|
||||||
- Internal API requests are now executed through middleware (https://github.com/flarum/core/pull/2783)
|
|
||||||
- Permission changes: `viewDiscussions` to `viewForum` and `viewUserList` to `searchUsers` (https://github.com/flarum/core/pull/2854)
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
- Javascript is shown when editing the title of a discussion (https://github.com/flarum/core/pull/2693)
|
|
||||||
- Canonical url logic uses request object which causes wrong URL's when a different page is default (https://github.com/flarum/core/pull/2674)
|
|
||||||
- Dropdown toggle has no aria label (https://github.com/flarum/core/pull/2668)
|
|
||||||
- Nav drawer is focusable when off-screen on small viewports (https://github.com/flarum/core/pull/2666)
|
|
||||||
- Search input has no aria-label and no role (https://github.com/flarum/core/pull/2669)
|
|
||||||
- Code duplication exists between SendConfirmationEmailController and AccountActivationMailer (https://github.com/flarum/core/pull/2493)
|
|
||||||
- When setting tags as homepage default, visiting a tag will show all posts (https://github.com/flarum/core/pull/2754)
|
|
||||||
- Locale cache is cleared twice when cache clearing (https://github.com/flarum/core/pull/2738)
|
|
||||||
- When cache clearing fails an exception can be thrown due to a partial flush (https://github.com/flarum/core/pull/2756)
|
|
||||||
- Database migrations rely on MyISAM even though the eventual migrated database does not use it (https://github.com/flarum/core/pull/2442)
|
|
||||||
- Discussion search result is not sorted by relevance by default (https://github.com/flarum/core/pull/2773)
|
|
||||||
- Extensions cannot register custom searcher classes (https://github.com/flarum/core/pull/2755)
|
|
||||||
- Searching discussion titles is not possible (https://github.com/flarum/core/pull/2698)
|
|
||||||
- Boot errors due to failing extenders throw a generic error (https://github.com/flarum/core/pull/2740)
|
|
||||||
- Required argument to `Component.$()` isn't really required (https://github.com/flarum/core/pull/2844)
|
|
||||||
- Component does not allows use of all mithril lifecycle functionality (https://github.com/flarum/core/pull/2847)
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- The `make:migration` command has been removed (https://github.com/flarum/core/pull/2686)
|
|
||||||
- Background fade on the header has been removed (https://github.com/flarum/core/pull/2685)
|
|
||||||
- Remove vendor prefixes in less (https://github.com/flarum/core/pull/2766)
|
|
||||||
- The session is no longer available from the User class (https://github.com/flarum/core/pull/2790)
|
|
||||||
- The `mail` key is removed from the laravel related config (https://github.com/flarum/core/pull/2796)
|
|
||||||
|
|
||||||
## [0.1.0-beta.16](https://github.com/flarum/core/compare/v0.1.0-beta.15...v0.1.0-beta.16)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Allow event subscribers (https://github.com/flarum/core/pull/2535)
|
|
||||||
- Allow Settings extender to have a default value (https://github.com/flarum/core/pull/2495)
|
|
||||||
- Allow hooking into the sending of notifications before being send (https://github.com/flarum/core/pull/2533)
|
|
||||||
- PHP 8 support (https://github.com/flarum/core/pull/2507)
|
|
||||||
- Search extender (https://github.com/flarum/core/pull/2483)
|
|
||||||
- User badges to post preview (https://github.com/flarum/core/pull/2555)
|
|
||||||
- Optional extension dependencies allow a booting order (https://github.com/flarum/core/pull/2579)
|
|
||||||
- Auth extender (https://github.com/flarum/core/pull/2176)
|
|
||||||
- `X-Powered-By` header added to allow indexers easier data aggregation of Flarum adoption (https://github.com/flarum/core/pull/2618)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Run integration tests in transaction (https://github.com/flarum/core/pull/2304)
|
|
||||||
- Allow policies to return a boolean for simplified allow/deny (https://github.com/flarum/core/pull/2534)
|
|
||||||
- Converted highlight helper to typescript (https://github.com/flarum/core/pull/2532)
|
|
||||||
- Add accessibility attributes to Mark as Read button (https://github.com/flarum/core/pull/2564)
|
|
||||||
- Dismiss errors on change email modal upon a new request ([00913d5](https://github.com/flarum/core/commit/00913d5b0be2172cfce1f16aaf64a24f3d2e6d4b))
|
|
||||||
- Disabled extensions now are marked with a red circle instead of a red dot (https://github.com/flarum/core/pull/2562)
|
|
||||||
- Extension dependency errors now show the extension title instead of the ID (https://github.com/flarum/core/pull/2563)
|
|
||||||
- Change `mutate` method on ApiSerializer extender to `attributes` (https://github.com/flarum/core/pull/2578)
|
|
||||||
- Moved locale files to the core from the language pack (https://github.com/flarum/core/pull/2408)
|
|
||||||
- AdminPage extensibility and generic improvements (https://github.com/flarum/core/pull/2593)
|
|
||||||
- Remove entry of authors, link to https://flarum.org/team (https://github.com/flarum/core/pull/2625)
|
|
||||||
- Search and filtering are split (https://github.com/flarum/core/pull/2454)
|
|
||||||
- Move IP identification into a middleware (https://github.com/flarum/core/pull/2624)
|
|
||||||
- Editor Driver abstraction introduced (https://github.com/flarum/core/pull/2594)
|
|
||||||
- Allow overriding routes (https://github.com/flarum/core/pull/2577)
|
|
||||||
- Split user edit permissions into permissions for editing of user credentials, username, groups and suspending (https://github.com/flarum/core/pull/2620)
|
|
||||||
- Reduced number of admin extension categories (https://github.com/flarum/core/pull/2604)
|
|
||||||
- Move search related classes to a dedicated Query namespace (https://github.com/flarum/core/pull/2645)
|
|
||||||
- Rewrite common helpers into typescript (https://github.com/flarum/core/pull/2541)
|
|
||||||
- `TextEditor` is moved to the common namespace for use in the admin frontend (https://github.com/flarum/core/pull/2649)
|
|
||||||
- Update Laravel/Illuminate components to 8 (https://github.com/flarum/core/pull/2576)
|
|
||||||
- Eager load relations in discussion listing to improve performance (https://github.com/flarum/core/pull/2639)
|
|
||||||
- Adopt flarum/testing package (https://github.com/flarum/core/pull/2545)
|
|
||||||
- Replace `user` gambit with `author` gambit ([612a57c](https://github.com/flarum/core/commit/612a57c4664415a3ea120103483645c32acc6f12))
|
|
||||||
- Posts page of on user profile loads posts using username instead of id ([30017ee](https://github.com/flarum/core/commit/30017eef09ae9e78640c4e2cacd4909fffa8d775))
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Transform css breaks iOS scroll functionality (https://github.com/flarum/core/pull/2527)
|
|
||||||
- Composer header is hidden on mobile devices (https://github.com/flarum/core/pull/2279)
|
|
||||||
- Cannot delete a post or discussion of a deleted user (https://github.com/flarum/core/pull/2521)
|
|
||||||
- DiscussionListPane jumps around not keeping the scroll position (https://github.com/flarum/core/pull/2402)
|
|
||||||
- Infinite scroll on notifications dropdown broken (https://github.com/flarum/core/pull/2524)
|
|
||||||
- The show language selector switch remains toggled on ([9347b12](https://github.com/flarum/core/commit/9347b12b47bf4ab97ffb7ca92673604b237c1012))
|
|
||||||
- Model Visibility extender throws exception on extensions that aren't installed or enabled (https://github.com/flarum/core/pull/2580)
|
|
||||||
- Extensions are marked as enabled when enabling fails to unmet extension dependencies (https://github.com/flarum/core/pull/2558)
|
|
||||||
- Routes to admin extension pages without a valid ID break the admin page (https://github.com/flarum/core/pull/2584)
|
|
||||||
- Disabled fieldset use an incorrect CSS property `disallowed` (https://github.com/flarum/core/pull/2585)
|
|
||||||
- Scrolling to a post that is already loaded the Load More button shows and does not trigger (https://github.com/flarum/core/pull/2388)
|
|
||||||
- Opening discussions on some mobile devices require a double tap (https://github.com/flarum/core/pull/2607)
|
|
||||||
- iOS devices show erratic behavior in the post stream while updating (https://github.com/flarum/core/pull/2548)
|
|
||||||
- Small mobile screens partially hides the composer when the keyboard is open (https://github.com/flarum/core/pull/2631)
|
|
||||||
- Clearing cache does not clear the template cache in storage/views (https://github.com/flarum/core/pull/2648)
|
|
||||||
- Boot errors show critical information (https://github.com/flarum/core/pull/2633)
|
|
||||||
- List user endpoint discloses last online even if user choose against it (https://github.com/flarum/core/pull/2634)
|
|
||||||
- Group gambit disclosed hidden groups (https://github.com/flarum/core/pull/2657)
|
|
||||||
- Search results on small windows not fully visible (https://github.com/flarum/core/pull/2650)
|
|
||||||
- Composer goes off screen on Safari when starting to type (https://github.com/flarum/core/pull/2660)
|
|
||||||
- A search that has no results shows the search results dropdown ([b88a7cb](https://github.com/flarum/core/commit/b88a7cb33b56e318f11670e9e2d563aef94db039))
|
|
||||||
- The composer modal moves around when typing on Safari ([a64c398](https://github.com/flarum/core/commit/a64c39835aba43e831209609f4a9638ae589aa41))
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- Deprecated CSRF wildcard path match
|
|
||||||
- Deprecated policy and visibility scoping events
|
|
||||||
- Deprecated post types event
|
|
||||||
- Deprecated validation events
|
|
||||||
- Deprecated notification events
|
|
||||||
- Deprecated floodgate
|
|
||||||
- Deprecated user preferences event
|
|
||||||
- Deprecated formatting events
|
|
||||||
- Deprecated api events
|
|
||||||
- Deprecated bootstrap.php support
|
|
||||||
- PHP 7.2 support (https://github.com/flarum/core/pull/2507)
|
|
||||||
- Bidi attribute in the rendered HTML (https://github.com/flarum/core/pull/2602)
|
|
||||||
- `AccessToken::find`, use `AccessToken::findValid` instead (https://github.com/flarum/core/pull/2651)
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- `GetModelIsPrivate` event (https://github.com/flarum/core/pull/2587)
|
|
||||||
- `CheckingPassword` event (https://github.com/flarum/core/pull/2176)
|
|
||||||
- `event()` helper (https://github.com/flarum/core/pull/2608)
|
|
||||||
- `AccessToken::generate` argument `$lifetime` (https://github.com/flarum/core/pull/2651)
|
|
||||||
- `Rememberer::remember` argument `$token` should receive an instance of `RememberAccessToken` with `AccessToken` being deprecated (https://github.com/flarum/core/pull/2651)
|
|
||||||
- `Rememberer::rememberUser` (https://github.com/flarum/core/pull/2651)
|
|
||||||
- `SessionAuthenticator::logIn` argument `$userId`, should be replaced with `AccessToken` (https://github.com/flarum/core/pull/2651)
|
|
||||||
- `TextEditor` has been moved to `common` (https://github.com/flarum/core/pull/2649)
|
|
||||||
- `UserFilter` ([91e8b56](https://github.com/flarum/core/commit/91e8b569618957c86757ef89bac666e9102db5ae))
|
|
||||||
|
|
||||||
|
|
||||||
## [0.1.0-beta.15](https://github.com/flarum/core/compare/v0.1.0-beta.14.1...v0.1.0-beta.15)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Slug drivers support (https://github.com/flarum/core/pull/2456).
|
|
||||||
- Notification type extender (https://github.com/flarum/core/pull/2424).
|
|
||||||
- Validation extender (https://github.com/flarum/core/pull/2102).
|
|
||||||
- Post extender (https://github.com/flarum/core/pull/2101).
|
|
||||||
- Notification channel extender (https://github.com/flarum/core/pull/2432).
|
|
||||||
- Service provider extender (https://github.com/flarum/core/pull/2437).
|
|
||||||
- API serializer extender (https://github.com/flarum/core/pull/2438).
|
|
||||||
- User preferences extender (https://github.com/flarum/core/pull/2463).
|
|
||||||
- Settings extender (https://github.com/flarum/core/pull/2452).
|
|
||||||
- ApiController extender (https://github.com/flarum/core/pull/2451).
|
|
||||||
- Model visibility extender (https://github.com/flarum/core/pull/2460).
|
|
||||||
- Policy extender (https://github.com/flarum/core/pull/2461).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Time helpers converted to Typescript (https://github.com/flarum/core/pull/2391).
|
|
||||||
- Improved the formatter extender (https://github.com/flarum/core/pull/2098).
|
|
||||||
- Improve wording on installer when facing file permission issues (https://github.com/flarum/core/pull/2435).
|
|
||||||
- Background color of checkbox toggles improved for better usability (https://github.com/flarum/core/pull/2443).
|
|
||||||
- Route resolving refactored (https://github.com/flarum/core/pull/2425).
|
|
||||||
- Administration panel UX refactored (https://github.com/flarum/core/pull/2409).
|
|
||||||
- Floodgate moved to middleware and extender added (https://github.com/flarum/core/pull/2170).
|
|
||||||
- DRY up image uploading logic (https://github.com/flarum/core/pull/2477).
|
|
||||||
- Process isolation on testing (https://github.com/flarum/core/commit/984f751c718c89501cc09857bc271efa2c7eea8c).
|
|
||||||
- Forum and admin javascript exports namespaced (https://github.com/flarum/core/pull/2488).
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Web updater does not take into account subfolder installations (https://github.com/flarum/core/pull/2426).
|
|
||||||
- Callables handling in extenders failed (https://github.com/flarum/core/pull/2423).
|
|
||||||
- Scrolling on mobile from PostSteam changes didn't work correctly (https://github.com/flarum/core/pull/2385).
|
|
||||||
- Side pane covers part of the discussion page due to `app.discussions` being empty (https://github.com/flarum/core/commit/102e76b084bf47fdfb4c73f95e1fbb322537f7aa).
|
|
||||||
- Change email modal keeps showing the previous error message even on success (https://github.com/flarum/core/pull/2467).
|
|
||||||
- Comment count not updated when discussions are deleted (https://github.com/flarum/core/pull/2472).
|
|
||||||
- `goToIndex` in PostStream does not trigger an xhr to retrieve new data (https://github.com/flarum/core/commit/09e2736cbcc267594b660beabbd001d9030f9880).
|
|
||||||
- On refresh the post number is reduced by one (https://github.com/flarum/core/pull/2476).
|
|
||||||
- Queue worker would instantiate a new Queue factory, not the bound one (https://github.com/flarum/core/pull/2481).
|
|
||||||
- Header accidentally has a border bottom (https://github.com/flarum/core/pull/2489).
|
|
||||||
- Namespace mentioned in docblock is incorrect (https://github.com/flarum/core/pull/2494).
|
|
||||||
- Scrolling inside longer discussions (especially Firefox) skips posts (https://github.com/flarum/core/commit/210a6b3e253d7917bd1eacd3ed8d2f95073ae99d).
|
|
||||||
- Uploading avatars that are jpg/jpeg fails with a validation error (https://github.com/flarum/core/pull/2497).
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- MomentJS alias (https://github.com/flarum/core/pull/2428).
|
|
||||||
- Deprecated user events `GetDisplayName` and `PrepareUserGroups` (https://github.com/flarum/core/pull/2428).
|
|
||||||
- AssertPermissionTrait (https://github.com/flarum/core/pull/2428).
|
|
||||||
- Path related helpers and methods in Application (https://github.com/flarum/core/pull/2428).
|
|
||||||
- Backward compatibility layers from the frontend rewrite (https://github.com/flarum/core/pull/2428).
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- `CheckingForFlooding` (https://github.com/flarum/core/commit/8e25bcb68f86cc992c46dfa70368419fe9f936ac).
|
|
||||||
|
|
||||||
## [0.1.0-beta.14.1](https://github.com/flarum/core/compare/v0.1.0-beta.14...v0.1.0-beta.14.1)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- SuperTextarea component is not exported.
|
|
||||||
- Symfony dependencies do not match those depended on by Laravel (https://github.com/flarum/core/pull/2407).
|
|
||||||
- Scripts from textformatter aren't executed (https://github.com/flarum/core/pull/2415)
|
|
||||||
- Sub path installations have no page title.
|
|
||||||
- Losing focus of Composer area when coming from fullscreen.
|
|
||||||
|
|
||||||
## [0.1.0-beta.14](https://github.com/flarum/core/compare/v0.1.0-beta.13...v0.1.0-beta.14)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Check dependencies before enabling / disabling extensions (https://github.com/flarum/core/pull/2188)
|
|
||||||
- Set up temporary infrastructure for TypeScript in core (https://github.com/flarum/core/pull/2206)
|
|
||||||
- Better UI for request error modals (https://github.com/flarum/core/pull/1929)
|
|
||||||
- Display name extender, tests, frontend UI (https://github.com/flarum/core/pull/2174)
|
|
||||||
- Scroll to post or show alert when editing a post from another page (https://github.com/flarum/core/pull/2108)
|
|
||||||
- Feature to test email config by sending an email to the current user (https://github.com/flarum/core/pull/2023)
|
|
||||||
- Allow searching users by group ID using the group gambit (https://github.com/flarum/core/pull/2192)
|
|
||||||
- Use `liveHumanTimes` helper to update times without reload/rerender (https://github.com/flarum/core/pull/2208)
|
|
||||||
- View extender, tests (https://github.com/flarum/core/pull/2134)
|
|
||||||
- User extender to replace `PrepareUserGroups` (https://github.com/flarum/core/pull/2110)
|
|
||||||
- Increase extensibility of skeleton PHP (https://github.com/flarum/core/pull/2308, https://github.com/flarum/core/pull/2318)
|
|
||||||
- Pass a translator instance to `getEmailSubject` in `MailableInterface` (https://github.com/flarum/core/pull/2244)
|
|
||||||
- Force LF line endings on windows (https://github.com/flarum/core/pull/2321)
|
|
||||||
- Add a `Link` component for internal and external links (https://github.com/flarum/core/pull/2315)
|
|
||||||
- `ConfirmDocumentUnload` component
|
|
||||||
- Error handler middleware can now be manipulated by the middleware extender
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Update to Mithril 2 (https://github.com/flarum/core/pull/2255)
|
|
||||||
- Stop storing component instances (https://github.com/flarum/core/issues/1821, https://github.com/flarum/core/issues/2144)
|
|
||||||
- Update to Laravel 6.x (https://github.com/flarum/core/issues/2055)
|
|
||||||
- `Flarum\Foundation\Application` no longer implements `Illuminate\Contracts\Foundation\Application` (#2142)
|
|
||||||
- `Flarum\Foundation\Application` no longer inherits `Illuminate\Container\Container` (#2142)
|
|
||||||
- `paths` have been split off from `Flarum\Foundation\Application` into `Flarum\Foundation\Paths`, which can be injected where needed (#2142)
|
|
||||||
- `Flarum\User\Gate` no longer implements `Illuminate\Contracts\Auth\Access\Gate` (https://github.com/flarum/core/pull/2181)
|
|
||||||
- Improve Group Gambit performance (https://github.com/flarum/core/pull/2192)
|
|
||||||
- Switch to `dayjs` from `momentjs` (https://github.com/flarum/core/pull/2219)
|
|
||||||
- Don't create a `bio` column in `users` for new installations (https://github.com/flarum/core/pull/2215)
|
|
||||||
- Start converting core JS to TypeScript (https://github.com/flarum/core/pull/2207)
|
|
||||||
- Make Carbon an explicit dependency (https://github.com/flarum/core/commit/3b39c212e0fef7522e7d541a9214ff3817138d5d)
|
|
||||||
- Use Symfony's translator interface instead of Laravel's (https://github.com/flarum/core/pull/2243)
|
|
||||||
- Use newer versions of fontawesome (https://github.com/flarum/core/pull/2274)
|
|
||||||
- Use URL generator instead of `app()->url()` where possible (https://github.com/flarum/core/pull/2302)
|
|
||||||
- Move config from `config.php` into an injectable helper class (https://github.com/flarum/core/pull/2271)
|
|
||||||
- Use reserved TLD for bogus and test urls (https://github.com/flarum/core/commit/6860b24b70bd04544dde90e537ce021a5fc5a689)
|
|
||||||
- Replace `m.stream` with `flarum/utils/Stream` (https://github.com/flarum/core/pull/2316)
|
|
||||||
- Replace `affixedSidebar` util with `AffixedSidebar` component
|
|
||||||
- Replace `m.withAttr` with `flarum/utils/withAttr`
|
|
||||||
- Scroll Listener is now passive, performance improvement (https://github.com/flarum/core/pull/2387)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `generate:migration` command for extensions (https://github.com/flarum/core/commit/443949f7b9d7558dbc1e0994cb898cbac59bec87)
|
|
||||||
- Container config for `UninstalledSite` (https://github.com/flarum/core/commit/ecdce44d555dd36a365fd472b2916e677ef173cf)
|
|
||||||
- Tooltip glitch on page chang (https://github.com/flarum/core/issues/2118)
|
|
||||||
- Using multiple extenders in tests (https://github.com/flarum/core/commit/c4f4f218bf4b175a30880b807f9ccb1a37a25330)
|
|
||||||
- Header glitch when opening modals (https://github.com/flarum/core/pull/2131)
|
|
||||||
- Ensure `SameSite` is explicitly set for cookies (https://github.com/flarum/core/pull/2159)
|
|
||||||
- Ensure `Flarum\User\Event\AvatarChanged` event is properly dispatched (https://github.com/flarum/core/pull/2197)
|
|
||||||
- Show correct error message on wrong password when changing email (https://github.com/flarum/core/pull/2171)
|
|
||||||
- Discussion unreadCount could be higher than commentCount if posts deleted (https://github.com/flarum/core/pull/2195)
|
|
||||||
- Don't show page title on the default route (https://github.com/flarum/core/pull/2047)
|
|
||||||
- Add page title to `All Discussions` page when it isn't the default route (https://github.com/flarum/core/pull/2047)
|
|
||||||
- Accept `'0'` as `false` for `flarum/components/Checkbox` (https://github.com/flarum/core/pull/2210)
|
|
||||||
- Fix PostStreamScrubber background (https://github.com/flarum/core/pull/2222)
|
|
||||||
- Test port on BaseUrl tests (https://github.com/flarum/core/pull/2226)
|
|
||||||
- `UrlGenerator` can now generate urls with optional parameters (https://github.com/flarum/core/pull/2246)
|
|
||||||
- Allow `less` to be compiled independently of Flarum (https://github.com/flarum/core/pull/2252)
|
|
||||||
- Use correct number abbreviation (https://github.com/flarum/core/pull/2261)
|
|
||||||
- Ensure avatar html uses alt tags for accessibility (https://github.com/flarum/core/pull/2269)
|
|
||||||
- Escape regex when searching (https://github.com/flarum/core/pull/2273)
|
|
||||||
- Remove unneeded semicolons inserted during JS compilation (https://github.com/flarum/core/pull/2280)
|
|
||||||
- Don't require a username/password for SMTP (https://github.com/flarum/core/pull/2287)
|
|
||||||
- Allow uppercase entries for SMTP encryption validation (https://github.com/flarum/core/pull/2289)
|
|
||||||
- Ensure that the right number of posts is returned from list posts API (https://github.com/flarum/core/pull/2291)
|
|
||||||
- Fix a variety of PostStream bugs (https://github.com/flarum/core/pull/2160, https://github.com/flarum/core/pull/2160)
|
|
||||||
- Sliding discussion glitch on mobile (https://github.com/flarum/core/pull/2324)
|
|
||||||
- Sliding discussion button in wrong place (https://github.com/flarum/core/pull/2330, https://github.com/flarum/core/pull/2383)
|
|
||||||
- Sliding discussion glitch on mobile (https://github.com/flarum/core/pull/2381)
|
|
||||||
- Fix PostStream for posts with top margins, and scrubber position when scrolling below posts (https://github.com/flarum/core/pull/2369)
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- `Flarum\Event\AbstractConfigureRoutes` event class
|
|
||||||
- `Flarum\Event\ConfigureApiRoutes` event class
|
|
||||||
- `Flarum\Event\ConfigureForumRoutes` event class
|
|
||||||
- `Flarum\Console\Event\Configuring` event class
|
|
||||||
- `Flarum\Event\ConfigureModelDates` event class
|
|
||||||
- `Flarum\Event\ConfigureLocales` event class
|
|
||||||
- `Flarum\Event\ConfigureModelDefaultAttributes` event class
|
|
||||||
- `Flarum\Event\GetModelRelationship` event class
|
|
||||||
- `Flarum\User\Event\BioChanged` event class
|
|
||||||
- `Flarum\Database\MigrationServiceProvider` moved into `Flarum\Database\DatabaseServiceProvider`
|
|
||||||
- Unused `admin/components/Widget` component (`admin/component/DashboardWidget` should be used instead)
|
|
||||||
- Mandrill mail driver (https://github.com/flarum/core/commit/bca833d3f1c34d45d95bf905902368a2753b8908)
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- `Flarum\User\Event\GetDisplayName` event class
|
|
||||||
- Global path helpers, `Flarum\Foundation\Application` path methods (https://github.com/flarum/core/pull/2155)
|
|
||||||
- `Flarum\User\AssertPermissionTrait` (https://github.com/flarum/core/pull/2044)
|
|
||||||
|
|
||||||
## [0.1.0-beta.13](https://github.com/flarum/core/compare/v0.1.0-beta.12...v0.1.0-beta.13)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Console extender (#2057)
|
|
||||||
- CSRF extender (#2095)
|
|
||||||
- Event extender (#2097)
|
|
||||||
- Mail extender (#2012)
|
|
||||||
- Model extender (#2100)
|
|
||||||
- Posts by users that started a discussion now have the CSS class `.Post--by-start-user`
|
|
||||||
- PHPUnit 8 compatibility
|
|
||||||
- Composer 2 compatibility
|
|
||||||
- Permission groups can now be hidden (#2129)
|
|
||||||
- Confirmation popup when hiding or deleting posts (#2135)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Updated less.php dependency version to 3.0
|
|
||||||
- Updated JS dependencies
|
|
||||||
- All notifications and other emails now processed through the queue, if enabled (#978, #1928, #1931, #2096)
|
|
||||||
- Simplified uploads, removing need to store intermediate files (#2117)
|
|
||||||
- Improved date handling for dates older than 1 year (#2034)
|
|
||||||
- Linting and automatic formatting for JS (#2099)
|
|
||||||
- Translation files from Language Packs are only loaded for extensions that are enabled (#2020)
|
|
||||||
- PHP extenders' properties are now `private` instead of `protected`, intentionally making it harder to extend these classes (#1958)
|
|
||||||
- Preparation for upgrading Laravel components to 5.8 and then 6.0 (#2055, #2117)
|
|
||||||
- Allowed permission checks based on model classes in addition to instances (#1977)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Users can no longer restore discussions hidden by admins (#2037)
|
|
||||||
- Issues of the Modal not showing or auto hiding (#1504, #1813, #2080)
|
|
||||||
- Columnar layout on admin extensions page was broken in Firefox (#2029, #2111)
|
|
||||||
- Non-dismissible modals could still be dismissed using the ESC key (#1917)
|
|
||||||
- New discussions were added to the discussion list above unread sticky posts (#1751, #1868)
|
|
||||||
- New discussions not visible to users when using Pusher (#2076, #2077)
|
|
||||||
- Permission icons were aligned unevenly in admin permissions list (#2016, #2018)
|
|
||||||
- Notification bubble not inversed on mobile with colored header (#1983, #2109)
|
|
||||||
- Post stream scrubber clicks jumped back to first post (#1945)
|
|
||||||
- Loading state of Switch toggle component was hard to see (#2039, #1491)
|
|
||||||
- `Flarum\Extend\Middleware`: The methods `insertBefore()` and `insertAfter()` did not work as described (#2063, #2084)
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- Support for PHP 7.1 (#2014)
|
|
||||||
- Zend compatibility bridge (#2010)
|
|
||||||
- SES mail support (#2011)
|
|
||||||
- Backward compatibility layer for `Flarum\Mail\DriverInterface`, new methods from beta.12 are now required
|
|
||||||
- `Flarum\Util\Str` helper class
|
|
||||||
- `Flarum\Event\ConfigureMiddleware` event
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- `Flarum\Event\AbstractConfigureRoutes` event class
|
|
||||||
- `Flarum\Event\ConfigureApiRoutes` event class
|
|
||||||
- `Flarum\Event\ConfigureForumRoutes` event class
|
|
||||||
- `Flarum\Event\ConfigureLocales` event class
|
|
||||||
|
|
||||||
## [0.1.0-beta.12](https://github.com/flarum/core/compare/v0.1.0-beta.11.1...v0.1.0-beta.12)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Full support for PHP 7.4 (#1980)
|
|
||||||
- Mail settings: Configure region for the Mailgun driver (#1834, #1850)
|
|
||||||
- Mail settings: Alert admins about incomplete settings (#1763, #1921)
|
|
||||||
- New permission that allows users to post without throttling (#1255, #1938)
|
|
||||||
- Basic transliteration of discussion "slugs" / pretty URLs (#194, #1975)
|
|
||||||
- User profiles: Render basic content on server side (#1901)
|
|
||||||
- New extender for configuring middleware (#1919, #1952, #1957, #1971)
|
|
||||||
- New extender for configuring error handling (#1781, #1970)
|
|
||||||
- Automated tests for PHP extenders to guarantee their backwards compatibility
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Profile URLs for non-existing users properly return HTTP 404 (#1846, #1901)
|
|
||||||
- Confirmation email subject no longer contains the forum title (#1613)
|
|
||||||
- Improved error handling during Flarum's early boot phase (#1607)
|
|
||||||
- Updated deprecated "Zend" libraries to their new "Laminas" equivalents (#1963)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Update page did not work when installed in subdirectories (#1947)
|
|
||||||
- Avatar upload did not work in IE11 / Edge (#1125, #1570)
|
|
||||||
- Translation fallback was ignored for client-rendered pages (#1774, #1961)
|
|
||||||
- The success alert when posting replies was invisible (#1976)
|
|
||||||
|
|
||||||
## [0.1.0-beta.11.1](https://github.com/flarum/core/compare/v0.1.0-beta.11...v0.1.0-beta.11.1)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Saving custom css in admin failed (#1946)
|
|
||||||
|
|
||||||
## [0.1.0-beta.11](https://github.com/flarum/core/compare/v0.1.0-beta.10...v0.1.0-beta.11)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Comments have an additional class `Post--by-actor` when posted by the user (#1927)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Improved support for URL identification during installation (#1861)
|
|
||||||
- KeyboardNavigatable now has a callback ability (#1922)
|
|
||||||
- Links are no longer opened with target `_blank` but in the same window (#859)
|
|
||||||
- Links now have `nofollow ugc` by default as their `rel` attribute (#859, #1884)
|
|
||||||
- Improved performance of the full text gambit when searching for users (#1877)
|
|
||||||
- The Queue implementation is now available under its Illuminate contract
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- No error handling was possible in the console/cli (#1789)
|
|
||||||
- Enable scrollbars in log in modals so it fits for GitHub (#1716)
|
|
||||||
- Reduce log in modal for SSO so it fits for Facebook (#1727)
|
|
||||||
- Deleting discussions permanently did not delete its posts (#1909)
|
|
||||||
- Fixed the queue:restart command (#1932)
|
|
||||||
- Deleted posts were visible to all visitors (#1827)
|
|
||||||
- Old avatars weren't being deleted when replaced (#1918)
|
|
||||||
- The search performance regression was reverted (#1764)
|
|
||||||
- No profile background could be set for remote images (#445)
|
|
||||||
- Back button sends to home even though it could actually go back (#1942)
|
|
||||||
- Debug button no longer visible (#1687)
|
|
||||||
- Modals on smaller screens use the whole width of the page
|
|
||||||
|
|
||||||
## [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))
|
|
||||||
- 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))
|
|
||||||
- Remove empty attributes on `<html>` tag ([796b577](https://github.com/flarum/core/commit/796b57753d34d4ea741dbebcbc550b17808f6c94))
|
|
@@ -1,7 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019-2021 Stichting Flarum (Flarum Foundation)
|
Copyright (c) 2014-2017 Toby Zerner
|
||||||
Copyright (c) 2014-2019 Toby Zerner (toby.zerner@gmail.com)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
37
README.md
37
README.md
@@ -1,38 +1,7 @@
|
|||||||
<p align="center"><img src="https://flarum.org/assets/img/logo.png"></p>
|
# Flarum Core
|
||||||
|
|
||||||
<p align="center">
|
This repository contains Flarum's core code. If you want to set up a forum, visit the [main Flarum repository](http://github.com/flarum/flarum).
|
||||||
<a href="https://github.com/flarum/core/actions?query=workflow%3ATests"><img src="https://github.com/flarum/core/workflows/Tests/badge.svg" alt="PHP Tests"></a>
|
|
||||||
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/packagist/dt/flarum/core" alt="Total Downloads"></a>
|
|
||||||
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/github/v/release/flarum/core?sort=semver" alt="Latest Version"></a>
|
|
||||||
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/packagist/l/flarum/core" alt="License"></a>
|
|
||||||
<a href="https://huntr.dev/bounties/disclose/?target=https://github.com/flarum/core"><img src="https://cdn.huntr.dev/huntr_security_badge_mono.svg" alt="huntr"></a>
|
|
||||||
<a href="https://github.styleci.io/repos/28257573"><img src="https://github.styleci.io/repos/28257573/shield?style=flat" alt="StyleCI"></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## About Flarum
|
|
||||||
|
|
||||||
**[Flarum](https://flarum.org/) is a delightfully simple discussion platform for your website.** It's fast and easy to use, with all the features you need to run a successful community. It is designed to be:
|
|
||||||
|
|
||||||
* **Fast and simple.** No clutter, no bloat, no complex dependencies. Flarum is built with PHP so it’s quick and easy to deploy. The interface is powered by Mithril, a performant JavaScript framework with a tiny footprint.
|
|
||||||
|
|
||||||
* **Beautiful and responsive.** This is forum software for humans. Flarum is carefully designed to be consistent and intuitive across platforms, out-of-the-box.
|
|
||||||
|
|
||||||
* **Powerful and extensible.** Customize, extend, and integrate Flarum to suit your community. Flarum’s architecture is amazingly flexible, with a powerful Extension API.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
This repository contains Flarum's core code. If you want to set up a forum, visit the [Flarum skeleton repository](https://github.com/flarum/flarum). For support, refer to the [documentation](https://docs.flarum.org/), and ask questions on [Flarum Discuss](https://discuss.flarum.org/) (our community forum) or [Discord server](https://flarum.org/discord/).
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://docs.flarum.org/contributing)** to learn how you can help.
|
Flarum is open-source and we would love your help building it! Please read the [Contributing Guide](https://github.com/flarum/flarum/blob/master/CONTRIBUTING.md) to learn how you can help.
|
||||||
|
|
||||||
## Security Vulnerabilities
|
|
||||||
|
|
||||||
If you discover a security vulnerability within Flarum, please send an e-mail to [security@flarum.org](mailto:security@flarum.org). All security vulnerabilities will be promptly addressed. More details can be found in our [security policy](https://github.com/flarum/core/security/policy).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Flarum is open-source software licensed under the [MIT License](https://github.com/flarum/flarum/blob/master/LICENSE).
|
|
||||||
|
147
composer.json
147
composer.json
@@ -1,92 +1,63 @@
|
|||||||
{
|
{
|
||||||
"name": "flarum/core",
|
"name": "flarum/core",
|
||||||
"description": "Delightfully simple forum software.",
|
"description": "Delightfully simple forum software.",
|
||||||
"keywords": [
|
"keywords": ["forum", "discussion"],
|
||||||
"forum",
|
"homepage": "http://flarum.org",
|
||||||
"discussion"
|
|
||||||
],
|
|
||||||
"homepage": "https://flarum.org/",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Flarum",
|
"name": "Toby Zerner",
|
||||||
"email": "info@flarum.org",
|
"email": "toby.zerner@gmail.com"
|
||||||
"homepage": "https://flarum.org/team"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/flarum"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "github",
|
"name": "Franz Liedke",
|
||||||
"url": "https://github.com/sponsors/flarum"
|
"email": "franz@develophp.org"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "other",
|
|
||||||
"url": "https://flarum.org/donate"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/flarum/core/issues",
|
"issues": "https://github.com/flarum/core/issues",
|
||||||
"source": "https://github.com/flarum/core",
|
"source": "https://github.com/flarum/core",
|
||||||
"docs": "https://docs.flarum.org",
|
"docs": "http://flarum.org/docs"
|
||||||
"forum": "https://discuss.flarum.org",
|
|
||||||
"chat": "https://flarum.org/chat"
|
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.3",
|
"php": ">=5.6.0",
|
||||||
"axy/sourcemap": "^0.1.4",
|
"dflydev/fig-cookies": "^1.0.2",
|
||||||
"components/font-awesome": "^5.14.0",
|
"doctrine/dbal": "^2.5",
|
||||||
"dflydev/fig-cookies": "^3.0.0",
|
"components/font-awesome": "^4.6",
|
||||||
"doctrine/dbal": "^2.7",
|
"franzl/whoops-middleware": "^0.4.0",
|
||||||
"dragonmantank/cron-expression": "^3.1.0",
|
"illuminate/bus": "5.1.*",
|
||||||
"franzl/whoops-middleware": "^2.0.0",
|
"illuminate/cache": "5.1.*",
|
||||||
"illuminate/bus": "^8.0",
|
"illuminate/config": "5.1.*",
|
||||||
"illuminate/cache": "^8.0",
|
"illuminate/container": "5.1.*",
|
||||||
"illuminate/config": "^8.0",
|
"illuminate/contracts": "5.1.*",
|
||||||
"illuminate/console": "^8.0",
|
"illuminate/database": "^5.1.31",
|
||||||
"illuminate/container": "^8.0",
|
"illuminate/events": "5.1.*",
|
||||||
"illuminate/contracts": "^8.0",
|
"illuminate/filesystem": "5.1.*",
|
||||||
"illuminate/database": "^8.0",
|
"illuminate/hashing": "5.1.*",
|
||||||
"illuminate/events": "^8.0",
|
"illuminate/mail": "5.1.*",
|
||||||
"illuminate/filesystem": "^8.0",
|
"illuminate/support": "5.1.*",
|
||||||
"illuminate/hashing": "^8.0",
|
"illuminate/validation": "5.1.*",
|
||||||
"illuminate/mail": "^8.0",
|
"illuminate/view": "5.1.*",
|
||||||
"illuminate/queue": "^8.0",
|
"intervention/image": "^2.3.0",
|
||||||
"illuminate/session": "^8.0",
|
|
||||||
"illuminate/support": "^8.0",
|
|
||||||
"illuminate/validation": "^8.0",
|
|
||||||
"illuminate/view": "^8.0",
|
|
||||||
"intervention/image": "2.5.* || ^2.6.1",
|
|
||||||
"laminas/laminas-diactoros": "^2.4.1",
|
|
||||||
"laminas/laminas-httphandlerrunner": "^1.2.0",
|
|
||||||
"laminas/laminas-stratigility": "^3.2.2",
|
|
||||||
"league/flysystem": "^1.0.11",
|
"league/flysystem": "^1.0.11",
|
||||||
|
"league/oauth2-client": "~1.0",
|
||||||
"matthiasmullie/minify": "^1.3",
|
"matthiasmullie/minify": "^1.3",
|
||||||
"middlewares/base-path": "^2.0.1",
|
|
||||||
"middlewares/base-path-router": "^2.0.1",
|
|
||||||
"middlewares/request-handler": "^2.0.1",
|
|
||||||
"monolog/monolog": "^1.16.0",
|
"monolog/monolog": "^1.16.0",
|
||||||
"nesbot/carbon": "^2.0",
|
|
||||||
"nikic/fast-route": "^0.6",
|
"nikic/fast-route": "^0.6",
|
||||||
|
"oyejorge/less.php": "~1.5",
|
||||||
"psr/http-message": "^1.0",
|
"psr/http-message": "^1.0",
|
||||||
"psr/http-server-handler": "^1.0",
|
"symfony/console": "^2.7",
|
||||||
"psr/http-server-middleware": "^1.0",
|
"symfony/http-foundation": "^2.7",
|
||||||
"s9e/text-formatter": "^2.3.6",
|
"symfony/translation": "^2.7",
|
||||||
"symfony/config": "^5.2.2",
|
"symfony/yaml": "^2.7",
|
||||||
"symfony/console": "^5.2.2",
|
"s9e/text-formatter": "^0.8.1",
|
||||||
"symfony/event-dispatcher": "^5.2.2",
|
|
||||||
"symfony/mime": "^5.2.0",
|
|
||||||
"symfony/polyfill-intl-messageformatter": "^1.22.0",
|
|
||||||
"symfony/translation": "^5.1.5",
|
|
||||||
"symfony/yaml": "^5.2.2",
|
|
||||||
"tobscure/json-api": "^0.3.0",
|
"tobscure/json-api": "^0.3.0",
|
||||||
"wikimedia/less.php": "^3.0"
|
"zendframework/zend-diactoros": "^1.1",
|
||||||
|
"zendframework/zend-stratigility": "^1.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"flarum/testing": "^1.0.0"
|
"mockery/mockery": "^0.9.4",
|
||||||
|
"phpunit/phpunit": "^4.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@@ -98,52 +69,12 @@
|
|||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Flarum\\Tests\\": "tests/"
|
"Tests\\": "tests/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
|
||||||
"sort-packages": true
|
|
||||||
},
|
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "1.x-dev"
|
"dev-master": "0.1.x-dev"
|
||||||
},
|
|
||||||
"flarum-cli": {
|
|
||||||
"excludeScaffolding": [
|
|
||||||
"LICENSE.md",
|
|
||||||
"js/tsconfig.json",
|
|
||||||
"js/webpack.config.js"
|
|
||||||
],
|
|
||||||
"modules": {
|
|
||||||
"backendTesting": true,
|
|
||||||
"js": true,
|
|
||||||
"gitConf": true,
|
|
||||||
"githubActions": true,
|
|
||||||
"prettier": true,
|
|
||||||
"typescript": true,
|
|
||||||
"bundlewatch": true,
|
|
||||||
"editorConfig": true,
|
|
||||||
"styleci": true,
|
|
||||||
"admin": true,
|
|
||||||
"forum": true,
|
|
||||||
"jsCommon": true,
|
|
||||||
"css": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"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."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
error/403.html
Normal file
13
error/403.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>403 Forbidden</h1>
|
||||||
|
<p>You do not have permissions to access this page.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
13
error/404.html
Normal file
13
error/404.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>404 Not Found</h1>
|
||||||
|
<p>Looks like this page could not be found.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
13
error/500.html
Normal file
13
error/500.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>500 Internal Server Error</h1>
|
||||||
|
<p>Something went wrong on our server.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
13
error/503.html
Normal file
13
error/503.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>503 Service Unavailable</h1>
|
||||||
|
<p>This forum is down for maintenance.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"files": [
|
|
||||||
{
|
|
||||||
"path": "./dist/*.js"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"defaultCompression": "gzip"
|
|
||||||
}
|
|
10
js/.gitignore
vendored
10
js/.gitignore
vendored
@@ -1,9 +1 @@
|
|||||||
.pnp.*
|
bower_components
|
||||||
.yarn/*
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/sdks
|
|
||||||
!.yarn/versions
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
|
768
js/.yarn/releases/yarn-3.1.1.cjs
vendored
768
js/.yarn/releases/yarn-3.1.1.cjs
vendored
File diff suppressed because one or more lines are too long
@@ -1,3 +0,0 @@
|
|||||||
nodeLinker: node-modules
|
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-3.1.1.cjs
|
|
@@ -1,2 +0,0 @@
|
|||||||
export * from './src/common';
|
|
||||||
export * from './src/admin';
|
|
1
js/admin/.gitignore
vendored
Normal file
1
js/admin/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
31
js/admin/Gulpfile.js
Normal file
31
js/admin/Gulpfile.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
var gulp = require('flarum-gulp');
|
||||||
|
|
||||||
|
var bowerDir = '../bower_components';
|
||||||
|
|
||||||
|
gulp({
|
||||||
|
includeHelpers: true,
|
||||||
|
files: [
|
||||||
|
bowerDir + '/es6-micro-loader/dist/system-polyfill.js',
|
||||||
|
|
||||||
|
bowerDir + '/mithril/mithril.js',
|
||||||
|
bowerDir + '/m.attrs.bidi/bidi.js',
|
||||||
|
bowerDir + '/jquery/dist/jquery.js',
|
||||||
|
bowerDir + '/moment/moment.js',
|
||||||
|
|
||||||
|
bowerDir + '/bootstrap/js/affix.js',
|
||||||
|
bowerDir + '/bootstrap/js/dropdown.js',
|
||||||
|
bowerDir + '/bootstrap/js/modal.js',
|
||||||
|
bowerDir + '/bootstrap/js/tooltip.js',
|
||||||
|
bowerDir + '/bootstrap/js/transition.js',
|
||||||
|
|
||||||
|
bowerDir + '/spin.js/spin.js',
|
||||||
|
bowerDir + '/spin.js/jquery.spin.js'
|
||||||
|
],
|
||||||
|
modules: {
|
||||||
|
'flarum': [
|
||||||
|
'src/**/*.js',
|
||||||
|
'../lib/**/*.js'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
outputFile: 'dist/app.js'
|
||||||
|
});
|
23990
js/admin/dist/app.js
vendored
Normal file
23990
js/admin/dist/app.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
js/admin/package.json
Normal file
7
js/admin/package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "^3.9.1",
|
||||||
|
"flarum-gulp": "^0.2.0"
|
||||||
|
}
|
||||||
|
}
|
33
js/admin/src/app.js
Normal file
33
js/admin/src/app.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import App from 'flarum/App';
|
||||||
|
import store from 'flarum/initializers/store';
|
||||||
|
import preload from 'flarum/initializers/preload';
|
||||||
|
import routes from 'flarum/initializers/routes';
|
||||||
|
import boot from 'flarum/initializers/boot';
|
||||||
|
|
||||||
|
const app = new App();
|
||||||
|
|
||||||
|
app.initializers.add('store', store);
|
||||||
|
app.initializers.add('routes', routes);
|
||||||
|
|
||||||
|
app.initializers.add('preload', preload, -100);
|
||||||
|
app.initializers.add('boot', boot, -100);
|
||||||
|
|
||||||
|
app.extensionSettings = {};
|
||||||
|
|
||||||
|
app.getRequiredPermissions = function(permission) {
|
||||||
|
const required = [];
|
||||||
|
|
||||||
|
if (permission === 'startDiscussion' || permission.indexOf('discussion.') === 0) {
|
||||||
|
required.push('viewDiscussions');
|
||||||
|
}
|
||||||
|
if (permission === 'discussion.delete') {
|
||||||
|
required.push('discussion.hide');
|
||||||
|
}
|
||||||
|
if (permission === 'discussion.deletePosts') {
|
||||||
|
required.push('discussion.editPosts');
|
||||||
|
}
|
||||||
|
|
||||||
|
return required;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default app;
|
30
js/admin/src/components/AddExtensionModal.js
Normal file
30
js/admin/src/components/AddExtensionModal.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Modal from 'flarum/components/Modal';
|
||||||
|
|
||||||
|
export default class AddExtensionModal extends Modal {
|
||||||
|
className() {
|
||||||
|
return 'AddExtensionModal Modal--small';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return app.translator.trans('core.admin.add_extension.title');
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<p>{app.translator.trans('core.admin.add_extension.temporary_text')}</p>
|
||||||
|
<p>{app.translator.trans('core.admin.add_extension.install_text', {a: <a href="https://discuss.flarum.org/t/extensions" target="_blank"/>})}</p>
|
||||||
|
<p>{app.translator.trans('core.admin.add_extension.developer_text', {a: <a href="http://flarum.org/docs/extend" target="_blank"/>})}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
24
js/admin/src/components/AdminLinkButton.js
Normal file
24
js/admin/src/components/AdminLinkButton.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import LinkButton from 'flarum/components/LinkButton';
|
||||||
|
|
||||||
|
export default class AdminLinkButton extends LinkButton {
|
||||||
|
getButtonContent() {
|
||||||
|
const content = super.getButtonContent();
|
||||||
|
|
||||||
|
content.push(
|
||||||
|
<div className="AdminLinkButton-description">
|
||||||
|
{this.props.description}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
79
js/admin/src/components/AdminNav.js
Normal file
79
js/admin/src/components/AdminNav.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Component from 'flarum/Component';
|
||||||
|
import AdminLinkButton from 'flarum/components/AdminLinkButton';
|
||||||
|
import SelectDropdown from 'flarum/components/SelectDropdown';
|
||||||
|
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
|
||||||
|
export default class AdminNav extends Component {
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<SelectDropdown
|
||||||
|
className="AdminNav App-titleControl"
|
||||||
|
buttonClassName="Button"
|
||||||
|
children={this.items().toArray()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an item list of links to show in the admin navigation.
|
||||||
|
*
|
||||||
|
* @return {ItemList}
|
||||||
|
*/
|
||||||
|
items() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('dashboard', AdminLinkButton.component({
|
||||||
|
href: app.route('dashboard'),
|
||||||
|
icon: 'bar-chart',
|
||||||
|
children: app.translator.trans('core.admin.nav.dashboard_button'),
|
||||||
|
description: app.translator.trans('core.admin.nav.dashboard_text')
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add('basics', AdminLinkButton.component({
|
||||||
|
href: app.route('basics'),
|
||||||
|
icon: 'pencil',
|
||||||
|
children: app.translator.trans('core.admin.nav.basics_button'),
|
||||||
|
description: app.translator.trans('core.admin.nav.basics_text')
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add('mail', AdminLinkButton.component({
|
||||||
|
href: app.route('mail'),
|
||||||
|
icon: 'envelope',
|
||||||
|
children: app.translator.trans('core.admin.nav.email_button'),
|
||||||
|
description: app.translator.trans('core.admin.nav.email_text')
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add('permissions', AdminLinkButton.component({
|
||||||
|
href: app.route('permissions'),
|
||||||
|
icon: 'key',
|
||||||
|
children: app.translator.trans('core.admin.nav.permissions_button'),
|
||||||
|
description: app.translator.trans('core.admin.nav.permissions_text')
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add('appearance', AdminLinkButton.component({
|
||||||
|
href: app.route('appearance'),
|
||||||
|
icon: 'paint-brush',
|
||||||
|
children: app.translator.trans('core.admin.nav.appearance_button'),
|
||||||
|
description: app.translator.trans('core.admin.nav.appearance_text')
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add('extensions', AdminLinkButton.component({
|
||||||
|
href: app.route('extensions'),
|
||||||
|
icon: 'puzzle-piece',
|
||||||
|
children: app.translator.trans('core.admin.nav.extensions_button'),
|
||||||
|
description: app.translator.trans('core.admin.nav.extensions_text')
|
||||||
|
}));
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
119
js/admin/src/components/AppearancePage.js
Normal file
119
js/admin/src/components/AppearancePage.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Switch from 'flarum/components/Switch';
|
||||||
|
import EditCustomCssModal from 'flarum/components/EditCustomCssModal';
|
||||||
|
import EditCustomHeaderModal from 'flarum/components/EditCustomHeaderModal';
|
||||||
|
import UploadImageButton from 'flarum/components/UploadImageButton';
|
||||||
|
import saveSettings from 'flarum/utils/saveSettings';
|
||||||
|
|
||||||
|
export default class AppearancePage extends Page {
|
||||||
|
init() {
|
||||||
|
super.init();
|
||||||
|
|
||||||
|
this.primaryColor = m.prop(app.data.settings.theme_primary_color);
|
||||||
|
this.secondaryColor = m.prop(app.data.settings.theme_secondary_color);
|
||||||
|
this.darkMode = m.prop(app.data.settings.theme_dark_mode === '1');
|
||||||
|
this.coloredHeader = m.prop(app.data.settings.theme_colored_header === '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<div className="AppearancePage">
|
||||||
|
<div className="container">
|
||||||
|
<form onsubmit={this.onsubmit.bind(this)}>
|
||||||
|
<fieldset className="AppearancePage-colors">
|
||||||
|
<legend>{app.translator.trans('core.admin.appearance.colors_heading')}</legend>
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.appearance.colors_text')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="AppearancePage-colors-input">
|
||||||
|
<input className="FormControl" type="color" placeholder="#aaaaaa" value={this.primaryColor()} onchange={m.withAttr('value', this.primaryColor)}/>
|
||||||
|
<input className="FormControl" type="color" placeholder="#aaaaaa" value={this.secondaryColor()} onchange={m.withAttr('value', this.secondaryColor)}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{Switch.component({
|
||||||
|
state: this.darkMode(),
|
||||||
|
children: app.translator.trans('core.admin.appearance.dark_mode_label'),
|
||||||
|
onchange: this.darkMode
|
||||||
|
})}
|
||||||
|
|
||||||
|
{Switch.component({
|
||||||
|
state: this.coloredHeader(),
|
||||||
|
children: app.translator.trans('core.admin.appearance.colored_header_label'),
|
||||||
|
onchange: this.coloredHeader
|
||||||
|
})}
|
||||||
|
|
||||||
|
{Button.component({
|
||||||
|
className: 'Button Button--primary',
|
||||||
|
type: 'submit',
|
||||||
|
children: app.translator.trans('core.admin.appearance.submit_button'),
|
||||||
|
loading: this.loading
|
||||||
|
})}
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{app.translator.trans('core.admin.appearance.logo_heading')}</legend>
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.appearance.logo_text')}
|
||||||
|
</div>
|
||||||
|
<UploadImageButton name="logo"/>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{app.translator.trans('core.admin.appearance.favicon_heading')}</legend>
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.appearance.favicon_text')}
|
||||||
|
</div>
|
||||||
|
<UploadImageButton name="favicon"/>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{app.translator.trans('core.admin.appearance.custom_header_heading')}</legend>
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.appearance.custom_header_text')}
|
||||||
|
</div>
|
||||||
|
{Button.component({
|
||||||
|
className: 'Button',
|
||||||
|
children: app.translator.trans('core.admin.appearance.edit_header_button'),
|
||||||
|
onclick: () => app.modal.show(new EditCustomHeaderModal())
|
||||||
|
})}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>{app.translator.trans('core.admin.appearance.custom_styles_heading')}</legend>
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.appearance.custom_styles_text')}
|
||||||
|
</div>
|
||||||
|
{Button.component({
|
||||||
|
className: 'Button',
|
||||||
|
children: app.translator.trans('core.admin.appearance.edit_css_button'),
|
||||||
|
onclick: () => app.modal.show(new EditCustomCssModal())
|
||||||
|
})}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onsubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const hex = /^#[0-9a-f]{3}([0-9a-f]{3})?$/i;
|
||||||
|
|
||||||
|
if (!hex.test(this.primaryColor()) || !hex.test(this.secondaryColor())) {
|
||||||
|
alert(app.translator.trans('core.admin.appearance.enter_hex_message'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
saveSettings({
|
||||||
|
theme_primary_color: this.primaryColor(),
|
||||||
|
theme_secondary_color: this.secondaryColor(),
|
||||||
|
theme_dark_mode: this.darkMode(),
|
||||||
|
theme_colored_header: this.coloredHeader()
|
||||||
|
}).then(() => window.location.reload());
|
||||||
|
}
|
||||||
|
}
|
166
js/admin/src/components/BasicsPage.js
Normal file
166
js/admin/src/components/BasicsPage.js
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
import FieldSet from 'flarum/components/FieldSet';
|
||||||
|
import Select from 'flarum/components/Select';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Alert from 'flarum/components/Alert';
|
||||||
|
import saveSettings from 'flarum/utils/saveSettings';
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
import Switch from 'flarum/components/Switch';
|
||||||
|
|
||||||
|
export default class BasicsPage extends Page {
|
||||||
|
init() {
|
||||||
|
super.init();
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
this.fields = [
|
||||||
|
'forum_title',
|
||||||
|
'forum_description',
|
||||||
|
'default_locale',
|
||||||
|
'show_language_selector',
|
||||||
|
'default_route',
|
||||||
|
'welcome_title',
|
||||||
|
'welcome_message'
|
||||||
|
];
|
||||||
|
this.values = {};
|
||||||
|
|
||||||
|
const settings = app.data.settings;
|
||||||
|
this.fields.forEach(key => this.values[key] = m.prop(settings[key] || false));
|
||||||
|
|
||||||
|
this.localeOptions = {};
|
||||||
|
const locales = app.data.locales;
|
||||||
|
for (const i in locales) {
|
||||||
|
this.localeOptions[i] = `${locales[i]} (${i})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.values.show_language_selector() !== "number") this.values.show_language_selector(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<div className="BasicsPage">
|
||||||
|
<div className="container">
|
||||||
|
<form onsubmit={this.onsubmit.bind(this)}>
|
||||||
|
{FieldSet.component({
|
||||||
|
label: app.translator.trans('core.admin.basics.forum_title_heading'),
|
||||||
|
children: [
|
||||||
|
<input className="FormControl" value={this.values.forum_title()} oninput={m.withAttr('value', this.values.forum_title)}/>
|
||||||
|
]
|
||||||
|
})}
|
||||||
|
|
||||||
|
{FieldSet.component({
|
||||||
|
label: app.translator.trans('core.admin.basics.forum_description_heading'),
|
||||||
|
children: [
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.basics.forum_description_text')}
|
||||||
|
</div>,
|
||||||
|
<textarea className="FormControl" value={this.values.forum_description()} oninput={m.withAttr('value', this.values.forum_description)}/>
|
||||||
|
]
|
||||||
|
})}
|
||||||
|
|
||||||
|
{Object.keys(this.localeOptions).length > 1
|
||||||
|
? FieldSet.component({
|
||||||
|
label: app.translator.trans('core.admin.basics.default_language_heading'),
|
||||||
|
children: [
|
||||||
|
Select.component({
|
||||||
|
options: this.localeOptions,
|
||||||
|
value: this.values.default_locale(),
|
||||||
|
onchange: this.values.default_locale
|
||||||
|
}),
|
||||||
|
Switch.component({
|
||||||
|
state: this.values.show_language_selector(),
|
||||||
|
onchange: this.values.show_language_selector,
|
||||||
|
children: app.translator.trans('core.admin.basics.show_language_selector_label'),
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
: ''}
|
||||||
|
|
||||||
|
{FieldSet.component({
|
||||||
|
label: app.translator.trans('core.admin.basics.home_page_heading'),
|
||||||
|
className: 'BasicsPage-homePage',
|
||||||
|
children: [
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.basics.home_page_text')}
|
||||||
|
</div>,
|
||||||
|
this.homePageItems().toArray().map(({path, label}) =>
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="homePage" value={path} checked={this.values.default_route() === path} onclick={m.withAttr('value', this.values.default_route)}/>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
)
|
||||||
|
]
|
||||||
|
})}
|
||||||
|
|
||||||
|
{FieldSet.component({
|
||||||
|
label: app.translator.trans('core.admin.basics.welcome_banner_heading'),
|
||||||
|
className: 'BasicsPage-welcomeBanner',
|
||||||
|
children: [
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.basics.welcome_banner_text')}
|
||||||
|
</div>,
|
||||||
|
<div className="BasicsPage-welcomeBanner-input">
|
||||||
|
<input className="FormControl" value={this.values.welcome_title()} oninput={m.withAttr('value', this.values.welcome_title)}/>
|
||||||
|
<textarea className="FormControl" value={this.values.welcome_message()} oninput={m.withAttr('value', this.values.welcome_message)}/>
|
||||||
|
</div>
|
||||||
|
]
|
||||||
|
})}
|
||||||
|
|
||||||
|
{Button.component({
|
||||||
|
type: 'submit',
|
||||||
|
className: 'Button Button--primary',
|
||||||
|
children: app.translator.trans('core.admin.basics.submit_button'),
|
||||||
|
loading: this.loading,
|
||||||
|
disabled: !this.changed()
|
||||||
|
})}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
changed() {
|
||||||
|
return this.fields.some(key => this.values[key]() !== app.data.settings[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a list of options for the default homepage. Each option must be an
|
||||||
|
* object with `path` and `label` properties.
|
||||||
|
*
|
||||||
|
* @return {ItemList}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
homePageItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('allDiscussions', {
|
||||||
|
path: '/all',
|
||||||
|
label: app.translator.trans('core.admin.basics.all_discussions_label')
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
onsubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (this.loading) return;
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
app.alerts.dismiss(this.successAlert);
|
||||||
|
|
||||||
|
const settings = {};
|
||||||
|
|
||||||
|
this.fields.forEach(key => settings[key] = this.values[key]());
|
||||||
|
|
||||||
|
saveSettings(settings)
|
||||||
|
.then(() => {
|
||||||
|
app.alerts.show(this.successAlert = new Alert({type: 'success', children: app.translator.trans('core.admin.basics.saved_message')}));
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.then(() => {
|
||||||
|
this.loading = false;
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
22
js/admin/src/components/DashboardPage.js
Normal file
22
js/admin/src/components/DashboardPage.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
|
||||||
|
export default class DashboardPage extends Page {
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<div className="DashboardPage">
|
||||||
|
<div className="container">
|
||||||
|
<h2>{app.translator.trans('core.admin.dashboard.welcome_text')}</h2>
|
||||||
|
<p>{app.translator.trans('core.admin.dashboard.version_text', {version: <strong>{app.forum.attribute('version')}</strong>})}</p>
|
||||||
|
<p>{app.translator.trans('core.admin.dashboard.beta_warning_text', {strong: <strong/>})}</p>
|
||||||
|
<ul>
|
||||||
|
<li>{app.translator.trans('core.admin.dashboard.contributing_text', {a: <a href="http://flarum.org/docs/contributing" target="_blank"/>})}</li>
|
||||||
|
<li>{app.translator.trans('core.admin.dashboard.troubleshooting_text', {a: <a href="http://flarum.org/docs/troubleshooting" target="_blank"/>})}</li>
|
||||||
|
<li>{app.translator.trans('core.admin.dashboard.support_text', {a: <a href="http://discuss.flarum.org/t/support" target="_blank"/>})}</li>
|
||||||
|
<li>{app.translator.trans('core.admin.dashboard.features_text', {a: <a href="http://discuss.flarum.org/t/features" target="_blank"/>})}</li>
|
||||||
|
<li>{app.translator.trans('core.admin.dashboard.extension_text', {a: <a href="http://flarum.org/docs/extend" target="_blank"/>})}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
24
js/admin/src/components/EditCustomCssModal.js
Normal file
24
js/admin/src/components/EditCustomCssModal.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import SettingsModal from 'flarum/components/SettingsModal';
|
||||||
|
|
||||||
|
export default class EditCustomCssModal extends SettingsModal {
|
||||||
|
className() {
|
||||||
|
return 'EditCustomCssModal Modal--large';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return app.translator.trans('core.admin.edit_css.title');
|
||||||
|
}
|
||||||
|
|
||||||
|
form() {
|
||||||
|
return [
|
||||||
|
<p>{app.translator.trans('core.admin.edit_css.customize_text', {a: <a href="https://github.com/flarum/core/tree/master/less" target="_blank"/>})}</p>,
|
||||||
|
<div className="Form-group">
|
||||||
|
<textarea className="FormControl" rows="30" bidi={this.setting('custom_less')}/>
|
||||||
|
</div>
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
onsaved() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,9 +1,8 @@
|
|||||||
import app from '../../admin/app';
|
import SettingsModal from 'flarum/components/SettingsModal';
|
||||||
import SettingsModal from './SettingsModal';
|
|
||||||
|
|
||||||
export default class EditCustomHeaderModal extends SettingsModal {
|
export default class EditCustomHeaderModal extends SettingsModal {
|
||||||
className() {
|
className() {
|
||||||
return 'EditCustomHeaderModal TextareaCodeModal Modal--large';
|
return 'EditCustomHeaderModal Modal--large';
|
||||||
}
|
}
|
||||||
|
|
||||||
title() {
|
title() {
|
||||||
@@ -14,8 +13,8 @@ export default class EditCustomHeaderModal extends SettingsModal {
|
|||||||
return [
|
return [
|
||||||
<p>{app.translator.trans('core.admin.edit_header.customize_text')}</p>,
|
<p>{app.translator.trans('core.admin.edit_header.customize_text')}</p>,
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_header')} />
|
<textarea className="FormControl" rows="30" bidi={this.setting('custom_header')}/>
|
||||||
</div>,
|
</div>
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
102
js/admin/src/components/EditGroupModal.js
Normal file
102
js/admin/src/components/EditGroupModal.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import Modal from 'flarum/components/Modal';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Badge from 'flarum/components/Badge';
|
||||||
|
import Group from 'flarum/models/Group';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `EditGroupModal` component shows a modal dialog which allows the user
|
||||||
|
* to create or edit a group.
|
||||||
|
*/
|
||||||
|
export default class EditGroupModal extends Modal {
|
||||||
|
init() {
|
||||||
|
this.group = this.props.group || app.store.createRecord('groups');
|
||||||
|
|
||||||
|
this.nameSingular = m.prop(this.group.nameSingular() || '');
|
||||||
|
this.namePlural = m.prop(this.group.namePlural() || '');
|
||||||
|
this.icon = m.prop(this.group.icon() || '');
|
||||||
|
this.color = m.prop(this.group.color() || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
className() {
|
||||||
|
return 'EditGroupModal Modal--small';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return [
|
||||||
|
this.color() || this.icon() ? Badge.component({
|
||||||
|
icon: this.icon(),
|
||||||
|
style: {backgroundColor: this.color()}
|
||||||
|
}) : '',
|
||||||
|
' ',
|
||||||
|
this.namePlural() || app.translator.trans('core.admin.edit_group.title')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<div className="Form">
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>{app.translator.trans('core.admin.edit_group.name_label')}</label>
|
||||||
|
<div className="EditGroupModal-name-input">
|
||||||
|
<input className="FormControl" placeholder={app.translator.trans('core.admin.edit_group.singular_placeholder')} value={this.nameSingular()} oninput={m.withAttr('value', this.nameSingular)}/>
|
||||||
|
<input className="FormControl" placeholder={app.translator.trans('core.admin.edit_group.plural_placeholder')} value={this.namePlural()} oninput={m.withAttr('value', this.namePlural)}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>{app.translator.trans('core.admin.edit_group.color_label')}</label>
|
||||||
|
<input className="FormControl" placeholder="#aaaaaa" value={this.color()} oninput={m.withAttr('value', this.color)}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>{app.translator.trans('core.admin.edit_group.icon_label')}</label>
|
||||||
|
<div className="helpText">
|
||||||
|
{app.translator.trans('core.admin.edit_group.icon_text', {a: <a href="http://fortawesome.github.io/Font-Awesome/icons/" tabindex="-1"/>})}
|
||||||
|
</div>
|
||||||
|
<input className="FormControl" placeholder="bolt" value={this.icon()} oninput={m.withAttr('value', this.icon)}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
{Button.component({
|
||||||
|
type: 'submit',
|
||||||
|
className: 'Button Button--primary EditGroupModal-save',
|
||||||
|
loading: this.loading,
|
||||||
|
children: app.translator.trans('core.admin.edit_group.submit_button')
|
||||||
|
})}
|
||||||
|
{this.group.exists && this.group.id() !== Group.ADMINISTRATOR_ID ? (
|
||||||
|
<button type="button" className="Button EditGroupModal-delete" onclick={this.deleteGroup.bind(this)}>
|
||||||
|
{app.translator.trans('core.admin.edit_group.delete_button')}
|
||||||
|
</button>
|
||||||
|
) : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onsubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
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;
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGroup() {
|
||||||
|
if (confirm(app.translator.trans('core.admin.edit_group.delete_confirmation'))) {
|
||||||
|
this.group.delete().then(() => m.redraw());
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
js/admin/src/components/ExtensionsPage.js
Normal file
114
js/admin/src/components/ExtensionsPage.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
import LinkButton from 'flarum/components/LinkButton';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Dropdown from 'flarum/components/Dropdown';
|
||||||
|
import Separator from 'flarum/components/Separator';
|
||||||
|
import AddExtensionModal from 'flarum/components/AddExtensionModal';
|
||||||
|
import LoadingModal from 'flarum/components/LoadingModal';
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
import icon from 'flarum/helpers/icon';
|
||||||
|
import listItems from 'flarum/helpers/listItems';
|
||||||
|
|
||||||
|
export default class ExtensionsPage extends Page {
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<div className="ExtensionsPage">
|
||||||
|
<div className="ExtensionsPage-header">
|
||||||
|
<div className="container">
|
||||||
|
{Button.component({
|
||||||
|
children: app.translator.trans('core.admin.extensions.add_button'),
|
||||||
|
icon: 'plus',
|
||||||
|
className: 'Button Button--primary',
|
||||||
|
onclick: () => app.modal.show(new AddExtensionModal())
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ExtensionsPage-list">
|
||||||
|
<div className="container">
|
||||||
|
<ul className="ExtensionList">
|
||||||
|
{Object.keys(app.data.extensions)
|
||||||
|
.map(id => {
|
||||||
|
const extension = app.data.extensions[id];
|
||||||
|
const controls = this.controlItems(extension.id).toArray();
|
||||||
|
|
||||||
|
return <li className={'ExtensionListItem ' + (!this.isEnabled(extension.id) ? 'disabled' : '')}>
|
||||||
|
<div className="ExtensionListItem-content">
|
||||||
|
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
|
||||||
|
{extension.icon ? icon(extension.icon.name) : ''}
|
||||||
|
</span>
|
||||||
|
{controls.length ? (
|
||||||
|
<Dropdown
|
||||||
|
className="ExtensionListItem-controls"
|
||||||
|
buttonClassName="Button Button--icon Button--flat"
|
||||||
|
menuClassName="Dropdown-menu--right"
|
||||||
|
icon="ellipsis-h">
|
||||||
|
{controls}
|
||||||
|
</Dropdown>
|
||||||
|
) : ''}
|
||||||
|
<label className="ExtensionListItem-title">
|
||||||
|
<input type="checkbox" checked={this.isEnabled(extension.id)} onclick={this.toggle.bind(this, extension.id)}/> {' '}
|
||||||
|
{extension.extra['flarum-extension'].title}
|
||||||
|
</label>
|
||||||
|
<div className="ExtensionListItem-version">{extension.version}</div>
|
||||||
|
</div>
|
||||||
|
</li>;
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
controlItems(name) {
|
||||||
|
const items = new ItemList();
|
||||||
|
const enabled = this.isEnabled(name);
|
||||||
|
|
||||||
|
if (app.extensionSettings[name]) {
|
||||||
|
items.add('settings', Button.component({
|
||||||
|
icon: 'cog',
|
||||||
|
children: app.translator.trans('core.admin.extensions.settings_button'),
|
||||||
|
onclick: app.extensionSettings[name]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
items.add('uninstall', Button.component({
|
||||||
|
icon: 'trash-o',
|
||||||
|
children: app.translator.trans('core.admin.extensions.uninstall_button'),
|
||||||
|
onclick: () => {
|
||||||
|
app.request({
|
||||||
|
url: app.forum.attribute('apiUrl') + '/extensions/' + name,
|
||||||
|
method: 'DELETE'
|
||||||
|
}).then(() => window.location.reload());
|
||||||
|
|
||||||
|
app.modal.show(new LoadingModal());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnabled(name) {
|
||||||
|
const enabled = JSON.parse(app.data.settings.extensions_enabled);
|
||||||
|
|
||||||
|
return enabled.indexOf(name) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle(id) {
|
||||||
|
const enabled = this.isEnabled(id);
|
||||||
|
|
||||||
|
app.request({
|
||||||
|
url: app.forum.attribute('apiUrl') + '/extensions/' + id,
|
||||||
|
method: 'PATCH',
|
||||||
|
data: {enabled: !enabled}
|
||||||
|
}).then(() => {
|
||||||
|
if (!enabled) localStorage.setItem('enabledExtension', id);
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.modal.show(new LoadingModal());
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
import Component from '../../common/Component';
|
import Component from 'flarum/Component';
|
||||||
import ItemList from '../../common/utils/ItemList';
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
import listItems from '../../common/helpers/listItems';
|
import listItems from 'flarum/helpers/listItems';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `HeaderPrimary` component displays primary header controls. On the
|
* The `HeaderPrimary` component displays primary header controls. On the
|
||||||
@@ -8,7 +8,11 @@ import listItems from '../../common/helpers/listItems';
|
|||||||
*/
|
*/
|
||||||
export default class HeaderPrimary extends Component {
|
export default class HeaderPrimary extends Component {
|
||||||
view() {
|
view() {
|
||||||
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
return (
|
||||||
|
<ul className="Header-controls">
|
||||||
|
{listItems(this.items().toArray())}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
config(isInitialized, context) {
|
config(isInitialized, context) {
|
||||||
@@ -21,7 +25,7 @@ export default class HeaderPrimary extends Component {
|
|||||||
/**
|
/**
|
||||||
* Build an item list for the controls.
|
* Build an item list for the controls.
|
||||||
*
|
*
|
||||||
* @return {ItemList<import('mithril').Children>}
|
* @return {ItemList}
|
||||||
*/
|
*/
|
||||||
items() {
|
items() {
|
||||||
return new ItemList();
|
return new ItemList();
|
37
js/admin/src/components/HeaderSecondary.js
Normal file
37
js/admin/src/components/HeaderSecondary.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import Component from 'flarum/Component';
|
||||||
|
import SessionDropdown from 'flarum/components/SessionDropdown';
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
import listItems from 'flarum/helpers/listItems';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `HeaderSecondary` component displays secondary header controls.
|
||||||
|
*/
|
||||||
|
export default class HeaderSecondary extends Component {
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<ul className="Header-controls">
|
||||||
|
{listItems(this.items().toArray())}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
config(isInitialized, context) {
|
||||||
|
// Since this component is 'above' the content of the page (that is, it is a
|
||||||
|
// part of the global UI that persists between routes), we will flag the DOM
|
||||||
|
// to be retained across route changes.
|
||||||
|
context.retain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an item list for the controls.
|
||||||
|
*
|
||||||
|
* @return {ItemList}
|
||||||
|
*/
|
||||||
|
items() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('session', SessionDropdown.component());
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
19
js/admin/src/components/LoadingModal.js
Normal file
19
js/admin/src/components/LoadingModal.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Modal from 'flarum/components/Modal';
|
||||||
|
|
||||||
|
export default class LoadingModal extends Modal {
|
||||||
|
isDismissible() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
className() {
|
||||||
|
return 'LoadingModal Modal--small';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return app.translator.trans('core.admin.loading.title');
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
124
js/admin/src/components/MailPage.js
Normal file
124
js/admin/src/components/MailPage.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
import FieldSet from 'flarum/components/FieldSet';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Alert from 'flarum/components/Alert';
|
||||||
|
import saveSettings from 'flarum/utils/saveSettings';
|
||||||
|
|
||||||
|
export default class MailPage extends Page {
|
||||||
|
init() {
|
||||||
|
super.init();
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
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]));
|
||||||
|
|
||||||
|
this.localeOptions = {};
|
||||||
|
const locales = app.locales;
|
||||||
|
for (const i in locales) {
|
||||||
|
this.localeOptions[i] = `${locales[i]} (${i})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<div className="MailPage">
|
||||||
|
<div className="container">
|
||||||
|
<form onsubmit={this.onsubmit.bind(this)}>
|
||||||
|
<h2>{app.translator.trans('core.admin.email.heading')}</h2>
|
||||||
|
<div className="helpText">
|
||||||
|
{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',
|
||||||
|
children: [
|
||||||
|
<div className="MailPage-MailSettings-input">
|
||||||
|
<label>{app.translator.trans('core.admin.email.from_label')}</label>
|
||||||
|
<input className="FormControl" value={this.values.mail_from() || ''} oninput={m.withAttr('value', this.values.mail_from)} />
|
||||||
|
</div>
|
||||||
|
]
|
||||||
|
})}
|
||||||
|
|
||||||
|
{Button.component({
|
||||||
|
type: 'submit',
|
||||||
|
className: 'Button Button--primary',
|
||||||
|
children: app.translator.trans('core.admin.email.submit_button'),
|
||||||
|
loading: this.loading,
|
||||||
|
disabled: !this.changed()
|
||||||
|
})}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
changed() {
|
||||||
|
return this.fields.some(key => this.values[key]() !== app.data.settings[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
onsubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (this.loading) return;
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
app.alerts.dismiss(this.successAlert);
|
||||||
|
|
||||||
|
const settings = {};
|
||||||
|
|
||||||
|
this.fields.forEach(key => settings[key] = this.values[key]());
|
||||||
|
|
||||||
|
saveSettings(settings)
|
||||||
|
.then(() => {
|
||||||
|
app.alerts.show(this.successAlert = new Alert({type: 'success', children: app.translator.trans('core.admin.basics.saved_message')}));
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.then(() => {
|
||||||
|
this.loading = false;
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
js/admin/src/components/Page.js
Normal file
32
js/admin/src/components/Page.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import Component from 'flarum/Component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `Page` component
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
export default class Page extends Component {
|
||||||
|
init() {
|
||||||
|
app.previous = app.current;
|
||||||
|
app.current = this;
|
||||||
|
|
||||||
|
app.modal.close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class name to apply to the body while the route is active.
|
||||||
|
*
|
||||||
|
* @type {String}
|
||||||
|
*/
|
||||||
|
this.bodyClass = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
config(isInitialized, context) {
|
||||||
|
if (isInitialized) return;
|
||||||
|
|
||||||
|
if (this.bodyClass) {
|
||||||
|
$('#app').addClass(this.bodyClass);
|
||||||
|
|
||||||
|
context.onunload = () => $('#app').removeClass(this.bodyClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
149
js/admin/src/components/PermissionDropdown.js
Normal file
149
js/admin/src/components/PermissionDropdown.js
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import Dropdown from 'flarum/components/Dropdown';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Separator from 'flarum/components/Separator';
|
||||||
|
import Group from 'flarum/models/Group';
|
||||||
|
import Badge from 'flarum/components/Badge';
|
||||||
|
import GroupBadge from 'flarum/components/GroupBadge';
|
||||||
|
|
||||||
|
function badgeForId(id) {
|
||||||
|
const group = app.store.getById('groups', id);
|
||||||
|
|
||||||
|
return group ? GroupBadge.component({group, label: null}) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterByRequiredPermissions(groupIds, permission) {
|
||||||
|
app.getRequiredPermissions(permission)
|
||||||
|
.forEach(required => {
|
||||||
|
const restrictToGroupIds = app.data.permissions[required] || [];
|
||||||
|
|
||||||
|
if (restrictToGroupIds.indexOf(Group.GUEST_ID) !== -1) {
|
||||||
|
// do nothing
|
||||||
|
} else if (restrictToGroupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
||||||
|
groupIds = groupIds.filter(id => id !== Group.GUEST_ID);
|
||||||
|
} else if (groupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
||||||
|
groupIds = restrictToGroupIds;
|
||||||
|
} else {
|
||||||
|
groupIds = restrictToGroupIds.filter(id => groupIds.indexOf(id) !== -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
groupIds = filterByRequiredPermissions(groupIds, required);
|
||||||
|
});
|
||||||
|
|
||||||
|
return groupIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PermissionDropdown extends Dropdown {
|
||||||
|
static initProps(props) {
|
||||||
|
super.initProps(props);
|
||||||
|
|
||||||
|
props.className = 'PermissionDropdown';
|
||||||
|
props.buttonClassName = 'Button Button--text';
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
this.props.children = [];
|
||||||
|
|
||||||
|
let groupIds = app.data.permissions[this.props.permission] || [];
|
||||||
|
|
||||||
|
groupIds = filterByRequiredPermissions(groupIds, this.props.permission);
|
||||||
|
|
||||||
|
const everyone = groupIds.indexOf(Group.GUEST_ID) !== -1;
|
||||||
|
const members = groupIds.indexOf(Group.MEMBER_ID) !== -1;
|
||||||
|
const adminGroup = app.store.getById('groups', Group.ADMINISTRATOR_ID);
|
||||||
|
|
||||||
|
if (everyone) {
|
||||||
|
this.props.label = Badge.component({icon: 'globe'});
|
||||||
|
} else if (members) {
|
||||||
|
this.props.label = Badge.component({icon: 'user'});
|
||||||
|
} else {
|
||||||
|
this.props.label = [
|
||||||
|
badgeForId(Group.ADMINISTRATOR_ID),
|
||||||
|
groupIds.map(badgeForId)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showing) {
|
||||||
|
if (this.props.allowGuest) {
|
||||||
|
this.props.children.push(
|
||||||
|
Button.component({
|
||||||
|
children: [Badge.component({icon: 'globe'}), ' ', app.translator.trans('core.admin.permissions_controls.everyone_button')],
|
||||||
|
icon: everyone ? 'check' : true,
|
||||||
|
onclick: () => this.save([Group.GUEST_ID]),
|
||||||
|
disabled: this.isGroupDisabled(Group.GUEST_ID)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.children.push(
|
||||||
|
Button.component({
|
||||||
|
children: [Badge.component({icon: 'user'}), ' ', app.translator.trans('core.admin.permissions_controls.members_button')],
|
||||||
|
icon: members ? 'check' : true,
|
||||||
|
onclick: () => this.save([Group.MEMBER_ID]),
|
||||||
|
disabled: this.isGroupDisabled(Group.MEMBER_ID)
|
||||||
|
}),
|
||||||
|
|
||||||
|
Separator.component(),
|
||||||
|
|
||||||
|
Button.component({
|
||||||
|
children: [badgeForId(adminGroup.id()), ' ', adminGroup.namePlural()],
|
||||||
|
icon: !everyone && !members ? 'check' : true,
|
||||||
|
disabled: !everyone && !members,
|
||||||
|
onclick: e => {
|
||||||
|
if (e.shiftKey) e.stopPropagation();
|
||||||
|
this.save([]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
[].push.apply(
|
||||||
|
this.props.children,
|
||||||
|
app.store.all('groups')
|
||||||
|
.filter(group => [Group.ADMINISTRATOR_ID, Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||||
|
.map(group => Button.component({
|
||||||
|
children: [badgeForId(group.id()), ' ', group.namePlural()],
|
||||||
|
icon: groupIds.indexOf(group.id()) !== -1 ? 'check' : true,
|
||||||
|
onclick: (e) => {
|
||||||
|
if (e.shiftKey) e.stopPropagation();
|
||||||
|
this.toggle(group.id());
|
||||||
|
},
|
||||||
|
disabled: this.isGroupDisabled(group.id()) && this.isGroupDisabled(Group.MEMBER_ID) && this.isGroupDisabled(Group.GUEST_ID)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.view();
|
||||||
|
}
|
||||||
|
|
||||||
|
save(groupIds) {
|
||||||
|
const permission = this.props.permission;
|
||||||
|
|
||||||
|
app.data.permissions[permission] = groupIds;
|
||||||
|
|
||||||
|
app.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: app.forum.attribute('apiUrl') + '/permission',
|
||||||
|
data: {permission, groupIds}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle(groupId) {
|
||||||
|
const permission = this.props.permission;
|
||||||
|
|
||||||
|
let groupIds = app.data.permissions[permission] || [];
|
||||||
|
|
||||||
|
const index = groupIds.indexOf(groupId);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
groupIds.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
groupIds.push(groupId);
|
||||||
|
groupIds = groupIds.filter(id => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(id) === -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.save(groupIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
isGroupDisabled(id) {
|
||||||
|
return filterByRequiredPermissions([id], this.props.permission).indexOf(id) === -1;
|
||||||
|
}
|
||||||
|
}
|
247
js/admin/src/components/PermissionGrid.js
Normal file
247
js/admin/src/components/PermissionGrid.js
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
import Component from 'flarum/Component';
|
||||||
|
import PermissionDropdown from 'flarum/components/PermissionDropdown';
|
||||||
|
import SettingDropdown from 'flarum/components/SettingDropdown';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
import icon from 'flarum/helpers/icon';
|
||||||
|
|
||||||
|
export default class PermissionGrid extends Component {
|
||||||
|
init() {
|
||||||
|
this.permissions = this.permissionItems().toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
const scopes = this.scopeItems().toArray();
|
||||||
|
|
||||||
|
const permissionCells = permission => {
|
||||||
|
return scopes.map(scope => (
|
||||||
|
<td>
|
||||||
|
{scope.render(permission)}
|
||||||
|
</td>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table className="PermissionGrid">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{scopes.map(scope => (
|
||||||
|
<th>
|
||||||
|
{scope.label}{' '}
|
||||||
|
{scope.onremove ? Button.component({icon: 'times', className: 'Button Button--text PermissionGrid-removeScope', onclick: scope.onremove}) : ''}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
<th>{this.scopeControlItems().toArray()}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{this.permissions.map(section => (
|
||||||
|
<tbody>
|
||||||
|
<tr className="PermissionGrid-section">
|
||||||
|
<th>{section.label}</th>
|
||||||
|
{permissionCells(section)}
|
||||||
|
<td/>
|
||||||
|
</tr>
|
||||||
|
{section.children.map(child => (
|
||||||
|
<tr className="PermissionGrid-child">
|
||||||
|
<th>{icon(child.icon)}{child.label}</th>
|
||||||
|
{permissionCells(child)}
|
||||||
|
<td/>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
))}
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('view', {
|
||||||
|
label: app.translator.trans('core.admin.permissions.read_heading'),
|
||||||
|
children: this.viewItems().toArray()
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
items.add('start', {
|
||||||
|
label: app.translator.trans('core.admin.permissions.create_heading'),
|
||||||
|
children: this.startItems().toArray()
|
||||||
|
}, 90);
|
||||||
|
|
||||||
|
items.add('reply', {
|
||||||
|
label: app.translator.trans('core.admin.permissions.participate_heading'),
|
||||||
|
children: this.replyItems().toArray()
|
||||||
|
}, 80);
|
||||||
|
|
||||||
|
items.add('moderate', {
|
||||||
|
label: app.translator.trans('core.admin.permissions.moderate_heading'),
|
||||||
|
children: this.moderateItems().toArray()
|
||||||
|
}, 70);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('viewDiscussions', {
|
||||||
|
icon: 'eye',
|
||||||
|
label: app.translator.trans('core.admin.permissions.view_discussions_label'),
|
||||||
|
permission: 'viewDiscussions',
|
||||||
|
allowGuest: true
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
items.add('viewUserList', {
|
||||||
|
icon: 'users',
|
||||||
|
label: app.translator.trans('core.admin.permissions.view_user_list_label'),
|
||||||
|
permission: 'viewUserList',
|
||||||
|
allowGuest: true
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
items.add('signUp', {
|
||||||
|
icon: 'user-plus',
|
||||||
|
label: app.translator.trans('core.admin.permissions.sign_up_label'),
|
||||||
|
setting: () => SettingDropdown.component({
|
||||||
|
key: 'allow_sign_up',
|
||||||
|
options: [
|
||||||
|
{value: '1', label: app.translator.trans('core.admin.permissions_controls.signup_open_button')},
|
||||||
|
{value: '0', label: app.translator.trans('core.admin.permissions_controls.signup_closed_button')}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}, 90);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
startItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('start', {
|
||||||
|
icon: 'edit',
|
||||||
|
label: app.translator.trans('core.admin.permissions.start_discussions_label'),
|
||||||
|
permission: 'startDiscussion'
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
items.add('allowRenaming', {
|
||||||
|
icon: 'i-cursor',
|
||||||
|
label: app.translator.trans('core.admin.permissions.allow_renaming_label'),
|
||||||
|
setting: () => {
|
||||||
|
const minutes = parseInt(app.data.settings.allow_renaming, 10);
|
||||||
|
|
||||||
|
return SettingDropdown.component({
|
||||||
|
defaultLabel: minutes
|
||||||
|
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, {count: minutes})
|
||||||
|
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||||
|
key: 'allow_renaming',
|
||||||
|
options: [
|
||||||
|
{value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button')},
|
||||||
|
{value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button')},
|
||||||
|
{value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button')}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 90);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
replyItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('reply', {
|
||||||
|
icon: 'reply',
|
||||||
|
label: app.translator.trans('core.admin.permissions.reply_to_discussions_label'),
|
||||||
|
permission: 'discussion.reply'
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
items.add('allowPostEditing', {
|
||||||
|
icon: 'pencil',
|
||||||
|
label: app.translator.trans('core.admin.permissions.allow_post_editing_label'),
|
||||||
|
setting: () => {
|
||||||
|
const minutes = parseInt(app.data.settings.allow_post_editing, 10);
|
||||||
|
|
||||||
|
return SettingDropdown.component({
|
||||||
|
defaultLabel: minutes
|
||||||
|
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, {count: minutes})
|
||||||
|
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||||
|
key: 'allow_post_editing',
|
||||||
|
options: [
|
||||||
|
{value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button')},
|
||||||
|
{value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button')},
|
||||||
|
{value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button')}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 90);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
moderateItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('viewIpsPosts', {
|
||||||
|
icon: 'bullseye',
|
||||||
|
label: app.translator.trans('core.admin.permissions.view_post_ips_label'),
|
||||||
|
permission: 'discussion.viewIpsPosts'
|
||||||
|
}, 110);
|
||||||
|
|
||||||
|
items.add('renameDiscussions', {
|
||||||
|
icon: 'i-cursor',
|
||||||
|
label: app.translator.trans('core.admin.permissions.rename_discussions_label'),
|
||||||
|
permission: 'discussion.rename'
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
items.add('hideDiscussions', {
|
||||||
|
icon: 'trash-o',
|
||||||
|
label: app.translator.trans('core.admin.permissions.delete_discussions_label'),
|
||||||
|
permission: 'discussion.hide'
|
||||||
|
}, 90);
|
||||||
|
|
||||||
|
items.add('deleteDiscussions', {
|
||||||
|
icon: 'times',
|
||||||
|
label: app.translator.trans('core.admin.permissions.delete_discussions_forever_label'),
|
||||||
|
permission: 'discussion.delete'
|
||||||
|
}, 80);
|
||||||
|
|
||||||
|
items.add('editPosts', {
|
||||||
|
icon: 'pencil',
|
||||||
|
label: app.translator.trans('core.admin.permissions.edit_and_delete_posts_label'),
|
||||||
|
permission: 'discussion.editPosts'
|
||||||
|
}, 70);
|
||||||
|
|
||||||
|
items.add('deletePosts', {
|
||||||
|
icon: 'times',
|
||||||
|
label: app.translator.trans('core.admin.permissions.delete_posts_forever_label'),
|
||||||
|
permission: 'discussion.deletePosts'
|
||||||
|
}, 60);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('global', {
|
||||||
|
label: app.translator.trans('core.admin.permissions.global_heading'),
|
||||||
|
render: item => {
|
||||||
|
if (item.setting) {
|
||||||
|
return item.setting();
|
||||||
|
} else if (item.permission) {
|
||||||
|
return PermissionDropdown.component({
|
||||||
|
permission: item.permission,
|
||||||
|
allowGuest: item.allowGuest
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeControlItems() {
|
||||||
|
return new ItemList();
|
||||||
|
}
|
||||||
|
}
|
41
js/admin/src/components/PermissionsPage.js
Normal file
41
js/admin/src/components/PermissionsPage.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
import GroupBadge from 'flarum/components/GroupBadge';
|
||||||
|
import EditGroupModal from 'flarum/components/EditGroupModal';
|
||||||
|
import Group from 'flarum/models/Group';
|
||||||
|
import icon from 'flarum/helpers/icon';
|
||||||
|
import PermissionGrid from 'flarum/components/PermissionGrid';
|
||||||
|
|
||||||
|
export default class PermissionsPage extends Page {
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<div className="PermissionsPage">
|
||||||
|
<div className="PermissionsPage-groups">
|
||||||
|
<div className="container">
|
||||||
|
{app.store.all('groups')
|
||||||
|
.filter(group => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||||
|
.map(group => (
|
||||||
|
<button className="Button Group" onclick={() => app.modal.show(new EditGroupModal({group}))}>
|
||||||
|
{GroupBadge.component({
|
||||||
|
group,
|
||||||
|
className: 'Group-icon',
|
||||||
|
label: null
|
||||||
|
})}
|
||||||
|
<span className="Group-name">{group.namePlural()}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
<button className="Button Group Group--add" onclick={() => app.modal.show(new EditGroupModal())}>
|
||||||
|
{icon('plus', {className: 'Group-icon'})}
|
||||||
|
<span className="Group-name">{app.translator.trans('core.admin.permissions.new_group_button')}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="PermissionsPage-permissions">
|
||||||
|
<div className="container">
|
||||||
|
{PermissionGrid.component()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
54
js/admin/src/components/SessionDropdown.js
Normal file
54
js/admin/src/components/SessionDropdown.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import avatar from 'flarum/helpers/avatar';
|
||||||
|
import username from 'flarum/helpers/username';
|
||||||
|
import Dropdown from 'flarum/components/Dropdown';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `SessionDropdown` component shows a button with the current user's
|
||||||
|
* avatar/name, with a dropdown of session controls.
|
||||||
|
*/
|
||||||
|
export default class SessionDropdown extends Dropdown {
|
||||||
|
static initProps(props) {
|
||||||
|
super.initProps(props);
|
||||||
|
|
||||||
|
props.className = 'SessionDropdown';
|
||||||
|
props.buttonClassName = 'Button Button--user Button--flat';
|
||||||
|
props.menuClassName = 'Dropdown-menu--right';
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
this.props.children = this.items().toArray();
|
||||||
|
|
||||||
|
return super.view();
|
||||||
|
}
|
||||||
|
|
||||||
|
getButtonContent() {
|
||||||
|
const user = app.session.user;
|
||||||
|
|
||||||
|
return [
|
||||||
|
avatar(user), ' ',
|
||||||
|
<span className="Button-label">{username(user)}</span>
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an item list for the contents of the dropdown menu.
|
||||||
|
*
|
||||||
|
* @return {ItemList}
|
||||||
|
*/
|
||||||
|
items() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('logOut',
|
||||||
|
Button.component({
|
||||||
|
icon: 'sign-out',
|
||||||
|
children: app.translator.trans('core.admin.header.log_out_button'),
|
||||||
|
onclick: app.session.logout.bind(app.session)
|
||||||
|
}),
|
||||||
|
-100
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
25
js/admin/src/components/SettingDropdown.js
Normal file
25
js/admin/src/components/SettingDropdown.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import SelectDropdown from 'flarum/components/SelectDropdown';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import saveSettings from 'flarum/utils/saveSettings';
|
||||||
|
|
||||||
|
export default class SettingDropdown extends SelectDropdown {
|
||||||
|
static initProps(props) {
|
||||||
|
super.initProps(props);
|
||||||
|
|
||||||
|
props.className = 'SettingDropdown';
|
||||||
|
props.buttonClassName = 'Button Button--text';
|
||||||
|
props.caretIcon = 'caret-down';
|
||||||
|
props.defaultLabel = 'Custom';
|
||||||
|
|
||||||
|
props.children = props.options.map(({value, label}) => {
|
||||||
|
const active = app.data.settings[props.key] === value;
|
||||||
|
|
||||||
|
return Button.component({
|
||||||
|
children: label,
|
||||||
|
icon: active ? 'check' : true,
|
||||||
|
onclick: saveSettings.bind(this, {[props.key]: value}),
|
||||||
|
active
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +1,9 @@
|
|||||||
import app from '../../admin/app';
|
import Modal from 'flarum/components/Modal';
|
||||||
import Modal from '../../common/components/Modal';
|
import Button from 'flarum/components/Button';
|
||||||
import Button from '../../common/components/Button';
|
import saveSettings from 'flarum/utils/saveSettings';
|
||||||
import Stream from '../../common/utils/Stream';
|
|
||||||
import saveSettings from '../utils/saveSettings';
|
|
||||||
|
|
||||||
export default class SettingsModal extends Modal {
|
export default class SettingsModal extends Modal {
|
||||||
oninit(vnode) {
|
init() {
|
||||||
super.oninit(vnode);
|
|
||||||
|
|
||||||
this.settings = {};
|
this.settings = {};
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
@@ -22,7 +18,9 @@ export default class SettingsModal extends Modal {
|
|||||||
<div className="Form">
|
<div className="Form">
|
||||||
{this.form()}
|
{this.form()}
|
||||||
|
|
||||||
<div className="Form-group">{this.submitButton()}</div>
|
<div className="Form-group">
|
||||||
|
{this.submitButton()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -30,14 +28,18 @@ export default class SettingsModal extends Modal {
|
|||||||
|
|
||||||
submitButton() {
|
submitButton() {
|
||||||
return (
|
return (
|
||||||
<Button type="submit" className="Button Button--primary" loading={this.loading} disabled={!this.changed()}>
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="Button Button--primary"
|
||||||
|
loading={this.loading}
|
||||||
|
disabled={!this.changed()}>
|
||||||
{app.translator.trans('core.admin.settings.submit_button')}
|
{app.translator.trans('core.admin.settings.submit_button')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setting(key, fallback = '') {
|
setting(key, fallback = '') {
|
||||||
this.settings[key] = this.settings[key] || Stream(app.data.settings[key] || fallback);
|
this.settings[key] = this.settings[key] || m.prop(app.data.settings[key] || fallback);
|
||||||
|
|
||||||
return this.settings[key];
|
return this.settings[key];
|
||||||
}
|
}
|
||||||
@@ -45,7 +47,7 @@ export default class SettingsModal extends Modal {
|
|||||||
dirty() {
|
dirty() {
|
||||||
const dirty = {};
|
const dirty = {};
|
||||||
|
|
||||||
Object.keys(this.settings).forEach((key) => {
|
Object.keys(this.settings).forEach(key => {
|
||||||
const value = this.settings[key]();
|
const value = this.settings[key]();
|
||||||
|
|
||||||
if (value !== app.data.settings[key]) {
|
if (value !== app.data.settings[key]) {
|
||||||
@@ -65,7 +67,10 @@ export default class SettingsModal extends Modal {
|
|||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
saveSettings(this.dirty()).then(this.onsaved.bind(this), this.loaded.bind(this));
|
saveSettings(this.dirty()).then(
|
||||||
|
this.onsaved.bind(this),
|
||||||
|
this.loaded.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onsaved() {
|
onsaved() {
|
97
js/admin/src/components/UploadImageButton.js
Normal file
97
js/admin/src/components/UploadImageButton.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
|
||||||
|
export default class UploadImageButton extends Button {
|
||||||
|
init() {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
this.props.loading = this.loading;
|
||||||
|
this.props.className = (this.props.className || '') + ' Button';
|
||||||
|
|
||||||
|
if (app.data.settings[this.props.name + '_path']) {
|
||||||
|
this.props.onclick = this.remove.bind(this);
|
||||||
|
this.props.children = app.translator.trans('core.admin.upload_image.remove_button');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p><img src={app.forum.attribute(this.props.name+'Url')} alt=""/></p>
|
||||||
|
<p>{super.view()}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.props.onclick = this.upload.bind(this);
|
||||||
|
this.props.children = app.translator.trans('core.admin.upload_image.upload_button');
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.view();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt the user to upload an image.
|
||||||
|
*/
|
||||||
|
upload() {
|
||||||
|
if (this.loading) return;
|
||||||
|
|
||||||
|
const $input = $('<input type="file">');
|
||||||
|
|
||||||
|
$input.appendTo('body').hide().click().on('change', e => {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append(this.props.name, $(e.target)[0].files[0]);
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
m.redraw();
|
||||||
|
|
||||||
|
app.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: this.resourceUrl(),
|
||||||
|
serialize: raw => raw,
|
||||||
|
data
|
||||||
|
}).then(
|
||||||
|
this.success.bind(this),
|
||||||
|
this.failure.bind(this)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the logo.
|
||||||
|
*/
|
||||||
|
remove() {
|
||||||
|
this.loading = true;
|
||||||
|
m.redraw();
|
||||||
|
|
||||||
|
app.request({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: this.resourceUrl()
|
||||||
|
}).then(
|
||||||
|
this.success.bind(this),
|
||||||
|
this.failure.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceUrl() {
|
||||||
|
return app.forum.attribute('apiUrl') + '/' + this.props.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After a successful upload/removal, reload the page.
|
||||||
|
*
|
||||||
|
* @param {Object} response
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
success(response) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If upload/removal fails, stop loading.
|
||||||
|
*
|
||||||
|
* @param {Object} response
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
failure(response) {
|
||||||
|
this.loading = false;
|
||||||
|
m.redraw();
|
||||||
|
}
|
||||||
|
}
|
66
js/admin/src/initializers/boot.js
Normal file
66
js/admin/src/initializers/boot.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*global FastClick*/
|
||||||
|
|
||||||
|
import ScrollListener from 'flarum/utils/ScrollListener';
|
||||||
|
import Drawer from 'flarum/utils/Drawer';
|
||||||
|
import mapRoutes from 'flarum/utils/mapRoutes';
|
||||||
|
|
||||||
|
import Navigation from 'flarum/components/Navigation';
|
||||||
|
import HeaderPrimary from 'flarum/components/HeaderPrimary';
|
||||||
|
import HeaderSecondary from 'flarum/components/HeaderSecondary';
|
||||||
|
import AdminNav from 'flarum/components/AdminNav';
|
||||||
|
import ModalManager from 'flarum/components/ModalManager';
|
||||||
|
import AlertManager from 'flarum/components/AlertManager';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `boot` initializer boots up the admin app. It initializes some app
|
||||||
|
* globals, mounts components to the page, and begins routing.
|
||||||
|
*
|
||||||
|
* @param {ForumApp} app
|
||||||
|
*/
|
||||||
|
export default function boot(app) {
|
||||||
|
m.startComputation();
|
||||||
|
|
||||||
|
m.mount(document.getElementById('app-navigation'), Navigation.component({className: 'App-backControl', drawer: true}));
|
||||||
|
m.mount(document.getElementById('header-navigation'), Navigation.component());
|
||||||
|
m.mount(document.getElementById('header-primary'), HeaderPrimary.component());
|
||||||
|
m.mount(document.getElementById('header-secondary'), HeaderSecondary.component());
|
||||||
|
m.mount(document.getElementById('admin-navigation'), AdminNav.component());
|
||||||
|
|
||||||
|
app.drawer = new Drawer();
|
||||||
|
app.modal = m.mount(document.getElementById('modal'), ModalManager.component());
|
||||||
|
app.alerts = m.mount(document.getElementById('alerts'), AlertManager.component());
|
||||||
|
app.history = {
|
||||||
|
canGoBack: () => true,
|
||||||
|
getPrevious: () => {},
|
||||||
|
backUrl: () => app.forum.attribute('baseUrl'),
|
||||||
|
back: function() {
|
||||||
|
window.location = this.backUrl();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
m.route.mode = 'hash';
|
||||||
|
m.route(document.getElementById('content'), '/', mapRoutes(app.routes));
|
||||||
|
|
||||||
|
m.endComputation();
|
||||||
|
|
||||||
|
// Add a class to the body which indicates that the page has been scrolled
|
||||||
|
// down.
|
||||||
|
new ScrollListener(top => {
|
||||||
|
const $app = $('#app');
|
||||||
|
const offset = $app.offset().top;
|
||||||
|
|
||||||
|
$app
|
||||||
|
.toggleClass('affix', top >= offset)
|
||||||
|
.toggleClass('scrolled', top > offset);
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
app.booted = true;
|
||||||
|
|
||||||
|
// If an extension has just been enabled, then we will run its settings
|
||||||
|
// callback.
|
||||||
|
const enabled = localStorage.getItem('enabledExtension');
|
||||||
|
if (enabled && app.extensionSettings[enabled]) {
|
||||||
|
app.extensionSettings[enabled]();
|
||||||
|
localStorage.removeItem('enabledExtension');
|
||||||
|
}
|
||||||
|
}
|
22
js/admin/src/initializers/routes.js
Normal file
22
js/admin/src/initializers/routes.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import DashboardPage from 'flarum/components/DashboardPage';
|
||||||
|
import BasicsPage from 'flarum/components/BasicsPage';
|
||||||
|
import PermissionsPage from 'flarum/components/PermissionsPage';
|
||||||
|
import AppearancePage from 'flarum/components/AppearancePage';
|
||||||
|
import ExtensionsPage from 'flarum/components/ExtensionsPage';
|
||||||
|
import MailPage from 'flarum/components/MailPage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `routes` initializer defines the admin app's routes.
|
||||||
|
*
|
||||||
|
* @param {App} app
|
||||||
|
*/
|
||||||
|
export default function(app) {
|
||||||
|
app.routes = {
|
||||||
|
'dashboard': {path: '/', component: DashboardPage.component()},
|
||||||
|
'basics': {path: '/basics', component: BasicsPage.component()},
|
||||||
|
'permissions': {path: '/permissions', component: PermissionsPage.component()},
|
||||||
|
'appearance': {path: '/appearance', component: AppearancePage.component()},
|
||||||
|
'extensions': {path: '/extensions', component: ExtensionsPage.component()},
|
||||||
|
'mail': {path: '/mail', component: MailPage.component()}
|
||||||
|
};
|
||||||
|
}
|
14
js/admin/src/utils/saveSettings.js
Normal file
14
js/admin/src/utils/saveSettings.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export default function saveSettings(settings) {
|
||||||
|
const oldSettings = JSON.parse(JSON.stringify(app.data.settings));
|
||||||
|
|
||||||
|
Object.assign(app.data.settings, settings);
|
||||||
|
|
||||||
|
return app.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: app.forum.attribute('apiUrl') + '/settings',
|
||||||
|
data: settings
|
||||||
|
}).catch(error => {
|
||||||
|
app.data.settings = oldSettings;
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
17
js/bower.json
Normal file
17
js/bower.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "flarum",
|
||||||
|
"dependencies": {
|
||||||
|
"jquery": "~2.1.3",
|
||||||
|
"jquery.hotkeys": "jeresig/jquery.hotkeys#0.2.0",
|
||||||
|
"bootstrap": "~3.3.2",
|
||||||
|
"spin.js": "~2.0.1",
|
||||||
|
"moment": "~2.8.4",
|
||||||
|
"color-thief": "v2.0",
|
||||||
|
"mithril": "lhorie/mithril.js#v0.2.5",
|
||||||
|
"es6-micro-loader": "caridy/es6-micro-loader#v0.2.1",
|
||||||
|
"fastclick": "~1.0.6",
|
||||||
|
"autolink": "~1.0.0",
|
||||||
|
"m.attrs.bidi": "tobscure/m.attrs.bidi",
|
||||||
|
"punycode": "http://cdnjs.cloudflare.com/ajax/libs/punycode/1.4.1/punycode.js"
|
||||||
|
}
|
||||||
|
}
|
138
js/dist-typings/@types/global.d.ts
vendored
138
js/dist-typings/@types/global.d.ts
vendored
@@ -1,138 +0,0 @@
|
|||||||
declare type Writable<T> = { -readonly [P in keyof T]: T[P] };
|
|
||||||
declare type DeepWritable<T> = { -readonly [P in keyof T]: DeepWritable<T[P]> };
|
|
||||||
|
|
||||||
declare type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UTILITY TYPES
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type that returns an array of all keys of a provided object that are of
|
|
||||||
* of the provided type, or a subtype of the type.
|
|
||||||
*/
|
|
||||||
declare type KeysOfType<Type extends object, Match> = {
|
|
||||||
[Key in keyof Type]-?: Type[Key] extends Match ? Key : never;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type that matches one of the keys of an object that is of the provided
|
|
||||||
* type, or a subtype of it.
|
|
||||||
*/
|
|
||||||
declare type KeyOfType<Type extends object, Match> = KeysOfType<Type, Match>[keyof Type];
|
|
||||||
|
|
||||||
type Component<A> = import('mithril').Component<A>;
|
|
||||||
|
|
||||||
declare type ComponentClass<Attrs = Record<string, unknown>, C extends Component<Attrs> = Component<Attrs>> = {
|
|
||||||
new (...args: any[]): Component<Attrs>;
|
|
||||||
prototype: C;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unfortunately, TypeScript only supports strings and classes for JSX tags.
|
|
||||||
* Therefore, our type definition should only allow for those two types.
|
|
||||||
*
|
|
||||||
* @see https://github.com/microsoft/TypeScript/issues/14789#issuecomment-412247771
|
|
||||||
*/
|
|
||||||
declare type VnodeElementTag<Attrs = Record<string, unknown>, C extends Component<Attrs> = Component<Attrs>> = string | ComponentClass<Attrs, C>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Please import `app` from a namespace instead of using it as a global variable.
|
|
||||||
*
|
|
||||||
* @example App in forum JS
|
|
||||||
* ```
|
|
||||||
* import app from 'flarum/forum/app';
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @example App in admin JS
|
|
||||||
* ```
|
|
||||||
* import app from 'flarum/admin/app';
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @example App in common JS
|
|
||||||
* ```
|
|
||||||
* import app from 'flarum/common/app';
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
declare const app: never;
|
|
||||||
|
|
||||||
declare const m: import('mithril').Static;
|
|
||||||
declare const dayjs: typeof import('dayjs');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* From https://github.com/lokesh/color-thief/issues/188
|
|
||||||
*/
|
|
||||||
declare module 'color-thief-browser' {
|
|
||||||
type Color = [number, number, number];
|
|
||||||
export default class ColorThief {
|
|
||||||
getColor: (img: HTMLImageElement | null) => Color;
|
|
||||||
getPalette: (img: HTMLImageElement | null) => Color[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ESModule = { __esModule: true; [key: string]: unknown };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The global `flarum` variable.
|
|
||||||
*
|
|
||||||
* Contains the compiled ES Modules for all Flarum extensions and core.
|
|
||||||
*
|
|
||||||
* @example <caption>Check if `flarum-tags` is present</captions>
|
|
||||||
* if ('flarum-tags' in flarum.extensions) {
|
|
||||||
* // Tags is installed and enabled!
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
interface FlarumObject {
|
|
||||||
/**
|
|
||||||
* Contains the compiled ES Module for Flarum's core.
|
|
||||||
*
|
|
||||||
* You shouldn't need to access this directly for any reason.
|
|
||||||
*/
|
|
||||||
core: Readonly<ESModule>;
|
|
||||||
/**
|
|
||||||
* Contains the compiled ES Modules for all Flarum extensions.
|
|
||||||
*
|
|
||||||
* @example <caption>Check if `flarum-tags` is present</captions>
|
|
||||||
* if ('flarum-tags' in flarum.extensions) {
|
|
||||||
* // Tags is installed and enabled!
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
extensions: Readonly<Record<string, ESModule>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const flarum: FlarumObject;
|
|
||||||
|
|
||||||
// Extend JQuery with our custom functions, defined with $.fn
|
|
||||||
interface JQuery {
|
|
||||||
/**
|
|
||||||
* Flarum's tooltip JQuery plugin.
|
|
||||||
*
|
|
||||||
* Do not use this directly. Instead use the `<Tooltip>` component that
|
|
||||||
* is exported from `flarum/common/components/Tooltip`.
|
|
||||||
*
|
|
||||||
* This will be removed in a future version of Flarum.
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
tooltip: import('./tooltips/index').TooltipJQueryFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For more info, see: https://www.typescriptlang.org/docs/handbook/jsx.html#attribute-type-checking
|
|
||||||
*
|
|
||||||
* In a nutshell, we need to add `ElementAttributesProperty` to tell Typescript
|
|
||||||
* what property on component classes to look at for attribute typings. For our
|
|
||||||
* Component class, this would be `attrs` (e.g. `this.attrs...`)
|
|
||||||
*/
|
|
||||||
interface JSX {
|
|
||||||
ElementAttributesProperty: {
|
|
||||||
attrs: Record<string, unknown>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Event {
|
|
||||||
/**
|
|
||||||
* Whether this event should trigger a Mithril redraw.
|
|
||||||
*/
|
|
||||||
redraw: boolean;
|
|
||||||
}
|
|
517
js/dist-typings/@types/mithril/index.d.ts
vendored
517
js/dist-typings/@types/mithril/index.d.ts
vendored
@@ -1,517 +0,0 @@
|
|||||||
// Type definitions for Mithril 2.0
|
|
||||||
// Project: https://mithril.js.org/, https://github.com/mithriljs/mithril.js
|
|
||||||
// Definitions by: Mike Linkovich <https://github.com/spacejack>, András Parditka <https://github.com/andraaspar>, Isiah Meadows <https://github.com/isiahmeadows>
|
|
||||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
||||||
// TypeScript Version: 3.2
|
|
||||||
/** Renders a vnode structure into a DOM element. */
|
|
||||||
declare function render(el: Element, vnodes: Mithril.Children): void;
|
|
||||||
|
|
||||||
/** Mounts a component to a DOM element, enabling it to autoredraw on user events. */
|
|
||||||
declare function mount(element: Element, component: Mithril.ComponentTypes<any, any>): void;
|
|
||||||
/** Unmounts a component from a DOM element. */
|
|
||||||
declare function mount(element: Element, component: null): void; // tslint:disable-line unified-signatures
|
|
||||||
|
|
||||||
/** Makes an XHR request and returns a promise. */
|
|
||||||
declare function request<T>(options: Mithril.RequestOptions<T> & { url: string }): Promise<T>;
|
|
||||||
/** Makes an XHR request and returns a promise. */
|
|
||||||
declare function request<T>(url: string, options?: Mithril.RequestOptions<T>): Promise<T>;
|
|
||||||
|
|
||||||
/** Makes a JSON-P request and returns a promise. */
|
|
||||||
declare function jsonp<T>(options: Mithril.JsonpOptions & { url: string }): Promise<T>; // tslint:disable-line:no-unnecessary-generics
|
|
||||||
/** Makes a JSON-P request and returns a promise. */
|
|
||||||
declare function jsonp<T>(url: string, options?: Mithril.JsonpOptions): Promise<T>; // tslint:disable-line:no-unnecessary-generics
|
|
||||||
|
|
||||||
declare namespace Mithril {
|
|
||||||
interface CommonAttributes<Attrs, State> {
|
|
||||||
/** The oninit hook is called before a vnode is touched by the virtual DOM engine. */
|
|
||||||
oninit?(this: State, vnode: Vnode<Attrs, State>): any;
|
|
||||||
/** The oncreate hook is called after a DOM element is created and attached to the document. */
|
|
||||||
oncreate?(this: State, vnode: VnodeDOM<Attrs, State>): any;
|
|
||||||
/** The onbeforeremove hook is called before a DOM element is detached from the document. If a Promise is returned, Mithril only detaches the DOM element after the promise completes. */
|
|
||||||
onbeforeremove?(this: State, vnode: VnodeDOM<Attrs, State>): Promise<any> | void;
|
|
||||||
/** The onremove hook is called before a DOM element is removed from the document. */
|
|
||||||
onremove?(this: State, vnode: VnodeDOM<Attrs, State>): any;
|
|
||||||
/** The onbeforeupdate hook is called before a vnode is diffed in a update. */
|
|
||||||
onbeforeupdate?(this: State, vnode: Vnode<Attrs, State>, old: VnodeDOM<Attrs, State>): boolean | void;
|
|
||||||
/** The onupdate hook is called after a DOM element is updated, while attached to the document. */
|
|
||||||
onupdate?(this: State, vnode: VnodeDOM<Attrs, State>): any;
|
|
||||||
/** A key to optionally associate with this element. */
|
|
||||||
key?: string | number | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Hyperscript {
|
|
||||||
/** Creates a virtual element (Vnode). */
|
|
||||||
(selector: string, ...children: Children[]): Vnode<any, any>;
|
|
||||||
/** Creates a virtual element (Vnode). */
|
|
||||||
(selector: string, attributes: Attributes, ...children: Children[]): Vnode<any, any>;
|
|
||||||
/** Creates a virtual element (Vnode). */
|
|
||||||
<Attrs, State>(component: ComponentTypes<Attrs, State>, ...args: Children[]): Vnode<Attrs, State>;
|
|
||||||
/** Creates a virtual element (Vnode). */
|
|
||||||
<Attrs, State>(
|
|
||||||
component: ComponentTypes<Attrs, State>,
|
|
||||||
attributes: Attrs & CommonAttributes<Attrs, State>,
|
|
||||||
...args: Children[]
|
|
||||||
): Vnode<Attrs, State>;
|
|
||||||
/** Creates a fragment virtual element (Vnode). */
|
|
||||||
fragment(attrs: CommonAttributes<any, any> & { [key: string]: any }, children: ChildArrayOrPrimitive): Vnode<any, any>;
|
|
||||||
/** Turns an HTML string into a virtual element (Vnode). Do not use trust on unsanitized user input. */
|
|
||||||
trust(html: string): Vnode<any, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RouteResolver<Attrs = {}, State = {}> {
|
|
||||||
/** The onmatch hook is called when the router needs to find a component to render. */
|
|
||||||
onmatch?(
|
|
||||||
this: this,
|
|
||||||
args: Attrs,
|
|
||||||
requestedPath: string,
|
|
||||||
route: string,
|
|
||||||
): ComponentTypes<any, any> | Promise<any> | void;
|
|
||||||
/** The render method is called on every redraw for a matching route. */
|
|
||||||
render?(this: this, vnode: Vnode<Attrs, State>): Children;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This represents a key-value mapping linking routes to components. */
|
|
||||||
interface RouteDefs {
|
|
||||||
/** The key represents the route. The value represents the corresponding component. */
|
|
||||||
[url: string]: ComponentTypes<any, any> | RouteResolver<any, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RouteOptions {
|
|
||||||
/** Routing parameters. If path has routing parameter slots, the properties of this object are interpolated into the path string. */
|
|
||||||
replace?: boolean | undefined;
|
|
||||||
/** The state object to pass to the underlying history.pushState / history.replaceState call. */
|
|
||||||
state?: any;
|
|
||||||
/** The title string to pass to the underlying history.pushState / history.replaceState call. */
|
|
||||||
title?: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RouteLinkAttrs extends Attributes {
|
|
||||||
href: string;
|
|
||||||
selector?: string | ComponentTypes<any> | undefined;
|
|
||||||
options?: RouteOptions | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Route {
|
|
||||||
/** Creates application routes and mounts Components and/or RouteResolvers to a DOM element. */
|
|
||||||
(element: Element, defaultRoute: string, routes: RouteDefs): void;
|
|
||||||
/** Returns the last fully resolved routing path, without the prefix. */
|
|
||||||
get(): string;
|
|
||||||
/** Redirects to a matching route or to the default route if no matching routes can be found. */
|
|
||||||
set(route: string, data?: any, options?: RouteOptions): void;
|
|
||||||
/** Defines a router prefix which is a fragment of the URL that dictates the underlying strategy used by the router. */
|
|
||||||
prefix: string;
|
|
||||||
/** This Component renders a link <a href> that will use the current routing strategy */
|
|
||||||
Link: Component<RouteLinkAttrs>;
|
|
||||||
/** Returns the named parameter value from the current route. */
|
|
||||||
param(name: string): string;
|
|
||||||
/** Gets all route parameters. */
|
|
||||||
param(): any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RequestOptions<T> {
|
|
||||||
/** The HTTP method to use. */
|
|
||||||
method?: string | undefined;
|
|
||||||
/** The data to be interpolated into the URL and serialized into the querystring. */
|
|
||||||
params?: { [key: string]: any } | undefined;
|
|
||||||
/** The data to be serialized into the request body. */
|
|
||||||
body?: (XMLHttpRequest['send'] extends (x: infer R) => any ? R : never) | (object & { [id: string]: any }) | undefined;
|
|
||||||
/** Whether the request should be asynchronous. Defaults to true. */
|
|
||||||
async?: boolean | undefined;
|
|
||||||
/** A username for HTTP authorization. */
|
|
||||||
user?: string | undefined;
|
|
||||||
/** A password for HTTP authorization. */
|
|
||||||
password?: string | undefined;
|
|
||||||
/** Whether to send cookies to 3rd party domains. */
|
|
||||||
withCredentials?: boolean | undefined;
|
|
||||||
/** Exposes the underlying XMLHttpRequest object for low-level configuration. */
|
|
||||||
config?(xhr: XMLHttpRequest, options: this): XMLHttpRequest | void;
|
|
||||||
/** Headers to append to the request before sending it. */
|
|
||||||
headers?: { [key: string]: string } | undefined;
|
|
||||||
/** A constructor to be applied to each object in the response. */
|
|
||||||
type?: new (o: any) => any;
|
|
||||||
/** A serialization method to be applied to data. Defaults to JSON.stringify, or if options.data is an instance of FormData, defaults to the identity function. */
|
|
||||||
serialize?(data: any): any;
|
|
||||||
/** A deserialization method to be applied to the response. Defaults to a small wrapper around JSON.parse that returns null for empty responses. */
|
|
||||||
deserialize?(data: string): T;
|
|
||||||
/** A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns xhr.responseText */
|
|
||||||
extract?(xhr: XMLHttpRequest, options: this): T;
|
|
||||||
/**
|
|
||||||
* Force the use of the HTTP body section for data in GET requests when set to true,
|
|
||||||
* or the use of querystring for other HTTP methods when set to false.
|
|
||||||
* Defaults to false for GET requests and true for other methods.
|
|
||||||
*/
|
|
||||||
useBody?: boolean | undefined;
|
|
||||||
/** If false, redraws mounted components upon completion of the request. If true, it does not. */
|
|
||||||
background?: boolean | undefined;
|
|
||||||
/** Milliseconds a request can take before automatically being terminated. */
|
|
||||||
timeout?: number | undefined;
|
|
||||||
/** The expected type of the response, as a legal value of XMLHttpRequest.responseType. */
|
|
||||||
responseType?: '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface JsonpOptions {
|
|
||||||
/** The data to be interpolated into the URL and serialized into the querystring. */
|
|
||||||
params?: { [id: string]: any } | undefined;
|
|
||||||
/** The data to be serialized into the request body. */
|
|
||||||
body?: any;
|
|
||||||
/** A constructor to be applied to each object in the response. */
|
|
||||||
type?: new (o: any) => any;
|
|
||||||
/** The name of the function that will be called as the callback. */
|
|
||||||
callbackName?: string | undefined;
|
|
||||||
/** The name of the querystring parameter name that specifies the callback name. */
|
|
||||||
callbackKey?: string | undefined;
|
|
||||||
/** If false, redraws mounted components upon completion of the request. If true, it does not. */
|
|
||||||
background?: boolean | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Redraw {
|
|
||||||
/** Manually triggers an asynchronous redraw of mounted components. */
|
|
||||||
(): void;
|
|
||||||
/** Manually triggers a synchronous redraw of mounted components. */
|
|
||||||
sync(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Params = object & ParamsRec;
|
|
||||||
|
|
||||||
interface ParamsRec {
|
|
||||||
// Ideally, it'd be this:
|
|
||||||
// `[key: string | number]: Params | !symbol & !object`
|
|
||||||
[key: string]: string | number | boolean | null | undefined | Params;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Static extends Hyperscript {
|
|
||||||
route: Route;
|
|
||||||
mount: typeof mount;
|
|
||||||
render: typeof render;
|
|
||||||
redraw: Redraw;
|
|
||||||
request: typeof request;
|
|
||||||
jsonp: typeof jsonp;
|
|
||||||
/** Returns an object with key/value pairs parsed from a string of the form: ?a=1&b=2 */
|
|
||||||
parseQueryString(queryString: string): Params;
|
|
||||||
/** Turns the key/value pairs of an object into a string of the form: a=1&b=2 */
|
|
||||||
buildQueryString(values: Params): string;
|
|
||||||
/** Parse path name */
|
|
||||||
parsePathname(url: string): { path: string; params: Params };
|
|
||||||
/** Build path name */
|
|
||||||
buildPathname(template: string, params?: Params): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vnode children types
|
|
||||||
type Child = Vnode<any, any> | string | number | boolean | null | undefined;
|
|
||||||
interface ChildArray extends Array<Children> {}
|
|
||||||
type Children = Child | ChildArray;
|
|
||||||
type ChildArrayOrPrimitive = ChildArray | string | number | boolean;
|
|
||||||
|
|
||||||
/** Virtual DOM nodes, or vnodes, are Javascript objects that represent an element (or parts of the DOM). */
|
|
||||||
interface Vnode<Attrs = {}, State = {}> {
|
|
||||||
/** The nodeName of a DOM element. It may also be the string [ if a vnode is a fragment, # if it's a text vnode, or < if it's a trusted HTML vnode. Additionally, it may be a component. */
|
|
||||||
tag: string | ComponentTypes<Attrs, State>;
|
|
||||||
/** A hashmap of DOM attributes, events, properties and lifecycle methods. */
|
|
||||||
attrs: Attrs;
|
|
||||||
/** An object that is persisted between redraws. In component vnodes, state is a shallow clone of the component object. */
|
|
||||||
state: State;
|
|
||||||
/** The value used to map a DOM element to its respective item in an array of data. */
|
|
||||||
key?: string | number | undefined;
|
|
||||||
/** In most vnode types, the children property is an array of vnodes. For text and trusted HTML vnodes, The children property is either a string, a number or a boolean. */
|
|
||||||
children?: ChildArrayOrPrimitive | undefined;
|
|
||||||
/**
|
|
||||||
* This is used instead of children if a vnode contains a text node as its only child.
|
|
||||||
* This is done for performance reasons.
|
|
||||||
* Component vnodes never use the text property even if they have a text node as their only child.
|
|
||||||
*/
|
|
||||||
text?: string | number | boolean | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In some lifecycle methods, Vnode will have a dom property
|
|
||||||
// and possibly a domSize property.
|
|
||||||
interface VnodeDOM<Attrs = {}, State = {}> extends Vnode<Attrs, State> {
|
|
||||||
/** Points to the element that corresponds to the vnode. */
|
|
||||||
dom: Element;
|
|
||||||
/** This defines the number of DOM elements that the vnode represents (starting from the element referenced by the dom property). */
|
|
||||||
domSize?: number | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
type _NoLifecycle<T> = Omit<T, keyof Component>;
|
|
||||||
|
|
||||||
interface CVnode<A = {}> extends Vnode<A, ClassComponent<A>> {}
|
|
||||||
|
|
||||||
interface CVnodeDOM<A = {}> extends VnodeDOM<A, ClassComponent<A>> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Components are a mechanism to encapsulate parts of a view to make code easier to organize and/or reuse.
|
|
||||||
* Any Javascript object that has a view method can be used as a Mithril component.
|
|
||||||
* Components can be consumed via the m() utility.
|
|
||||||
*/
|
|
||||||
interface Component<Attrs = {}, State = {}> {
|
|
||||||
/** The oninit hook is called before a vnode is touched by the virtual DOM engine. */
|
|
||||||
oninit?(this: _NoLifecycle<this & State>, vnode: Vnode<Attrs, _NoLifecycle<this & State>>): any;
|
|
||||||
/** The oncreate hook is called after a DOM element is created and attached to the document. */
|
|
||||||
oncreate?(this: _NoLifecycle<this & State>, vnode: VnodeDOM<Attrs, _NoLifecycle<this & State>>): any;
|
|
||||||
/** The onbeforeremove hook is called before a DOM element is detached from the document. If a Promise is returned, Mithril only detaches the DOM element after the promise completes. */
|
|
||||||
onbeforeremove?(this: _NoLifecycle<this & State>, vnode: VnodeDOM<Attrs, _NoLifecycle<this & State>>): Promise<any> | void;
|
|
||||||
/** The onremove hook is called before a DOM element is removed from the document. */
|
|
||||||
onremove?(this: _NoLifecycle<this & State>, vnode: VnodeDOM<Attrs, _NoLifecycle<this & State>>): any;
|
|
||||||
/** The onbeforeupdate hook is called before a vnode is diffed in a update. */
|
|
||||||
onbeforeupdate?(this: _NoLifecycle<this & State>, vnode: Vnode<Attrs, _NoLifecycle<this & State>>, old: VnodeDOM<Attrs, _NoLifecycle<this & State>>): boolean | void;
|
|
||||||
/** The onupdate hook is called after a DOM element is updated, while attached to the document. */
|
|
||||||
onupdate?(this: _NoLifecycle<this & State>, vnode: VnodeDOM<Attrs, _NoLifecycle<this & State>>): any;
|
|
||||||
/** Creates a view out of virtual elements. */
|
|
||||||
view(this: _NoLifecycle<this & State>, vnode: Vnode<Attrs, _NoLifecycle<this & State>>): Children | null | void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Components are a mechanism to encapsulate parts of a view to make code easier to organize and/or reuse.
|
|
||||||
* Any class that implements a view method can be used as a Mithril component.
|
|
||||||
* Components can be consumed via the m() utility.
|
|
||||||
*/
|
|
||||||
interface ClassComponent<A = {}> {
|
|
||||||
/** The oninit hook is called before a vnode is touched by the virtual DOM engine. */
|
|
||||||
oninit?(vnode: Vnode<A, this>): any;
|
|
||||||
/** The oncreate hook is called after a DOM element is created and attached to the document. */
|
|
||||||
oncreate?(vnode: VnodeDOM<A, this>): any;
|
|
||||||
/** The onbeforeremove hook is called before a DOM element is detached from the document. If a Promise is returned, Mithril only detaches the DOM element after the promise completes. */
|
|
||||||
onbeforeremove?(vnode: VnodeDOM<A, this>): Promise<any> | void;
|
|
||||||
/** The onremove hook is called before a DOM element is removed from the document. */
|
|
||||||
onremove?(vnode: VnodeDOM<A, this>): any;
|
|
||||||
/** The onbeforeupdate hook is called before a vnode is diffed in a update. */
|
|
||||||
onbeforeupdate?(vnode: Vnode<A, this>, old: VnodeDOM<A, this>): boolean | void;
|
|
||||||
/** The onupdate hook is called after a DOM element is updated, while attached to the document. */
|
|
||||||
onupdate?(vnode: VnodeDOM<A, this>): any;
|
|
||||||
/** Creates a view out of virtual elements. */
|
|
||||||
view(vnode: Vnode<A, this>): Children | null | void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Components are a mechanism to encapsulate parts of a view to make code easier to organize and/or reuse.
|
|
||||||
* Any function that returns an object with a view method can be used as a Mithril component.
|
|
||||||
* Components can be consumed via the m() utility.
|
|
||||||
*/
|
|
||||||
type FactoryComponent<A = {}> = (vnode: Vnode<A>) => Component<A>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Components are a mechanism to encapsulate parts of a view to make code easier to organize and/or reuse.
|
|
||||||
* Any function that returns an object with a view method can be used as a Mithril component.
|
|
||||||
* Components can be consumed via the m() utility.
|
|
||||||
*/
|
|
||||||
type ClosureComponent<A = {}> = FactoryComponent<A>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Components are a mechanism to encapsulate parts of a view to make code easier to organize and/or reuse.
|
|
||||||
* Any Javascript object that has a view method is a Mithril component. Components can be consumed via the m() utility.
|
|
||||||
*/
|
|
||||||
type Comp<Attrs = {}, State = {}> = _NoLifecycle<State> & Component<Attrs, _NoLifecycle<State>>;
|
|
||||||
|
|
||||||
/** Components are a mechanism to encapsulate parts of a view to make code easier to organize and/or reuse. Components can be consumed via the m() utility. */
|
|
||||||
type ComponentTypes<A = {}, S = {}> =
|
|
||||||
| Component<A, S>
|
|
||||||
| { new (vnode: CVnode<A>): ClassComponent<A> }
|
|
||||||
| FactoryComponent<A>;
|
|
||||||
|
|
||||||
/** This represents the attributes available for configuring virtual elements, beyond the applicable DOM attributes. */
|
|
||||||
interface Attributes extends CommonAttributes<any, any> {
|
|
||||||
/** The class name(s) for this virtual element, as a space-separated list. */
|
|
||||||
className?: string | undefined;
|
|
||||||
/** The class name(s) for this virtual element, as a space-separated list. */
|
|
||||||
class?: string | undefined;
|
|
||||||
/** Any other virtual element properties, including attributes and event handlers. */
|
|
||||||
[property: string]: any;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
namespace JSX {
|
|
||||||
// tslint:disable-next-line:no-empty-interface
|
|
||||||
interface Element extends Mithril.Vnode {}
|
|
||||||
|
|
||||||
// tslint:disable-next-line:no-empty-interface
|
|
||||||
interface IntrinsicAttributes extends Mithril.Attributes {}
|
|
||||||
// tslint:disable-next-line:no-empty-interface
|
|
||||||
interface IntrinsicClassAttributes extends Mithril.Attributes {}
|
|
||||||
|
|
||||||
interface IntrinsicElements {
|
|
||||||
// HTML
|
|
||||||
a: Mithril.Attributes;
|
|
||||||
abbr: Mithril.Attributes;
|
|
||||||
address: Mithril.Attributes;
|
|
||||||
area: Mithril.Attributes;
|
|
||||||
article: Mithril.Attributes;
|
|
||||||
aside: Mithril.Attributes;
|
|
||||||
audio: Mithril.Attributes;
|
|
||||||
b: Mithril.Attributes;
|
|
||||||
base: Mithril.Attributes;
|
|
||||||
bdi: Mithril.Attributes;
|
|
||||||
bdo: Mithril.Attributes;
|
|
||||||
big: Mithril.Attributes;
|
|
||||||
blockquote: Mithril.Attributes;
|
|
||||||
body: Mithril.Attributes;
|
|
||||||
br: Mithril.Attributes;
|
|
||||||
button: Mithril.Attributes;
|
|
||||||
canvas: Mithril.Attributes;
|
|
||||||
caption: Mithril.Attributes;
|
|
||||||
cite: Mithril.Attributes;
|
|
||||||
code: Mithril.Attributes;
|
|
||||||
col: Mithril.Attributes;
|
|
||||||
colgroup: Mithril.Attributes;
|
|
||||||
data: Mithril.Attributes;
|
|
||||||
datalist: Mithril.Attributes;
|
|
||||||
dd: Mithril.Attributes;
|
|
||||||
del: Mithril.Attributes;
|
|
||||||
details: Mithril.Attributes;
|
|
||||||
dfn: Mithril.Attributes;
|
|
||||||
dialog: Mithril.Attributes;
|
|
||||||
div: Mithril.Attributes;
|
|
||||||
dl: Mithril.Attributes;
|
|
||||||
dt: Mithril.Attributes;
|
|
||||||
em: Mithril.Attributes;
|
|
||||||
embed: Mithril.Attributes;
|
|
||||||
fieldset: Mithril.Attributes;
|
|
||||||
figcaption: Mithril.Attributes;
|
|
||||||
figure: Mithril.Attributes;
|
|
||||||
footer: Mithril.Attributes;
|
|
||||||
form: Mithril.Attributes;
|
|
||||||
h1: Mithril.Attributes;
|
|
||||||
h2: Mithril.Attributes;
|
|
||||||
h3: Mithril.Attributes;
|
|
||||||
h4: Mithril.Attributes;
|
|
||||||
h5: Mithril.Attributes;
|
|
||||||
h6: Mithril.Attributes;
|
|
||||||
head: Mithril.Attributes;
|
|
||||||
header: Mithril.Attributes;
|
|
||||||
hgroup: Mithril.Attributes;
|
|
||||||
hr: Mithril.Attributes;
|
|
||||||
html: Mithril.Attributes;
|
|
||||||
i: Mithril.Attributes;
|
|
||||||
iframe: Mithril.Attributes;
|
|
||||||
img: Mithril.Attributes;
|
|
||||||
input: Mithril.Attributes;
|
|
||||||
ins: Mithril.Attributes;
|
|
||||||
kbd: Mithril.Attributes;
|
|
||||||
keygen: Mithril.Attributes;
|
|
||||||
label: Mithril.Attributes;
|
|
||||||
legend: Mithril.Attributes;
|
|
||||||
li: Mithril.Attributes;
|
|
||||||
link: Mithril.Attributes;
|
|
||||||
main: Mithril.Attributes;
|
|
||||||
map: Mithril.Attributes;
|
|
||||||
mark: Mithril.Attributes;
|
|
||||||
menu: Mithril.Attributes;
|
|
||||||
menuitem: Mithril.Attributes;
|
|
||||||
meta: Mithril.Attributes;
|
|
||||||
meter: Mithril.Attributes;
|
|
||||||
nav: Mithril.Attributes;
|
|
||||||
noindex: Mithril.Attributes;
|
|
||||||
noscript: Mithril.Attributes;
|
|
||||||
object: Mithril.Attributes;
|
|
||||||
ol: Mithril.Attributes;
|
|
||||||
optgroup: Mithril.Attributes;
|
|
||||||
option: Mithril.Attributes;
|
|
||||||
output: Mithril.Attributes;
|
|
||||||
p: Mithril.Attributes;
|
|
||||||
param: Mithril.Attributes;
|
|
||||||
picture: Mithril.Attributes;
|
|
||||||
pre: Mithril.Attributes;
|
|
||||||
progress: Mithril.Attributes;
|
|
||||||
q: Mithril.Attributes;
|
|
||||||
rp: Mithril.Attributes;
|
|
||||||
rt: Mithril.Attributes;
|
|
||||||
ruby: Mithril.Attributes;
|
|
||||||
s: Mithril.Attributes;
|
|
||||||
samp: Mithril.Attributes;
|
|
||||||
script: Mithril.Attributes;
|
|
||||||
section: Mithril.Attributes;
|
|
||||||
select: Mithril.Attributes;
|
|
||||||
small: Mithril.Attributes;
|
|
||||||
source: Mithril.Attributes;
|
|
||||||
span: Mithril.Attributes;
|
|
||||||
strong: Mithril.Attributes;
|
|
||||||
style: Mithril.Attributes;
|
|
||||||
sub: Mithril.Attributes;
|
|
||||||
summary: Mithril.Attributes;
|
|
||||||
sup: Mithril.Attributes;
|
|
||||||
table: Mithril.Attributes;
|
|
||||||
template: Mithril.Attributes;
|
|
||||||
tbody: Mithril.Attributes;
|
|
||||||
td: Mithril.Attributes;
|
|
||||||
textarea: Mithril.Attributes;
|
|
||||||
tfoot: Mithril.Attributes;
|
|
||||||
th: Mithril.Attributes;
|
|
||||||
thead: Mithril.Attributes;
|
|
||||||
time: Mithril.Attributes;
|
|
||||||
title: Mithril.Attributes;
|
|
||||||
tr: Mithril.Attributes;
|
|
||||||
track: Mithril.Attributes;
|
|
||||||
u: Mithril.Attributes;
|
|
||||||
ul: Mithril.Attributes;
|
|
||||||
var: Mithril.Attributes;
|
|
||||||
video: Mithril.Attributes;
|
|
||||||
wbr: Mithril.Attributes;
|
|
||||||
webview: Mithril.Attributes;
|
|
||||||
|
|
||||||
// SVG
|
|
||||||
svg: Mithril.Attributes;
|
|
||||||
animate: Mithril.Attributes;
|
|
||||||
animateMotion: Mithril.Attributes;
|
|
||||||
animateTransform: Mithril.Attributes;
|
|
||||||
circle: Mithril.Attributes;
|
|
||||||
clipPath: Mithril.Attributes;
|
|
||||||
defs: Mithril.Attributes;
|
|
||||||
desc: Mithril.Attributes;
|
|
||||||
ellipse: Mithril.Attributes;
|
|
||||||
feBlend: Mithril.Attributes;
|
|
||||||
feColorMatrix: Mithril.Attributes;
|
|
||||||
feComponentTransfer: Mithril.Attributes;
|
|
||||||
feComposite: Mithril.Attributes;
|
|
||||||
feConvolveMatrix: Mithril.Attributes;
|
|
||||||
feDiffuseLighting: Mithril.Attributes;
|
|
||||||
feDisplacementMap: Mithril.Attributes;
|
|
||||||
feDistantLight: Mithril.Attributes;
|
|
||||||
feDropShadow: Mithril.Attributes;
|
|
||||||
feFlood: Mithril.Attributes;
|
|
||||||
feFuncA: Mithril.Attributes;
|
|
||||||
feFuncB: Mithril.Attributes;
|
|
||||||
feFuncG: Mithril.Attributes;
|
|
||||||
feFuncR: Mithril.Attributes;
|
|
||||||
feGaussianBlur: Mithril.Attributes;
|
|
||||||
feImage: Mithril.Attributes;
|
|
||||||
feMerge: Mithril.Attributes;
|
|
||||||
feMergeNode: Mithril.Attributes;
|
|
||||||
feMorphology: Mithril.Attributes;
|
|
||||||
feOffset: Mithril.Attributes;
|
|
||||||
fePointLight: Mithril.Attributes;
|
|
||||||
feSpecularLighting: Mithril.Attributes;
|
|
||||||
feSpotLight: Mithril.Attributes;
|
|
||||||
feTile: Mithril.Attributes;
|
|
||||||
feTurbulence: Mithril.Attributes;
|
|
||||||
filter: Mithril.Attributes;
|
|
||||||
foreignObject: Mithril.Attributes;
|
|
||||||
g: Mithril.Attributes;
|
|
||||||
image: Mithril.Attributes;
|
|
||||||
line: Mithril.Attributes;
|
|
||||||
linearGradient: Mithril.Attributes;
|
|
||||||
marker: Mithril.Attributes;
|
|
||||||
mask: Mithril.Attributes;
|
|
||||||
metadata: Mithril.Attributes;
|
|
||||||
mpath: Mithril.Attributes;
|
|
||||||
path: Mithril.Attributes;
|
|
||||||
pattern: Mithril.Attributes;
|
|
||||||
polygon: Mithril.Attributes;
|
|
||||||
polyline: Mithril.Attributes;
|
|
||||||
radialGradient: Mithril.Attributes;
|
|
||||||
rect: Mithril.Attributes;
|
|
||||||
stop: Mithril.Attributes;
|
|
||||||
switch: Mithril.Attributes;
|
|
||||||
symbol: Mithril.Attributes;
|
|
||||||
text: Mithril.Attributes;
|
|
||||||
textPath: Mithril.Attributes;
|
|
||||||
tspan: Mithril.Attributes;
|
|
||||||
use: Mithril.Attributes;
|
|
||||||
view: Mithril.Attributes;
|
|
||||||
|
|
||||||
// Special Mithril types
|
|
||||||
'[': Mithril.Attributes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const Mithril: Mithril.Static;
|
|
||||||
export = Mithril;
|
|
43
js/dist-typings/@types/mithril/stream/index.d.ts
vendored
43
js/dist-typings/@types/mithril/stream/index.d.ts
vendored
@@ -1,43 +0,0 @@
|
|||||||
// tslint:disable:rulename strict-export-declare-modifiers
|
|
||||||
/** Creates an empty stream. */
|
|
||||||
declare function Stream<T>(): Stream<T>; // tslint:disable-line no-unnecessary-generics
|
|
||||||
/** Creates a stream with an initial value. */
|
|
||||||
declare function Stream<T>(value: T): Stream<T>; // tslint:disable-line unified-signatures
|
|
||||||
|
|
||||||
declare interface Stream<T> {
|
|
||||||
/** Returns the value of the stream. */
|
|
||||||
(): T;
|
|
||||||
/** Sets the value of the stream. */
|
|
||||||
(value: T): this;
|
|
||||||
/** Creates a dependent stream whose value is set to the result of the callback function. */
|
|
||||||
map<U>(f: (current: T) => U | typeof Stream.SKIP): Stream<U>;
|
|
||||||
/** This method is functionally identical to stream. It exists to conform to Fantasy Land's Applicative specification. */
|
|
||||||
of(val: T): Stream<T>;
|
|
||||||
/** Apply. */
|
|
||||||
ap<U>(f: Stream<(value: T) => U>): Stream<U>;
|
|
||||||
/** A co-dependent stream that unregisters dependent streams when set to true. */
|
|
||||||
end: Stream<boolean>;
|
|
||||||
/** When a stream is passed as the argument to JSON.stringify(), the value of the stream is serialized. */
|
|
||||||
toJSON(): string;
|
|
||||||
/** Returns the value of the stream. */
|
|
||||||
valueOf(): T;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare namespace Stream {
|
|
||||||
/** Creates a computed stream that reactively updates if any of its upstreams are updated. */
|
|
||||||
export function combine<T>(combiner: (...streams: any[]) => T, streams: Array<Stream<any>>): Stream<T>;
|
|
||||||
/** Combines the values of one or more streams into a single stream that is updated whenever one or more of the sources are updated */
|
|
||||||
export function lift<S extends any[], T>(fn: (...values: S) => T, ...streams: {[I in keyof S]: Stream<S[I]>}): Stream<T>;
|
|
||||||
/** Creates a stream whose value is the array of values from an array of streams. */
|
|
||||||
export function merge<S extends any[]>(streams: {[I in keyof S]: Stream<S[I]>}): Stream<{[I in keyof S]: S[I]}>;
|
|
||||||
/** Creates a new stream with the results of calling the function on every incoming stream with and accumulator and the incoming value. */
|
|
||||||
export function scan<T, U>(fn: (acc: U, value: T) => U, acc: U, stream: Stream<T>): Stream<U>;
|
|
||||||
/** Takes an array of pairs of streams and scan functions and merges all those streams using the given functions into a single stream. */
|
|
||||||
export function scanMerge<T, U>(pairs: Array<[Stream<T>, (acc: U, value: T) => U]>, acc: U): Stream<U>;
|
|
||||||
/** Takes an array of pairs of streams and scan functions and merges all those streams using the given functions into a single stream. */
|
|
||||||
export function scanMerge<U>(pairs: Array<[Stream<any>, (acc: U, value: any) => U]>, acc: U): Stream<U>;
|
|
||||||
/** A special value that can be returned to stream callbacks to skip execution of downstreams. */
|
|
||||||
export const SKIP: unique symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
export = Stream;
|
|
68
js/dist-typings/@types/tooltips/index.d.ts
vendored
68
js/dist-typings/@types/tooltips/index.d.ts
vendored
@@ -1,68 +0,0 @@
|
|||||||
/**
|
|
||||||
* Selection of options accepted by [Bootstrap's tooltips](https://getbootstrap.com/docs/3.3/javascript/#tooltips-options).
|
|
||||||
*
|
|
||||||
* ---
|
|
||||||
*
|
|
||||||
* Not all options are present from Bootstrap to discourage the use of options
|
|
||||||
* that will be deprecated in the future.
|
|
||||||
*
|
|
||||||
* More commonly used options that will be deprecated remain, but are marked as
|
|
||||||
* such.
|
|
||||||
*
|
|
||||||
* @see https://getbootstrap.com/docs/3.3/javascript/#tooltips-options
|
|
||||||
*/
|
|
||||||
export interface TooltipCreationOptions {
|
|
||||||
/**
|
|
||||||
* Whether HTML content is allowed in the tooltip.
|
|
||||||
*
|
|
||||||
* ---
|
|
||||||
*
|
|
||||||
* **Warning:** this is a possible XSS attack vector. This option shouldn't
|
|
||||||
* be used wherever possible, and will not work when we migrate to CSS-only
|
|
||||||
* tooltips.
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
html?: boolean;
|
|
||||||
/**
|
|
||||||
* Tooltip position around the target element.
|
|
||||||
*/
|
|
||||||
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
||||||
/**
|
|
||||||
* Sets the delay between a trigger state occurring and the tooltip appearing
|
|
||||||
* on-screen.
|
|
||||||
*
|
|
||||||
* ---
|
|
||||||
*
|
|
||||||
* **Warning:** this option will be removed when we switch to CSS-only
|
|
||||||
* tooltips.
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
delay?: number;
|
|
||||||
/**
|
|
||||||
* Value used if no `title` attribute is present on the HTML element.
|
|
||||||
*
|
|
||||||
* If a function is given, it will be called with its `this` reference set to
|
|
||||||
* the element that the tooltip is attached to.
|
|
||||||
*/
|
|
||||||
title?: string;
|
|
||||||
/**
|
|
||||||
* How the tooltip is triggered.
|
|
||||||
*
|
|
||||||
* Either on `hover`, on `hover focus` (either of the two).
|
|
||||||
*
|
|
||||||
* ---
|
|
||||||
*
|
|
||||||
* **Warning:** `manual`, `click` and `focus` on its own are deprecated options
|
|
||||||
* which will not be supported in the future.
|
|
||||||
*/
|
|
||||||
trigger?: 'hover' | 'hover focus';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a tooltip on a jQuery element reference.
|
|
||||||
*
|
|
||||||
* Returns the same jQuery reference to allow for method chaining.
|
|
||||||
*/
|
|
||||||
export type TooltipJQueryFunction = (tooltipOptions?: TooltipCreationOptions | 'destroy' | 'show' | 'hide') => JQuery;
|
|
26
js/dist-typings/@types/translator-icu-rich.d.ts
vendored
26
js/dist-typings/@types/translator-icu-rich.d.ts
vendored
@@ -1,26 +0,0 @@
|
|||||||
declare module '@askvortsov/rich-icu-message-formatter' {
|
|
||||||
type IValues = Record<string, any>;
|
|
||||||
|
|
||||||
type ITypeHandler = (
|
|
||||||
value: string,
|
|
||||||
matches: string,
|
|
||||||
locale: string,
|
|
||||||
values: IValues,
|
|
||||||
format: (message: string, values: IValues) => string
|
|
||||||
) => string;
|
|
||||||
type IRichHandler = (tag: any, values: IValues, contents: string) => any;
|
|
||||||
|
|
||||||
type ValueOrArray<T> = T | ValueOrArray<T>[];
|
|
||||||
type NestedStringArray = ValueOrArray<string>;
|
|
||||||
|
|
||||||
export class RichMessageFormatter {
|
|
||||||
locale: string | null;
|
|
||||||
constructor(locale: string | null, typeHandlers: Record<string, ITypeHandler>, richHandler: IRichHandler);
|
|
||||||
|
|
||||||
format(message: string, values: IValues): string;
|
|
||||||
process(message: string, values: IValues): NestedStringArray;
|
|
||||||
rich(message: string, values: IValues): NestedStringArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mithrilRichHandler(tag: any, values: IValues, contents: string): any;
|
|
||||||
}
|
|
17
js/dist-typings/@types/translator-icu.d.ts
vendored
17
js/dist-typings/@types/translator-icu.d.ts
vendored
@@ -1,17 +0,0 @@
|
|||||||
declare module '@ultraq/icu-message-formatter' {
|
|
||||||
export function pluralTypeHandler(
|
|
||||||
value: string,
|
|
||||||
matches: string,
|
|
||||||
locale: string,
|
|
||||||
values: Record<string, any>,
|
|
||||||
format: (text: string, values: Record<string, any>) => string
|
|
||||||
): string;
|
|
||||||
|
|
||||||
export function selectTypeHandler(
|
|
||||||
value: string,
|
|
||||||
matches: string,
|
|
||||||
locale: string,
|
|
||||||
values: Record<string, any>,
|
|
||||||
format: (text: string, values: Record<string, any>) => string
|
|
||||||
): string;
|
|
||||||
}
|
|
61
js/dist-typings/admin/AdminApplication.d.ts
vendored
61
js/dist-typings/admin/AdminApplication.d.ts
vendored
@@ -1,61 +0,0 @@
|
|||||||
import Application from '../common/Application';
|
|
||||||
import ExtensionData from './utils/ExtensionData';
|
|
||||||
export declare type Extension = {
|
|
||||||
id: string;
|
|
||||||
version: string;
|
|
||||||
description?: string;
|
|
||||||
icon?: {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
links: {
|
|
||||||
authors?: {
|
|
||||||
name?: string;
|
|
||||||
link?: string;
|
|
||||||
}[];
|
|
||||||
discuss?: string;
|
|
||||||
documentation?: string;
|
|
||||||
support?: string;
|
|
||||||
website?: string;
|
|
||||||
donate?: string;
|
|
||||||
source?: string;
|
|
||||||
};
|
|
||||||
extra: {
|
|
||||||
'flarum-extension': {
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export default class AdminApplication extends Application {
|
|
||||||
extensionData: ExtensionData;
|
|
||||||
extensionCategories: {
|
|
||||||
feature: number;
|
|
||||||
theme: number;
|
|
||||||
language: number;
|
|
||||||
};
|
|
||||||
history: {
|
|
||||||
canGoBack: () => boolean;
|
|
||||||
getPrevious: () => void;
|
|
||||||
backUrl: () => string;
|
|
||||||
back: () => void;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Settings are serialized to the admin dashboard as strings.
|
|
||||||
* Additional encoding/decoding is possible, but must take
|
|
||||||
* place on the client side.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
data: Application['data'] & {
|
|
||||||
extensions: Record<string, Extension>;
|
|
||||||
settings: Record<string, string>;
|
|
||||||
modelStatistics: Record<string, {
|
|
||||||
total: number;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
constructor();
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
mount(): void;
|
|
||||||
getRequiredPermissions(permission: string): string[];
|
|
||||||
}
|
|
3
js/dist-typings/admin/app.d.ts
vendored
3
js/dist-typings/admin/app.d.ts
vendored
@@ -1,3 +0,0 @@
|
|||||||
import Admin from './AdminApplication';
|
|
||||||
declare const app: Admin;
|
|
||||||
export default app;
|
|
165
js/dist-typings/admin/compat.d.ts
vendored
165
js/dist-typings/admin/compat.d.ts
vendored
@@ -1,165 +0,0 @@
|
|||||||
declare var _default: {
|
|
||||||
extend: any;
|
|
||||||
Session: typeof import("../common/Session").default;
|
|
||||||
Store: typeof import("../common/Store").default;
|
|
||||||
'utils/BasicEditorDriver': typeof import("../common/utils/BasicEditorDriver").default;
|
|
||||||
'utils/evented': {
|
|
||||||
handlers: Record<string, unknown>;
|
|
||||||
getHandlers(event: string): Function[];
|
|
||||||
trigger(event: string, ...args: any[]): void;
|
|
||||||
on(event: string, handler: Function): void;
|
|
||||||
one(event: string, handler: Function): void;
|
|
||||||
off(event: string, handler: Function): void;
|
|
||||||
};
|
|
||||||
'utils/liveHumanTimes': typeof import("../common/utils/liveHumanTimes").default;
|
|
||||||
'utils/ItemList': typeof import("../common/utils/ItemList").default;
|
|
||||||
'utils/mixin': typeof import("../common/utils/mixin").default;
|
|
||||||
'utils/humanTime': typeof import("../common/utils/humanTime").default;
|
|
||||||
'utils/computed': typeof import("../common/utils/computed").default;
|
|
||||||
'utils/insertText': typeof import("../common/utils/insertText").default;
|
|
||||||
'utils/styleSelectedText': typeof import("../common/utils/styleSelectedText").default;
|
|
||||||
'utils/Drawer': typeof import("../common/utils/Drawer").default;
|
|
||||||
'utils/anchorScroll': typeof import("../common/utils/anchorScroll").default;
|
|
||||||
'utils/RequestError': typeof import("../common/utils/RequestError").default;
|
|
||||||
'utils/abbreviateNumber': typeof import("../common/utils/abbreviateNumber").default;
|
|
||||||
'utils/string': typeof import("../common/utils/string");
|
|
||||||
'utils/SubtreeRetainer': typeof import("../common/utils/SubtreeRetainer").default;
|
|
||||||
'utils/escapeRegExp': typeof import("../common/utils/escapeRegExp").default;
|
|
||||||
'utils/extract': typeof import("../common/utils/extract").default;
|
|
||||||
'utils/ScrollListener': typeof import("../common/utils/ScrollListener").default;
|
|
||||||
'utils/stringToColor': typeof import("../common/utils/stringToColor").default;
|
|
||||||
'utils/Stream': typeof import("mithril/stream");
|
|
||||||
'utils/subclassOf': typeof import("../common/utils/subclassOf").default;
|
|
||||||
'utils/setRouteWithForcedRefresh': typeof import("../common/utils/setRouteWithForcedRefresh").default;
|
|
||||||
'utils/patchMithril': typeof import("../common/utils/patchMithril").default;
|
|
||||||
'utils/proxifyCompat': typeof import("../common/utils/proxifyCompat").default;
|
|
||||||
'utils/classList': (...classes: import("clsx").ClassValue[]) => string;
|
|
||||||
'utils/extractText': typeof import("../common/utils/extractText").default;
|
|
||||||
'utils/formatNumber': typeof import("../common/utils/formatNumber").default;
|
|
||||||
'utils/mapRoutes': typeof import("../common/utils/mapRoutes").default;
|
|
||||||
'utils/withAttr': (key: string, cb: Function) => (this: Element) => void;
|
|
||||||
'utils/throttleDebounce': typeof import("../common/utils/throttleDebounce");
|
|
||||||
'utils/isObject': typeof import("../common/utils/isObject").default;
|
|
||||||
'utils/focusTrap': typeof import("../common/utils/focusTrap");
|
|
||||||
'models/Notification': typeof import("../common/models/Notification").default;
|
|
||||||
'models/User': typeof import("../common/models/User").default;
|
|
||||||
'models/Post': typeof import("../common/models/Post").default;
|
|
||||||
'models/Discussion': typeof import("../common/models/Discussion").default;
|
|
||||||
'models/Group': typeof import("../common/models/Group").default;
|
|
||||||
'models/Forum': typeof import("../common/models/Forum").default;
|
|
||||||
Component: typeof import("../common/Component").default;
|
|
||||||
Fragment: typeof import("../common/Fragment").default;
|
|
||||||
Translator: typeof import("../common/Translator").default;
|
|
||||||
'components/AlertManager': typeof import("../common/components/AlertManager").default;
|
|
||||||
'components/Page': typeof import("../common/components/Page").default;
|
|
||||||
'components/Switch': typeof import("../common/components/Switch").default;
|
|
||||||
'components/Badge': typeof import("../common/components/Badge").default;
|
|
||||||
'components/LoadingIndicator': typeof import("../common/components/LoadingIndicator").default;
|
|
||||||
'components/Placeholder': typeof import("../common/components/Placeholder").default;
|
|
||||||
'components/Separator': typeof import("../common/components/Separator").default;
|
|
||||||
'components/Dropdown': typeof import("../common/components/Dropdown").default;
|
|
||||||
'components/SplitDropdown': typeof import("../common/components/SplitDropdown").default;
|
|
||||||
'components/RequestErrorModal': typeof import("../common/components/RequestErrorModal").default;
|
|
||||||
'components/FieldSet': typeof import("../common/components/FieldSet").default;
|
|
||||||
'components/Select': typeof import("../common/components/Select").default;
|
|
||||||
'components/Navigation': typeof import("../common/components/Navigation").default;
|
|
||||||
'components/Alert': typeof import("../common/components/Alert").default;
|
|
||||||
'components/Link': typeof import("../common/components/Link").default;
|
|
||||||
'components/LinkButton': typeof import("../common/components/LinkButton").default;
|
|
||||||
'components/Checkbox': typeof import("../common/components/Checkbox").default;
|
|
||||||
'components/ColorPreviewInput': typeof import("../common/components/ColorPreviewInput").default;
|
|
||||||
'components/SelectDropdown': typeof import("../common/components/SelectDropdown").default;
|
|
||||||
'components/ModalManager': typeof import("../common/components/ModalManager").default;
|
|
||||||
'components/Button': typeof import("../common/components/Button").default;
|
|
||||||
'components/Modal': typeof import("../common/components/Modal").default;
|
|
||||||
'components/GroupBadge': typeof import("../common/components/GroupBadge").default;
|
|
||||||
'components/TextEditor': typeof import("../common/components/TextEditor").default;
|
|
||||||
'components/TextEditorButton': typeof import("../common/components/TextEditorButton").default;
|
|
||||||
'components/Tooltip': typeof import("../common/components/Tooltip").default;
|
|
||||||
'components/EditUserModal': typeof import("../common/components/EditUserModal").default;
|
|
||||||
Model: typeof import("../common/Model").default;
|
|
||||||
Application: typeof import("../common/Application").default;
|
|
||||||
'helpers/fullTime': typeof import("../common/helpers/fullTime").default;
|
|
||||||
'helpers/avatar': typeof import("../common/helpers/avatar").default;
|
|
||||||
'helpers/icon': typeof import("../common/helpers/icon").default;
|
|
||||||
'helpers/humanTime': typeof import("../common/helpers/humanTime").default;
|
|
||||||
'helpers/punctuateSeries': typeof import("../common/helpers/punctuateSeries").default;
|
|
||||||
'helpers/highlight': typeof import("../common/helpers/highlight").default;
|
|
||||||
'helpers/username': typeof import("../common/helpers/username").default;
|
|
||||||
'helpers/userOnline': typeof import("../common/helpers/userOnline").default;
|
|
||||||
'helpers/listItems': typeof import("../common/helpers/listItems").default;
|
|
||||||
'resolvers/DefaultResolver': typeof import("../common/resolvers/DefaultResolver").default;
|
|
||||||
'states/PaginatedListState': typeof import("../common/states/PaginatedListState").default;
|
|
||||||
} & {
|
|
||||||
'utils/saveSettings': typeof saveSettings;
|
|
||||||
'utils/ExtensionData': typeof ExtensionData;
|
|
||||||
'utils/isExtensionEnabled': typeof isExtensionEnabled;
|
|
||||||
'utils/getCategorizedExtensions': typeof getCategorizedExtensions;
|
|
||||||
'utils/generateElementId': typeof generateElementId;
|
|
||||||
'components/SettingDropdown': typeof SettingDropdown;
|
|
||||||
'components/EditCustomFooterModal': typeof EditCustomFooterModal;
|
|
||||||
'components/SessionDropdown': typeof SessionDropdown;
|
|
||||||
'components/HeaderPrimary': typeof HeaderPrimary;
|
|
||||||
'components/AdminPage': typeof AdminPage;
|
|
||||||
'components/AppearancePage': typeof AppearancePage;
|
|
||||||
'components/StatusWidget': typeof StatusWidget;
|
|
||||||
'components/ExtensionsWidget': typeof ExtensionsWidget;
|
|
||||||
'components/HeaderSecondary': typeof HeaderSecondary;
|
|
||||||
'components/SettingsModal': typeof SettingsModal;
|
|
||||||
'components/DashboardWidget': typeof DashboardWidget;
|
|
||||||
'components/ExtensionPage': typeof ExtensionPage;
|
|
||||||
'components/ExtensionLinkButton': typeof ExtensionLinkButton;
|
|
||||||
'components/PermissionGrid': typeof PermissionGrid;
|
|
||||||
'components/ExtensionPermissionGrid': typeof ExtensionPermissionGrid;
|
|
||||||
'components/MailPage': typeof MailPage;
|
|
||||||
'components/UploadImageButton': typeof UploadImageButton;
|
|
||||||
'components/LoadingModal': typeof LoadingModal;
|
|
||||||
'components/DashboardPage': typeof DashboardPage;
|
|
||||||
'components/BasicsPage': typeof BasicsPage;
|
|
||||||
'components/UserListPage': typeof UserListPage;
|
|
||||||
'components/EditCustomHeaderModal': typeof EditCustomHeaderModal;
|
|
||||||
'components/PermissionsPage': typeof PermissionsPage;
|
|
||||||
'components/PermissionDropdown': typeof PermissionDropdown;
|
|
||||||
'components/AdminNav': typeof AdminNav;
|
|
||||||
'components/AdminHeader': typeof AdminHeader;
|
|
||||||
'components/EditCustomCssModal': typeof EditCustomCssModal;
|
|
||||||
'components/EditGroupModal': typeof EditGroupModal;
|
|
||||||
routes: typeof routes;
|
|
||||||
AdminApplication: typeof AdminApplication;
|
|
||||||
};
|
|
||||||
export default _default;
|
|
||||||
import saveSettings from "./utils/saveSettings";
|
|
||||||
import ExtensionData from "./utils/ExtensionData";
|
|
||||||
import isExtensionEnabled from "./utils/isExtensionEnabled";
|
|
||||||
import getCategorizedExtensions from "./utils/getCategorizedExtensions";
|
|
||||||
import generateElementId from "./utils/generateElementId";
|
|
||||||
import SettingDropdown from "./components/SettingDropdown";
|
|
||||||
import EditCustomFooterModal from "./components/EditCustomFooterModal";
|
|
||||||
import SessionDropdown from "./components/SessionDropdown";
|
|
||||||
import HeaderPrimary from "./components/HeaderPrimary";
|
|
||||||
import AdminPage from "./components/AdminPage";
|
|
||||||
import AppearancePage from "./components/AppearancePage";
|
|
||||||
import StatusWidget from "./components/StatusWidget";
|
|
||||||
import ExtensionsWidget from "./components/ExtensionsWidget";
|
|
||||||
import HeaderSecondary from "./components/HeaderSecondary";
|
|
||||||
import SettingsModal from "./components/SettingsModal";
|
|
||||||
import DashboardWidget from "./components/DashboardWidget";
|
|
||||||
import ExtensionPage from "./components/ExtensionPage";
|
|
||||||
import ExtensionLinkButton from "./components/ExtensionLinkButton";
|
|
||||||
import PermissionGrid from "./components/PermissionGrid";
|
|
||||||
import ExtensionPermissionGrid from "./components/ExtensionPermissionGrid";
|
|
||||||
import MailPage from "./components/MailPage";
|
|
||||||
import UploadImageButton from "./components/UploadImageButton";
|
|
||||||
import LoadingModal from "./components/LoadingModal";
|
|
||||||
import DashboardPage from "./components/DashboardPage";
|
|
||||||
import BasicsPage from "./components/BasicsPage";
|
|
||||||
import UserListPage from "./components/UserListPage";
|
|
||||||
import EditCustomHeaderModal from "./components/EditCustomHeaderModal";
|
|
||||||
import PermissionsPage from "./components/PermissionsPage";
|
|
||||||
import PermissionDropdown from "./components/PermissionDropdown";
|
|
||||||
import AdminNav from "./components/AdminNav";
|
|
||||||
import AdminHeader from "./components/AdminHeader";
|
|
||||||
import EditCustomCssModal from "./components/EditCustomCssModal";
|
|
||||||
import EditGroupModal from "./components/EditGroupModal";
|
|
||||||
import routes from "./routes";
|
|
||||||
import AdminApplication from "./AdminApplication";
|
|
@@ -1,4 +0,0 @@
|
|||||||
export default class AdminHeader extends Component<import("../../common/Component").ComponentAttrs, undefined> {
|
|
||||||
constructor();
|
|
||||||
}
|
|
||||||
import Component from "../../common/Component";
|
|
15
js/dist-typings/admin/components/AdminNav.d.ts
vendored
15
js/dist-typings/admin/components/AdminNav.d.ts
vendored
@@ -1,15 +0,0 @@
|
|||||||
export default class AdminNav extends Component<import("../../common/Component").ComponentAttrs, undefined> {
|
|
||||||
constructor();
|
|
||||||
query: Stream<string> | undefined;
|
|
||||||
scrollToActive(): void;
|
|
||||||
/**
|
|
||||||
* Build an item list of main links to show in the admin navigation.
|
|
||||||
*
|
|
||||||
* @return {ItemList<import('mithril').Children>}
|
|
||||||
*/
|
|
||||||
items(): ItemList<import('mithril').Children>;
|
|
||||||
extensionItems(): ItemList<any>;
|
|
||||||
}
|
|
||||||
import Component from "../../common/Component";
|
|
||||||
import Stream from "../../common/utils/Stream";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
167
js/dist-typings/admin/components/AdminPage.d.ts
vendored
167
js/dist-typings/admin/components/AdminPage.d.ts
vendored
@@ -1,167 +0,0 @@
|
|||||||
import type Mithril from 'mithril';
|
|
||||||
import Page, { IPageAttrs } from '../../common/components/Page';
|
|
||||||
import Stream from '../../common/utils/Stream';
|
|
||||||
export interface AdminHeaderOptions {
|
|
||||||
title: Mithril.Children;
|
|
||||||
description: Mithril.Children;
|
|
||||||
icon: string;
|
|
||||||
/**
|
|
||||||
* Will be used as the class for the AdminPage.
|
|
||||||
*
|
|
||||||
* Will also be appended with `-header` and set as the class for the `AdminHeader` component.
|
|
||||||
*/
|
|
||||||
className: string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* A type that matches any valid value for the `type` attribute on an HTML `<input>` element.
|
|
||||||
*
|
|
||||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-type
|
|
||||||
*
|
|
||||||
* Note: this will be exported from a different location in the future.
|
|
||||||
*
|
|
||||||
* @see https://github.com/flarum/core/issues/3039
|
|
||||||
*/
|
|
||||||
export declare type HTMLInputTypes = 'button' | 'checkbox' | 'color' | 'date' | 'datetime-local' | 'email' | 'file' | 'hidden' | 'image' | 'month' | 'number' | 'password' | 'radio' | 'range' | 'reset' | 'search' | 'submit' | 'tel' | 'text' | 'time' | 'url' | 'week';
|
|
||||||
interface CommonSettingsItemOptions extends Mithril.Attributes {
|
|
||||||
setting: string;
|
|
||||||
label: Mithril.Children;
|
|
||||||
help?: Mithril.Children;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Valid options for the setting component builder to generate an HTML input element.
|
|
||||||
*/
|
|
||||||
export interface HTMLInputSettingsComponentOptions extends CommonSettingsItemOptions {
|
|
||||||
/**
|
|
||||||
* Any valid HTML input `type` value.
|
|
||||||
*/
|
|
||||||
type: HTMLInputTypes;
|
|
||||||
}
|
|
||||||
declare const BooleanSettingTypes: readonly ["bool", "checkbox", "switch", "boolean"];
|
|
||||||
declare const SelectSettingTypes: readonly ["select", "dropdown", "selectdropdown"];
|
|
||||||
declare const TextareaSettingTypes: readonly ["textarea"];
|
|
||||||
declare const ColorPreviewSettingType = "color-preview";
|
|
||||||
/**
|
|
||||||
* Valid options for the setting component builder to generate a Switch.
|
|
||||||
*/
|
|
||||||
export interface SwitchSettingComponentOptions extends CommonSettingsItemOptions {
|
|
||||||
type: typeof BooleanSettingTypes[number];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Valid options for the setting component builder to generate a Select dropdown.
|
|
||||||
*/
|
|
||||||
export interface SelectSettingComponentOptions extends CommonSettingsItemOptions {
|
|
||||||
type: typeof SelectSettingTypes[number];
|
|
||||||
/**
|
|
||||||
* Map of values to their labels
|
|
||||||
*/
|
|
||||||
options: {
|
|
||||||
[value: string]: Mithril.Children;
|
|
||||||
};
|
|
||||||
default: string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Valid options for the setting component builder to generate a Textarea.
|
|
||||||
*/
|
|
||||||
export interface TextareaSettingComponentOptions extends CommonSettingsItemOptions {
|
|
||||||
type: typeof TextareaSettingTypes[number];
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Valid options for the setting component builder to generate a ColorPreviewInput.
|
|
||||||
*/
|
|
||||||
export interface ColorPreviewSettingComponentOptions extends CommonSettingsItemOptions {
|
|
||||||
type: typeof ColorPreviewSettingType;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* All valid options for the setting component builder.
|
|
||||||
*/
|
|
||||||
export declare type SettingsComponentOptions = HTMLInputSettingsComponentOptions | SwitchSettingComponentOptions | SelectSettingComponentOptions | TextareaSettingComponentOptions | ColorPreviewSettingComponentOptions;
|
|
||||||
/**
|
|
||||||
* Valid attrs that can be returned by the `headerInfo` function
|
|
||||||
*/
|
|
||||||
export declare type AdminHeaderAttrs = AdminHeaderOptions & Partial<Omit<Mithril.Attributes, 'class'>>;
|
|
||||||
export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> {
|
|
||||||
settings: Record<string, Stream<string>>;
|
|
||||||
loading: boolean;
|
|
||||||
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
|
|
||||||
/**
|
|
||||||
* Returns the content of the AdminPage.
|
|
||||||
*/
|
|
||||||
abstract content(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
|
|
||||||
/**
|
|
||||||
* Returns the submit button for this AdminPage.
|
|
||||||
*
|
|
||||||
* Calls `this.saveSettings` when the button is clicked.
|
|
||||||
*/
|
|
||||||
submitButton(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
|
|
||||||
/**
|
|
||||||
* Returns the Header component for this AdminPage.
|
|
||||||
*/
|
|
||||||
header(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
|
|
||||||
/**
|
|
||||||
* Returns the options passed to the AdminHeader component.
|
|
||||||
*/
|
|
||||||
headerInfo(): AdminHeaderAttrs;
|
|
||||||
/**
|
|
||||||
* `buildSettingComponent` takes a settings object and turns it into a component.
|
|
||||||
* Depending on the type of input, you can set the type to 'bool', 'select', or
|
|
||||||
* any standard <input> type. Any values inside the 'extra' object will be added
|
|
||||||
* to the component as an attribute.
|
|
||||||
*
|
|
||||||
* Alternatively, you can pass a callback that will be executed in ExtensionPage's
|
|
||||||
* context to include custom JSX elements.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* setting: 'acme.checkbox',
|
|
||||||
* label: app.translator.trans('acme.admin.setting_label'),
|
|
||||||
* type: 'bool',
|
|
||||||
* help: app.translator.trans('acme.admin.setting_help'),
|
|
||||||
* className: 'Setting-item'
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* setting: 'acme.select',
|
|
||||||
* label: app.translator.trans('acme.admin.setting_label'),
|
|
||||||
* type: 'select',
|
|
||||||
* options: {
|
|
||||||
* 'option1': 'Option 1 label',
|
|
||||||
* 'option2': 'Option 2 label',
|
|
||||||
* },
|
|
||||||
* default: 'option1',
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* () => {
|
|
||||||
* return <p>My cool component</p>;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
buildSettingComponent(entry: ((this: this) => Mithril.Children) | SettingsComponentOptions): Mithril.Children;
|
|
||||||
/**
|
|
||||||
* Called when `saveSettings` completes successfully.
|
|
||||||
*/
|
|
||||||
onsaved(): void;
|
|
||||||
/**
|
|
||||||
* Returns a function that fetches the setting from the `app` global.
|
|
||||||
*/
|
|
||||||
setting(key: string, fallback?: string): Stream<string>;
|
|
||||||
/**
|
|
||||||
* Returns a map of settings keys to values which includes only those which have been modified but not yet saved.
|
|
||||||
*/
|
|
||||||
dirty(): Record<string, string>;
|
|
||||||
/**
|
|
||||||
* Returns the number of settings that have been modified.
|
|
||||||
*/
|
|
||||||
isChanged(): number;
|
|
||||||
/**
|
|
||||||
* Saves the modified settings to the database.
|
|
||||||
*/
|
|
||||||
saveSettings(e: SubmitEvent & {
|
|
||||||
redraw: boolean;
|
|
||||||
}): Promise<void>;
|
|
||||||
}
|
|
||||||
export {};
|
|
@@ -1,6 +0,0 @@
|
|||||||
export default class AppearancePage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
|
|
||||||
constructor();
|
|
||||||
colorItems(): ItemList<any>;
|
|
||||||
}
|
|
||||||
import AdminPage from "./AdminPage";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
18
js/dist-typings/admin/components/BasicsPage.d.ts
vendored
18
js/dist-typings/admin/components/BasicsPage.d.ts
vendored
@@ -1,18 +0,0 @@
|
|||||||
export default class BasicsPage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
|
|
||||||
constructor();
|
|
||||||
localeOptions: {} | undefined;
|
|
||||||
displayNameOptions: {} | undefined;
|
|
||||||
slugDriverOptions: {} | undefined;
|
|
||||||
/**
|
|
||||||
* Build a list of options for the default homepage. Each option must be an
|
|
||||||
* object with `path` and `label` properties.
|
|
||||||
*
|
|
||||||
* @return {ItemList<{ path: string, label: import('mithril').Children }>}
|
|
||||||
*/
|
|
||||||
homePageItems(): ItemList<{
|
|
||||||
path: string;
|
|
||||||
label: import('mithril').Children;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
import AdminPage from "./AdminPage";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,6 +0,0 @@
|
|||||||
export default class DashboardPage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
|
|
||||||
constructor();
|
|
||||||
availableWidgets(): ItemList<any>;
|
|
||||||
}
|
|
||||||
import AdminPage from "./AdminPage";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,16 +0,0 @@
|
|||||||
export default class DashboardWidget extends Component<import("../../common/Component").ComponentAttrs, undefined> {
|
|
||||||
constructor();
|
|
||||||
/**
|
|
||||||
* Get the class name to apply to the widget.
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
className(): string;
|
|
||||||
/**
|
|
||||||
* Get the content of the widget.
|
|
||||||
*
|
|
||||||
* @return {import('mithril').Children}
|
|
||||||
*/
|
|
||||||
content(): import('mithril').Children;
|
|
||||||
}
|
|
||||||
import Component from "../../common/Component";
|
|
@@ -1,3 +0,0 @@
|
|||||||
export default class EditCustomCssModal extends SettingsModal {
|
|
||||||
}
|
|
||||||
import SettingsModal from "./SettingsModal";
|
|
@@ -1,3 +0,0 @@
|
|||||||
export default class EditCustomFooterModal extends SettingsModal {
|
|
||||||
}
|
|
||||||
import SettingsModal from "./SettingsModal";
|
|
@@ -1,3 +0,0 @@
|
|||||||
export default class EditCustomHeaderModal extends SettingsModal {
|
|
||||||
}
|
|
||||||
import SettingsModal from "./SettingsModal";
|
|
@@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* The `EditGroupModal` component shows a modal dialog which allows the user
|
|
||||||
* to create or edit a group.
|
|
||||||
*/
|
|
||||||
export default class EditGroupModal extends Modal<import("../../common/components/Modal").IInternalModalAttrs> {
|
|
||||||
constructor();
|
|
||||||
group: any;
|
|
||||||
nameSingular: Stream<any> | undefined;
|
|
||||||
namePlural: Stream<any> | undefined;
|
|
||||||
icon: Stream<any> | undefined;
|
|
||||||
color: Stream<any> | undefined;
|
|
||||||
isHidden: Stream<any> | undefined;
|
|
||||||
fields(): ItemList<any>;
|
|
||||||
submitData(): {
|
|
||||||
nameSingular: any;
|
|
||||||
namePlural: any;
|
|
||||||
color: any;
|
|
||||||
icon: any;
|
|
||||||
isHidden: any;
|
|
||||||
};
|
|
||||||
deleteGroup(): void;
|
|
||||||
}
|
|
||||||
import Modal from "../../common/components/Modal";
|
|
||||||
import Stream from "../../common/utils/Stream";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,5 +0,0 @@
|
|||||||
export default class ExtensionLinkButton extends LinkButton {
|
|
||||||
statusItems(name: any): ItemList<any>;
|
|
||||||
}
|
|
||||||
import LinkButton from "../../common/components/LinkButton";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,32 +0,0 @@
|
|||||||
import ItemList from '../../common/utils/ItemList';
|
|
||||||
import AdminPage from './AdminPage';
|
|
||||||
import RequestError from '../../common/utils/RequestError';
|
|
||||||
import { Extension } from '../AdminApplication';
|
|
||||||
import { IPageAttrs } from '../../common/components/Page';
|
|
||||||
import type Mithril from 'mithril';
|
|
||||||
export interface ExtensionPageAttrs extends IPageAttrs {
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs> extends AdminPage<Attrs> {
|
|
||||||
extension: Extension;
|
|
||||||
changingState: boolean;
|
|
||||||
infoFields: {
|
|
||||||
discuss: string;
|
|
||||||
documentation: string;
|
|
||||||
support: string;
|
|
||||||
website: string;
|
|
||||||
donate: string;
|
|
||||||
source: string;
|
|
||||||
};
|
|
||||||
oninit(vnode: Mithril.Vnode<Attrs, this>): void;
|
|
||||||
className(): string;
|
|
||||||
view(vnode: Mithril.VnodeDOM<Attrs, this>): JSX.Element | null;
|
|
||||||
header(): JSX.Element[];
|
|
||||||
sections(vnode: Mithril.VnodeDOM<Attrs, this>): ItemList<unknown>;
|
|
||||||
content(vnode: Mithril.VnodeDOM<Attrs, this>): JSX.Element;
|
|
||||||
topItems(): ItemList<Mithril.Children>;
|
|
||||||
infoItems(): ItemList<Mithril.Children>;
|
|
||||||
toggle(): void;
|
|
||||||
isEnabled(): any;
|
|
||||||
onerror(e: RequestError): void;
|
|
||||||
}
|
|
@@ -1,19 +0,0 @@
|
|||||||
import PermissionGrid, { PermissionGridEntry } from './PermissionGrid';
|
|
||||||
import ItemList from '../../common/utils/ItemList';
|
|
||||||
import Mithril from 'mithril';
|
|
||||||
export interface IExtensionPermissionGridAttrs {
|
|
||||||
extensionId: string;
|
|
||||||
}
|
|
||||||
export default class ExtensionPermissionGrid<CustomAttrs extends IExtensionPermissionGridAttrs = IExtensionPermissionGridAttrs> extends PermissionGrid<CustomAttrs> {
|
|
||||||
protected extensionId: string;
|
|
||||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
|
|
||||||
permissionItems(): ItemList<{
|
|
||||||
label: Mithril.Children;
|
|
||||||
children: PermissionGridEntry[];
|
|
||||||
}>;
|
|
||||||
viewItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
|
|
||||||
startItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
|
|
||||||
replyItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
|
|
||||||
moderateItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
|
|
||||||
scopeControlItems(): ItemList<unknown>;
|
|
||||||
}
|
|
@@ -1,6 +0,0 @@
|
|||||||
export default class ExtensionsWidget extends DashboardWidget {
|
|
||||||
categorizedExtensions: {} | undefined;
|
|
||||||
extensionCategory(category: any): JSX.Element;
|
|
||||||
extensionWidget(extension: any): JSX.Element;
|
|
||||||
}
|
|
||||||
import DashboardWidget from "./DashboardWidget";
|
|
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* The `HeaderPrimary` component displays primary header controls. On the
|
|
||||||
* default skin, these are shown just to the right of the forum title.
|
|
||||||
*/
|
|
||||||
export default class HeaderPrimary extends Component<import("../../common/Component").ComponentAttrs, undefined> {
|
|
||||||
constructor();
|
|
||||||
config(isInitialized: any, context: any): void;
|
|
||||||
/**
|
|
||||||
* Build an item list for the controls.
|
|
||||||
*
|
|
||||||
* @return {ItemList<import('mithril').Children>}
|
|
||||||
*/
|
|
||||||
items(): ItemList<import('mithril').Children>;
|
|
||||||
}
|
|
||||||
import Component from "../../common/Component";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* The `HeaderSecondary` component displays secondary header controls.
|
|
||||||
*/
|
|
||||||
export default class HeaderSecondary extends Component<import("../../common/Component").ComponentAttrs, undefined> {
|
|
||||||
constructor();
|
|
||||||
/**
|
|
||||||
* Build an item list for the controls.
|
|
||||||
*
|
|
||||||
* @return {ItemList<import('mithril').Children>}
|
|
||||||
*/
|
|
||||||
items(): ItemList<import('mithril').Children>;
|
|
||||||
}
|
|
||||||
import Component from "../../common/Component";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,14 +0,0 @@
|
|||||||
/// <reference path="../../@types/translator-icu-rich.d.ts" />
|
|
||||||
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
|
|
||||||
export interface ILoadingModalAttrs extends IInternalModalAttrs {
|
|
||||||
}
|
|
||||||
export default class LoadingModal<ModalAttrs extends ILoadingModalAttrs = ILoadingModalAttrs> extends Modal<ModalAttrs> {
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
static readonly isDismissible: boolean;
|
|
||||||
className(): string;
|
|
||||||
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
|
||||||
content(): string;
|
|
||||||
onsubmit(e: Event): void;
|
|
||||||
}
|
|
13
js/dist-typings/admin/components/MailPage.d.ts
vendored
13
js/dist-typings/admin/components/MailPage.d.ts
vendored
@@ -1,13 +0,0 @@
|
|||||||
export default class MailPage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
|
|
||||||
constructor();
|
|
||||||
sendingTest: boolean | undefined;
|
|
||||||
refresh(): void;
|
|
||||||
status: {
|
|
||||||
sending: boolean;
|
|
||||||
errors: {};
|
|
||||||
} | undefined;
|
|
||||||
driverFields: any;
|
|
||||||
sendTestEmail(): void;
|
|
||||||
testEmailSuccessAlert: number | undefined;
|
|
||||||
}
|
|
||||||
import AdminPage from "./AdminPage";
|
|
@@ -1,6 +0,0 @@
|
|||||||
export default class PermissionDropdown extends Dropdown {
|
|
||||||
save(groupIds: any): void;
|
|
||||||
toggle(groupId: any): void;
|
|
||||||
isGroupDisabled(id: any): boolean;
|
|
||||||
}
|
|
||||||
import Dropdown from "../../common/components/Dropdown";
|
|
@@ -1,36 +0,0 @@
|
|||||||
import Component, { ComponentAttrs } from '../../common/Component';
|
|
||||||
import ItemList from '../../common/utils/ItemList';
|
|
||||||
import type Mithril from 'mithril';
|
|
||||||
export interface PermissionConfig {
|
|
||||||
permission: string;
|
|
||||||
icon: string;
|
|
||||||
label: Mithril.Children;
|
|
||||||
allowGuest?: boolean;
|
|
||||||
}
|
|
||||||
export interface PermissionSetting {
|
|
||||||
setting: () => Mithril.Children;
|
|
||||||
icon: string;
|
|
||||||
label: Mithril.Children;
|
|
||||||
}
|
|
||||||
export declare type PermissionGridEntry = PermissionConfig | PermissionSetting;
|
|
||||||
export declare type PermissionType = 'view' | 'start' | 'reply' | 'moderate';
|
|
||||||
export interface ScopeItem {
|
|
||||||
label: Mithril.Children;
|
|
||||||
render: (permission: PermissionGridEntry) => Mithril.Children;
|
|
||||||
onremove?: () => void;
|
|
||||||
}
|
|
||||||
export interface IPermissionGridAttrs extends ComponentAttrs {
|
|
||||||
}
|
|
||||||
export default class PermissionGrid<CustomAttrs extends IPermissionGridAttrs = IPermissionGridAttrs> extends Component<CustomAttrs> {
|
|
||||||
view(vnode: Mithril.Vnode<CustomAttrs, this>): JSX.Element;
|
|
||||||
permissionItems(): ItemList<{
|
|
||||||
label: Mithril.Children;
|
|
||||||
children: PermissionGridEntry[];
|
|
||||||
}>;
|
|
||||||
viewItems(): ItemList<PermissionGridEntry>;
|
|
||||||
startItems(): ItemList<PermissionGridEntry>;
|
|
||||||
replyItems(): ItemList<PermissionGridEntry>;
|
|
||||||
moderateItems(): ItemList<PermissionGridEntry>;
|
|
||||||
scopeItems(): ItemList<ScopeItem>;
|
|
||||||
scopeControlItems(): ItemList<unknown>;
|
|
||||||
}
|
|
@@ -1,4 +0,0 @@
|
|||||||
export default class PermissionsPage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
|
|
||||||
constructor();
|
|
||||||
}
|
|
||||||
import AdminPage from "./AdminPage";
|
|
@@ -1,18 +0,0 @@
|
|||||||
/// <reference path="../../@types/translator-icu-rich.d.ts" />
|
|
||||||
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
|
|
||||||
import ExtensionReadme from '../models/ExtensionReadme';
|
|
||||||
import type Mithril from 'mithril';
|
|
||||||
import type { Extension } from '../AdminApplication';
|
|
||||||
export interface IReadmeModalAttrs extends IInternalModalAttrs {
|
|
||||||
extension: Extension;
|
|
||||||
}
|
|
||||||
export default class ReadmeModal<CustomAttrs extends IReadmeModalAttrs = IReadmeModalAttrs> extends Modal<CustomAttrs> {
|
|
||||||
protected name: string;
|
|
||||||
protected extName: string;
|
|
||||||
protected readme: ExtensionReadme;
|
|
||||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
|
|
||||||
className(): string;
|
|
||||||
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
|
||||||
content(): JSX.Element;
|
|
||||||
loadReadme(): Promise<void>;
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* The `SessionDropdown` component shows a button with the current user's
|
|
||||||
* avatar/name, with a dropdown of session controls.
|
|
||||||
*/
|
|
||||||
export default class SessionDropdown extends Dropdown {
|
|
||||||
/**
|
|
||||||
* Build an item list for the contents of the dropdown menu.
|
|
||||||
*
|
|
||||||
* @return {ItemList}
|
|
||||||
*/
|
|
||||||
items(): ItemList<any>;
|
|
||||||
}
|
|
||||||
import Dropdown from "../../common/components/Dropdown";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,3 +0,0 @@
|
|||||||
export default class SettingDropdown extends SelectDropdown {
|
|
||||||
}
|
|
||||||
import SelectDropdown from "../../common/components/SelectDropdown";
|
|
@@ -1,11 +0,0 @@
|
|||||||
export default class SettingsModal extends Modal<import("../../common/components/Modal").IInternalModalAttrs> {
|
|
||||||
constructor();
|
|
||||||
settings: {} | undefined;
|
|
||||||
form(): string;
|
|
||||||
submitButton(): JSX.Element;
|
|
||||||
setting(key: any, fallback?: string): any;
|
|
||||||
dirty(): {};
|
|
||||||
changed(): number;
|
|
||||||
onsaved(): void;
|
|
||||||
}
|
|
||||||
import Modal from "../../common/components/Modal";
|
|
@@ -1,7 +0,0 @@
|
|||||||
export default class StatusWidget extends DashboardWidget {
|
|
||||||
items(): ItemList<any>;
|
|
||||||
toolsItems(): ItemList<any>;
|
|
||||||
handleClearCache(e: any): void;
|
|
||||||
}
|
|
||||||
import DashboardWidget from "./DashboardWidget";
|
|
||||||
import ItemList from "../../common/utils/ItemList";
|
|
@@ -1,28 +0,0 @@
|
|||||||
export default class UploadImageButton extends Button<import("../../common/components/Button").IButtonAttrs> {
|
|
||||||
constructor();
|
|
||||||
loading: boolean;
|
|
||||||
/**
|
|
||||||
* Prompt the user to upload an image.
|
|
||||||
*/
|
|
||||||
upload(): void;
|
|
||||||
/**
|
|
||||||
* Remove the logo.
|
|
||||||
*/
|
|
||||||
remove(): void;
|
|
||||||
resourceUrl(): string;
|
|
||||||
/**
|
|
||||||
* After a successful upload/removal, reload the page.
|
|
||||||
*
|
|
||||||
* @param {object} response
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected success(response: object): void;
|
|
||||||
/**
|
|
||||||
* If upload/removal fails, stop loading.
|
|
||||||
*
|
|
||||||
* @param {object} response
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected failure(response: object): void;
|
|
||||||
}
|
|
||||||
import Button from "../../common/components/Button";
|
|
@@ -1,85 +0,0 @@
|
|||||||
/// <reference path="../../@types/translator-icu-rich.d.ts" />
|
|
||||||
import type Mithril from 'mithril';
|
|
||||||
import type User from '../../common/models/User';
|
|
||||||
import ItemList from '../../common/utils/ItemList';
|
|
||||||
import AdminPage from './AdminPage';
|
|
||||||
declare type ColumnData = {
|
|
||||||
/**
|
|
||||||
* Column title
|
|
||||||
*/
|
|
||||||
name: Mithril.Children;
|
|
||||||
/**
|
|
||||||
* Component(s) to show for this column.
|
|
||||||
*/
|
|
||||||
content: (user: User) => Mithril.Children;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Admin page which displays a paginated list of all users on the forum.
|
|
||||||
*/
|
|
||||||
export default class UserListPage extends AdminPage {
|
|
||||||
/**
|
|
||||||
* Number of users to load per page.
|
|
||||||
*/
|
|
||||||
private numPerPage;
|
|
||||||
/**
|
|
||||||
* Current page number. Zero-indexed.
|
|
||||||
*/
|
|
||||||
private pageNumber;
|
|
||||||
/**
|
|
||||||
* Total number of forum users.
|
|
||||||
*
|
|
||||||
* Fetched from the active `AdminApplication` (`app`), with
|
|
||||||
* data provided by `AdminPayload.php`, or `flarum/statistics`
|
|
||||||
* if installed.
|
|
||||||
*/
|
|
||||||
readonly userCount: number;
|
|
||||||
/**
|
|
||||||
* Get total number of user pages.
|
|
||||||
*/
|
|
||||||
private getTotalPageCount;
|
|
||||||
/**
|
|
||||||
* This page's array of users.
|
|
||||||
*
|
|
||||||
* `undefined` when page loads as no data has been fetched.
|
|
||||||
*/
|
|
||||||
private pageData;
|
|
||||||
/**
|
|
||||||
* Are there more users available?
|
|
||||||
*/
|
|
||||||
private moreData;
|
|
||||||
private isLoadingPage;
|
|
||||||
/**
|
|
||||||
* Component to render.
|
|
||||||
*/
|
|
||||||
content(): JSX.Element[];
|
|
||||||
/**
|
|
||||||
* Build an item list of columns to show for each user.
|
|
||||||
*
|
|
||||||
* Each column in the list should be an object with keys `name` and `content`.
|
|
||||||
*
|
|
||||||
* `name` is a string that will be used as the column name.
|
|
||||||
* `content` is a function with the User model passed as the first and only argument.
|
|
||||||
*
|
|
||||||
* See `UserListPage.tsx` for examples.
|
|
||||||
*/
|
|
||||||
columns(): ItemList<ColumnData>;
|
|
||||||
headerInfo(): {
|
|
||||||
className: string;
|
|
||||||
icon: string;
|
|
||||||
title: import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
|
||||||
description: import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Asynchronously fetch the next set of users to be rendered.
|
|
||||||
*
|
|
||||||
* Returns an array of Users, plus the raw API payload.
|
|
||||||
*
|
|
||||||
* Uses the `this.numPerPage` as the response limit, and automatically calculates the offset required from `pageNumber`.
|
|
||||||
*
|
|
||||||
* @param pageNumber The page number to load and display
|
|
||||||
*/
|
|
||||||
loadPage(pageNumber: number): Promise<void>;
|
|
||||||
nextPage(): void;
|
|
||||||
previousPage(): void;
|
|
||||||
}
|
|
||||||
export {};
|
|
3
js/dist-typings/admin/index.d.ts
vendored
3
js/dist-typings/admin/index.d.ts
vendored
@@ -1,3 +0,0 @@
|
|||||||
import app from './app';
|
|
||||||
export { app };
|
|
||||||
export declare const compat: Record<string, unknown>;
|
|
@@ -1,4 +0,0 @@
|
|||||||
export default class ExtensionReadme extends Model {
|
|
||||||
content: () => any;
|
|
||||||
}
|
|
||||||
import Model from "../../common/Model";
|
|
@@ -1,10 +0,0 @@
|
|||||||
import DefaultResolver from '../../common/resolvers/DefaultResolver';
|
|
||||||
import ExtensionPage, { ExtensionPageAttrs } from '../components/ExtensionPage';
|
|
||||||
/**
|
|
||||||
* A custom route resolver for ExtensionPage that generates handles routes
|
|
||||||
* to default extension pages or a page provided by an extension.
|
|
||||||
*/
|
|
||||||
export default class ExtensionPageResolver<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs, RouteArgs extends Record<string, unknown> = {}> extends DefaultResolver<Attrs, ExtensionPage<Attrs>, RouteArgs> {
|
|
||||||
static extension: string | null;
|
|
||||||
onmatch(args: Attrs & RouteArgs, requestedPath: string, route: string): new () => ExtensionPage<Attrs>;
|
|
||||||
}
|
|
5
js/dist-typings/admin/routes.d.ts
vendored
5
js/dist-typings/admin/routes.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
import AdminApplication from './AdminApplication';
|
|
||||||
/**
|
|
||||||
* The `routes` initializer defines the forum app's routes.
|
|
||||||
*/
|
|
||||||
export default function (app: AdminApplication): void;
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user