mirror of
https://github.com/flarum/core.git
synced 2025-08-14 04:14:06 +02:00
Compare commits
194 Commits
v0.1.0-bet
...
ck/code-sp
Author | SHA1 | Date | |
---|---|---|---|
|
8a529d3753 | ||
|
f900e1165f | ||
|
3b2d3dc8bc | ||
|
a18e64c47f | ||
|
5e7a260853 | ||
|
3a1803ee0f | ||
|
008206738f | ||
|
c006931798 | ||
|
a5ec39b5cf | ||
|
c75db75efe | ||
|
300dadff60 | ||
|
94d69fe15f | ||
|
da598db376 | ||
|
d31e0573f8 | ||
|
2968341f77 | ||
|
9839370701 | ||
|
40dc6d0feb | ||
|
945f6478b5 | ||
|
69a10c97be | ||
|
0074f0c984 | ||
|
19465fb522 | ||
|
0fe7723a7f | ||
|
fbe2813378 | ||
|
4b69a35260 | ||
|
5e8155e1cc | ||
|
0f0f2b6d4e | ||
|
3dae397c65 | ||
|
7025a7f5e0 | ||
|
12f6b1b375 | ||
|
2de57af7c8 | ||
|
1c4817a0b3 | ||
|
0eefbf0374 | ||
|
90c0bc410e | ||
|
d642fb531c | ||
|
706eaeda41 | ||
|
3cc18c1da2 | ||
|
8dd57ffed2 | ||
|
d29495203b | ||
|
783c563305 | ||
|
908d087e00 | ||
|
374189d48e | ||
|
fe8dda6fd0 | ||
|
cd9ee48af6 | ||
|
2e9078a7cf | ||
|
0cc12aed95 | ||
|
59fdd7628a | ||
|
298f6c39f2 | ||
|
233b97329c | ||
|
1b5b143930 | ||
|
0d139e6133 | ||
|
0e6a60bd5b | ||
|
6e4c75eba6 | ||
|
386f3d3db1 | ||
|
9fffb8ec1a | ||
|
92e590f1ab | ||
|
098908cb4a | ||
|
901846d0cf | ||
|
5a3aefb76c | ||
|
cf2a636e81 | ||
|
a8ba510655 | ||
|
9c3b6c596f | ||
|
2310d782a3 | ||
|
e9642250ae | ||
|
a6dd545dbc | ||
|
a64c39835a | ||
|
db0d8e89c7 | ||
|
4e126708e9 | ||
|
b88a7cb33b | ||
|
a2f52c09fd | ||
|
30017eef09 | ||
|
d0ffa26b0b | ||
|
612a57c466 | ||
|
91e8b56961 | ||
|
ba9665e9db | ||
|
8eea0985a4 | ||
|
1bcb9d3ea1 | ||
|
2c3e1f9923 | ||
|
bc607e089e | ||
|
91d5d9c176 | ||
|
3aa118ab94 | ||
|
4b0ad6972d | ||
|
84ded0ce50 | ||
|
725863a6e2 | ||
|
c81f629b0b | ||
|
15cbe4daaa | ||
|
ddac07d991 | ||
|
08ba2599d7 | ||
|
8eef7230e9 | ||
|
a61f9e7328 | ||
|
a65e1de641 | ||
|
bed3207798 | ||
|
fc73d47e4c | ||
|
6e01c47c11 | ||
|
a9526917b8 | ||
|
e37fdef709 | ||
|
56d7796c47 | ||
|
b7379bf91b | ||
|
7fa22a131f | ||
|
f0c6050654 | ||
|
9627eb73f1 | ||
|
d0adb244da | ||
|
458a5cc6be | ||
|
ea840ba594 | ||
|
ea291508ab | ||
|
7d79912d36 | ||
|
67306a9d34 | ||
|
8cc207b139 | ||
|
023871ef86 | ||
|
1c578a83e4 | ||
|
454c525cb2 | ||
|
49009d268f | ||
|
ef2d6a65f4 | ||
|
509adf228a | ||
|
fa10d794a4 | ||
|
40ede179cd | ||
|
0ed71ed581 | ||
|
dc75ebad00 | ||
|
900711687f | ||
|
71ccdc00e6 | ||
|
c4ebebe48e | ||
|
56d8301b2d | ||
|
09076e005b | ||
|
73a8efaec2 | ||
|
cdeb229396 | ||
|
122a99b51e | ||
|
e7aed89e8f | ||
|
a1254bc21a | ||
|
03231b2931 | ||
|
a2901cef23 | ||
|
95b021a839 | ||
|
76d6442557 | ||
|
5df22e92ae | ||
|
7306d8ef13 | ||
|
0595aba76a | ||
|
8366ec720e | ||
|
17f15e36eb | ||
|
ac249e5b07 | ||
|
e13772075c | ||
|
0fa33439d7 | ||
|
a4880453a4 | ||
|
964f827ee5 | ||
|
843daf633d | ||
|
930fcf9250 | ||
|
9bb4423dd7 | ||
|
9347b12b47 | ||
|
65b5c2043c | ||
|
08f72e7135 | ||
|
26c4e492fe | ||
|
00913d5b0b | ||
|
1851d1678e | ||
|
14dc46e226 | ||
|
be163412ab | ||
|
92d5c716be | ||
|
e42df50d31 | ||
|
203a6456ee | ||
|
40b918e139 | ||
|
f8eea5b7c7 | ||
|
b50d806534 | ||
|
cbcf83ed3b | ||
|
3394ff31e9 | ||
|
86d39fb003 | ||
|
bbb7679417 | ||
|
46248f601d | ||
|
a68e2b27a4 | ||
|
e2335e867d | ||
|
a10da427ff | ||
|
4561f56fb9 | ||
|
fae79ea910 | ||
|
9493e6230d | ||
|
927ea4eec5 | ||
|
89e821e70f | ||
|
9b2d7856d1 | ||
|
f93ec1b3b8 | ||
|
2e3197d510 | ||
|
85210ff6a1 | ||
|
e5f277e640 | ||
|
4bac667dfd | ||
|
6771b3e3b7 | ||
|
fd79a14cac | ||
|
c1aa1455d3 | ||
|
ae280016e7 | ||
|
0a8816938a | ||
|
008ec95505 | ||
|
925628c208 | ||
|
aae83c4fbc | ||
|
cacc8b4945 | ||
|
31765388c1 | ||
|
a08fd3e475 | ||
|
d4b2d89da0 | ||
|
9b27b0d9d7 | ||
|
a47187462d | ||
|
843a149b80 | ||
|
94381dca62 | ||
|
a2d5dd3397 |
@@ -15,5 +15,5 @@ indent_size = 2
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{php,xml}]
|
||||
[*.{php,xml,json}]
|
||||
indent_size = 4
|
||||
|
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@@ -7,10 +7,24 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: JS / Build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: flarum/action-build@master
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Restore npm cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('js/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
# Our action will install npm, cd into `./js`, run `npm run build`,
|
||||
# then commit and upload any changes
|
||||
- name: Build production JS
|
||||
uses: flarum/action-build@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
76
.github/workflows/codeql-analysis.yml
vendored
Normal file
76
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# 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
|
21
.github/workflows/lint.yml
vendored
21
.github/workflows/lint.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'js/src/**'
|
||||
@@ -10,22 +11,18 @@ on:
|
||||
|
||||
jobs:
|
||||
prettier:
|
||||
name: JS / Prettier
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: JS / Prettier
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "12"
|
||||
node-version: "14"
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: npm ci
|
||||
working-directory: ./js
|
||||
|
||||
- name: Check JS code for formatting
|
||||
run: node_modules/.bin/prettier --check src
|
||||
- name: Check JS formatting
|
||||
run: npx prettier --check src
|
||||
working-directory: ./js
|
||||
|
41
.github/workflows/pr_size_change.yml
vendored
Normal file
41
.github/workflows/pr_size_change.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Bundle size checker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
push:
|
||||
paths:
|
||||
- "js/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "js/**"
|
||||
|
||||
jobs:
|
||||
bundlewatch:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: Bundlewatch
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: npm ci
|
||||
working-directory: ./js
|
||||
|
||||
- name: Build production assets
|
||||
run: npm run build
|
||||
working-directory: ./js
|
||||
|
||||
- name: Check bundle size change
|
||||
run: node_modules/.bin/bundlewatch --config .bundlewatch.config.json
|
||||
working-directory: ./js
|
||||
env:
|
||||
BUNDLEWATCH_GITHUB_TOKEN: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
28
.github/workflows/test.yml
vendored
28
.github/workflows/test.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php: [7.2, 7.3, 7.4]
|
||||
php: [7.3, 7.4, '8.0']
|
||||
service: ['mysql:5.7', mariadb]
|
||||
prefix: ['', flarum_]
|
||||
|
||||
@@ -21,16 +21,16 @@ jobs:
|
||||
prefixStr: (prefix)
|
||||
|
||||
exclude:
|
||||
- php: 7.2
|
||||
- php: 7.3
|
||||
service: 'mysql:5.7'
|
||||
prefix: flarum_
|
||||
- php: 7.2
|
||||
- php: 7.3
|
||||
service: mariadb
|
||||
prefix: flarum_
|
||||
- php: 7.3
|
||||
- php: 8.0
|
||||
service: 'mysql:5.7'
|
||||
prefix: flarum_
|
||||
- php: 7.3
|
||||
- php: 8.0
|
||||
service: mariadb
|
||||
prefix: flarum_
|
||||
|
||||
@@ -43,15 +43,25 @@ jobs:
|
||||
name: 'PHP ${{ matrix.php }} / ${{ matrix.db }} ${{ matrix.prefixStr }}'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Select PHP version
|
||||
run: sudo update-alternatives --set php $(which php${{ matrix.php }})
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@0b9d33cd0782337377999751fc10ea079fdd7104 # pin@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: xdebug
|
||||
extensions: curl, dom, gd, json, mbstring, openssl, pdo_mysql, tokenizer, zip
|
||||
tools: phpunit, composer:v2
|
||||
|
||||
# The authentication alter is necessary because newer mysql versions use the `caching_sha2_password` driver,
|
||||
# which isn't supported prior to PHP7.4
|
||||
# When we drop support for PHP7.3, we should remove this from the setup.
|
||||
- name: Create MySQL Database
|
||||
run: |
|
||||
sudo systemctl start mysql
|
||||
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
|
||||
mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" --port 13306
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install
|
||||
@@ -65,3 +75,5 @@ jobs:
|
||||
|
||||
- name: Run Composer tests
|
||||
run: composer test
|
||||
env:
|
||||
COMPOSER_PROCESS_TIMEOUT: 600
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,6 +4,8 @@ composer.phar
|
||||
node_modules
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
tests/.phpunit.result.cache
|
||||
/tests/integration/tmp
|
||||
.vagrant
|
||||
.idea/*
|
||||
.vscode
|
||||
|
@@ -12,7 +12,3 @@ disabled:
|
||||
- phpdoc_order
|
||||
- phpdoc_separation
|
||||
- phpdoc_types
|
||||
|
||||
finder:
|
||||
exclude:
|
||||
- "stubs"
|
||||
|
93
CHANGELOG.md
93
CHANGELOG.md
@@ -1,5 +1,96 @@
|
||||
# Changelog
|
||||
|
||||
## [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
|
||||
@@ -210,7 +301,7 @@
|
||||
- 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
|
||||
- `Flarum\Event\ConfigureMiddleware` event
|
||||
|
||||
### Deprecated
|
||||
- `Flarum\Event\AbstractConfigureRoutes` event class
|
||||
|
@@ -1,36 +1,17 @@
|
||||
{
|
||||
"name": "flarum/core",
|
||||
"description": "Delightfully simple forum software.",
|
||||
"keywords": ["forum", "discussion"],
|
||||
"keywords": [
|
||||
"forum",
|
||||
"discussion"
|
||||
],
|
||||
"homepage": "https://flarum.org/",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Franz Liedke",
|
||||
"email": "franz@develophp.org"
|
||||
},
|
||||
{
|
||||
"name": "Daniël Klabbers",
|
||||
"email": "daniel@klabbers.email",
|
||||
"homepage": "https://luceos.com"
|
||||
},
|
||||
{
|
||||
"name": "David Sevilla Martin",
|
||||
"email": "me+flarum@datitisev.me",
|
||||
"homepage": "https://datitisev.me"
|
||||
},
|
||||
{
|
||||
"name": "Clark Winkelmann",
|
||||
"email": "clark.winkelmann@gmail.com",
|
||||
"homepage": "https://clarkwinkelmann.com"
|
||||
},
|
||||
{
|
||||
"name": "Matthew Kilgore",
|
||||
"email": "matthew@kilgore.dev"
|
||||
},
|
||||
{
|
||||
"name": "Alexander (Sasha) Skvortsov",
|
||||
"email": "askvortsov@flarum.org"
|
||||
"name": "Flarum",
|
||||
"email": "info@flarum.org",
|
||||
"homepage": "https://flarum.org/team"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
@@ -39,36 +20,38 @@
|
||||
"docs": "https://flarum.org/docs/"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"php": ">=7.3",
|
||||
"axy/sourcemap": "^0.1.4",
|
||||
"components/font-awesome": "^5.14.0",
|
||||
"dflydev/fig-cookies": "^2.0.1",
|
||||
"dflydev/fig-cookies": "^3.0.0",
|
||||
"doctrine/dbal": "^2.7",
|
||||
"franzl/whoops-middleware": "^0.4.0",
|
||||
"illuminate/bus": "^6.0",
|
||||
"illuminate/cache": "^6.0",
|
||||
"illuminate/config": "^6.0",
|
||||
"illuminate/container": "^6.0",
|
||||
"illuminate/contracts": "^6.0",
|
||||
"illuminate/database": "^6.0",
|
||||
"illuminate/events": "^6.0",
|
||||
"illuminate/filesystem": "^6.0",
|
||||
"illuminate/hashing": "^6.0",
|
||||
"illuminate/mail": "^6.0",
|
||||
"illuminate/queue": "^6.0",
|
||||
"illuminate/session": "^6.0",
|
||||
"illuminate/support": "^6.0",
|
||||
"illuminate/validation": "^6.0",
|
||||
"illuminate/view": "^6.0",
|
||||
"dragonmantank/cron-expression": "^3.1.0",
|
||||
"franzl/whoops-middleware": "^2.0.0",
|
||||
"illuminate/bus": "^8.0",
|
||||
"illuminate/cache": "^8.0",
|
||||
"illuminate/config": "^8.0",
|
||||
"illuminate/console": "^8.0",
|
||||
"illuminate/container": "^8.0",
|
||||
"illuminate/contracts": "^8.0",
|
||||
"illuminate/database": "^8.0",
|
||||
"illuminate/events": "^8.0",
|
||||
"illuminate/filesystem": "^8.0",
|
||||
"illuminate/hashing": "^8.0",
|
||||
"illuminate/mail": "^8.0",
|
||||
"illuminate/queue": "^8.0",
|
||||
"illuminate/session": "^8.0",
|
||||
"illuminate/support": "^8.0",
|
||||
"illuminate/validation": "^8.0",
|
||||
"illuminate/view": "^8.0",
|
||||
"intervention/image": "^2.5.0",
|
||||
"laminas/laminas-diactoros": "^1.8.4",
|
||||
"laminas/laminas-httphandlerrunner": "^1.0",
|
||||
"laminas/laminas-stratigility": "^3.0",
|
||||
"laminas/laminas-diactoros": "^2.4.1",
|
||||
"laminas/laminas-httphandlerrunner": "^1.2.0",
|
||||
"laminas/laminas-stratigility": "^3.2.2",
|
||||
"league/flysystem": "^1.0.11",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"middlewares/base-path": "^1.1",
|
||||
"middlewares/base-path-router": "^0.2.1",
|
||||
"middlewares/request-handler": "^1.2",
|
||||
"middlewares/base-path": "^2.0.1",
|
||||
"middlewares/base-path-router": "^2.0.1",
|
||||
"middlewares/request-handler": "^2.0.1",
|
||||
"monolog/monolog": "^1.16.0",
|
||||
"nesbot/carbon": "^2.0",
|
||||
"nikic/fast-route": "^0.6",
|
||||
@@ -76,18 +59,17 @@
|
||||
"psr/http-server-handler": "^1.0",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"s9e/text-formatter": "^2.3.6",
|
||||
"symfony/config": "^4.3.4",
|
||||
"symfony/console": "^4.3.4",
|
||||
"symfony/event-dispatcher": "^4.3.4",
|
||||
"symfony/config": "^5.2.2",
|
||||
"symfony/console": "^5.2.2",
|
||||
"symfony/event-dispatcher": "^5.2.2",
|
||||
"symfony/mime": "^5.2.0",
|
||||
"symfony/translation": "^4.3.4",
|
||||
"symfony/yaml": "^4.3.4",
|
||||
"symfony/translation": "^5.1.5",
|
||||
"symfony/yaml": "^5.2.2",
|
||||
"tobscure/json-api": "^0.3.0",
|
||||
"wikimedia/less.php": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"flarum/testing": "^0.1.0-beta.16"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
8
js/.bundlewatch.config.json
Normal file
8
js/.bundlewatch.config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"path": "./dist/*.js"
|
||||
}
|
||||
],
|
||||
"defaultCompression": "gzip"
|
||||
}
|
40
js/CustomLoader.js
Normal file
40
js/CustomLoader.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// Hi early reviewer! I'm a temporary file and
|
||||
// will be moved to the Flarum webpack config soon!
|
||||
|
||||
const loaderUtils = require('loader-utils');
|
||||
|
||||
// Custom loader logic
|
||||
module.exports = function (source) {
|
||||
|
||||
// Get the type of the module to be exported
|
||||
const location = loaderUtils.interpolateName(this, '[folder]/', {
|
||||
context: this.rootContext || this.context,
|
||||
});
|
||||
|
||||
// Get the name of module to be exported
|
||||
const moduleName = loaderUtils.interpolateName(this, '[name]', {
|
||||
context: this.rootContext || this.context,
|
||||
});
|
||||
|
||||
// Don't export low level files
|
||||
if (/.*\/(admin|forum)$/.test(location) || /(index|app|compat|FlarumRegistry)$/.test(moduleName)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
let addition = "";
|
||||
|
||||
// Find the export names
|
||||
const matches = [...source.matchAll(/export\s+?(?:default\s?|function|abstract\s?|class)+?\s([^(\s<;]*)/gm)];
|
||||
matches.map(match => {
|
||||
let name = match[1]
|
||||
|
||||
if (!name || name === 'interface') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add code at the end of the file to add the file to registry
|
||||
addition += `\nwindow.flreg.add('${location}${name}', ${name})`
|
||||
});
|
||||
|
||||
return source + addition;
|
||||
}
|
@@ -8,4 +8,4 @@
|
||||
*/
|
||||
|
||||
export * from './src/common';
|
||||
export * from './src/admin';
|
||||
export * from './src/admin';
|
||||
|
26
js/dist/admin.js
vendored
26
js/dist/admin.js
vendored
File diff suppressed because one or more lines are too long
1
js/dist/admin.js.map
vendored
1
js/dist/admin.js.map
vendored
File diff suppressed because one or more lines are too long
2
js/dist/admin/flarum-core.js
vendored
Normal file
2
js/dist/admin/flarum-core.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/admin/flarum-core.js.map
vendored
Normal file
1
js/dist/admin/flarum-core.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/admin/runtime.js
vendored
Normal file
2
js/dist/admin/runtime.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(()=>{var e,r={},o={};function t(e){var n=o[e];if(void 0!==n)return n.exports;var l=o[e]={exports:{}};return r[e].call(l.exports,l,l.exports,t),l.exports}t.m=r,e=[],t.O=(r,o,n,l)=>{if(!o){var a=1/0;for(f=0;f<e.length;f++){for(var[o,n,l]=e[f],i=!0,u=0;u<o.length;u++)(!1&l||a>=l)&&Object.keys(t.O).every((e=>t.O[e](o[u])))?o.splice(u--,1):(i=!1,l<a&&(a=l));i&&(e.splice(f--,1),r=n())}return r}l=l||0;for(var f=e.length;f>0&&e[f-1][2]>l;f--)e[f]=e[f-1];e[f]=[o,n,l]},t.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return t.d(r,{a:r}),r},t.d=(e,r)=>{for(var o in r)t.o(r,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),t.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e={93:0};t.O.j=r=>0===e[r];var r=(r,o)=>{var n,l,[a,i,u]=o,f=0;for(n in i)t.o(i,n)&&(t.m[n]=i[n]);for(u&&u(t),r&&r(o);f<a.length;f++)l=a[f],t.o(e,l)&&e[l]&&e[l][0](),e[a[f]]=0;t.O()},o=self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[];o.forEach(r.bind(null,0)),o.push=r.bind(null,o.push.bind(o))})(),t.O()})();
|
||||
//# sourceMappingURL=runtime.js.map
|
1
js/dist/admin/runtime.js.map
vendored
Normal file
1
js/dist/admin/runtime.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/common/common.js
vendored
Normal file
2
js/dist/common/common.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/common/common.js.map
vendored
Normal file
1
js/dist/common/common.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/common/components/TextEditor.js
vendored
Normal file
2
js/dist/common/components/TextEditor.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/common/components/TextEditor.js.map
vendored
Normal file
1
js/dist/common/components/TextEditor.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
53
js/dist/forum.js
vendored
53
js/dist/forum.js
vendored
File diff suppressed because one or more lines are too long
1
js/dist/forum.js.map
vendored
1
js/dist/forum.js.map
vendored
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/ChangeEmailModal.js
vendored
Normal file
2
js/dist/forum/components/ChangeEmailModal.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[372],{4146:(a,s,t)=>{t.r(s),t.d(s,{default:()=>n});var e=t(1788),o=t(5556),r=t(2587),i=t(1024),n=function(a){function s(){return a.apply(this,arguments)||this}(0,e.Z)(s,a);var t=s.prototype;return t.oninit=function(s){a.prototype.oninit.call(this,s),this.success=!1,this.email=(0,i.Z)(app.session.user.email()),this.password=(0,i.Z)("")},t.className=function(){return"ChangeEmailModal Modal--small"},t.title=function(){return app.translator.trans("core.forum.change_email.title")},t.content=function(){return this.success?m("div",{className:"Modal-body"},m("div",{className:"Form Form--centered"},m("p",{className:"helpText"},app.translator.trans("core.forum.change_email.confirmation_message",{email:m("strong",null,this.email())})),m("div",{className:"Form-group"},m(r.Z,{className:"Button Button--primary Button--block",onclick:this.hide.bind(this)},app.translator.trans("core.forum.change_email.dismiss_button"))))):m("div",{className:"Modal-body"},m("div",{className:"Form Form--centered"},m("div",{className:"Form-group"},m("input",{type:"email",name:"email",className:"FormControl",placeholder:app.session.user.email(),bidi:this.email,disabled:this.loading})),m("div",{className:"Form-group"},m("input",{type:"password",name:"password",className:"FormControl",placeholder:app.translator.trans("core.forum.change_email.confirm_password_placeholder"),bidi:this.password,disabled:this.loading})),m("div",{className:"Form-group"},r.Z.component({className:"Button Button--primary Button--block",type:"submit",loading:this.loading},app.translator.trans("core.forum.change_email.submit_button")))))},t.onsubmit=function(a){var s=this;a.preventDefault(),this.email()!==app.session.user.email()?(this.loading=!0,this.alertAttrs=null,app.session.user.save({email:this.email()},{errorHandler:this.onerror.bind(this),meta:{password:this.password()}}).then((function(){s.success=!0})).catch((function(){})).then(this.loaded.bind(this))):this.hide()},t.onerror=function(s){401===s.status&&(s.alert.content=app.translator.trans("core.forum.change_email.incorrect_password_message")),a.prototype.onerror.call(this,s)},s}(o.Z);window.flreg.add("components/ChangeEmailModal",n)}}]);
|
||||
//# sourceMappingURL=ChangeEmailModal.js.map
|
1
js/dist/forum/components/ChangeEmailModal.js.map
vendored
Normal file
1
js/dist/forum/components/ChangeEmailModal.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/ChangePasswordModal.js
vendored
Normal file
2
js/dist/forum/components/ChangePasswordModal.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[503],{22:(t,a,e)=>{e.r(a),e.d(a,{default:()=>s});var o=e(1788),r=e(5556),n=e(2587),s=function(t){function a(){return t.apply(this,arguments)||this}(0,o.Z)(a,t);var e=a.prototype;return e.className=function(){return"ChangePasswordModal Modal--small"},e.title=function(){return app.translator.trans("core.forum.change_password.title")},e.content=function(){return m("div",{className:"Modal-body"},m("div",{className:"Form Form--centered"},m("p",{className:"helpText"},app.translator.trans("core.forum.change_password.text")),m("div",{className:"Form-group"},n.Z.component({className:"Button Button--primary Button--block",type:"submit",loading:this.loading},app.translator.trans("core.forum.change_password.send_button")))))},e.onsubmit=function(t){t.preventDefault(),this.loading=!0,app.request({method:"POST",url:app.forum.attribute("apiUrl")+"/forgot",body:{email:app.session.user.email()}}).then(this.hide.bind(this),this.loaded.bind(this))},a}(r.Z);window.flreg.add("components/ChangePasswordModal",s)}}]);
|
||||
//# sourceMappingURL=ChangePasswordModal.js.map
|
1
js/dist/forum/components/ChangePasswordModal.js.map
vendored
Normal file
1
js/dist/forum/components/ChangePasswordModal.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack://@flarum/core/./src/forum/components/ChangePasswordModal.js"],"names":["ChangePasswordModal","className","title","app","translator","trans","content","Button","type","loading","this","onsubmit","e","preventDefault","request","method","url","forum","attribute","body","email","session","user","then","hide","bind","loaded","Modal","window","flreg","add"],"mappings":"4JAOqBA,E,sGACnBC,UAAA,WACE,MAAO,oC,EAGTC,MAAA,WACE,OAAOC,IAAIC,WAAWC,MAAM,qC,EAG9BC,QAAA,WACE,OACE,SAAKL,UAAU,cACb,SAAKA,UAAU,uBACb,OAAGA,UAAU,YAAYE,IAAIC,WAAWC,MAAM,oCAC9C,SAAKJ,UAAU,cACZM,cACC,CACEN,UAAW,uCACXO,KAAM,SACNC,QAASC,KAAKD,SAEhBN,IAAIC,WAAWC,MAAM,+C,EAQjCM,SAAA,SAASC,GACPA,EAAEC,iBAEFH,KAAKD,SAAU,EAEfN,IACGW,QAAQ,CACPC,OAAQ,OACRC,IAAKb,IAAIc,MAAMC,UAAU,UAAY,UACrCC,KAAM,CAAEC,MAAOjB,IAAIkB,QAAQC,KAAKF,WAEjCG,KAAKb,KAAKc,KAAKC,KAAKf,MAAOA,KAAKgB,OAAOD,KAAKf,Q,GAxCFiB,KA4CjDC,OAAOC,MAAMC,IAAI,iCAAkC9B","file":"forum/components/ChangePasswordModal.js","sourcesContent":["import Modal from '../../common/components/Modal';\nimport Button from '../../common/components/Button';\n\n/**\n * The `ChangePasswordModal` component shows a modal dialog which allows the\n * user to send themself a password reset email.\n */\nexport default class ChangePasswordModal extends Modal {\n className() {\n return 'ChangePasswordModal Modal--small';\n }\n\n title() {\n return app.translator.trans('core.forum.change_password.title');\n }\n\n content() {\n return (\n <div className=\"Modal-body\">\n <div className=\"Form Form--centered\">\n <p className=\"helpText\">{app.translator.trans('core.forum.change_password.text')}</p>\n <div className=\"Form-group\">\n {Button.component(\n {\n className: 'Button Button--primary Button--block',\n type: 'submit',\n loading: this.loading,\n },\n app.translator.trans('core.forum.change_password.send_button')\n )}\n </div>\n </div>\n </div>\n );\n }\n\n onsubmit(e) {\n e.preventDefault();\n\n this.loading = true;\n\n app\n .request({\n method: 'POST',\n url: app.forum.attribute('apiUrl') + '/forgot',\n body: { email: app.session.user.email() },\n })\n .then(this.hide.bind(this), this.loaded.bind(this));\n }\n}\n\nwindow.flreg.add('components/ChangePasswordModal', ChangePasswordModal)"],"sourceRoot":""}
|
2
js/dist/forum/components/DiscussionComposer.js
vendored
Normal file
2
js/dist/forum/components/DiscussionComposer.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[437],{3406:(t,s,o)=>{o.r(s),o.d(s,{default:()=>a});var e=o(1788),i=o(4810),r=o(5731),n=o(1024),a=function(t){function s(){return t.apply(this,arguments)||this}(0,e.Z)(s,t),s.initAttrs=function(s){t.initAttrs.call(this,s),s.placeholder=s.placeholder||(0,r.Z)(app.translator.trans("core.forum.composer_discussion.body_placeholder")),s.submitLabel=s.submitLabel||app.translator.trans("core.forum.composer_discussion.submit_button"),s.confirmExit=s.confirmExit||(0,r.Z)(app.translator.trans("core.forum.composer_discussion.discard_confirmation")),s.titlePlaceholder=s.titlePlaceholder||(0,r.Z)(app.translator.trans("core.forum.composer_discussion.title_placeholder")),s.className="ComposerBody--discussion"};var o=s.prototype;return o.oninit=function(s){t.prototype.oninit.call(this,s),this.composer.fields.title=this.composer.fields.title||(0,n.Z)(""),this.title=this.composer.fields.title},o.headerItems=function(){var s=t.prototype.headerItems.call(this);return s.add("title",m("h3",null,app.translator.trans("core.forum.composer_discussion.title")),100),s.add("discussionTitle",m("h3",null,m("input",{className:"FormControl",bidi:this.title,placeholder:this.attrs.titlePlaceholder,disabled:!!this.attrs.disabled,onkeydown:this.onkeydown.bind(this)}))),s},o.onkeydown=function(t){13===t.which&&(t.preventDefault(),this.composer.editor.moveCursorTo(0)),t.redraw=!1},o.hasChanges=function(){return this.title()||this.composer.fields.content()},o.data=function(){return{title:this.title(),content:this.composer.fields.content()}},o.onsubmit=function(){var t=this;this.loading=!0;var s=this.data();app.store.createRecord("discussions").save(s).then((function(s){t.composer.hide(),app.discussions.refresh({deferClear:!0}),m.route.set(app.route.discussion(s))}),this.loaded.bind(this))},s}(i.Z);window.flreg.add("components/DiscussionComposer",a)}}]);
|
||||
//# sourceMappingURL=DiscussionComposer.js.map
|
1
js/dist/forum/components/DiscussionComposer.js.map
vendored
Normal file
1
js/dist/forum/components/DiscussionComposer.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/DiscussionsUserPage.js
vendored
Normal file
2
js/dist/forum/components/DiscussionsUserPage.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[799],{5813:(t,e,s)=>{s.a(t,(async t=>{s.r(e),s.d(e,{default:()=>r});var n=s(1788),a=s(486),o=s(2060),r=function(t){function e(){return t.apply(this,arguments)||this}(0,n.Z)(e,t);var s=e.prototype;return s.oninit=function(e){t.prototype.oninit.call(this,e),this.loadUser(m.route.param("username"))},s.show=function(e){t.prototype.show.call(this,e),this.state=new o.Z({q:"author:"+e.username(),sort:"newest"}),this.state.refresh()},s.content=function(){return m("div",{className:"DiscussionsUserPage"},a.Z.component({state:this.state}))},e}((await s.e(251).then(s.bind(s,6068))).default);window.flreg.add("components/DiscussionsUserPage",r),t()}),1)}}]);
|
||||
//# sourceMappingURL=DiscussionsUserPage.js.map
|
1
js/dist/forum/components/DiscussionsUserPage.js.map
vendored
Normal file
1
js/dist/forum/components/DiscussionsUserPage.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack://@flarum/core/./src/forum/components/DiscussionsUserPage.js"],"names":["DiscussionsUserPage","oninit","vnode","this","loadUser","m","route","param","show","user","state","DiscussionListState","q","username","sort","refresh","content","className","DiscussionList","window","flreg","add"],"mappings":"8KASqBA,E,sGACnBC,OAAA,SAAOC,GACL,YAAMD,OAAN,UAAaC,GAEbC,KAAKC,SAASC,EAAEC,MAAMC,MAAM,c,EAG9BC,KAAA,SAAKC,GACH,YAAMD,KAAN,UAAWC,GAEXN,KAAKO,MAAQ,IAAIC,IAAoB,CACnCC,EAAG,UAAYH,EAAKI,WACpBC,KAAM,WAGRX,KAAKO,MAAMK,W,EAGbC,QAAA,WACE,OAAO,SAAKC,UAAU,uBAAuBC,cAAyB,CAAER,MAAOP,KAAKO,U,UAzBhE,+BAAP,SA6BjBS,OAAOC,MAAMC,IAAI,iCAAkCrB,G","file":"forum/components/DiscussionsUserPage.js","sourcesContent":["import DiscussionList from './DiscussionList';\nimport DiscussionListState from '../states/DiscussionListState';\n\nconst UserPage = (await import(/* webpackChunkName: \"forum/components/UserPage\" */ './UserPage')).default;\n\n/**\n * The `DiscussionsUserPage` component shows a discussion list inside of a user\n * page.\n */\nexport default class DiscussionsUserPage extends UserPage {\n oninit(vnode) {\n super.oninit(vnode);\n\n this.loadUser(m.route.param('username'));\n }\n\n show(user) {\n super.show(user);\n\n this.state = new DiscussionListState({\n q: 'author:' + user.username(),\n sort: 'newest',\n });\n\n this.state.refresh();\n }\n\n content() {\n return <div className=\"DiscussionsUserPage\">{DiscussionList.component({ state: this.state })}</div>;\n }\n}\n\nwindow.flreg.add('components/DiscussionsUserPage', DiscussionsUserPage)"],"sourceRoot":""}
|
2
js/dist/forum/components/EditPostComposer.js
vendored
Normal file
2
js/dist/forum/components/EditPostComposer.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[293],{763:(t,o,s)=>{s.r(o),s.d(o,{default:()=>u});var e=s(1788),r=s(4810),n=s(2587),i=s(8046),a=s(9100);function p(t){app.composer.isFullScreen()&&(app.composer.minimize(),t.stopPropagation())}var u=function(t){function o(){return t.apply(this,arguments)||this}(0,e.Z)(o,t),o.initAttrs=function(o){t.initAttrs.call(this,o),o.submitLabel=o.submitLabel||app.translator.trans("core.forum.composer_edit.submit_button"),o.confirmExit=o.confirmExit||app.translator.trans("core.forum.composer_edit.discard_confirmation"),o.originalContent=o.originalContent||o.post.content(),o.user=o.user||o.post.user(),o.post.editedContent=o.originalContent};var s=o.prototype;return s.headerItems=function(){var o=t.prototype.headerItems.call(this),s=this.attrs.post;return o.add("title",m("h3",null,(0,a.Z)("fas fa-pencil-alt")," ",m(i.Z,{href:app.route.discussion(s.discussion(),s.number()),onclick:p},app.translator.trans("core.forum.composer_edit.post_link",{number:s.number(),discussion:s.discussion().title()})))),o},s.jumpToPreview=function(t){p(t),m.route.set(app.route.post(this.attrs.post))},s.data=function(){return{content:this.composer.fields.content()}},s.onsubmit=function(){var t=this,o=this.attrs.post.discussion();this.loading=!0;var s=this.data();this.attrs.post.save(s).then((function(s){if(app.viewingDiscussion(o))app.current.get("stream").goToNumber(s.number());else{var e,r=n.Z.component({className:"Button Button--link",onclick:function(){m.route.set(app.route.post(s)),app.alerts.dismiss(e)}},app.translator.trans("core.forum.composer_edit.view_button"));e=app.alerts.show({type:"success",controls:[r]},app.translator.trans("core.forum.composer_edit.edited_message"))}t.composer.hide()}),this.loaded.bind(this))},o}(r.Z);window.flreg.add("components/EditPostComposer",u)}}]);
|
||||
//# sourceMappingURL=EditPostComposer.js.map
|
1
js/dist/forum/components/EditPostComposer.js.map
vendored
Normal file
1
js/dist/forum/components/EditPostComposer.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/EditUserModal.js
vendored
Normal file
2
js/dist/forum/components/EditUserModal.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[352],{3089:(s,r,t)=>{t.r(r),t.d(r,{default:()=>p});var e=t(1788),a=t(5556),i=t(2587),n=t(7880),o=t(4032),d=t(5731),u=t(4594),l=t(1024),p=function(s){function r(){return s.apply(this,arguments)||this}(0,e.Z)(r,s);var t=r.prototype;return t.oninit=function(r){var t=this;s.prototype.oninit.call(this,r);var e=this.attrs.user;this.username=(0,l.Z)(e.username()||""),this.email=(0,l.Z)(e.email()||""),this.isEmailConfirmed=(0,l.Z)(e.isEmailConfirmed()||!1),this.setPassword=(0,l.Z)(!1),this.password=(0,l.Z)(e.password()||""),this.groups={},app.store.all("groups").filter((function(s){return-1===[o.Z.GUEST_ID,o.Z.MEMBER_ID].indexOf(s.id())})).forEach((function(s){return t.groups[s.id()]=(0,l.Z)(-1!==e.groups().indexOf(s))}))},t.className=function(){return"EditUserModal Modal--small"},t.title=function(){return app.translator.trans("core.forum.edit_user.title")},t.content=function(){var s=this.fields().toArray();return m("div",{className:"Modal-body"},s.length>1?m("div",{className:"Form"},this.fields().toArray()):app.translator.trans("core.forum.edit_user.nothing_available"))},t.fields=function(){var s=this,r=new u.Z;return app.session.user.canEditCredentials()&&(r.add("username",m("div",{className:"Form-group"},m("label",null,app.translator.trans("core.forum.edit_user.username_heading")),m("input",{className:"FormControl",placeholder:(0,d.Z)(app.translator.trans("core.forum.edit_user.username_label")),bidi:this.username,disabled:this.nonAdminEditingAdmin()})),40),app.session.user!==this.attrs.user&&(r.add("email",m("div",{className:"Form-group"},m("label",null,app.translator.trans("core.forum.edit_user.email_heading")),m("div",null,m("input",{className:"FormControl",placeholder:(0,d.Z)(app.translator.trans("core.forum.edit_user.email_label")),bidi:this.email,disabled:this.nonAdminEditingAdmin()})),!this.isEmailConfirmed()&&this.userIsAdmin(app.session.user)?m("div",null,i.Z.component({className:"Button Button--block",loading:this.loading,onclick:this.activate.bind(this)},app.translator.trans("core.forum.edit_user.activate_button"))):""),30),r.add("password",m("div",{className:"Form-group"},m("label",null,app.translator.trans("core.forum.edit_user.password_heading")),m("div",null,m("label",{className:"checkbox"},m("input",{type:"checkbox",onchange:function(r){s.setPassword(r.target.checked),m.redraw.sync(),r.target.checked&&s.$("[name=password]").select(),r.redraw=!1},disabled:this.nonAdminEditingAdmin()}),app.translator.trans("core.forum.edit_user.set_password_label")),this.setPassword()?m("input",{className:"FormControl",type:"password",name:"password",placeholder:(0,d.Z)(app.translator.trans("core.forum.edit_user.password_label")),bidi:this.password,disabled:this.nonAdminEditingAdmin()}):"")),20))),app.session.user.canEditGroups()&&r.add("groups",m("div",{className:"Form-group EditUserModal-groups"},m("label",null,app.translator.trans("core.forum.edit_user.groups_heading")),m("div",null,Object.keys(this.groups).map((function(s){return app.store.getById("groups",s)})).map((function(r){return m("label",{className:"checkbox"},m("input",{type:"checkbox",bidi:s.groups[r.id()],disabled:r.id()===o.Z.ADMINISTRATOR_ID&&(s.attrs.user===app.session.user||!s.userIsAdmin(app.session.user))}),n.Z.component({group:r,label:""})," ",r.nameSingular())})))),10),r.add("submit",m("div",{className:"Form-group"},i.Z.component({className:"Button Button--primary",type:"submit",loading:this.loading},app.translator.trans("core.forum.edit_user.submit_button"))),-10),r},t.activate=function(){var s=this;this.loading=!0;var r={username:this.username(),isEmailConfirmed:!0};this.attrs.user.save(r,{errorHandler:this.onerror.bind(this)}).then((function(){s.isEmailConfirmed(!0),s.loading=!1,m.redraw()})).catch((function(){s.loading=!1,m.redraw()}))},t.data=function(){var s=this,r={relationships:{}};return this.attrs.user.canEditCredentials()&&!this.nonAdminEditingAdmin()&&(r.username=this.username(),app.session.user!==this.attrs.user&&(r.email=this.email()),this.setPassword()&&(r.password=this.password())),this.attrs.user.canEditGroups()&&(r.relationships.groups=Object.keys(this.groups).filter((function(r){return s.groups[r]()})).map((function(s){return app.store.getById("groups",s)}))),r},t.onsubmit=function(s){var r=this;s.preventDefault(),this.loading=!0,this.attrs.user.save(this.data(),{errorHandler:this.onerror.bind(this)}).then(this.hide.bind(this)).catch((function(){r.loading=!1,m.redraw()}))},t.nonAdminEditingAdmin=function(){return this.userIsAdmin(this.attrs.user)&&!this.userIsAdmin(app.session.user)},t.userIsAdmin=function(s){return s.groups().some((function(s){return s.id()===o.Z.ADMINISTRATOR_ID}))},r}(a.Z);window.flreg.add("components/EditUserModal",p)}}]);
|
||||
//# sourceMappingURL=EditUserModal.js.map
|
1
js/dist/forum/components/EditUserModal.js.map
vendored
Normal file
1
js/dist/forum/components/EditUserModal.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/ForgotPasswordModal.js
vendored
Normal file
2
js/dist/forum/components/ForgotPasswordModal.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[502],{5833:(t,o,r)=>{r.r(o),r.d(o,{default:()=>l});var a=r(1788),s=r(5556),e=r(2587),n=r(5731),i=r(1024),l=function(t){function o(){return t.apply(this,arguments)||this}(0,a.Z)(o,t);var r=o.prototype;return r.oninit=function(o){t.prototype.oninit.call(this,o),this.email=(0,i.Z)(this.attrs.email||""),this.success=!1},r.className=function(){return"ForgotPasswordModal Modal--small"},r.title=function(){return app.translator.trans("core.forum.forgot_password.title")},r.content=function(){return this.success?m("div",{className:"Modal-body"},m("div",{className:"Form Form--centered"},m("p",{className:"helpText"},app.translator.trans("core.forum.forgot_password.email_sent_message")),m("div",{className:"Form-group"},m(e.Z,{className:"Button Button--primary Button--block",onclick:this.hide.bind(this)},app.translator.trans("core.forum.forgot_password.dismiss_button"))))):m("div",{className:"Modal-body"},m("div",{className:"Form Form--centered"},m("p",{className:"helpText"},app.translator.trans("core.forum.forgot_password.text")),m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"email",type:"email",placeholder:(0,n.Z)(app.translator.trans("core.forum.forgot_password.email_placeholder")),bidi:this.email,disabled:this.loading})),m("div",{className:"Form-group"},e.Z.component({className:"Button Button--primary Button--block",type:"submit",loading:this.loading},app.translator.trans("core.forum.forgot_password.submit_button")))))},r.onsubmit=function(t){var o=this;t.preventDefault(),this.loading=!0,app.request({method:"POST",url:app.forum.attribute("apiUrl")+"/forgot",body:{email:this.email()},errorHandler:this.onerror.bind(this)}).then((function(){o.success=!0,o.alert=null})).catch((function(){})).then(this.loaded.bind(this))},r.onerror=function(o){404===o.status&&(o.alert.content=app.translator.trans("core.forum.forgot_password.not_found_message")),t.prototype.onerror.call(this,o)},o}(s.Z);window.flreg.add("components/ForgotPasswordModal",l)}}]);
|
||||
//# sourceMappingURL=ForgotPasswordModal.js.map
|
1
js/dist/forum/components/ForgotPasswordModal.js.map
vendored
Normal file
1
js/dist/forum/components/ForgotPasswordModal.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/IndexPage.js
vendored
Normal file
2
js/dist/forum/components/IndexPage.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/forum/components/IndexPage.js.map
vendored
Normal file
1
js/dist/forum/components/IndexPage.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/LogInModal.js
vendored
Normal file
2
js/dist/forum/components/LogInModal.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[460],{7527:(t,o,i)=>{i.d(o,{Z:()=>e});var n=i(1788),r=i(8931),a=i(4594),e=function(t){function o(){return t.apply(this,arguments)||this}(0,n.Z)(o,t);var i=o.prototype;return i.view=function(){return m("div",{className:"LogInButtons"},this.items().toArray())},i.items=function(){return new a.Z},o}(r.Z);window.flreg.add("components/LogInButtons",e)},1498:(t,o,i)=>{i.r(o),i.d(o,{default:()=>c});var n=i(1788),r=i(5556),a=i(2587),e=i(7527),s=i(5731),l=i(4594),d=i(1024),c=function(t){function o(){return t.apply(this,arguments)||this}(0,n.Z)(o,t);var r=o.prototype;return r.oninit=function(o){t.prototype.oninit.call(this,o),this.identification=(0,d.Z)(this.attrs.identification||""),this.password=(0,d.Z)(this.attrs.password||""),this.remember=(0,d.Z)(!!this.attrs.remember)},r.className=function(){return"LogInModal Modal--small"},r.title=function(){return app.translator.trans("core.forum.log_in.title")},r.content=function(){return[m("div",{className:"Modal-body"},this.body()),m("div",{className:"Modal-footer"},this.footer())]},r.body=function(){return[m(e.Z,null),m("div",{className:"Form Form--centered"},this.fields().toArray())]},r.fields=function(){var t=new l.Z;return t.add("identification",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"identification",type:"text",placeholder:(0,s.Z)(app.translator.trans("core.forum.log_in.username_or_email_placeholder")),bidi:this.identification,disabled:this.loading})),30),t.add("password",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"password",type:"password",placeholder:(0,s.Z)(app.translator.trans("core.forum.log_in.password_placeholder")),bidi:this.password,disabled:this.loading})),20),t.add("remember",m("div",{className:"Form-group"},m("div",null,m("label",{className:"checkbox"},m("input",{type:"checkbox",bidi:this.remember,disabled:this.loading}),app.translator.trans("core.forum.log_in.remember_me_label")))),10),t.add("submit",m("div",{className:"Form-group"},a.Z.component({className:"Button Button--primary Button--block",type:"submit",loading:this.loading},app.translator.trans("core.forum.log_in.submit_button"))),-10),t},r.footer=function(){return[m("p",{className:"LogInModal-forgotPassword"},m("a",{onclick:this.forgotPassword.bind(this)},app.translator.trans("core.forum.log_in.forgot_password_link"))),app.forum.attribute("allowSignUp")?m("p",{className:"LogInModal-signUp"},app.translator.trans("core.forum.log_in.sign_up_text",{a:m("a",{onclick:this.signUp.bind(this)})})):""]},r.forgotPassword=function(){var t=this.identification(),o=-1!==t.indexOf("@")?{email:t}:void 0;app.modal.show((function(){return i.e(502).then(i.bind(i,5833))}),o)},r.signUp=function(){var t={password:this.password()},o=this.identification();t[-1!==o.indexOf("@")?"email":"username"]=o,app.modal.show((function(){return i.e(395).then(i.bind(i,3235))}),t)},r.onready=function(){this.$("[name="+(this.identification()?"password":"identification")+"]").select()},r.onsubmit=function(t){t.preventDefault(),this.loading=!0;var o=this.identification(),i=this.password(),n=this.remember();app.session.login({identification:o,password:i,remember:n},{errorHandler:this.onerror.bind(this)}).then((function(){return window.location.reload()}),this.loaded.bind(this))},r.onerror=function(o){401===o.status&&(o.alert.content=app.translator.trans("core.forum.log_in.invalid_login_message")),t.prototype.onerror.call(this,o)},o}(r.Z);window.flreg.add("components/LogInModal",c)}}]);
|
||||
//# sourceMappingURL=LogInModal.js.map
|
1
js/dist/forum/components/LogInModal.js.map
vendored
Normal file
1
js/dist/forum/components/LogInModal.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/NotificationList.js
vendored
Normal file
2
js/dist/forum/components/NotificationList.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[77],{3490:(t,i,n)=>{n.r(i),n.d(i,{default:()=>N});var o=n(1788),a=n(8931),s=n(7473),e=n(2587),c=n(8046),r=n(8726),l=n(8197),u=n(9908),f=n(9100),p=n(6961),d=function(t){function i(){return t.apply(this,arguments)||this}(0,o.Z)(i,t);var n=i.prototype;return n.view=function(){var t=this,i=this.attrs.notification,n=this.href();return m(c.Z,{className:"Notification Notification--"+i.contentType()+" "+(i.isRead()?"":"unread"),href:n,external:n.includes("://"),onclick:this.markAsRead.bind(this)},!i.isRead()&&e.Z.component({className:"Notification-action Button Button--icon Button--link",icon:"fas fa-check",title:app.translator.trans("core.forum.notifications.mark_as_read_tooltip"),onclick:function(i){i.preventDefault(),i.stopPropagation(),t.markAsRead()}}),(0,u.Z)(i.fromUser()),(0,f.Z)(this.icon(),{className:"Notification-icon"}),m("span",{className:"Notification-content"},this.content()),(0,p.Z)(i.createdAt()),m("div",{className:"Notification-excerpt"},this.excerpt()))},n.icon=function(){},n.href=function(){},n.content=function(){},n.excerpt=function(){},n.markAsRead=function(){this.attrs.notification.isRead()||(app.session.user.pushAttributes({unreadNotificationCount:app.session.user.unreadNotificationCount()-1}),this.attrs.notification.save({isRead:!0}))},i}(a.Z);window.flreg.add("components/Notification",d);var h=function(t){function i(){return t.apply(this,arguments)||this}(0,o.Z)(i,t);var n=i.prototype;return n.icon=function(){return"fas fa-pencil-alt"},n.href=function(){var t=this.attrs.notification;return app.route.discussion(t.subject(),t.content().postNumber)},n.content=function(){return app.translator.trans("core.forum.notifications.discussion_renamed_text",{user:this.attrs.notification.fromUser()})},i}(d);window.flreg.add("components/DiscussionRenamedNotification",h);var N=function(t){function i(){return t.apply(this,arguments)||this}(0,o.Z)(i,t);var n=i.prototype;return n.view=function(){var t=this,i=this.attrs.state,n=i.getNotificationPages();return m("div",{className:"NotificationList"},m("div",{className:"NotificationList-header"},m("div",{className:"App-primaryControl"},e.Z.component({className:"Button Button--icon Button--link",icon:"fas fa-check",title:app.translator.trans("core.forum.notifications.mark_all_as_read_tooltip"),onclick:i.markAllAsRead.bind(i)})),m("h4",{className:"App-titleControl App-titleControl--text"},app.translator.trans("core.forum.notifications.title"))),m("div",{className:"NotificationList-content"},n.length?n.map((function(i){var n=[],o={};return i.forEach((function(t){var i=t.subject();if(void 0!==i){var a=!1;i instanceof l.Z?a=i:i&&i.discussion&&(a=i.discussion());var s=a?a.id():0;o[s]=o[s]||{discussion:a,notifications:[]},o[s].notifications.push(t),-1===n.indexOf(o[s])&&n.push(o[s])}})),n.map((function(i){var n=i.discussion&&i.discussion.badges().toArray();return m("div",{className:"NotificationGroup"},i.discussion?m(c.Z,{className:"NotificationGroup-header",href:app.route.discussion(i.discussion)},n&&n.length?m("ul",{className:"NotificationGroup-badges badges"},(0,s.Z)(n)):"",i.discussion.title()):m("div",{className:"NotificationGroup-header"},app.forum.attribute("title")),m("ul",{className:"NotificationGroup-content"},i.notifications.map((function(i){var n=t.notificationComponents()[i.contentType()];return n?m("li",null,n.component({notification:i})):""}))))}))})):"",i.isLoading()?m(r.Z,{className:"LoadingIndicator--block"}):n.length?"":m("div",{className:"NotificationList-empty"},app.translator.trans("core.forum.notifications.empty_text"))))},n.notificationComponents=function(){return{discussionRenamed:h}},n.oncreate=function(i){t.prototype.oncreate.call(this,i),this.$notifications=this.$(".NotificationList-content"),this.$scrollParent=this.inPanel()?this.$notifications:$(window),this.boundScrollHandler=this.scrollHandler.bind(this),this.$scrollParent.on("scroll",this.boundScrollHandler)},n.onremove=function(){this.$scrollParent.off("scroll",this.boundScrollHandler)},n.scrollHandler=function(){var t=this.attrs.state,i=this.inPanel()?this.$scrollParent[0]:document.documentElement,n=Math.abs(i.scrollHeight-i.scrollTop-i.clientHeight)<=1;t.hasMoreResults()&&!t.isLoading()&&n&&t.loadMore()},n.inPanel=function(){return"auto"===this.$notifications.css("overflow")},i}(a.Z);window.flreg.add("components/NotificationList",N)}}]);
|
||||
//# sourceMappingURL=NotificationList.js.map
|
1
js/dist/forum/components/NotificationList.js.map
vendored
Normal file
1
js/dist/forum/components/NotificationList.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/NotificationsDropdown.js
vendored
Normal file
2
js/dist/forum/components/NotificationsDropdown.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[765],{9440:(t,n,o)=>{o.r(n),o.d(n,{default:()=>s});var a=o(1788),e=o(9657),i=o(9100),s=function(t){function n(){return t.apply(this,arguments)||this}(0,a.Z)(n,t),n.initAttrs=function(n){n.className=n.className||"NotificationsDropdown",n.buttonClassName=n.buttonClassName||"Button Button--flat",n.menuClassName=n.menuClassName||"Dropdown-menu--right",n.label=n.label||app.translator.trans("core.forum.notifications.tooltip"),n.icon=n.icon||"fas fa-bell",n.accessibleToggleLabel=n.accessibleToggleLabel||app.translator.trans("core.forum.notifications.toggle_dropdown_accessible_label"),t.initAttrs.call(this,n)};var e=n.prototype;return e.getButton=function(){var n=this.getNewCount(),o=t.prototype.getButton.call(this);return o.attrs.title=this.attrs.label,o.attrs.className+=n?" new":"",o.attrs.onclick=this.onclick.bind(this),o},e.getButtonContent=function(){var t=this.getUnreadCount();return[(0,i.Z)(this.attrs.icon,{className:"Button-icon"}),t?m("span",{className:"NotificationsDropdown-unread"},t):"",m("span",{className:"Button-label"},this.attrs.label)]},e.getMenu=function(){var t=this.NotificationList;return m("div",{className:"Dropdown-menu "+this.attrs.menuClassName,onclick:this.menuClick.bind(this)},this.showing&&t?m(t,{state:this.attrs.state}):"")},e.onclick=function(){var t=this;o.e(77).then(o.bind(o,3490)).then((function(n){t.NotificationList=n.default,m.redraw()})),app.drawer.isOpen()?this.goToRoute():this.attrs.state.load()},e.goToRoute=function(){m.route.set(app.route("notifications"))},e.getUnreadCount=function(){return app.session.user.unreadNotificationCount()},e.getNewCount=function(){return app.session.user.newNotificationCount()},e.menuClick=function(t){(t.shiftKey||t.metaKey||t.ctrlKey||2===t.which)&&t.stopPropagation()},n}(e.Z);window.flreg.add("components/NotificationsDropdown",s)}}]);
|
||||
//# sourceMappingURL=NotificationsDropdown.js.map
|
1
js/dist/forum/components/NotificationsDropdown.js.map
vendored
Normal file
1
js/dist/forum/components/NotificationsDropdown.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack://@flarum/core/./src/forum/components/NotificationsDropdown.js"],"names":["NotificationsDropdown","initAttrs","attrs","className","buttonClassName","menuClassName","label","app","translator","trans","icon","accessibleToggleLabel","getButton","newNotifications","this","getNewCount","vdom","title","onclick","bind","getButtonContent","unread","getUnreadCount","getMenu","NotificationList","menuClick","showing","state","then","m","redraw","drawer","isOpen","goToRoute","load","route","set","session","user","unreadNotificationCount","newNotificationCount","e","shiftKey","metaKey","ctrlKey","which","stopPropagation","Dropdown","window","flreg","add"],"mappings":"8JAGqBA,E,6EACZC,UAAP,SAAiBC,GACfA,EAAMC,UAAYD,EAAMC,WAAa,wBACrCD,EAAME,gBAAkBF,EAAME,iBAAmB,sBACjDF,EAAMG,cAAgBH,EAAMG,eAAiB,uBAC7CH,EAAMI,MAAQJ,EAAMI,OAASC,IAAIC,WAAWC,MAAM,oCAClDP,EAAMQ,KAAOR,EAAMQ,MAAQ,cAE3BR,EAAMS,sBAAwBT,EAAMS,uBAAyBJ,IAAIC,WAAWC,MAAM,6DAElF,EAAMR,UAAN,UAAgBC,I,2BAGlBU,UAAA,WACE,IAAMC,EAAmBC,KAAKC,cACxBC,EAAO,EAAH,UAASJ,UAAT,WAOV,OALAI,EAAKd,MAAMe,MAAQH,KAAKZ,MAAMI,MAE9BU,EAAKd,MAAMC,WAAaU,EAAmB,OAAS,GACpDG,EAAKd,MAAMgB,QAAUJ,KAAKI,QAAQC,KAAKL,MAEhCE,G,EAGTI,iBAAA,WACE,IAAMC,EAASP,KAAKQ,iBAEpB,MAAO,EACLZ,OAAKI,KAAKZ,MAAMQ,KAAM,CAAEP,UAAW,gBACnCkB,EAAS,UAAMlB,UAAU,gCAAgCkB,GAAiB,GAC1E,UAAMlB,UAAU,gBAAgBW,KAAKZ,MAAMI,S,EAI/CiB,QAAA,WACE,IAAMC,EAAmBV,KAAKU,iBAE9B,OACE,SAAKrB,UAAW,iBAAmBW,KAAKZ,MAAMG,cAAea,QAASJ,KAAKW,UAAUN,KAAKL,OACvFA,KAAKY,SAAWF,EAAmB,EAACA,EAAD,CAAkBG,MAAOb,KAAKZ,MAAMyB,QAAY,K,EAK1FT,QAAA,WAAU,WACR,6BAAyFU,MAAK,SAACJ,GAC7F,EAAKA,iBAAmBA,EAAgB,QACxCK,EAAEC,YAGAvB,IAAIwB,OAAOC,SACblB,KAAKmB,YAELnB,KAAKZ,MAAMyB,MAAMO,Q,EAIrBD,UAAA,WACEJ,EAAEM,MAAMC,IAAI7B,IAAI4B,MAAM,mB,EAGxBb,eAAA,WACE,OAAOf,IAAI8B,QAAQC,KAAKC,2B,EAG1BxB,YAAA,WACE,OAAOR,IAAI8B,QAAQC,KAAKE,wB,EAG1Bf,UAAA,SAAUgB,IAGJA,EAAEC,UAAYD,EAAEE,SAAWF,EAAEG,SAAuB,IAAZH,EAAEI,QAAaJ,EAAEK,mB,GAzEdC,KA6EnDC,OAAOC,MAAMC,IAAI,mCAAoClD","file":"forum/components/NotificationsDropdown.js","sourcesContent":["import Dropdown from '../../common/components/Dropdown';\nimport icon from '../../common/helpers/icon';\n\nexport default class NotificationsDropdown extends Dropdown {\n static initAttrs(attrs) {\n attrs.className = attrs.className || 'NotificationsDropdown';\n attrs.buttonClassName = attrs.buttonClassName || 'Button Button--flat';\n attrs.menuClassName = attrs.menuClassName || 'Dropdown-menu--right';\n attrs.label = attrs.label || app.translator.trans('core.forum.notifications.tooltip');\n attrs.icon = attrs.icon || 'fas fa-bell';\n // For best a11y support, both `title` and `aria-label` should be used\n attrs.accessibleToggleLabel = attrs.accessibleToggleLabel || app.translator.trans('core.forum.notifications.toggle_dropdown_accessible_label');\n\n super.initAttrs(attrs);\n }\n\n getButton() {\n const newNotifications = this.getNewCount();\n const vdom = super.getButton();\n\n vdom.attrs.title = this.attrs.label;\n\n vdom.attrs.className += newNotifications ? ' new' : '';\n vdom.attrs.onclick = this.onclick.bind(this);\n\n return vdom;\n }\n\n getButtonContent() {\n const unread = this.getUnreadCount();\n\n return [\n icon(this.attrs.icon, { className: 'Button-icon' }),\n unread ? <span className=\"NotificationsDropdown-unread\">{unread}</span> : '',\n <span className=\"Button-label\">{this.attrs.label}</span>,\n ];\n }\n\n getMenu() {\n const NotificationList = this.NotificationList;\n\n return (\n <div className={'Dropdown-menu ' + this.attrs.menuClassName} onclick={this.menuClick.bind(this)}>\n {this.showing && NotificationList ? <NotificationList state={this.attrs.state} /> : ''}\n </div>\n );\n }\n\n onclick() {\n import(/* webpackChunkName: \"forum/components/NotificationList\" */ './NotificationList').then((NotificationList) => {\n this.NotificationList = NotificationList.default;\n m.redraw();\n });\n\n if (app.drawer.isOpen()) {\n this.goToRoute();\n } else {\n this.attrs.state.load();\n }\n }\n\n goToRoute() {\n m.route.set(app.route('notifications'));\n }\n\n getUnreadCount() {\n return app.session.user.unreadNotificationCount();\n }\n\n getNewCount() {\n return app.session.user.newNotificationCount();\n }\n\n menuClick(e) {\n // Don't close the notifications dropdown if the user is opening a link in a\n // new tab or window.\n if (e.shiftKey || e.metaKey || e.ctrlKey || e.which === 2) e.stopPropagation();\n }\n}\n\nwindow.flreg.add('components/NotificationsDropdown', NotificationsDropdown)"],"sourceRoot":""}
|
2
js/dist/forum/components/Post.js
vendored
Normal file
2
js/dist/forum/components/Post.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/forum/components/Post.js.map
vendored
Normal file
1
js/dist/forum/components/Post.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/PostStream.js
vendored
Normal file
2
js/dist/forum/components/PostStream.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/forum/components/PostStream.js.map
vendored
Normal file
1
js/dist/forum/components/PostStream.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/PostUser.js
vendored
Normal file
2
js/dist/forum/components/PostUser.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/forum/components/PostUser.js.map
vendored
Normal file
1
js/dist/forum/components/PostUser.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/PostsUserPage.js
vendored
Normal file
2
js/dist/forum/components/PostsUserPage.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/forum/components/PostsUserPage.js.map
vendored
Normal file
1
js/dist/forum/components/PostsUserPage.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/RenameDiscussionModal.js
vendored
Normal file
2
js/dist/forum/components/RenameDiscussionModal.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[564],{7214:(t,i,n)=>{n.r(i),n.d(i,{default:()=>a});var s=n(1788),e=n(5556),r=n(2587),o=n(1024),a=function(t){function i(){return t.apply(this,arguments)||this}(0,s.Z)(i,t);var n=i.prototype;return n.oninit=function(i){t.prototype.oninit.call(this,i),this.discussion=this.attrs.discussion,this.currentTitle=this.attrs.currentTitle,this.newTitle=(0,o.Z)(this.currentTitle)},n.className=function(){return"RenameDiscussionModal Modal--small"},n.title=function(){return app.translator.trans("core.forum.rename_discussion.title")},n.content=function(){return m("div",{className:"Modal-body"},m("div",{className:"Form Form--centered"},m("div",{className:"Form-group"},m("input",{className:"FormControl",bidi:this.newTitle,type:"text"})),m("div",{className:"Form-group"},r.Z.component({className:"Button Button--primary Button--block",type:"submit",loading:this.loading},app.translator.trans("core.forum.rename_discussion.submit_button")))))},n.onsubmit=function(t){var i=this;t.preventDefault(),this.loading=!0;var n=this.newTitle(),s=this.currentTitle;if(n&&n!==s)return this.discussion.save({title:n}).then((function(){app.viewingDiscussion(i.discussion)&&app.current.get("stream").update(),m.redraw(),i.hide()})).catch((function(){i.loading=!1,m.redraw()}));this.hide()},i}(e.Z);window.flreg.add("components/RenameDiscussionModal",a)}}]);
|
||||
//# sourceMappingURL=RenameDiscussionModal.js.map
|
1
js/dist/forum/components/RenameDiscussionModal.js.map
vendored
Normal file
1
js/dist/forum/components/RenameDiscussionModal.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack://@flarum/core/./src/forum/components/RenameDiscussionModal.js"],"names":["RenameDiscussionModal","oninit","vnode","this","discussion","attrs","currentTitle","newTitle","Stream","className","title","app","translator","trans","content","bidi","type","Button","loading","onsubmit","e","preventDefault","save","then","viewingDiscussion","current","get","update","m","redraw","hide","Modal","window","flreg","add"],"mappings":"wKAOqBA,E,sGACnBC,OAAA,SAAOC,GACL,YAAMD,OAAN,UAAaC,GAEbC,KAAKC,WAAaD,KAAKE,MAAMD,WAC7BD,KAAKG,aAAeH,KAAKE,MAAMC,aAC/BH,KAAKI,UAAWC,OAAOL,KAAKG,e,EAG9BG,UAAA,WACE,MAAO,sC,EAGTC,MAAA,WACE,OAAOC,IAAIC,WAAWC,MAAM,uC,EAG9BC,QAAA,WACE,OACE,SAAKL,UAAU,cACb,SAAKA,UAAU,uBACb,SAAKA,UAAU,cACb,WAAOA,UAAU,cAAcM,KAAMZ,KAAKI,SAAUS,KAAK,UAE3D,SAAKP,UAAU,cACZQ,cACC,CACER,UAAW,uCACXO,KAAM,SACNE,QAASf,KAAKe,SAEhBP,IAAIC,WAAWC,MAAM,mD,EAQjCM,SAAA,SAASC,GAAG,WACVA,EAAEC,iBAEFlB,KAAKe,SAAU,EAEf,IAAMR,EAAQP,KAAKI,WACbD,EAAeH,KAAKG,aAK1B,GAAII,GAASA,IAAUJ,EACrB,OAAOH,KAAKC,WACTkB,KAAK,CAAEZ,UACPa,MAAK,WACAZ,IAAIa,kBAAkB,EAAKpB,aAC7BO,IAAIc,QAAQC,IAAI,UAAUC,SAE5BC,EAAEC,SACF,EAAKC,UAPF,OASE,WACL,EAAKZ,SAAU,EACfU,EAAEC,YAGN1B,KAAK2B,Q,GAjEwCC,KAsEnDC,OAAOC,MAAMC,IAAI,mCAAoClC","file":"forum/components/RenameDiscussionModal.js","sourcesContent":["import Modal from '../../common/components/Modal';\nimport Button from '../../common/components/Button';\nimport Stream from '../../common/utils/Stream';\n\n/**\n * The 'RenameDiscussionModal' displays a modal dialog with an input to rename a discussion\n */\nexport default class RenameDiscussionModal extends Modal {\n oninit(vnode) {\n super.oninit(vnode);\n\n this.discussion = this.attrs.discussion;\n this.currentTitle = this.attrs.currentTitle;\n this.newTitle = Stream(this.currentTitle);\n }\n\n className() {\n return 'RenameDiscussionModal Modal--small';\n }\n\n title() {\n return app.translator.trans('core.forum.rename_discussion.title');\n }\n\n content() {\n return (\n <div className=\"Modal-body\">\n <div className=\"Form Form--centered\">\n <div className=\"Form-group\">\n <input className=\"FormControl\" bidi={this.newTitle} type=\"text\" />\n </div>\n <div className=\"Form-group\">\n {Button.component(\n {\n className: 'Button Button--primary Button--block',\n type: 'submit',\n loading: this.loading,\n },\n app.translator.trans('core.forum.rename_discussion.submit_button')\n )}\n </div>\n </div>\n </div>\n );\n }\n\n onsubmit(e) {\n e.preventDefault();\n\n this.loading = true;\n\n const title = this.newTitle();\n const currentTitle = this.currentTitle;\n\n // If the title is different to what it was before, then save it. After the\n // save has completed, update the post stream as there will be a new post\n // indicating that the discussion was renamed.\n if (title && title !== currentTitle) {\n return this.discussion\n .save({ title })\n .then(() => {\n if (app.viewingDiscussion(this.discussion)) {\n app.current.get('stream').update();\n }\n m.redraw();\n this.hide();\n })\n .catch(() => {\n this.loading = false;\n m.redraw();\n });\n } else {\n this.hide();\n }\n }\n}\n\nwindow.flreg.add('components/RenameDiscussionModal', RenameDiscussionModal)"],"sourceRoot":""}
|
2
js/dist/forum/components/SessionDropdown.js
vendored
Normal file
2
js/dist/forum/components/SessionDropdown.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[557],{2304:(t,n,o)=>{o.r(n),o.d(n,{default:()=>f});var a=o(2122),e=o(1788),r=o(9908),s=o(507),i=o(9657),p=o(2558),u=o(2587),l=o(4594),c=o(5128),f=function(t){function n(){return t.apply(this,arguments)||this}(0,e.Z)(n,t),n.initAttrs=function(n){t.initAttrs.call(this,n),n.className="SessionDropdown",n.buttonClassName="Button Button--user Button--flat",n.menuClassName="Dropdown-menu--right",n.accessibleToggleLabel=app.translator.trans("core.forum.header.session_dropdown_accessible_label")};var o=n.prototype;return o.view=function(n){return t.prototype.view.call(this,(0,a.Z)({},n,{children:this.items().toArray()}))},o.getButtonContent=function(){var t=app.session.user;return[(0,r.Z)(t)," ",m("span",{className:"Button-label"},(0,s.Z)(t))]},o.items=function(){var t=new l.Z,n=app.session.user;return t.add("profile",p.Z.component({icon:"fas fa-user",href:app.route.user(n)},app.translator.trans("core.forum.header.profile_button")),100),t.add("settings",p.Z.component({icon:"fas fa-cog",href:app.route("settings")},app.translator.trans("core.forum.header.settings_button")),50),app.forum.attribute("adminUrl")&&t.add("administration",p.Z.component({icon:"fas fa-wrench",href:app.forum.attribute("adminUrl"),target:"_blank"},app.translator.trans("core.forum.header.admin_button")),0),t.add("separator",c.Z.component(),-90),t.add("logOut",u.Z.component({icon:"fas fa-sign-out-alt",onclick:app.session.logout.bind(app.session)},app.translator.trans("core.forum.header.log_out_button")),-100),t},n}(i.Z);window.flreg.add("components/SessionDropdown",f)}}]);
|
||||
//# sourceMappingURL=SessionDropdown.js.map
|
1
js/dist/forum/components/SessionDropdown.js.map
vendored
Normal file
1
js/dist/forum/components/SessionDropdown.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack://@flarum/core/./src/forum/components/SessionDropdown.js"],"names":["SessionDropdown","initAttrs","attrs","className","buttonClassName","menuClassName","accessibleToggleLabel","app","translator","trans","view","vnode","children","this","items","toArray","getButtonContent","user","session","avatar","username","ItemList","add","LinkButton","icon","href","route","forum","attribute","target","Separator","Button","onclick","logout","bind","Dropdown","window","flreg"],"mappings":"yNAYqBA,E,6EACZC,UAAP,SAAiBC,GACf,EAAMD,UAAN,UAAgBC,GAEhBA,EAAMC,UAAY,kBAClBD,EAAME,gBAAkB,mCACxBF,EAAMG,cAAgB,uBAEtBH,EAAMI,sBAAwBC,IAAIC,WAAWC,MAAM,wD,2BAGrDC,KAAA,SAAKC,GACH,mBAAaD,KAAb,qBAAuBC,EAAvB,CAA8BC,SAAUC,KAAKC,QAAQC,c,EAGvDC,iBAAA,WACE,IAAMC,EAAOV,IAAIW,QAAQD,KAEzB,MAAO,EAACE,OAAOF,GAAO,IAAK,UAAMd,UAAU,iBAAgBiB,OAASH,M,EAQtEH,MAAA,WACE,IAAMA,EAAQ,IAAIO,IACZJ,EAAOV,IAAIW,QAAQD,KAuDzB,OArDAH,EAAMQ,IACJ,UACAC,cACE,CACEC,KAAM,cACNC,KAAMlB,IAAImB,MAAMT,KAAKA,IAEvBV,IAAIC,WAAWC,MAAM,qCAEvB,KAGFK,EAAMQ,IACJ,WACAC,cACE,CACEC,KAAM,aACNC,KAAMlB,IAAImB,MAAM,aAElBnB,IAAIC,WAAWC,MAAM,sCAEvB,IAGEF,IAAIoB,MAAMC,UAAU,aACtBd,EAAMQ,IACJ,iBACAC,cACE,CACEC,KAAM,gBACNC,KAAMlB,IAAIoB,MAAMC,UAAU,YAC1BC,OAAQ,UAEVtB,IAAIC,WAAWC,MAAM,mCAEvB,GAIJK,EAAMQ,IAAI,YAAaQ,iBAAwB,IAE/ChB,EAAMQ,IACJ,SACAS,cACE,CACEP,KAAM,sBACNQ,QAASzB,IAAIW,QAAQe,OAAOC,KAAK3B,IAAIW,UAEvCX,IAAIC,WAAWC,MAAM,sCAEtB,KAGIK,G,GAnFkCqB,KAuF7CC,OAAOC,MAAMf,IAAI,6BAA8BtB","file":"forum/components/SessionDropdown.js","sourcesContent":["import avatar from '../../common/helpers/avatar';\nimport username from '../../common/helpers/username';\nimport Dropdown from '../../common/components/Dropdown';\nimport LinkButton from '../../common/components/LinkButton';\nimport Button from '../../common/components/Button';\nimport ItemList from '../../common/utils/ItemList';\nimport Separator from '../../common/components/Separator';\n\n/**\n * The `SessionDropdown` component shows a button with the current user's\n * avatar/name, with a dropdown of session controls.\n */\nexport default class SessionDropdown extends Dropdown {\n static initAttrs(attrs) {\n super.initAttrs(attrs);\n\n attrs.className = 'SessionDropdown';\n attrs.buttonClassName = 'Button Button--user Button--flat';\n attrs.menuClassName = 'Dropdown-menu--right';\n\n attrs.accessibleToggleLabel = app.translator.trans('core.forum.header.session_dropdown_accessible_label');\n }\n\n view(vnode) {\n return super.view({ ...vnode, children: this.items().toArray() });\n }\n\n getButtonContent() {\n const user = app.session.user;\n\n return [avatar(user), ' ', <span className=\"Button-label\">{username(user)}</span>];\n }\n\n /**\n * Build an item list for the contents of the dropdown menu.\n *\n * @return {ItemList}\n */\n items() {\n const items = new ItemList();\n const user = app.session.user;\n\n items.add(\n 'profile',\n LinkButton.component(\n {\n icon: 'fas fa-user',\n href: app.route.user(user),\n },\n app.translator.trans('core.forum.header.profile_button')\n ),\n 100\n );\n\n items.add(\n 'settings',\n LinkButton.component(\n {\n icon: 'fas fa-cog',\n href: app.route('settings'),\n },\n app.translator.trans('core.forum.header.settings_button')\n ),\n 50\n );\n\n if (app.forum.attribute('adminUrl')) {\n items.add(\n 'administration',\n LinkButton.component(\n {\n icon: 'fas fa-wrench',\n href: app.forum.attribute('adminUrl'),\n target: '_blank',\n },\n app.translator.trans('core.forum.header.admin_button')\n ),\n 0\n );\n }\n\n items.add('separator', Separator.component(), -90);\n\n items.add(\n 'logOut',\n Button.component(\n {\n icon: 'fas fa-sign-out-alt',\n onclick: app.session.logout.bind(app.session),\n },\n app.translator.trans('core.forum.header.log_out_button')\n ),\n -100\n );\n\n return items;\n }\n}\n\nwindow.flreg.add('components/SessionDropdown', SessionDropdown)"],"sourceRoot":""}
|
2
js/dist/forum/components/SettingsPage.js
vendored
Normal file
2
js/dist/forum/components/SettingsPage.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[826],{6991:(t,n,e)=>{e.d(n,{Z:()=>c});var i=e(1788),a=e(8931),o=e(5971),r=e(9100),s=e(4594),c=function(t){function n(){return t.apply(this,arguments)||this}(0,i.Z)(n,t);var e=n.prototype;return e.oninit=function(n){t.prototype.oninit.call(this,n),this.methods=this.notificationMethods().toArray(),this.loading={},this.types=this.notificationTypes().toArray()},e.view=function(){var t=this,n=this.attrs.user.preferences();return m("table",{className:"NotificationGrid"},m("thead",null,m("tr",null,m("td",null),this.methods.map((function(n){return m("th",{className:"NotificationGrid-groupToggle",onclick:t.toggleMethod.bind(t,n.name)},(0,r.Z)(n.icon)," ",n.label)})))),m("tbody",null,this.types.map((function(e){return m("tr",null,m("td",{className:"NotificationGrid-groupToggle",onclick:t.toggleType.bind(t,e.name)},(0,r.Z)(e.icon)," ",e.label),t.methods.map((function(i){var a=t.preferenceKey(e.name,i.name);return m("td",{className:"NotificationGrid-checkbox"},m(o.Z,{state:!!n[a],loading:t.loading[a],disabled:!(a in n),onchange:t.toggle.bind(t,[a])}))})))}))))},e.oncreate=function(n){t.prototype.oncreate.call(this,n),this.$("thead .NotificationGrid-groupToggle").bind("mouseenter mouseleave",(function(t){var n=parseInt($(this).index(),10)+1;$(this).parents("table").find("td:nth-child("+n+")").toggleClass("highlighted","mouseenter"===t.type)})),this.$("tbody .NotificationGrid-groupToggle").bind("mouseenter mouseleave",(function(t){$(this).parent().find("td").toggleClass("highlighted","mouseenter"===t.type)}))},e.toggle=function(t){var n=this,e=this.attrs.user,i=e.preferences(),a=!i[t[0]];t.forEach((function(t){n.loading[t]=!0,i[t]=a})),m.redraw(),e.save({preferences:i}).then((function(){t.forEach((function(t){return n.loading[t]=!1})),m.redraw()}))},e.toggleMethod=function(t){var n=this,e=this.types.map((function(e){return n.preferenceKey(e.name,t)})).filter((function(t){return t in n.attrs.user.preferences()}));this.toggle(e)},e.toggleType=function(t){var n=this,e=this.methods.map((function(e){return n.preferenceKey(t,e.name)})).filter((function(t){return t in n.attrs.user.preferences()}));this.toggle(e)},e.preferenceKey=function(t,n){return"notify_"+t+"_"+n},e.notificationMethods=function(){var t=new s.Z;return t.add("alert",{name:"alert",icon:"fas fa-bell",label:app.translator.trans("core.forum.settings.notify_by_web_heading")}),t.add("email",{name:"email",icon:"far fa-envelope",label:app.translator.trans("core.forum.settings.notify_by_email_heading")}),t},e.notificationTypes=function(){var t=new s.Z;return t.add("discussionRenamed",{name:"discussionRenamed",icon:"fas fa-pencil-alt",label:app.translator.trans("core.forum.settings.notify_discussion_renamed_label")}),t},n}(a.Z);window.flreg.add("components/NotificationGrid",c)},5792:(t,n,e)=>{e.a(t,(async t=>{e.r(n),e.d(n,{default:()=>u});var i=e(1788),a=e(4594),o=e(833),r=e(2587),s=e(4926),c=e(6991),l=e(7473),u=function(t){function n(){return t.apply(this,arguments)||this}(0,i.Z)(n,t);var u=n.prototype;return u.oninit=function(n){t.prototype.oninit.call(this,n),this.show(app.session.user),app.setTitle(app.translator.trans("core.forum.settings.title"))},u.content=function(){return m("div",{className:"SettingsPage"},m("ul",null,(0,l.Z)(this.settingsItems().toArray())))},u.settingsItems=function(){var t=this,n=new a.Z;return["account","notifications","privacy"].forEach((function(e){n.add(e,m(s.Z,{className:"Settings-"+e,label:app.translator.trans("core.forum.settings."+e+"_heading")},t[e+"Items"]().toArray()))})),n},u.accountItems=function(){var t=new a.Z,n=function(){return e.e(372).then(e.bind(e,4146))},i=function(){return e.e(503).then(e.bind(e,22))};return t.add("changePassword",m(r.Z,{className:"Button",onclick:function(){return app.modal.show(i)}},app.translator.trans("core.forum.settings.change_password_button"))),t.add("changeEmail",m(r.Z,{className:"Button",onclick:function(){return app.modal.show(n)}},app.translator.trans("core.forum.settings.change_email_button"))),t},u.notificationsItems=function(){var t=new a.Z;return t.add("notificationGrid",m(c.Z,{user:this.user})),t},u.privacyItems=function(){var t=this,n=new a.Z;return n.add("discloseOnline",m(o.Z,{state:this.user.preferences().discloseOnline,onchange:function(n){t.discloseOnlineLoading=!0,t.user.savePreferences({discloseOnline:n}).then((function(){t.discloseOnlineLoading=!1,m.redraw()}))},loading:this.discloseOnlineLoading},app.translator.trans("core.forum.settings.privacy_disclose_online_label"))),n},n}((await e.e(251).then(e.bind(e,6068))).default);window.flreg.add("components/SettingsPage",u),t()}),1)}}]);
|
||||
//# sourceMappingURL=SettingsPage.js.map
|
1
js/dist/forum/components/SettingsPage.js.map
vendored
Normal file
1
js/dist/forum/components/SettingsPage.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/SignUpModal.js
vendored
Normal file
2
js/dist/forum/components/SignUpModal.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[]).push([[395],{7527:(t,a,s)=>{s.d(a,{Z:()=>n});var r=s(1788),e=s(8931),i=s(4594),n=function(t){function a(){return t.apply(this,arguments)||this}(0,r.Z)(a,t);var s=a.prototype;return s.view=function(){return m("div",{className:"LogInButtons"},this.items().toArray())},s.items=function(){return new i.Z},a}(e.Z);window.flreg.add("components/LogInButtons",n)},3235:(t,a,s)=>{s.r(a),s.d(a,{default:()=>u});var r=s(1788),e=s(5556),i=s(2587),n=s(7527),o=s(5731),l=s(4594),d=s(1024),u=function(t){function a(){return t.apply(this,arguments)||this}(0,r.Z)(a,t);var e=a.prototype;return e.oninit=function(a){t.prototype.oninit.call(this,a),this.username=(0,d.Z)(this.attrs.username||""),this.email=(0,d.Z)(this.attrs.email||""),this.password=(0,d.Z)(this.attrs.password||"")},e.className=function(){return"Modal--small SignUpModal"},e.title=function(){return app.translator.trans("core.forum.sign_up.title")},e.content=function(){return[m("div",{className:"Modal-body"},this.body()),m("div",{className:"Modal-footer"},this.footer())]},e.isProvided=function(t){return this.attrs.provided&&-1!==this.attrs.provided.indexOf(t)},e.body=function(){return[this.attrs.token?"":m(n.Z,null),m("div",{className:"Form Form--centered"},this.fields().toArray())]},e.fields=function(){var t=new l.Z;return t.add("username",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"username",type:"text",placeholder:(0,o.Z)(app.translator.trans("core.forum.sign_up.username_placeholder")),bidi:this.username,disabled:this.loading||this.isProvided("username")})),30),t.add("email",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"email",type:"email",placeholder:(0,o.Z)(app.translator.trans("core.forum.sign_up.email_placeholder")),bidi:this.email,disabled:this.loading||this.isProvided("email")})),20),this.attrs.token||t.add("password",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"password",type:"password",placeholder:(0,o.Z)(app.translator.trans("core.forum.sign_up.password_placeholder")),bidi:this.password,disabled:this.loading})),10),t.add("submit",m("div",{className:"Form-group"},m(i.Z,{className:"Button Button--primary Button--block",type:"submit",loading:this.loading},app.translator.trans("core.forum.sign_up.submit_button"))),-10),t},e.footer=function(){return[m("p",{className:"SignUpModal-logIn"},app.translator.trans("core.forum.sign_up.log_in_text",{a:m("a",{onclick:this.logIn.bind(this)})}))]},e.logIn=function(){var t={identification:this.email()||this.username(),password:this.password()};app.modal.show((function(){return s.e(460).then(s.bind(s,1498))}),t)},e.onready=function(){this.attrs.username&&!this.attrs.email?this.$("[name=email]").select():this.$("[name=username]").select()},e.onsubmit=function(t){t.preventDefault(),this.loading=!0;var a=this.submitData();app.request({url:app.forum.attribute("baseUrl")+"/register",method:"POST",body:a,errorHandler:this.onerror.bind(this)}).then((function(){return window.location.reload()}),this.loaded.bind(this))},e.submitData=function(){var t={username:this.username(),email:this.email()};return this.attrs.token?t.token=this.attrs.token:t.password=this.password(),t},a}(e.Z);window.flreg.add("components/SignUpModal",u)}}]);
|
||||
//# sourceMappingURL=SignUpModal.js.map
|
1
js/dist/forum/components/SignUpModal.js.map
vendored
Normal file
1
js/dist/forum/components/SignUpModal.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/components/UserPage.js
vendored
Normal file
2
js/dist/forum/components/UserPage.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
js/dist/forum/components/UserPage.js.map
vendored
Normal file
1
js/dist/forum/components/UserPage.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
js/dist/forum/flarum-core.js
vendored
Normal file
3
js/dist/forum/flarum-core.js
vendored
Normal file
File diff suppressed because one or more lines are too long
28
js/dist/forum/flarum-core.js.LICENSE.txt
vendored
Normal file
28
js/dist/forum/flarum-core.js.LICENSE.txt
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* Block below copied from Protovis: http://mbostock.github.com/protovis/
|
||||
* Copyright 2010 Stanford Visualization Group
|
||||
* Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Color Thief v2.0
|
||||
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||
*
|
||||
* Thanks
|
||||
* ------
|
||||
* Nick Rabinowitz - For creating quantize.js.
|
||||
* John Schulz - For clean up and optimization. @JFSIII
|
||||
* Nathan Spady - For adding drag and drop support to the demo page.
|
||||
*
|
||||
* License
|
||||
* -------
|
||||
* Copyright 2011, 2015 Lokesh Dhakar
|
||||
* Released under the MIT license
|
||||
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* quantize.js Copyright 2008 Nick Rabinowitz.
|
||||
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
1
js/dist/forum/flarum-core.js.map
vendored
Normal file
1
js/dist/forum/flarum-core.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
js/dist/forum/runtime.js
vendored
Normal file
2
js/dist/forum/runtime.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(()=>{var e,o,r,t,n,a,s,i,u={},c={};function m(e){var o=c[e];if(void 0!==o)return o.exports;var r=c[e]={exports:{}};return u[e].call(r.exports,r,r.exports,m),r.exports}m.m=u,e="function"==typeof Symbol?Symbol("webpack then"):"__webpack_then__",o="function"==typeof Symbol?Symbol("webpack exports"):"__webpack_exports__",r=e=>{e&&(e.forEach((e=>e.r--)),e.forEach((e=>e.r--?e.r++:e())))},t=e=>!--e.r&&e(),n=(e,o)=>e?e.push(o):t(o),m.a=(a,s,i)=>{var u,c,m,p=i&&[],f=a.exports,l=!0,d=!1,h=(o,r,t)=>{d||(d=!0,r.r+=o.length,o.map(((o,n)=>o[e](r,t))),d=!1)},b=new Promise(((e,o)=>{m=o,c=()=>(e(f),r(p),p=0)}));b[o]=f,b[e]=(e,o)=>{if(l)return t(e);u&&h(u,e,o),n(p,e),b.catch(o)},a.exports=b,s((a=>{if(!a)return c();var s,i;u=(a=>a.map((a=>{if(null!==a&&"object"==typeof a){if(a[e])return a;if(a.then){var s=[];a.then((e=>{i[o]=e,r(s),s=0}));var i={[e]:(e,o)=>(n(s,e),a.catch(o))};return i}}return{[e]:e=>t(e),[o]:a}})))(a);var m=new Promise(((e,r)=>{(s=()=>e(i=u.map((e=>e[o])))).r=0,h(u,s,r)}));return s.r?m:i})).then(c,m),l=!1},a=[],m.O=(e,o,r,t)=>{if(!o){var n=1/0;for(u=0;u<a.length;u++){for(var[o,r,t]=a[u],s=!0,i=0;i<o.length;i++)(!1&t||n>=t)&&Object.keys(m.O).every((e=>m.O[e](o[i])))?o.splice(i--,1):(s=!1,t<n&&(n=t));s&&(a.splice(u--,1),e=r())}return e}t=t||0;for(var u=a.length;u>0&&a[u-1][2]>t;u--)a[u]=a[u-1];a[u]=[o,r,t]},m.n=e=>{var o=e&&e.__esModule?()=>e.default:()=>e;return m.d(o,{a:o}),o},m.d=(e,o)=>{for(var r in o)m.o(o,r)&&!m.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:o[r]})},m.f={},m.e=e=>Promise.all(Object.keys(m.f).reduce(((o,r)=>(m.f[r](e,o),o)),[])),m.u=e=>({28:"forum/components/PostsUserPage",77:"forum/components/NotificationList",192:"forum/components/Post",247:"common/components/TextEditor",251:"forum/components/UserPage",293:"forum/components/EditPostComposer",352:"forum/components/EditUserModal",372:"forum/components/ChangeEmailModal",395:"forum/components/SignUpModal",437:"forum/components/DiscussionComposer",460:"forum/components/LogInModal",502:"forum/components/ForgotPasswordModal",503:"forum/components/ChangePasswordModal",557:"forum/components/SessionDropdown",563:"forum/components/PostStream",564:"forum/components/RenameDiscussionModal",765:"forum/components/NotificationsDropdown",779:"forum/components/PostUser",799:"forum/components/DiscussionsUserPage",826:"forum/components/SettingsPage",847:"forum/components/IndexPage"}[e]+".js"),m.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),m.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),s={},i="@flarum/core:",m.l=(e,o,r,t)=>{if(s[e])s[e].push(o);else{var n,a;if(void 0!==r)for(var u=document.getElementsByTagName("script"),c=0;c<u.length;c++){var p=u[c];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==i+r){n=p;break}}n||(a=!0,(n=document.createElement("script")).charset="utf-8",n.timeout=120,m.nc&&n.setAttribute("nonce",m.nc),n.setAttribute("data-webpack",i+r),n.src=e),s[e]=[o];var f=(o,r)=>{n.onerror=n.onload=null,clearTimeout(l);var t=s[e];if(delete s[e],n.parentNode&&n.parentNode.removeChild(n),t&&t.forEach((e=>e(r))),o)return o(r)},l=setTimeout(f.bind(null,void 0,{type:"timeout",target:n}),12e4);n.onerror=f.bind(null,n.onerror),n.onload=f.bind(null,n.onload),a&&document.head.appendChild(n)}},m.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},m.p="/assets/",(()=>{var e={406:0};m.f.j=(o,r)=>{var t=m.o(e,o)?e[o]:void 0;if(0!==t)if(t)r.push(t[2]);else if(406!=o){var n=new Promise(((r,n)=>t=e[o]=[r,n]));r.push(t[2]=n);var a=m.p+m.u(o),s=new Error;m.l(a,(r=>{if(m.o(e,o)&&(0!==(t=e[o])&&(e[o]=void 0),t)){var n=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;s.message="Loading chunk "+o+" failed.\n("+n+": "+a+")",s.name="ChunkLoadError",s.type=n,s.request=a,t[1](s)}}),"chunk-"+o,o)}else e[o]=0},m.O.j=o=>0===e[o];var o=(o,r)=>{var t,n,[a,s,i]=r,u=0;for(t in s)m.o(s,t)&&(m.m[t]=s[t]);for(i&&i(m),o&&o(r);u<a.length;u++)n=a[u],m.o(e,n)&&e[n]&&e[n][0](),e[a[u]]=0;m.O()},r=self.webpackChunk_flarum_core=self.webpackChunk_flarum_core||[];r.forEach(o.bind(null,0)),r.push=o.bind(null,r.push.bind(r))})(),m.O()})();
|
||||
//# sourceMappingURL=runtime.js.map
|
1
js/dist/forum/runtime.js.map
vendored
Normal file
1
js/dist/forum/runtime.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
js/dist/forum/vendor.js
vendored
Normal file
3
js/dist/forum/vendor.js
vendored
Normal file
File diff suppressed because one or more lines are too long
24
js/dist/forum/vendor.js.LICENSE.txt
vendored
Normal file
24
js/dist/forum/vendor.js.LICENSE.txt
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* Sizzle CSS Selector Engine v2.3.6
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright JS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://js.foundation/
|
||||
*
|
||||
* Date: 2021-02-16
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.6.0
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2021-03-02T17:08Z
|
||||
*/
|
1
js/dist/forum/vendor.js.map
vendored
Normal file
1
js/dist/forum/vendor.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -8,4 +8,4 @@
|
||||
*/
|
||||
|
||||
export * from './src/common';
|
||||
export * from './src/forum';
|
||||
export * from './src/forum';
|
||||
|
10982
js/package-lock.json
generated
10982
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,28 +2,41 @@
|
||||
"private": true,
|
||||
"name": "@flarum/core",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.10.1",
|
||||
"@types/mithril": "^2.0.3",
|
||||
"bootstrap": "^3.4.1",
|
||||
"classnames": "^2.2.5",
|
||||
"clsx": "^1.1.1",
|
||||
"color-thief-browser": "^2.0.2",
|
||||
"dayjs": "^1.8.28",
|
||||
"expose-loader": "^0.7.5",
|
||||
"flarum-webpack-config": "0.1.0-beta.10",
|
||||
"jquery": "^3.5.1",
|
||||
"dayjs": "^1.10.4",
|
||||
"jquery": "^3.6.0",
|
||||
"jquery.hotkeys": "^0.1.0",
|
||||
"lodash-es": "^4.17.14",
|
||||
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mithril": "^2.0.4",
|
||||
"punycode": "^2.1.1",
|
||||
"spin.js": "^3.1.0",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-merge": "^4.1.4"
|
||||
"textarea-caret": "^3.1.0",
|
||||
"webpack-visualizer-plugin": "^0.1.11",
|
||||
"zepto": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"husky": "^4.2.5",
|
||||
"prettier": "2.0.2"
|
||||
"@babel/plugin-proposal-export-default-from": "^7.12.13",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-syntax-top-level-await": "^7.12.13",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@types/jquery": "^3.5.5",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"@types/mithril": "^2.0.7",
|
||||
"@types/punycode": "^2.1.0",
|
||||
"@types/textarea-caret": "^3.0.0",
|
||||
"babel-plugin-add-module-exports": "^1.0.4",
|
||||
"bundlewatch": "^0.3.2",
|
||||
"expose-loader": "^2.0.0",
|
||||
"flarum-webpack-config": "0.1.0-beta.10",
|
||||
"husky": "^4.3.8",
|
||||
"loader-utils": "^1.4.0",
|
||||
"prettier": "^2.2.1",
|
||||
"webpack": "~5.33.2",
|
||||
"webpack-bundle-analyzer": "^4.4.1",
|
||||
"webpack-cli": "~4.6.0",
|
||||
"webpack-merge": "~5.7.3"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "webpack --mode development --watch",
|
||||
|
17
js/shims.d.ts
vendored
17
js/shims.d.ts
vendored
@@ -8,6 +8,8 @@ import * as _$ from 'jquery';
|
||||
// Globals from flarum/core
|
||||
import Application from './src/common/Application';
|
||||
|
||||
import FlarumRegistry from './src/common/FlarumRegistry';
|
||||
|
||||
/**
|
||||
* flarum/core exposes several extensions globally:
|
||||
*
|
||||
@@ -19,9 +21,21 @@ import Application from './src/common/Application';
|
||||
* to (and should not) bundle these themselves.
|
||||
*/
|
||||
declare global {
|
||||
const $: typeof _$;
|
||||
// $ is already defined by `@types/jquery`
|
||||
const m: Mithril.Static;
|
||||
const dayjs: typeof _dayjs;
|
||||
|
||||
// Extend JQuery with our custom functions, defined with $.fn
|
||||
interface JQuery {
|
||||
/**
|
||||
* Creates a tooltip on a jQuery element reference.
|
||||
*
|
||||
* Optionally accepts placement and delay options.
|
||||
*
|
||||
* Returns the same reference to allow for method chaining.
|
||||
*/
|
||||
tooltip: (tooltipOptions?: { placement?: 'top' | 'bottom' | 'left' | 'right'; delay?: number }) => JQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,4 +43,5 @@ declare global {
|
||||
*/
|
||||
declare global {
|
||||
const app: Application;
|
||||
const flreg: FlarumRegistry;
|
||||
}
|
||||
|
@@ -1,27 +1,18 @@
|
||||
import HeaderPrimary from './components/HeaderPrimary';
|
||||
import HeaderSecondary from './components/HeaderSecondary';
|
||||
import routes from './routes';
|
||||
import ExtensionPage from './components/ExtensionPage';
|
||||
import Application from '../common/Application';
|
||||
import Navigation from '../common/components/Navigation';
|
||||
import AdminNav from './components/AdminNav';
|
||||
import ExtensionData from './utils/ExtensionData';
|
||||
|
||||
export default class AdminApplication extends Application {
|
||||
// Deprecated as of beta 15
|
||||
extensionSettings = {};
|
||||
|
||||
extensionData = new ExtensionData();
|
||||
|
||||
extensionCategories = {
|
||||
discussion: 70,
|
||||
moderation: 60,
|
||||
feature: 50,
|
||||
formatting: 40,
|
||||
theme: 30,
|
||||
authentication: 20,
|
||||
feature: 30,
|
||||
theme: 20,
|
||||
language: 10,
|
||||
other: 0,
|
||||
};
|
||||
|
||||
history = {
|
||||
@@ -61,14 +52,6 @@ export default class AdminApplication extends Application {
|
||||
m.mount(document.getElementById('header-primary'), HeaderPrimary);
|
||||
m.mount(document.getElementById('header-secondary'), HeaderSecondary);
|
||||
m.mount(document.getElementById('admin-navigation'), AdminNav);
|
||||
|
||||
// If an extension has just been enabled, then we will run its settings
|
||||
// callback.
|
||||
const enabled = localStorage.getItem('enabledExtension');
|
||||
if (enabled && this.extensionSettings[enabled] && typeof this.extensionSettings[enabled] === 'function') {
|
||||
this.extensionSettings[enabled]();
|
||||
localStorage.removeItem('enabledExtension');
|
||||
}
|
||||
}
|
||||
|
||||
getRequiredPermissions(permission) {
|
||||
|
8
js/src/admin/app.ts
Normal file
8
js/src/admin/app.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import Admin from './AdminApplication';
|
||||
|
||||
const app = new Admin();
|
||||
|
||||
// @ts-ignore
|
||||
window.app = app;
|
||||
|
||||
export default app;
|
@@ -8,6 +8,7 @@ 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';
|
||||
@@ -16,7 +17,6 @@ import SettingsModal from './components/SettingsModal';
|
||||
import DashboardWidget from './components/DashboardWidget';
|
||||
import ExtensionPage from './components/ExtensionPage';
|
||||
import ExtensionLinkButton from './components/ExtensionLinkButton';
|
||||
import AdminLinkButton from './components/AdminLinkButton';
|
||||
import PermissionGrid from './components/PermissionGrid';
|
||||
import ExtensionPermissionGrid from './components/ExtensionPermissionGrid';
|
||||
import MailPage from './components/MailPage';
|
||||
@@ -43,6 +43,7 @@ export default Object.assign(compat, {
|
||||
'components/EditCustomFooterModal': EditCustomFooterModal,
|
||||
'components/SessionDropdown': SessionDropdown,
|
||||
'components/HeaderPrimary': HeaderPrimary,
|
||||
'components/AdminPage': AdminPage,
|
||||
'components/AppearancePage': AppearancePage,
|
||||
'components/StatusWidget': StatusWidget,
|
||||
'components/ExtensionsWidget': ExtensionsWidget,
|
||||
@@ -51,7 +52,6 @@ export default Object.assign(compat, {
|
||||
'components/DashboardWidget': DashboardWidget,
|
||||
'components/ExtensionPage': ExtensionPage,
|
||||
'components/ExtensionLinkButton': ExtensionLinkButton,
|
||||
'components/AdminLinkButton': AdminLinkButton,
|
||||
'components/PermissionGrid': PermissionGrid,
|
||||
'components/ExtensionPermissionGrid': ExtensionPermissionGrid,
|
||||
'components/MailPage': MailPage,
|
||||
|
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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 '../../common/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>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* 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 '../../common/components/LinkButton';
|
||||
|
||||
export default class AdminLinkButton extends LinkButton {
|
||||
getButtonContent(children) {
|
||||
return [...super.getButtonContent(children), <div className="AdminLinkButton-description">{this.attrs.description}</div>];
|
||||
}
|
||||
}
|
@@ -126,16 +126,17 @@ export default class AdminNav extends Component {
|
||||
|
||||
categorizedExtensions[category].map((extension) => {
|
||||
const query = this.query().toUpperCase();
|
||||
const title = extension.extra['flarum-extension'].title;
|
||||
const title = extension.extra['flarum-extension'].title || '';
|
||||
const description = extension.description || '';
|
||||
|
||||
if (!query || title.toUpperCase().includes(query) || extension.description.toUpperCase().includes(query)) {
|
||||
if (!query || title.toUpperCase().includes(query) || description.toUpperCase().includes(query)) {
|
||||
items.add(
|
||||
`extension-${extension.id}`,
|
||||
<ExtensionLinkButton
|
||||
href={app.route('extension', { id: extension.id })}
|
||||
extensionId={extension.id}
|
||||
className="ExtensionNavButton"
|
||||
title={extension.description}
|
||||
title={description}
|
||||
>
|
||||
{title}
|
||||
</ExtensionLinkButton>,
|
||||
|
180
js/src/admin/components/AdminPage.js
Normal file
180
js/src/admin/components/AdminPage.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import Page from '../../common/components/Page';
|
||||
import Button from '../../common/components/Button';
|
||||
import Switch from '../../common/components/Switch';
|
||||
import Select from '../../common/components/Select';
|
||||
import classList from '../../common/utils/classList';
|
||||
import Stream from '../../common/utils/Stream';
|
||||
import saveSettings from '../utils/saveSettings';
|
||||
import AdminHeader from './AdminHeader';
|
||||
|
||||
export default class AdminPage extends Page {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.settings = {};
|
||||
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
view() {
|
||||
const className = classList(['AdminPage', this.headerInfo().className]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{this.header()}
|
||||
<div className="container">{this.content()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
content() {
|
||||
return '';
|
||||
}
|
||||
|
||||
submitButton() {
|
||||
return (
|
||||
<Button onclick={this.saveSettings.bind(this)} className="Button Button--primary" loading={this.loading} disabled={!this.isChanged()}>
|
||||
{app.translator.trans('core.admin.settings.submit_button')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
header() {
|
||||
const headerInfo = this.headerInfo();
|
||||
|
||||
return (
|
||||
<AdminHeader icon={headerInfo.icon} description={headerInfo.description} className={headerInfo.className + '-header'}>
|
||||
{headerInfo.title}
|
||||
</AdminHeader>
|
||||
);
|
||||
}
|
||||
|
||||
headerInfo() {
|
||||
return {
|
||||
className: '',
|
||||
icon: '',
|
||||
title: '',
|
||||
description: '',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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',
|
||||
* }
|
||||
*
|
||||
* @param setting
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
buildSettingComponent(entry) {
|
||||
if (typeof entry === 'function') {
|
||||
return entry.call(this);
|
||||
}
|
||||
|
||||
const { setting, help, ...componentAttrs } = entry;
|
||||
|
||||
delete componentAttrs.help;
|
||||
|
||||
const value = this.setting([setting])();
|
||||
if (['bool', 'checkbox', 'switch', 'boolean'].includes(componentAttrs.type)) {
|
||||
return (
|
||||
<div className="Form-group">
|
||||
<Switch state={!!value && value !== '0'} onchange={this.settings[setting]} {...componentAttrs}>
|
||||
{componentAttrs.label}
|
||||
</Switch>
|
||||
<div className="helpText">{help}</div>
|
||||
</div>
|
||||
);
|
||||
} else if (['select', 'dropdown', 'selectdropdown'].includes(componentAttrs.type)) {
|
||||
return (
|
||||
<div className="Form-group">
|
||||
<label>{componentAttrs.label}</label>
|
||||
<div className="helpText">{help}</div>
|
||||
<Select
|
||||
value={value || componentAttrs.default}
|
||||
options={componentAttrs.options}
|
||||
buttonClassName="Button"
|
||||
onchange={this.settings[setting]}
|
||||
{...componentAttrs}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
componentAttrs.className = classList(['FormControl', componentAttrs.className]);
|
||||
return (
|
||||
<div className="Form-group">
|
||||
{componentAttrs.label ? <label>{componentAttrs.label}</label> : ''}
|
||||
<div className="helpText">{help}</div>
|
||||
<input type={componentAttrs.type} bidi={this.setting(setting)} {...componentAttrs} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onsaved() {
|
||||
this.loading = false;
|
||||
|
||||
app.alerts.show({ type: 'success' }, app.translator.trans('core.admin.settings.saved_message'));
|
||||
}
|
||||
|
||||
setting(key, fallback = '') {
|
||||
this.settings[key] = this.settings[key] || Stream(app.data.settings[key] || fallback);
|
||||
|
||||
return this.settings[key];
|
||||
}
|
||||
|
||||
dirty() {
|
||||
const dirty = {};
|
||||
|
||||
Object.keys(this.settings).forEach((key) => {
|
||||
const value = this.settings[key]();
|
||||
|
||||
if (value !== app.data.settings[key]) {
|
||||
dirty[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
isChanged() {
|
||||
return Object.keys(this.dirty()).length;
|
||||
}
|
||||
|
||||
saveSettings(e) {
|
||||
e.preventDefault();
|
||||
|
||||
app.alerts.clear();
|
||||
|
||||
this.loading = true;
|
||||
|
||||
return saveSettings(this.dirty()).then(this.onsaved.bind(this));
|
||||
}
|
||||
}
|
@@ -1,141 +1,120 @@
|
||||
import Page from '../../common/components/Page';
|
||||
import Button from '../../common/components/Button';
|
||||
import Switch from '../../common/components/Switch';
|
||||
import Stream from '../../common/utils/Stream';
|
||||
import EditCustomCssModal from './EditCustomCssModal';
|
||||
import EditCustomHeaderModal from './EditCustomHeaderModal';
|
||||
import EditCustomFooterModal from './EditCustomFooterModal';
|
||||
import UploadImageButton from './UploadImageButton';
|
||||
import saveSettings from '../utils/saveSettings';
|
||||
import AdminHeader from './AdminHeader';
|
||||
import AdminPage from './AdminPage';
|
||||
|
||||
export default class AppearancePage extends Page {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.primaryColor = Stream(app.data.settings.theme_primary_color);
|
||||
this.secondaryColor = Stream(app.data.settings.theme_secondary_color);
|
||||
this.darkMode = Stream(app.data.settings.theme_dark_mode);
|
||||
this.coloredHeader = Stream(app.data.settings.theme_colored_header);
|
||||
export default class AppearancePage extends AdminPage {
|
||||
headerInfo() {
|
||||
return {
|
||||
className: 'AppearancePage',
|
||||
icon: 'fas fa-paint-brush',
|
||||
title: app.translator.trans('core.admin.appearance.title'),
|
||||
description: app.translator.trans('core.admin.appearance.description'),
|
||||
};
|
||||
}
|
||||
|
||||
view() {
|
||||
return (
|
||||
<div className="AppearancePage">
|
||||
<AdminHeader
|
||||
icon="fas fa-paint-brush"
|
||||
description={app.translator.trans('core.admin.appearance.description')}
|
||||
className="AppearancePage-header"
|
||||
>
|
||||
{app.translator.trans('core.admin.appearance.title')}
|
||||
</AdminHeader>
|
||||
<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>
|
||||
content() {
|
||||
return [
|
||||
<div className="Form">
|
||||
<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="text" placeholder="#aaaaaa" bidi={this.primaryColor} />
|
||||
<input className="FormControl" type="text" placeholder="#aaaaaa" bidi={this.secondaryColor} />
|
||||
</div>
|
||||
<div className="AppearancePage-colors-input">
|
||||
{this.buildSettingComponent({
|
||||
type: 'text',
|
||||
setting: 'theme_primary_color',
|
||||
placeholder: '#aaaaaa',
|
||||
})}
|
||||
{this.buildSettingComponent({
|
||||
type: 'text',
|
||||
setting: 'theme_secondary_color',
|
||||
placeholder: '#aaaaaa',
|
||||
})}
|
||||
</div>
|
||||
|
||||
{Switch.component(
|
||||
{
|
||||
state: this.darkMode(),
|
||||
onchange: this.darkMode,
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.dark_mode_label')
|
||||
)}
|
||||
{this.buildSettingComponent({
|
||||
type: 'switch',
|
||||
setting: 'theme_dark_mode',
|
||||
label: app.translator.trans('core.admin.appearance.dark_mode_label'),
|
||||
})}
|
||||
|
||||
{Switch.component(
|
||||
{
|
||||
state: this.coloredHeader(),
|
||||
onchange: this.coloredHeader,
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.colored_header_label')
|
||||
)}
|
||||
{this.buildSettingComponent({
|
||||
type: 'switch',
|
||||
setting: 'theme_colored_header',
|
||||
label: app.translator.trans('core.admin.appearance.colored_header_label'),
|
||||
})}
|
||||
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button Button--primary',
|
||||
type: 'submit',
|
||||
loading: this.loading,
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.submit_button')
|
||||
)}
|
||||
</fieldset>
|
||||
</form>
|
||||
{this.submitButton()}
|
||||
</fieldset>
|
||||
</div>,
|
||||
|
||||
<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.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.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',
|
||||
onclick: () => app.modal.show(EditCustomHeaderModal),
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.edit_header_button')
|
||||
)}
|
||||
</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',
|
||||
onclick: () => app.modal.show(EditCustomHeaderModal),
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.edit_header_button')
|
||||
)}
|
||||
</fieldset>,
|
||||
|
||||
<fieldset>
|
||||
<legend>{app.translator.trans('core.admin.appearance.custom_footer_heading')}</legend>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_footer_text')}</div>
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button',
|
||||
onclick: () => app.modal.show(EditCustomFooterModal),
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.edit_footer_button')
|
||||
)}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{app.translator.trans('core.admin.appearance.custom_footer_heading')}</legend>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_footer_text')}</div>
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button',
|
||||
onclick: () => app.modal.show(EditCustomFooterModal),
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.edit_footer_button')
|
||||
)}
|
||||
</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',
|
||||
onclick: () => app.modal.show(EditCustomCssModal),
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.edit_css_button')
|
||||
)}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<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',
|
||||
onclick: () => app.modal.show(EditCustomCssModal),
|
||||
},
|
||||
app.translator.trans('core.admin.appearance.edit_css_button')
|
||||
)}
|
||||
</fieldset>,
|
||||
];
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
onsaved() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
saveSettings(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const hex = /^#[0-9a-f]{3}([0-9a-f]{3})?$/i;
|
||||
|
||||
if (!hex.test(this.primaryColor()) || !hex.test(this.secondaryColor())) {
|
||||
if (!hex.test(this.settings['theme_primary_color']()) || !hex.test(this.settings['theme_secondary_color']())) {
|
||||
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());
|
||||
super.saveSettings(e);
|
||||
}
|
||||
}
|
||||
|
@@ -1,31 +1,11 @@
|
||||
import Page from '../../common/components/Page';
|
||||
import FieldSet from '../../common/components/FieldSet';
|
||||
import Select from '../../common/components/Select';
|
||||
import Button from '../../common/components/Button';
|
||||
import saveSettings from '../utils/saveSettings';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import Switch from '../../common/components/Switch';
|
||||
import Stream from '../../common/utils/Stream';
|
||||
import withAttr from '../../common/utils/withAttr';
|
||||
import AdminHeader from './AdminHeader';
|
||||
import AdminPage from './AdminPage';
|
||||
|
||||
export default class BasicsPage extends Page {
|
||||
export default class BasicsPage extends AdminPage {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.loading = false;
|
||||
|
||||
this.fields = [
|
||||
'forum_title',
|
||||
'forum_description',
|
||||
'default_locale',
|
||||
'show_language_selector',
|
||||
'default_route',
|
||||
'welcome_title',
|
||||
'welcome_message',
|
||||
'display_name_driver',
|
||||
];
|
||||
|
||||
this.localeOptions = {};
|
||||
const locales = app.data.locales;
|
||||
for (const i in locales) {
|
||||
@@ -40,157 +20,99 @@ export default class BasicsPage extends Page {
|
||||
|
||||
this.slugDriverOptions = {};
|
||||
Object.keys(app.data.slugDrivers).forEach((model) => {
|
||||
this.fields.push(`slug_driver_${model}`);
|
||||
this.slugDriverOptions[model] = {};
|
||||
|
||||
app.data.slugDrivers[model].forEach((option) => {
|
||||
this.slugDriverOptions[model][option] = option;
|
||||
});
|
||||
});
|
||||
|
||||
this.values = {};
|
||||
|
||||
const settings = app.data.settings;
|
||||
this.fields.forEach((key) => (this.values[key] = Stream(settings[key])));
|
||||
|
||||
if (!this.values.display_name_driver() && displayNameDrivers.includes('username')) this.values.display_name_driver('username');
|
||||
|
||||
Object.keys(app.data.slugDrivers).forEach((model) => {
|
||||
if (!this.values[`slug_driver_${model}`]() && 'default' in this.slugDriverOptions[model]) {
|
||||
this.values[`slug_driver_${model}`]('default');
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof this.values.show_language_selector() !== 'number') this.values.show_language_selector(1);
|
||||
}
|
||||
|
||||
view() {
|
||||
return (
|
||||
<div className="BasicsPage">
|
||||
<AdminHeader icon="fas fa-pencil-alt" description={app.translator.trans('core.admin.basics.description')} className="BasicsPage-header">
|
||||
{app.translator.trans('core.admin.basics.title')}
|
||||
</AdminHeader>
|
||||
<div className="container">
|
||||
<form onsubmit={this.onsubmit.bind(this)}>
|
||||
{FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans('core.admin.basics.forum_title_heading'),
|
||||
},
|
||||
[<input className="FormControl" bidi={this.values.forum_title} />]
|
||||
)}
|
||||
headerInfo() {
|
||||
return {
|
||||
className: 'BasicsPage',
|
||||
icon: 'fas fa-pencil-alt',
|
||||
title: app.translator.trans('core.admin.basics.title'),
|
||||
description: app.translator.trans('core.admin.basics.description'),
|
||||
};
|
||||
}
|
||||
|
||||
{FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans('core.admin.basics.forum_description_heading'),
|
||||
},
|
||||
[
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.forum_description_text')}</div>,
|
||||
<textarea className="FormControl" bidi={this.values.forum_description} />,
|
||||
]
|
||||
)}
|
||||
content() {
|
||||
return [
|
||||
<div className="Form">
|
||||
{this.buildSettingComponent({
|
||||
type: 'text',
|
||||
setting: 'forum_title',
|
||||
label: app.translator.trans('core.admin.basics.forum_title_heading'),
|
||||
})}
|
||||
{this.buildSettingComponent({
|
||||
type: 'text',
|
||||
setting: 'forum_description',
|
||||
label: app.translator.trans('core.admin.basics.forum_description_heading'),
|
||||
help: app.translator.trans('core.admin.basics.forum_description_text'),
|
||||
})}
|
||||
|
||||
{Object.keys(this.localeOptions).length > 1
|
||||
? FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans('core.admin.basics.default_language_heading'),
|
||||
},
|
||||
[
|
||||
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,
|
||||
},
|
||||
app.translator.trans('core.admin.basics.show_language_selector_label')
|
||||
),
|
||||
]
|
||||
)
|
||||
: ''}
|
||||
{Object.keys(this.localeOptions).length > 1
|
||||
? [
|
||||
this.buildSettingComponent({
|
||||
type: 'select',
|
||||
setting: 'default_locale',
|
||||
options: this.localeOptions,
|
||||
label: app.translator.trans('core.admin.basics.default_language_heading'),
|
||||
}),
|
||||
this.buildSettingComponent({
|
||||
type: 'switch',
|
||||
setting: 'show_language_selector',
|
||||
label: 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',
|
||||
},
|
||||
[
|
||||
<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={withAttr('value', this.values.default_route)}
|
||||
/>
|
||||
{label}
|
||||
</label>
|
||||
)),
|
||||
]
|
||||
)}
|
||||
<FieldSet className="BasicsPage-homePage Form-group" label={app.translator.trans('core.admin.basics.home_page_heading')}>
|
||||
<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} bidi={this.setting('default_route')} />
|
||||
{label}
|
||||
</label>
|
||||
))}
|
||||
</FieldSet>
|
||||
|
||||
{FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans('core.admin.basics.welcome_banner_heading'),
|
||||
className: 'BasicsPage-welcomeBanner',
|
||||
},
|
||||
[
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.welcome_banner_text')}</div>,
|
||||
<div className="BasicsPage-welcomeBanner-input">
|
||||
<input className="FormControl" bidi={this.values.welcome_title} />
|
||||
<textarea className="FormControl" bidi={this.values.welcome_message} />
|
||||
</div>,
|
||||
]
|
||||
)}
|
||||
|
||||
{Object.keys(this.displayNameOptions).length > 1 ? (
|
||||
<FieldSet label={app.translator.trans('core.admin.basics.display_name_heading')}>
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.display_name_text')}</div>
|
||||
<Select
|
||||
options={this.displayNameOptions}
|
||||
value={this.values.display_name_driver()}
|
||||
onchange={this.values.display_name_driver}
|
||||
></Select>
|
||||
</FieldSet>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{Object.keys(this.slugDriverOptions).map((model) => {
|
||||
const options = this.slugDriverOptions[model];
|
||||
if (Object.keys(options).length > 1) {
|
||||
return (
|
||||
<FieldSet label={app.translator.trans('core.admin.basics.slug_driver_heading', { model })}>
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.slug_driver_text', { model })}</div>
|
||||
<Select options={options} value={this.values[`slug_driver_${model}`]()} onchange={this.values[`slug_driver_${model}`]}></Select>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
})}
|
||||
|
||||
{Button.component(
|
||||
{
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary',
|
||||
loading: this.loading,
|
||||
disabled: !this.changed(),
|
||||
},
|
||||
app.translator.trans('core.admin.basics.submit_button')
|
||||
)}
|
||||
</form>
|
||||
<div className="Form-group BasicsPage-welcomeBanner-input">
|
||||
<label>{app.translator.trans('core.admin.basics.welcome_banner_heading')}</label>
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.welcome_banner_text')}</div>
|
||||
<input type="text" className="FormControl" bidi={this.setting('welcome_title')} />
|
||||
<textarea className="FormControl" bidi={this.setting('welcome_message')} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
changed() {
|
||||
return this.fields.some((key) => this.values[key]() !== app.data.settings[key]);
|
||||
{Object.keys(this.displayNameOptions).length > 1
|
||||
? this.buildSettingComponent({
|
||||
type: 'select',
|
||||
setting: 'display_name_driver',
|
||||
options: this.displayNameOptions,
|
||||
label: app.translator.trans('core.admin.basics.display_name_heading'),
|
||||
help: app.translator.trans('core.admin.basics.display_name_text'),
|
||||
})
|
||||
: ''}
|
||||
|
||||
{Object.keys(this.slugDriverOptions).map((model) => {
|
||||
const options = this.slugDriverOptions[model];
|
||||
if (Object.keys(options).length > 1) {
|
||||
return this.buildSettingComponent({
|
||||
type: 'select',
|
||||
setting: `slug_driver_${model}`,
|
||||
options,
|
||||
label: app.translator.trans('core.admin.basics.slug_driver_heading', { model }),
|
||||
help: app.translator.trans('core.admin.basics.slug_driver_text', { model }),
|
||||
});
|
||||
}
|
||||
})}
|
||||
|
||||
{this.submitButton()}
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,27 +132,4 @@ export default class BasicsPage extends Page {
|
||||
|
||||
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(() => {
|
||||
this.successAlert = app.alerts.show({ type: 'success' }, app.translator.trans('core.admin.basics.saved_message'));
|
||||
})
|
||||
.catch(() => {})
|
||||
.then(() => {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,20 @@
|
||||
import Page from '../../common/components/Page';
|
||||
import StatusWidget from './StatusWidget';
|
||||
import ExtensionsWidget from './ExtensionsWidget';
|
||||
import AdminHeader from './AdminHeader';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import listItems from '../../common/helpers/listItems';
|
||||
import AdminPage from './AdminPage';
|
||||
|
||||
export default class DashboardPage extends Page {
|
||||
view() {
|
||||
return (
|
||||
<div className="DashboardPage">
|
||||
<AdminHeader icon="fas fa-chart-bar" description={app.translator.trans('core.admin.dashboard.description')} className="DashboardPage-header">
|
||||
{app.translator.trans('core.admin.dashboard.title')}
|
||||
</AdminHeader>
|
||||
<div className="container">{this.availableWidgets().toArray()}</div>
|
||||
</div>
|
||||
);
|
||||
export default class DashboardPage extends AdminPage {
|
||||
headerInfo() {
|
||||
return {
|
||||
className: 'DashboardPage',
|
||||
icon: 'fas fa-chart-bar',
|
||||
title: app.translator.trans('core.admin.dashboard.title'),
|
||||
description: app.translator.trans('core.admin.dashboard.description'),
|
||||
};
|
||||
}
|
||||
|
||||
content() {
|
||||
return this.availableWidgets().toArray();
|
||||
}
|
||||
|
||||
availableWidgets() {
|
||||
|
@@ -1,27 +1,22 @@
|
||||
import Button from '../../common/components/Button';
|
||||
import Link from '../../common/components/Link';
|
||||
import LinkButton from '../../common/components/LinkButton';
|
||||
import Page from '../../common/components/Page';
|
||||
import Select from '../../common/components/Select';
|
||||
import Switch from '../../common/components/Switch';
|
||||
import icon from '../../common/helpers/icon';
|
||||
import punctuateSeries from '../../common/helpers/punctuateSeries';
|
||||
import listItems from '../../common/helpers/listItems';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import Stream from '../../common/utils/Stream';
|
||||
import LoadingModal from './LoadingModal';
|
||||
import ExtensionPermissionGrid from './ExtensionPermissionGrid';
|
||||
import saveSettings from '../utils/saveSettings';
|
||||
import isExtensionEnabled from '../utils/isExtensionEnabled';
|
||||
import AdminPage from './AdminPage';
|
||||
|
||||
export default class ExtensionPage extends Page {
|
||||
export default class ExtensionPage extends AdminPage {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.loading = false;
|
||||
this.extension = app.data.extensions[this.attrs.id];
|
||||
this.changingState = false;
|
||||
this.settings = {};
|
||||
|
||||
this.infoFields = {
|
||||
discuss: 'fas fa-comment-alt',
|
||||
@@ -32,18 +27,20 @@ export default class ExtensionPage extends Page {
|
||||
source: 'fas fa-code',
|
||||
};
|
||||
|
||||
// Backwards compatibility layer will be removed in
|
||||
// Beta 16
|
||||
if (app.extensionSettings[this.extension.id]) {
|
||||
app.extensionData[this.extension.id] = app.extensionSettings[this.extension.id];
|
||||
if (!this.extension) {
|
||||
return m.route.set(app.route('dashboard'));
|
||||
}
|
||||
}
|
||||
|
||||
className() {
|
||||
if (!this.extension) return '';
|
||||
|
||||
return this.extension.id + '-Page';
|
||||
}
|
||||
|
||||
view() {
|
||||
if (!this.extension) return null;
|
||||
|
||||
return (
|
||||
<div className={'ExtensionPage ' + this.className()}>
|
||||
{this.header()}
|
||||
@@ -59,6 +56,8 @@ export default class ExtensionPage extends Page {
|
||||
}
|
||||
|
||||
header() {
|
||||
const isEnabled = this.isEnabled();
|
||||
|
||||
return [
|
||||
<div className="ExtensionPage-header">
|
||||
<div className="container">
|
||||
@@ -75,10 +74,12 @@ export default class ExtensionPage extends Page {
|
||||
</div>
|
||||
<div className="helpText">{this.extension.description}</div>
|
||||
<div className="ExtensionPage-headerItems">
|
||||
<Switch state={this.isEnabled()} onchange={this.toggle.bind(this, this.extension.id)}>
|
||||
{this.isEnabled(this.extension.id)
|
||||
? app.translator.trans('core.admin.extension.enabled')
|
||||
: app.translator.trans('core.admin.extension.disabled')}
|
||||
<Switch
|
||||
state={this.changingState ? !isEnabled : isEnabled}
|
||||
loading={this.changingState}
|
||||
onchange={this.toggle.bind(this, this.extension.id)}
|
||||
>
|
||||
{isEnabled ? app.translator.trans('core.admin.extension.enabled') : app.translator.trans('core.admin.extension.disabled')}
|
||||
</Switch>
|
||||
<aside className="ExtensionInfo">
|
||||
<ul>{listItems(this.infoItems().toArray())}</ul>
|
||||
@@ -120,11 +121,7 @@ export default class ExtensionPage extends Page {
|
||||
return (
|
||||
<div className="ExtensionPage-settings">
|
||||
<div className="container">
|
||||
{typeof app.extensionData[this.extension.id] === 'function' ? (
|
||||
<Button onclick={app.extensionData[this.extension.id].bind(this)} className="Button Button--primary">
|
||||
{app.translator.trans('core.admin.extension.open_modal')}
|
||||
</Button>
|
||||
) : settings ? (
|
||||
{settings ? (
|
||||
<div className="Form">
|
||||
{settings.map(this.buildSettingComponent.bind(this))}
|
||||
<div className="Form-group">{this.submitButton()}</div>
|
||||
@@ -200,78 +197,6 @@ export default class ExtensionPage extends Page {
|
||||
return items;
|
||||
}
|
||||
|
||||
submitButton() {
|
||||
return (
|
||||
<Button onclick={this.saveSettings.bind(this)} className="Button Button--primary" loading={this.loading} disabled={!this.isChanged()}>
|
||||
{app.translator.trans('core.admin.settings.submit_button')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* getSetting 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.
|
||||
*
|
||||
* 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'
|
||||
* }
|
||||
*
|
||||
* @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',
|
||||
* }
|
||||
*
|
||||
* @param setting
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
buildSettingComponent(entry) {
|
||||
if (typeof entry === 'function') {
|
||||
return entry.call(this);
|
||||
}
|
||||
|
||||
const setting = entry.setting;
|
||||
const value = this.setting([setting])();
|
||||
if (['bool', 'checkbox', 'switch', 'boolean'].includes(entry.type)) {
|
||||
return (
|
||||
<div className="Form-group">
|
||||
<Switch state={!!value && value !== '0'} onchange={this.settings[setting]}>
|
||||
{entry.label}
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
} else if (['select', 'dropdown', 'selectdropdown'].includes(entry.type)) {
|
||||
return (
|
||||
<div className="Form-group">
|
||||
<label>{entry.label}</label>
|
||||
<Select value={value || entry.default} options={entry.options} buttonClassName="Button" onchange={this.settings[setting]} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="Form-group">
|
||||
<label>{entry.label}</label>
|
||||
<input type={entry.type} className="FormControl" bidi={this.setting(setting)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
toggle() {
|
||||
const enabled = this.isEnabled();
|
||||
|
||||
@@ -292,50 +217,8 @@ export default class ExtensionPage extends Page {
|
||||
app.modal.show(LoadingModal);
|
||||
}
|
||||
|
||||
dirty() {
|
||||
const dirty = {};
|
||||
|
||||
Object.keys(this.settings).forEach((key) => {
|
||||
const value = this.settings[key]();
|
||||
|
||||
if (value !== app.data.settings[key]) {
|
||||
dirty[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
isChanged() {
|
||||
return Object.keys(this.dirty()).length;
|
||||
}
|
||||
|
||||
saveSettings(e) {
|
||||
e.preventDefault();
|
||||
|
||||
app.alerts.clear();
|
||||
|
||||
this.loading = true;
|
||||
|
||||
saveSettings(this.dirty()).then(this.onsaved.bind(this));
|
||||
}
|
||||
|
||||
onsaved() {
|
||||
this.loading = false;
|
||||
|
||||
app.alerts.show({ type: 'success' }, app.translator.trans('core.admin.extension.saved_message'));
|
||||
}
|
||||
|
||||
setting(key, fallback = '') {
|
||||
this.settings[key] = this.settings[key] || Stream(app.data.settings[key] || fallback);
|
||||
|
||||
return this.settings[key];
|
||||
}
|
||||
|
||||
isEnabled() {
|
||||
let isEnabled = isExtensionEnabled(this.extension.id);
|
||||
|
||||
return this.changingState ? !isEnabled : isEnabled;
|
||||
return isExtensionEnabled(this.extension.id);
|
||||
}
|
||||
|
||||
onerror(e) {
|
||||
@@ -346,6 +229,8 @@ export default class ExtensionPage extends Page {
|
||||
app.modal.close();
|
||||
}, 300); // Bootstrap's Modal.TRANSITION_DURATION is 300 ms.
|
||||
|
||||
this.changingState = false;
|
||||
|
||||
if (e.status !== 409) {
|
||||
throw e;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import PermissionGrid from './PermissionGrid';
|
||||
import Button from '../../common/components/Button';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
|
||||
export default class ExtensionPermissionGrid extends PermissionGrid {
|
||||
@@ -36,4 +37,17 @@ export default class ExtensionPermissionGrid extends PermissionGrid {
|
||||
moderateItems() {
|
||||
return app.extensionData.getExtensionPermissions(this.extensionId, 'moderate') || new ItemList();
|
||||
}
|
||||
|
||||
scopeControlItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'configureScopes',
|
||||
<Button className="Button Button--text" onclick={() => m.route.set(app.route('permissions'))}>
|
||||
{app.translator.trans('core.admin.extension.configure_scopes')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
@@ -5,42 +5,47 @@ import Link from '../../common/components/Link';
|
||||
import icon from '../../common/helpers/icon';
|
||||
|
||||
export default class ExtensionsWidget extends DashboardWidget {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.categorizedExtensions = getCategorizedExtensions();
|
||||
}
|
||||
|
||||
className() {
|
||||
return 'ExtensionsWidget';
|
||||
}
|
||||
|
||||
content() {
|
||||
const categorizedExtensions = getCategorizedExtensions();
|
||||
const categories = app.extensionCategories;
|
||||
|
||||
return (
|
||||
<div className="ExtensionsWidget-list">
|
||||
{Object.keys(categories).map((category) => {
|
||||
if (categorizedExtensions[category]) {
|
||||
return (
|
||||
<div className="ExtensionList-Category">
|
||||
<h4 className="ExtensionList-Label">{app.translator.trans(`core.admin.nav.categories.${category}`)}</h4>
|
||||
<ul className="ExtensionList">
|
||||
{categorizedExtensions[category].map((extension) => {
|
||||
return (
|
||||
<li className={'ExtensionListItem ' + (!isExtensionEnabled(extension.id) ? 'disabled' : '')}>
|
||||
<Link href={app.route('extension', { id: extension.id })}>
|
||||
<div className="ExtensionListItem-content">
|
||||
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
|
||||
{extension.icon ? icon(extension.icon.name) : ''}
|
||||
</span>
|
||||
<span className="ExtensionListItem-title">{extension.extra['flarum-extension'].title}</span>
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{Object.keys(categories).map((category) => (this.categorizedExtensions[category] ? this.extensionCategory(category) : ''))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
extensionCategory(category) {
|
||||
return (
|
||||
<div className="ExtensionList-Category">
|
||||
<h4 className="ExtensionList-Label">{app.translator.trans(`core.admin.nav.categories.${category}`)}</h4>
|
||||
<ul className="ExtensionList">{this.categorizedExtensions[category].map((extension) => this.extensionWidget(extension))}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
extensionWidget(extension) {
|
||||
return (
|
||||
<li className={'ExtensionListItem ' + (!isExtensionEnabled(extension.id) ? 'disabled' : '')}>
|
||||
<Link href={app.route('extension', { id: extension.id })}>
|
||||
<div className="ExtensionListItem-content">
|
||||
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
|
||||
{extension.icon ? icon(extension.icon.name) : ''}
|
||||
</span>
|
||||
<span className="ExtensionListItem-title">{extension.extra['flarum-extension'].title}</span>
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,34 +1,31 @@
|
||||
import Page from '../../common/components/Page';
|
||||
import FieldSet from '../../common/components/FieldSet';
|
||||
import Button from '../../common/components/Button';
|
||||
import Alert from '../../common/components/Alert';
|
||||
import Select from '../../common/components/Select';
|
||||
import LoadingIndicator from '../../common/components/LoadingIndicator';
|
||||
import saveSettings from '../utils/saveSettings';
|
||||
import Stream from '../../common/utils/Stream';
|
||||
import icon from '../../common/helpers/icon';
|
||||
import AdminHeader from './AdminHeader';
|
||||
import AdminPage from './AdminPage';
|
||||
|
||||
export default class MailPage extends Page {
|
||||
export default class MailPage extends AdminPage {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.saving = false;
|
||||
this.sendingTest = false;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
headerInfo() {
|
||||
return {
|
||||
className: 'MailPage',
|
||||
icon: 'fas fa-envelope',
|
||||
title: app.translator.trans('core.admin.email.title'),
|
||||
description: app.translator.trans('core.admin.email.description'),
|
||||
};
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.loading = true;
|
||||
|
||||
this.driverFields = {};
|
||||
this.fields = ['mail_driver', 'mail_from'];
|
||||
this.values = {};
|
||||
this.status = { sending: false, errors: {} };
|
||||
|
||||
const settings = app.data.settings;
|
||||
this.fields.forEach((key) => (this.values[key] = Stream(settings[key])));
|
||||
|
||||
app
|
||||
.request({
|
||||
method: 'GET',
|
||||
@@ -39,150 +36,78 @@ export default class MailPage extends Page {
|
||||
this.status.sending = response['data']['attributes']['sending'];
|
||||
this.status.errors = response['data']['attributes']['errors'];
|
||||
|
||||
for (const driver in this.driverFields) {
|
||||
for (const field in this.driverFields[driver]) {
|
||||
this.fields.push(field);
|
||||
this.values[field] = Stream(settings[field]);
|
||||
}
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
view() {
|
||||
if (this.loading || this.saving) {
|
||||
return (
|
||||
<div className="MailPage">
|
||||
<div className="container">
|
||||
<LoadingIndicator />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
content() {
|
||||
if (this.loading) {
|
||||
return <LoadingIndicator />;
|
||||
}
|
||||
|
||||
const fields = this.driverFields[this.values.mail_driver()];
|
||||
const fields = this.driverFields[this.setting('mail_driver')()];
|
||||
const fieldKeys = Object.keys(fields);
|
||||
|
||||
return (
|
||||
<div className="MailPage">
|
||||
<AdminHeader icon="fas fa-envelope" description={app.translator.trans('core.admin.email.description')} className="MailPage-header">
|
||||
{app.translator.trans('core.admin.email.title')}
|
||||
</AdminHeader>
|
||||
<div className="container">
|
||||
<form onsubmit={this.onsubmit.bind(this)}>
|
||||
{FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans('core.admin.email.addresses_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
},
|
||||
[
|
||||
<div className="MailPage-MailSettings-input">
|
||||
<label>
|
||||
{app.translator.trans('core.admin.email.from_label')}
|
||||
<input className="FormControl" bidi={this.values.mail_from} />
|
||||
</label>
|
||||
</div>,
|
||||
]
|
||||
)}
|
||||
<div className="Form">
|
||||
{this.buildSettingComponent({
|
||||
type: 'text',
|
||||
setting: 'mail_from',
|
||||
label: app.translator.trans('core.admin.email.addresses_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
})}
|
||||
{this.buildSettingComponent({
|
||||
type: 'select',
|
||||
setting: 'mail_driver',
|
||||
options: Object.keys(this.driverFields).reduce((memo, val) => ({ ...memo, [val]: val }), {}),
|
||||
label: app.translator.trans('core.admin.email.driver_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
})}
|
||||
{this.status.sending ||
|
||||
Alert.component(
|
||||
{
|
||||
dismissible: false,
|
||||
},
|
||||
app.translator.trans('core.admin.email.not_sending_message')
|
||||
)}
|
||||
|
||||
{FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans('core.admin.email.driver_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
},
|
||||
[
|
||||
<div className="MailPage-MailSettings-input">
|
||||
<label>
|
||||
{app.translator.trans('core.admin.email.driver_label')}
|
||||
<Select
|
||||
value={this.values.mail_driver()}
|
||||
options={Object.keys(this.driverFields).reduce((memo, val) => ({ ...memo, [val]: val }), {})}
|
||||
onchange={this.values.mail_driver}
|
||||
/>
|
||||
</label>
|
||||
</div>,
|
||||
]
|
||||
)}
|
||||
{fieldKeys.length > 0 && (
|
||||
<FieldSet label={app.translator.trans(`core.admin.email.${this.setting('mail_driver')()}_heading`)} className="MailPage-MailSettings">
|
||||
<div className="MailPage-MailSettings-input">
|
||||
{fieldKeys.map((field) => {
|
||||
const fieldInfo = fields[field];
|
||||
|
||||
{this.status.sending ||
|
||||
Alert.component(
|
||||
{
|
||||
dismissible: false,
|
||||
},
|
||||
app.translator.trans('core.admin.email.not_sending_message')
|
||||
)}
|
||||
return [
|
||||
this.buildSettingComponent({
|
||||
type: typeof this.setting(field)() === 'string' ? 'text' : 'select',
|
||||
label: app.translator.trans(`core.admin.email.${field}_label`),
|
||||
setting: field,
|
||||
options: fieldInfo,
|
||||
}),
|
||||
this.status.errors[field] && <p className="ValidationError">{this.status.errors[field]}</p>,
|
||||
];
|
||||
})}
|
||||
</div>
|
||||
</FieldSet>
|
||||
)}
|
||||
{this.submitButton()}
|
||||
|
||||
{fieldKeys.length > 0 &&
|
||||
FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans(`core.admin.email.${this.values.mail_driver()}_heading`),
|
||||
className: 'MailPage-MailSettings',
|
||||
},
|
||||
[
|
||||
<div className="MailPage-MailSettings-input">
|
||||
{fieldKeys.map((field) => [
|
||||
<label>
|
||||
{app.translator.trans(`core.admin.email.${field}_label`)}
|
||||
{this.renderField(field)}
|
||||
</label>,
|
||||
this.status.errors[field] && <p className="ValidationError">{this.status.errors[field]}</p>,
|
||||
])}
|
||||
</div>,
|
||||
]
|
||||
)}
|
||||
|
||||
<FieldSet>
|
||||
{Button.component(
|
||||
{
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary',
|
||||
disabled: !this.changed(),
|
||||
},
|
||||
app.translator.trans('core.admin.email.submit_button')
|
||||
)}
|
||||
</FieldSet>
|
||||
|
||||
{FieldSet.component(
|
||||
{
|
||||
label: app.translator.trans('core.admin.email.send_test_mail_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
},
|
||||
[
|
||||
<div className="helpText">{app.translator.trans('core.admin.email.send_test_mail_text', { email: app.session.user.email() })}</div>,
|
||||
Button.component(
|
||||
{
|
||||
className: 'Button Button--primary',
|
||||
disabled: this.sendingTest || this.changed(),
|
||||
onclick: () => this.sendTestEmail(),
|
||||
},
|
||||
app.translator.trans('core.admin.email.send_test_mail_button')
|
||||
),
|
||||
]
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
<FieldSet label={app.translator.trans('core.admin.email.send_test_mail_heading')} className="MailPage-MailSettings">
|
||||
<div className="helpText">{app.translator.trans('core.admin.email.send_test_mail_text', { email: app.session.user.email() })}</div>
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button Button--primary',
|
||||
disabled: this.sendingTest || this.isChanged(),
|
||||
onclick: () => this.sendTestEmail(),
|
||||
},
|
||||
app.translator.trans('core.admin.email.send_test_mail_button')
|
||||
)}
|
||||
</FieldSet>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderField(name) {
|
||||
const driver = this.values.mail_driver();
|
||||
const field = this.driverFields[driver][name];
|
||||
const prop = this.values[name];
|
||||
|
||||
if (typeof field === 'string') {
|
||||
return <input className="FormControl" bidi={prop} />;
|
||||
} else {
|
||||
return <Select value={prop()} options={field} onchange={prop} />;
|
||||
}
|
||||
}
|
||||
|
||||
changed() {
|
||||
return this.fields.some((key) => this.values[key]() !== app.data.settings[key]);
|
||||
}
|
||||
|
||||
sendTestEmail() {
|
||||
if (this.saving || this.sendingTest) return;
|
||||
|
||||
@@ -205,26 +130,7 @@ export default class MailPage extends Page {
|
||||
});
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.saving || this.sendingTest) return;
|
||||
|
||||
this.saving = true;
|
||||
app.alerts.dismiss(this.successAlert);
|
||||
|
||||
const settings = {};
|
||||
|
||||
this.fields.forEach((key) => (settings[key] = this.values[key]()));
|
||||
|
||||
saveSettings(settings)
|
||||
.then(() => {
|
||||
this.successAlert = app.alerts.show({ type: 'success' }, app.translator.trans('core.admin.basics.saved_message'));
|
||||
})
|
||||
.catch(() => {})
|
||||
.then(() => {
|
||||
this.saving = false;
|
||||
this.refresh();
|
||||
});
|
||||
saveSettings(e) {
|
||||
super.saveSettings(e).then(this.refresh());
|
||||
}
|
||||
}
|
||||
|
@@ -327,9 +327,29 @@ export default class PermissionGrid extends Component {
|
||||
);
|
||||
|
||||
items.add(
|
||||
'userEdit',
|
||||
'userEditCredentials',
|
||||
{
|
||||
icon: 'fas fa-user-cog',
|
||||
label: app.translator.trans('core.admin.permissions.edit_users_credentials_label'),
|
||||
permission: 'user.editCredentials',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
items.add(
|
||||
'userEditGroups',
|
||||
{
|
||||
icon: 'fas fa-users-cog',
|
||||
label: app.translator.trans('core.admin.permissions.edit_users_groups_label'),
|
||||
permission: 'user.editGroups',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
items.add(
|
||||
'userEdit',
|
||||
{
|
||||
icon: 'fas fa-address-card',
|
||||
label: app.translator.trans('core.admin.permissions.edit_users_label'),
|
||||
permission: 'user.edit',
|
||||
},
|
||||
|
@@ -1,44 +1,43 @@
|
||||
import Page from '../../common/components/Page';
|
||||
import GroupBadge from '../../common/components/GroupBadge';
|
||||
import EditGroupModal from './EditGroupModal';
|
||||
import Group from '../../common/models/Group';
|
||||
import icon from '../../common/helpers/icon';
|
||||
import PermissionGrid from './PermissionGrid';
|
||||
import AdminHeader from './AdminHeader';
|
||||
import AdminPage from './AdminPage';
|
||||
|
||||
export default class PermissionsPage extends Page {
|
||||
view() {
|
||||
return (
|
||||
<div className="PermissionsPage">
|
||||
<AdminHeader icon="fas fa-key" description={app.translator.trans('core.admin.permissions.description')} className="PermissionsPage-header">
|
||||
{app.translator.trans('core.admin.permissions.title')}
|
||||
</AdminHeader>
|
||||
<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(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(EditGroupModal)}>
|
||||
{icon('fas fa-plus', { className: 'Group-icon' })}
|
||||
<span className="Group-name">{app.translator.trans('core.admin.permissions.new_group_button')}</span>
|
||||
export default class PermissionsPage extends AdminPage {
|
||||
headerInfo() {
|
||||
return {
|
||||
className: 'PermissionsPage',
|
||||
icon: 'fas fa-key',
|
||||
title: app.translator.trans('core.admin.permissions.title'),
|
||||
description: app.translator.trans('core.admin.permissions.description'),
|
||||
};
|
||||
}
|
||||
|
||||
content() {
|
||||
return [
|
||||
<div className="PermissionsPage-groups">
|
||||
{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(EditGroupModal, { group })}>
|
||||
{GroupBadge.component({
|
||||
group,
|
||||
className: 'Group-icon',
|
||||
label: null,
|
||||
})}
|
||||
<span className="Group-name">{group.namePlural()}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<button className="Button Group Group--add" onclick={() => app.modal.show(EditGroupModal)}>
|
||||
{icon('fas fa-plus', { className: 'Group-icon' })}
|
||||
<span className="Group-name">{app.translator.trans('core.admin.permissions.new_group_button')}</span>
|
||||
</button>
|
||||
</div>,
|
||||
|
||||
<div className="PermissionsPage-permissions">
|
||||
<div className="container">{PermissionGrid.component()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<div className="PermissionsPage-permissions">{PermissionGrid.component()}</div>,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ export default class UploadImageButton extends Button {
|
||||
$input
|
||||
.appendTo('body')
|
||||
.hide()
|
||||
.click()
|
||||
.trigger('click')
|
||||
.on('change', (e) => {
|
||||
const body = new FormData();
|
||||
body.append(this.attrs.name, $(e.target)[0].files[0]);
|
||||
|
@@ -1,18 +1,5 @@
|
||||
import AdminApplication from './AdminApplication';
|
||||
|
||||
const app = new AdminApplication();
|
||||
|
||||
// Backwards compatibility
|
||||
window.app = app;
|
||||
import app from './app';
|
||||
|
||||
export { app };
|
||||
|
||||
// Export public API
|
||||
|
||||
// Export compat API
|
||||
import compatObj from './compat';
|
||||
import proxifyCompat from '../common/utils/proxifyCompat';
|
||||
|
||||
compatObj.app = app;
|
||||
|
||||
export const compat = proxifyCompat(compatObj, 'admin');
|
||||
|
@@ -15,9 +15,9 @@ export default function getCategorizedExtensions() {
|
||||
|
||||
extensions[category].push(extension);
|
||||
} else {
|
||||
extensions.other = extensions.other || [];
|
||||
extensions.feature = extensions.feature || [];
|
||||
|
||||
extensions.other.push(extension);
|
||||
extensions.feature.push(extension);
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -159,6 +159,8 @@ export default class Application {
|
||||
title = '';
|
||||
titleCount = 0;
|
||||
|
||||
initialRoute;
|
||||
|
||||
load(payload) {
|
||||
this.data = payload;
|
||||
this.translator.locale = payload.locale;
|
||||
@@ -174,6 +176,8 @@ export default class Application {
|
||||
this.session = new Session(this.store.getById('users', this.data.session.userId), this.data.session.csrfToken);
|
||||
|
||||
this.mount();
|
||||
|
||||
this.initialRoute = window.location.href;
|
||||
}
|
||||
|
||||
bootExtensions(extensions) {
|
||||
@@ -226,7 +230,8 @@ export default class Application {
|
||||
* @public
|
||||
*/
|
||||
preloadedApiDocument() {
|
||||
if (this.data.apiDocument) {
|
||||
// If the URL has changed, the preloaded Api document is invalid.
|
||||
if (this.data.apiDocument && window.location.href === this.initialRoute) {
|
||||
const results = this.store.pushPayload(this.data.apiDocument);
|
||||
|
||||
this.data.apiDocument = null;
|
||||
|
@@ -77,12 +77,12 @@ export default abstract class Component<T extends ComponentAttrs = ComponentAttr
|
||||
* containing all of the `li` elements inside the DOM element of this
|
||||
* component.
|
||||
*
|
||||
* @param {String} [selector] a jQuery-compatible selector string
|
||||
* @returns {jQuery} the jQuery object for the DOM node
|
||||
* @param [selector] a jQuery-compatible selector string
|
||||
* @returns the jQuery object for the DOM node
|
||||
* @final
|
||||
*/
|
||||
protected $(selector) {
|
||||
const $element = $(this.element);
|
||||
protected $(selector: string): JQuery {
|
||||
const $element = $(this.element) as JQuery<HTMLElement>;
|
||||
|
||||
return selector ? $element.find(selector) : $element;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ export default abstract class Component<T extends ComponentAttrs = ComponentAttr
|
||||
* @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children
|
||||
*/
|
||||
static component(attrs = {}, children = null): Mithril.Vnode {
|
||||
const componentAttrs = Object.assign({}, attrs);
|
||||
const componentAttrs = Object.assign({}, attrs) as Record<string, unknown>;
|
||||
|
||||
return m(this as any, componentAttrs, children);
|
||||
}
|
||||
|
55
js/src/common/FlarumRegistry.ts
Normal file
55
js/src/common/FlarumRegistry.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
interface ExportRegistry {
|
||||
moduleExports: object;
|
||||
|
||||
onLoads: object;
|
||||
|
||||
/**
|
||||
* Add an instance to the registry.
|
||||
* This serves as the equivalent of `flarum.core.compat[id] = object`
|
||||
*/
|
||||
add(id: string, object: any);
|
||||
|
||||
/**
|
||||
* Add a function to run when object of id "id" is added (or overriden).
|
||||
* If such an object is already registered, the handler will be applied immediately.
|
||||
*/
|
||||
onLoad(id: string, handler: Function);
|
||||
|
||||
/**
|
||||
* Retrieve an object of type `id` from the registry.
|
||||
*/
|
||||
get(id: string): any;
|
||||
}
|
||||
|
||||
export default class FlarumRegistry implements ExportRegistry {
|
||||
moduleExports = {};
|
||||
|
||||
onLoads = {};
|
||||
|
||||
add(id: string, object: any) {
|
||||
const onLoads = this.onLoads[id];
|
||||
if (onLoads) {
|
||||
onLoads.map((onLoad) => {
|
||||
object = onLoad(object);
|
||||
});
|
||||
}
|
||||
|
||||
this.moduleExports[id] = object;
|
||||
}
|
||||
|
||||
onLoad(id: string, handler: Function) {
|
||||
const loadedObject = this.moduleExports[id];
|
||||
if (loadedObject) {
|
||||
this.moduleExports[id] = handler(loadedObject);
|
||||
} else {
|
||||
this.onLoads[id] = this.onLoads[id] || [];
|
||||
this.onLoads[id].push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
get(id: string): any {
|
||||
if (this.moduleExports[id]) {
|
||||
return this.moduleExports[id];
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,147 +0,0 @@
|
||||
import * as extend from './extend';
|
||||
import Session from './Session';
|
||||
import Store from './Store';
|
||||
import evented from './utils/evented';
|
||||
import liveHumanTimes from './utils/liveHumanTimes';
|
||||
import ItemList from './utils/ItemList';
|
||||
import mixin from './utils/mixin';
|
||||
import humanTime from './utils/humanTime';
|
||||
import computed from './utils/computed';
|
||||
import Drawer from './utils/Drawer';
|
||||
import anchorScroll from './utils/anchorScroll';
|
||||
import RequestError from './utils/RequestError';
|
||||
import abbreviateNumber from './utils/abbreviateNumber';
|
||||
import * as string from './utils/string';
|
||||
import Stream from './utils/Stream';
|
||||
import SubtreeRetainer from './utils/SubtreeRetainer';
|
||||
import setRouteWithForcedRefresh from './utils/setRouteWithForcedRefresh';
|
||||
import extract from './utils/extract';
|
||||
import ScrollListener from './utils/ScrollListener';
|
||||
import stringToColor from './utils/stringToColor';
|
||||
import subclassOf from './utils/subclassOf';
|
||||
import SuperTextarea from './utils/SuperTextarea';
|
||||
import patchMithril from './utils/patchMithril';
|
||||
import proxifyCompat from './utils/proxifyCompat';
|
||||
import classList from './utils/classList';
|
||||
import extractText from './utils/extractText';
|
||||
import formatNumber from './utils/formatNumber';
|
||||
import mapRoutes from './utils/mapRoutes';
|
||||
import withAttr from './utils/withAttr';
|
||||
import Notification from './models/Notification';
|
||||
import User from './models/User';
|
||||
import Post from './models/Post';
|
||||
import Discussion from './models/Discussion';
|
||||
import Group from './models/Group';
|
||||
import Forum from './models/Forum';
|
||||
import Component from './Component';
|
||||
import Translator from './Translator';
|
||||
import AlertManager from './components/AlertManager';
|
||||
import Page from './components/Page';
|
||||
import Switch from './components/Switch';
|
||||
import Badge from './components/Badge';
|
||||
import LoadingIndicator from './components/LoadingIndicator';
|
||||
import Placeholder from './components/Placeholder';
|
||||
import Separator from './components/Separator';
|
||||
import Dropdown from './components/Dropdown';
|
||||
import SplitDropdown from './components/SplitDropdown';
|
||||
import RequestErrorModal from './components/RequestErrorModal';
|
||||
import FieldSet from './components/FieldSet';
|
||||
import Select from './components/Select';
|
||||
import Navigation from './components/Navigation';
|
||||
import Alert from './components/Alert';
|
||||
import Link from './components/Link';
|
||||
import LinkButton from './components/LinkButton';
|
||||
import Checkbox from './components/Checkbox';
|
||||
import SelectDropdown from './components/SelectDropdown';
|
||||
import ModalManager from './components/ModalManager';
|
||||
import Button from './components/Button';
|
||||
import Modal from './components/Modal';
|
||||
import GroupBadge from './components/GroupBadge';
|
||||
import Model from './Model';
|
||||
import Application from './Application';
|
||||
import fullTime from './helpers/fullTime';
|
||||
import avatar from './helpers/avatar';
|
||||
import icon from './helpers/icon';
|
||||
import humanTimeHelper from './helpers/humanTime';
|
||||
import punctuateSeries from './helpers/punctuateSeries';
|
||||
import highlight from './helpers/highlight';
|
||||
import username from './helpers/username';
|
||||
import userOnline from './helpers/userOnline';
|
||||
import listItems from './helpers/listItems';
|
||||
import Fragment from './Fragment';
|
||||
import DefaultResolver from './resolvers/DefaultResolver';
|
||||
|
||||
export default {
|
||||
extend: extend,
|
||||
Session: Session,
|
||||
Store: Store,
|
||||
'utils/evented': evented,
|
||||
'utils/liveHumanTimes': liveHumanTimes,
|
||||
'utils/ItemList': ItemList,
|
||||
'utils/mixin': mixin,
|
||||
'utils/humanTime': humanTime,
|
||||
'utils/computed': computed,
|
||||
'utils/Drawer': Drawer,
|
||||
'utils/anchorScroll': anchorScroll,
|
||||
'utils/RequestError': RequestError,
|
||||
'utils/abbreviateNumber': abbreviateNumber,
|
||||
'utils/string': string,
|
||||
'utils/SubtreeRetainer': SubtreeRetainer,
|
||||
'utils/extract': extract,
|
||||
'utils/ScrollListener': ScrollListener,
|
||||
'utils/stringToColor': stringToColor,
|
||||
'utils/Stream': Stream,
|
||||
'utils/subclassOf': subclassOf,
|
||||
'utils/SuperTextarea': SuperTextarea,
|
||||
'utils/setRouteWithForcedRefresh': setRouteWithForcedRefresh,
|
||||
'utils/patchMithril': patchMithril,
|
||||
'utils/proxifyCompat': proxifyCompat,
|
||||
'utils/classList': classList,
|
||||
'utils/extractText': extractText,
|
||||
'utils/formatNumber': formatNumber,
|
||||
'utils/mapRoutes': mapRoutes,
|
||||
'utils/withAttr': withAttr,
|
||||
'models/Notification': Notification,
|
||||
'models/User': User,
|
||||
'models/Post': Post,
|
||||
'models/Discussion': Discussion,
|
||||
'models/Group': Group,
|
||||
'models/Forum': Forum,
|
||||
Component: Component,
|
||||
Fragment: Fragment,
|
||||
Translator: Translator,
|
||||
'components/AlertManager': AlertManager,
|
||||
'components/Page': Page,
|
||||
'components/Switch': Switch,
|
||||
'components/Badge': Badge,
|
||||
'components/LoadingIndicator': LoadingIndicator,
|
||||
'components/Placeholder': Placeholder,
|
||||
'components/Separator': Separator,
|
||||
'components/Dropdown': Dropdown,
|
||||
'components/SplitDropdown': SplitDropdown,
|
||||
'components/RequestErrorModal': RequestErrorModal,
|
||||
'components/FieldSet': FieldSet,
|
||||
'components/Select': Select,
|
||||
'components/Navigation': Navigation,
|
||||
'components/Alert': Alert,
|
||||
'components/Link': Link,
|
||||
'components/LinkButton': LinkButton,
|
||||
'components/Checkbox': Checkbox,
|
||||
'components/SelectDropdown': SelectDropdown,
|
||||
'components/ModalManager': ModalManager,
|
||||
'components/Button': Button,
|
||||
'components/Modal': Modal,
|
||||
'components/GroupBadge': GroupBadge,
|
||||
Model: Model,
|
||||
Application: Application,
|
||||
'helpers/fullTime': fullTime,
|
||||
'helpers/avatar': avatar,
|
||||
'helpers/icon': icon,
|
||||
'helpers/humanTime': humanTimeHelper,
|
||||
'helpers/punctuateSeries': punctuateSeries,
|
||||
'helpers/highlight': highlight,
|
||||
'helpers/username': username,
|
||||
'helpers/userOnline': userOnline,
|
||||
'helpers/listItems': listItems,
|
||||
'resolvers/DefaultResolver': DefaultResolver,
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user