Merge branch 'master' into 1236-database-changes

This commit is contained in:
Daniel Klabbers 2018-06-22 07:48:21 +02:00
commit 1800f4290a
246 changed files with 9580 additions and 57708 deletions

BIN
.deploy.enc Normal file

Binary file not shown.

4
.gitattributes vendored
View File

@ -1,6 +1,8 @@
.gitattributes export-ignore
.gitignore export-ignore
.gitmodules export-ignore
.github export-ignore
.travis export-ignore
.travis.yml export-ignore
.editorconfig export-ignore
.styleci.yml export-ignore
@ -8,4 +10,4 @@
phpunit.xml export-ignore
tests export-ignore
js/*/dist/*.js -diff
js/dist/* -diff

View File

@ -1,29 +0,0 @@
> Issues on Github are meant for bug reporting. Please post feature requests on the [discussion forum](https://discuss.flarum.org/t/features).
---
> Try to complete the below form as far as you are able and are willing to share. Add a screenshot of the issue if you can.
## Explanation
Explain, in simple terms, but with as much detail as possible, your issue.
Be specific: What happened? What would you expect to happen? What have you tried so far?
## Technical details
- Version of Flarum: x.y.z
- Website URL where the bug is visible: http://example.com
- The webserver you are running: apache, nginx or something else
- PHP version: x.y.z
- Hosted environment: shared or vps
- Hosting provider: http://some-amazing-provider.com
## Flarum info
```
Output of "php flarum info", run this in terminal in your Flarum directory.
```
## Log files
```
Put any relevant logs here.
```

40
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@ -0,0 +1,40 @@
---
name: "🐛 Bug Report"
about: "If something isn't working as expected"
---
## Bug Report
**Current Behavior**
A clear and concise description of the behavior.
**Steps to Reproduce**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected Behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment**
- Flarum version: x.y.z
- Website URL: http://example.com
- Webserver: [e.g. apache, nginx]
- Hosting environment: [e.g. shared, vps]
- PHP version: x.y.z
- Browser: [e.g. chrome 67, safari 11]
```
Output of "php flarum info", run this in terminal in your Flarum directory.
```
**Possible Solution**
<!--- Only if you have suggestions or a fix for the bug -->
**Additional Context**
Add any other context about the problem here.

View File

@ -0,0 +1,26 @@
---
name: "🚀 Feature Request"
about: "I have a suggestion (and may want to implement it!)"
---
<!--
Note: The issue tracker is only for serious requests which have had some thought and planning put into them. If you just have an idea you would like to discuss, please post in the Flarum Community: https://discuss.flarum.org/t/feedback
-->
## Feature Request
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. eg. I have an issue when [...]
**Describe the solution you'd like**
A detailed description of your proposed solution. Include:
- How the feature would work/behave
- Any potential drawbacks
- Maybe a screenshot, design, or example code
**Justify why this feature belongs in Flarum's core, rather than in a third-party extension**
Consider who this change will be useful to – most Flarum forums, or just a few?
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

View File

@ -0,0 +1,11 @@
---
name: "🙋‍ Support Question"
about: "If you have a question, please check out our forum or Discord!"
---
We primarily use GitHub as an issue tracker; for usage and support questions, please check out these resources below. Thanks!
* Flarum Community: https://discuss.flarum.org
* Discord Chat: https://flarum.org/discord
* Twitter: https://twitter.com/flarum

10
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,10 @@
**Fixes #0000**
**Changes proposed in this pull request:**
<!-- fill this out -->
**Reviewers should focus on:**
<!-- fill this out -->
**Screenshot**
<!-- include an image of the most relevant user-facing change, if any -->

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
/vendor
composer.lock
composer.phar
node_modules
.DS_Store
Thumbs.db
/tests/tmp

View File

@ -1,44 +1,45 @@
language: php
sudo: false
env:
global:
- DB=mariadb
cache:
directories:
- $HOME/.composer/cache
- $HOME/.npm
addons:
mariadb: '10.2'
php:
- 7.1
- 7.2
matrix:
fast_finish: true
include:
- php: 7.1
addons: # to prevent mariadb
services: mysql
env:
- DB=mysql
- php: 7.2
addons: # to prevent mariadb
services: mysql
env:
- DB=mysql
before_install:
- mysql -e 'CREATE DATABASE flarum;'
before_script:
- composer self-update
install:
- composer install
- mysql -e 'CREATE DATABASE flarum;'
script:
- vendor/bin/phpunit --coverage-clover=coverage.xml
notifications:
email:
on_failure: change
after_success:
- bash <(curl -s https://codecov.io/bash)
jobs:
include:
- php: 7.1
env: DB=mysql
- php: 7.2
env: DB=mysql
- php: 7.1
addons:
mariadb: '10.2'
env: DB=mariadb
- php: 7.2
addons:
mariadb: '10.2'
env: DB=mariadb
- stage: build
language: generic
if: branch = master AND type = push
install: skip
script: bash .travis/build.sh
-k $encrypted_678139e2bc67_key
-i $encrypted_678139e2bc67_iv
after_success: skip

33
.travis/build.sh Executable file
View File

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

1
js/.gitignore vendored
View File

@ -1 +0,0 @@
bower_components

11
js/admin.js Normal file
View File

@ -0,0 +1,11 @@
/*
* 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.
*/
export * from './src/common';
export * from './src/admin';

1
js/admin/.gitignore vendored
View File

@ -1 +0,0 @@
node_modules

View File

@ -1,31 +0,0 @@
var gulp = require('flarum-gulp');
var bowerDir = '../bower_components';
gulp({
includeHelpers: true,
files: [
bowerDir + '/es6-micro-loader/dist/system-polyfill.js',
bowerDir + '/mithril/mithril.js',
bowerDir + '/m.attrs.bidi/bidi.js',
bowerDir + '/jquery/dist/jquery.js',
bowerDir + '/moment/moment.js',
bowerDir + '/bootstrap/js/affix.js',
bowerDir + '/bootstrap/js/dropdown.js',
bowerDir + '/bootstrap/js/modal.js',
bowerDir + '/bootstrap/js/tooltip.js',
bowerDir + '/bootstrap/js/transition.js',
bowerDir + '/spin.js/spin.js',
bowerDir + '/spin.js/jquery.spin.js'
],
modules: {
'flarum': [
'src/**/*.js',
'../lib/**/*.js'
]
},
outputFile: 'dist/app.js'
});

24217
js/admin/dist/app.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
{
"private": true,
"devDependencies": {
"gulp": "^3.9.1",
"flarum-gulp": "^0.2.0"
}
}

View File

@ -1,33 +0,0 @@
import App from 'flarum/App';
import store from 'flarum/initializers/store';
import preload from 'flarum/initializers/preload';
import routes from 'flarum/initializers/routes';
import boot from 'flarum/initializers/boot';
const app = new App();
app.initializers.add('store', store);
app.initializers.add('routes', routes);
app.initializers.add('preload', preload, -100);
app.initializers.add('boot', boot, -100);
app.extensionSettings = {};
app.getRequiredPermissions = function(permission) {
const required = [];
if (permission === 'startDiscussion' || permission.indexOf('discussion.') === 0) {
required.push('viewDiscussions');
}
if (permission === 'discussion.delete') {
required.push('discussion.hide');
}
if (permission === 'discussion.deletePosts') {
required.push('discussion.editPosts');
}
return required;
};
export default app;

View File

@ -1,66 +0,0 @@
/*global FastClick*/
import ScrollListener from 'flarum/utils/ScrollListener';
import Drawer from 'flarum/utils/Drawer';
import mapRoutes from 'flarum/utils/mapRoutes';
import Navigation from 'flarum/components/Navigation';
import HeaderPrimary from 'flarum/components/HeaderPrimary';
import HeaderSecondary from 'flarum/components/HeaderSecondary';
import AdminNav from 'flarum/components/AdminNav';
import ModalManager from 'flarum/components/ModalManager';
import AlertManager from 'flarum/components/AlertManager';
/**
* The `boot` initializer boots up the admin app. It initializes some app
* globals, mounts components to the page, and begins routing.
*
* @param {ForumApp} app
*/
export default function boot(app) {
m.startComputation();
m.mount(document.getElementById('app-navigation'), Navigation.component({className: 'App-backControl', drawer: true}));
m.mount(document.getElementById('header-navigation'), Navigation.component());
m.mount(document.getElementById('header-primary'), HeaderPrimary.component());
m.mount(document.getElementById('header-secondary'), HeaderSecondary.component());
m.mount(document.getElementById('admin-navigation'), AdminNav.component());
app.drawer = new Drawer();
app.modal = m.mount(document.getElementById('modal'), ModalManager.component());
app.alerts = m.mount(document.getElementById('alerts'), AlertManager.component());
app.history = {
canGoBack: () => true,
getPrevious: () => {},
backUrl: () => app.forum.attribute('baseUrl'),
back: function() {
window.location = this.backUrl();
}
};
m.route.mode = 'hash';
m.route(document.getElementById('content'), '/', mapRoutes(app.routes));
m.endComputation();
// Add a class to the body which indicates that the page has been scrolled
// down.
new ScrollListener(top => {
const $app = $('#app');
const offset = $app.offset().top;
$app
.toggleClass('affix', top >= offset)
.toggleClass('scrolled', top > offset);
}).start();
app.booted = true;
// If an extension has just been enabled, then we will run its settings
// callback.
const enabled = localStorage.getItem('enabledExtension');
if (enabled && app.extensionSettings[enabled]) {
app.extensionSettings[enabled]();
localStorage.removeItem('enabledExtension');
}
}

View File

@ -1,15 +0,0 @@
{
"name": "flarum",
"dependencies": {
"jquery": "~2.1.3",
"jquery.hotkeys": "jeresig/jquery.hotkeys#0.2.0",
"bootstrap": "~3.3.2",
"spin.js": "~2.0.1",
"moment": "~2.8.4",
"color-thief": "v2.0",
"mithril": "lhorie/mithril.js#v0.2.5",
"es6-micro-loader": "caridy/es6-micro-loader#v0.2.1",
"m.attrs.bidi": "tobscure/m.attrs.bidi",
"punycode": "http://cdnjs.cloudflare.com/ajax/libs/punycode/1.4.1/punycode.js"
}
}

40
js/dist/admin.js vendored Normal file

File diff suppressed because one or more lines are too long

1
js/dist/admin.js.map vendored Normal file

File diff suppressed because one or more lines are too long

67
js/dist/forum.js vendored Normal file

File diff suppressed because one or more lines are too long

1
js/dist/forum.js.map vendored Normal file

File diff suppressed because one or more lines are too long

11
js/forum.js Normal file
View File

@ -0,0 +1,11 @@
/*
* 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.
*/
export * from './src/common';
export * from './src/forum';

1
js/forum/.gitignore vendored
View File

@ -1 +0,0 @@
node_modules

View File

@ -1,34 +0,0 @@
var gulp = require('flarum-gulp');
var bowerDir = '../bower_components';
gulp({
includeHelpers: true,
files: [
bowerDir + '/es6-micro-loader/dist/system-polyfill.js',
bowerDir + '/mithril/mithril.js',
bowerDir + '/m.attrs.bidi/bidi.js',
bowerDir + '/jquery/dist/jquery.js',
bowerDir + '/jquery.hotkeys/jquery.hotkeys.js',
bowerDir + '/color-thief/src/color-thief.js',
bowerDir + '/moment/moment.js',
bowerDir + '/bootstrap/js/affix.js',
bowerDir + '/bootstrap/js/dropdown.js',
bowerDir + '/bootstrap/js/modal.js',
bowerDir + '/bootstrap/js/tooltip.js',
bowerDir + '/bootstrap/js/transition.js',
bowerDir + '/spin.js/spin.js',
bowerDir + '/spin.js/jquery.spin.js',
bowerDir + '/punycode/index.js'
],
modules: {
'flarum': [
'src/**/*.js',
'../lib/**/*.js'
]
},
outputFile: 'dist/app.js'
});

32178
js/forum/dist/app.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
{
"private": true,
"devDependencies": {
"gulp": "^3.9.1",
"flarum-gulp": "^0.2.0"
}
}

View File

@ -1,103 +0,0 @@
import History from 'flarum/utils/History';
import App from 'flarum/App';
import Search from 'flarum/components/Search';
import Composer from 'flarum/components/Composer';
import ReplyComposer from 'flarum/components/ReplyComposer';
import DiscussionPage from 'flarum/components/DiscussionPage';
import SignUpModal from 'flarum/components/SignUpModal';
export default class ForumApp extends App {
constructor(...args) {
super(...args);
/**
* The app's history stack, which keeps track of which routes the user visits
* so that they can easily navigate back to the previous route.
*
* @type {History}
*/
this.history = new History();
/**
* An object which controls the state of the page's side pane.
*
* @type {Pane}
*/
this.pane = null;
/**
* The page's search component instance.
*
* @type {SearchBox}
*/
this.search = new Search();
/**
* An object which controls the state of the page's drawer.
*
* @type {Drawer}
*/
this.drawer = null;
/**
* A map of post types to their components.
*
* @type {Object}
*/
this.postComponents = {};
/**
* A map of notification types to their components.
*
* @type {Object}
*/
this.notificationComponents = {};
}
/**
* Check whether or not the user is currently composing a reply to a
* discussion.
*
* @param {Discussion} discussion
* @return {Boolean}
*/
composingReplyTo(discussion) {
return this.composer.component instanceof ReplyComposer &&
this.composer.component.props.discussion === discussion &&
this.composer.position !== Composer.PositionEnum.HIDDEN;
}
/**
* Check whether or not the user is currently viewing a discussion.
*
* @param {Discussion} discussion
* @return {Boolean}
*/
viewingDiscussion(discussion) {
return this.current instanceof DiscussionPage &&
this.current.discussion === discussion;
}
/**
* Callback for when an external authenticator (social login) action has
* completed.
*
* If the payload indicates that the user has been logged in, then the page
* will be reloaded. Otherwise, a SignUpModal will be opened, prefilled
* with the provided details.
*
* @param {Object} payload A dictionary of props to pass into the sign up
* modal. A truthy `authenticated` prop indicates that the user has logged
* in, and thus the page is reloaded.
* @public
*/
authenticationComplete(payload) {
if (payload.authenticated) {
window.location.reload();
} else {
const modal = new SignUpModal(payload);
this.modal.show(modal);
modal.$('[name=password]').focus();
}
}
}

View File

@ -1,21 +0,0 @@
import ForumApp from 'flarum/ForumApp';
import store from 'flarum/initializers/store';
import preload from 'flarum/initializers/preload';
import routes from 'flarum/initializers/routes';
import components from 'flarum/initializers/components';
import humanTime from 'flarum/initializers/humanTime';
import boot from 'flarum/initializers/boot';
import alertEmailConfirmation from 'flarum/initializers/alertEmailConfirmation';
const app = new ForumApp();
app.initializers.add('store', store);
app.initializers.add('routes', routes);
app.initializers.add('components', components);
app.initializers.add('humanTime', humanTime);
app.initializers.add('preload', preload, -100);
app.initializers.add('boot', boot, -100);
app.initializers.add('alertEmailConfirmation', alertEmailConfirmation, -100);
export default app;

View File

@ -1,86 +0,0 @@
/*global FastClick*/
import ScrollListener from 'flarum/utils/ScrollListener';
import Pane from 'flarum/utils/Pane';
import Drawer from 'flarum/utils/Drawer';
import mapRoutes from 'flarum/utils/mapRoutes';
import Navigation from 'flarum/components/Navigation';
import HeaderPrimary from 'flarum/components/HeaderPrimary';
import HeaderSecondary from 'flarum/components/HeaderSecondary';
import Composer from 'flarum/components/Composer';
import ModalManager from 'flarum/components/ModalManager';
import AlertManager from 'flarum/components/AlertManager';
/**
* The `boot` initializer boots up the forum app. It initializes some app
* globals, mounts components to the page, and begins routing.
*
* @param {ForumApp} app
*/
export default function boot(app) {
// Get the configured default route and update that route's path to be '/'.
// Push the homepage as the first route, so that the user will always be
// able to click on the 'back' button to go home, regardless of which page
// they started on.
const defaultRoute = app.forum.attribute('defaultRoute');
let defaultAction = 'index';
for (const i in app.routes) {
if (app.routes[i].path === defaultRoute) defaultAction = i;
}
app.routes[defaultAction].path = '/';
app.history.push(defaultAction, app.translator.trans('core.forum.header.back_to_index_tooltip'), '/');
m.startComputation();
m.mount(document.getElementById('app-navigation'), Navigation.component({className: 'App-backControl', drawer: true}));
m.mount(document.getElementById('header-navigation'), Navigation.component());
m.mount(document.getElementById('header-primary'), HeaderPrimary.component());
m.mount(document.getElementById('header-secondary'), HeaderSecondary.component());
app.pane = new Pane(document.getElementById('app'));
app.drawer = new Drawer();
app.composer = m.mount(document.getElementById('composer'), Composer.component());
app.modal = m.mount(document.getElementById('modal'), ModalManager.component());
app.alerts = m.mount(document.getElementById('alerts'), AlertManager.component());
const basePath = app.forum.attribute('basePath');
m.route.mode = 'pathname';
m.route(
document.getElementById('content'),
basePath + '/',
mapRoutes(app.routes, basePath)
);
m.endComputation();
// Route the home link back home when clicked. We do not want it to register
// if the user is opening it in a new tab, however.
$('#home-link').click(e => {
if (e.ctrlKey || e.metaKey || e.which === 2) return;
e.preventDefault();
app.history.home();
if (app.session.user) {
app.store.find('users', app.session.user.id());
m.redraw();
}
});
// Add a class to the body which indicates that the page has been scrolled
// down.
new ScrollListener(top => {
const $app = $('#app');
const offset = $app.offset().top;
$app
.toggleClass('affix', top >= offset)
.toggleClass('scrolled', top > offset);
}).start();
$(() => {
$('body').addClass('ontouchstart' in window ? 'touch' : 'no-touch');
});
app.booted = true;
}

View File

@ -1,16 +0,0 @@
import CommentPost from 'flarum/components/CommentPost';
import DiscussionRenamedPost from 'flarum/components/DiscussionRenamedPost';
import DiscussionRenamedNotification from 'flarum/components/DiscussionRenamedNotification';
/**
* The `components` initializer registers components to display the default post
* types, activity types, and notifications type with the application.
*
* @param {ForumApp} app
*/
export default function components(app) {
app.postComponents.comment = CommentPost;
app.postComponents.discussionRenamed = DiscussionRenamedPost;
app.notificationComponents.discussionRenamed = DiscussionRenamedNotification;
}

View File

@ -1,24 +0,0 @@
import Session from 'flarum/Session';
/**
* The `preload` initializer creates the application session and preloads it
* with data that has been set on the application's `preload` property. It also
* preloads any data on the application's `preload` property into the store.
* Finally, it sets the application's `forum` instance to the one that was
* preloaded.
*
* `app.preload.session` should be the same as the response from the /api/token
* endpoint: it should contain `token` and `userId` keys.
*
* @param {App} app
*/
export default function preload(app) {
app.store.pushPayload({data: app.data.resources});
app.forum = app.store.getById('forums', 1);
app.session = new Session(
app.store.getById('users', app.data.session.userId),
app.data.session.csrfToken
);
}

View File

@ -1,26 +0,0 @@
import Store from 'flarum/Store';
import Forum from 'flarum/models/Forum';
import User from 'flarum/models/User';
import Discussion from 'flarum/models/Discussion';
import Post from 'flarum/models/Post';
import Group from 'flarum/models/Group';
import Activity from 'flarum/models/Activity';
import Notification from 'flarum/models/Notification';
/**
* The `store` initializer creates the application's data store and registers
* the default resource types to their models.
*
* @param {App} app
*/
export default function store(app) {
app.store = new Store({
forums: Forum,
users: User,
discussions: Discussion,
posts: Post,
groups: Group,
activity: Activity,
notifications: Notification
});
}

7797
js/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
js/package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "@flarum/core",
"version": "0.0.0",
"dependencies": {
"bootstrap": "^3.3.7",
"classnames": "^2.2.5",
"color-thief-browser": "^2.0.2",
"expose-loader": "^0.7.5",
"flarum-webpack-config": "^0.1.0-beta.8",
"jquery": "^3.3.1",
"jquery.hotkeys": "^0.1.0",
"lodash-es": "^4.17.10",
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
"mithril": "^0.2.8",
"moment": "^2.22.2",
"punycode": "^2.1.1",
"spin.js": "^3.1.0",
"webpack": "^4.0.0",
"webpack-cli": "^2.1.5",
"webpack-merge": "^4.1.2"
},
"scripts": {
"build": "webpack --mode production",
"watch": "webpack --mode development --watch"
}
}

View File

@ -0,0 +1,63 @@
import HeaderPrimary from './components/HeaderPrimary';
import HeaderSecondary from './components/HeaderSecondary';
import routes from './routes';
import Application from '../common/Application';
import Navigation from '../common/components/Navigation';
import AdminNav from './components/AdminNav';
export default class AdminApplication extends Application {
extensionSettings = {};
history = {
canGoBack: () => true,
getPrevious: () => {},
backUrl: () => this.forum.attribute('baseUrl'),
back: function() {
window.location = this.backUrl();
}
};
constructor() {
super();
routes(this);
}
/**
* @inheritdoc
*/
mount() {
m.mount(document.getElementById('app-navigation'), Navigation.component({className: 'App-backControl', drawer: true}));
m.mount(document.getElementById('header-navigation'), Navigation.component());
m.mount(document.getElementById('header-primary'), HeaderPrimary.component());
m.mount(document.getElementById('header-secondary'), HeaderSecondary.component());
m.mount(document.getElementById('admin-navigation'), AdminNav.component());
m.route.mode = 'hash';
super.mount();
// If an extension has just been enabled, then we will run its settings
// callback.
const enabled = localStorage.getItem('enabledExtension');
if (enabled && this.extensionSettings[enabled]) {
this.extensionSettings[enabled]();
localStorage.removeItem('enabledExtension');
}
}
getRequiredPermissions(permission) {
const required = [];
if (permission === 'startDiscussion' || permission.indexOf('discussion.') === 0) {
required.push('viewDiscussions');
}
if (permission === 'discussion.delete') {
required.push('discussion.hide');
}
if (permission === 'discussion.deletePosts') {
required.push('discussion.editPosts');
}
return required;
};
}

63
js/src/admin/compat.js Normal file
View File

@ -0,0 +1,63 @@
import compat from '../common/compat';
import saveSettings from './utils/saveSettings';
import SettingDropdown from './components/SettingDropdown';
import EditCustomFooterModal from './components/EditCustomFooterModal';
import SessionDropdown from './components/SessionDropdown';
import HeaderPrimary from './components/HeaderPrimary';
import AppearancePage from './components/AppearancePage';
import Page from './components/Page';
import StatusWidget from './components/StatusWidget';
import HeaderSecondary from './components/HeaderSecondary';
import SettingsModal from './components/SettingsModal';
import DashboardWidget from './components/DashboardWidget';
import AddExtensionModal from './components/AddExtensionModal';
import ExtensionsPage from './components/ExtensionsPage';
import AdminLinkButton from './components/AdminLinkButton';
import PermissionGrid from './components/PermissionGrid';
import Widget from './components/Widget';
import MailPage from './components/MailPage';
import UploadImageButton from './components/UploadImageButton';
import LoadingModal from './components/LoadingModal';
import DashboardPage from './components/DashboardPage';
import BasicsPage from './components/BasicsPage';
import EditCustomHeaderModal from './components/EditCustomHeaderModal';
import PermissionsPage from './components/PermissionsPage';
import PermissionDropdown from './components/PermissionDropdown';
import AdminNav from './components/AdminNav';
import EditCustomCssModal from './components/EditCustomCssModal';
import EditGroupModal from './components/EditGroupModal';
import routes from './routes';
import AdminApplication from './AdminApplication';
export default Object.assign(compat, {
'utils/saveSettings': saveSettings,
'components/SettingDropdown': SettingDropdown,
'components/EditCustomFooterModal': EditCustomFooterModal,
'components/SessionDropdown': SessionDropdown,
'components/HeaderPrimary': HeaderPrimary,
'components/AppearancePage': AppearancePage,
'components/Page': Page,
'components/StatusWidget': StatusWidget,
'components/HeaderSecondary': HeaderSecondary,
'components/SettingsModal': SettingsModal,
'components/DashboardWidget': DashboardWidget,
'components/AddExtensionModal': AddExtensionModal,
'components/ExtensionsPage': ExtensionsPage,
'components/AdminLinkButton': AdminLinkButton,
'components/PermissionGrid': PermissionGrid,
'components/Widget': Widget,
'components/MailPage': MailPage,
'components/UploadImageButton': UploadImageButton,
'components/LoadingModal': LoadingModal,
'components/DashboardPage': DashboardPage,
'components/BasicsPage': BasicsPage,
'components/EditCustomHeaderModal': EditCustomHeaderModal,
'components/PermissionsPage': PermissionsPage,
'components/PermissionDropdown': PermissionDropdown,
'components/AdminNav': AdminNav,
'components/EditCustomCssModal': EditCustomCssModal,
'components/EditGroupModal': EditGroupModal,
'routes': routes,
'AdminApplication': AdminApplication
});

View File

@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/
import Modal from 'flarum/components/Modal';
import Modal from '../../common/components/Modal';
export default class AddExtensionModal extends Modal {
className() {

View File

@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/
import LinkButton from 'flarum/components/LinkButton';
import LinkButton from '../../common/components/LinkButton';
export default class AdminLinkButton extends LinkButton {
getButtonContent() {

View File

@ -7,11 +7,10 @@
* file that was distributed with this source code.
*/
import Component from 'flarum/Component';
import AdminLinkButton from 'flarum/components/AdminLinkButton';
import SelectDropdown from 'flarum/components/SelectDropdown';
import ItemList from 'flarum/utils/ItemList';
import Component from '../../common/Component';
import AdminLinkButton from './AdminLinkButton';
import SelectDropdown from '../../common/components/SelectDropdown';
import ItemList from '../../common/utils/ItemList';
export default class AdminNav extends Component {
view() {

View File

@ -1,11 +1,11 @@
import Page from 'flarum/components/Page';
import Button from 'flarum/components/Button';
import Switch from 'flarum/components/Switch';
import EditCustomCssModal from 'flarum/components/EditCustomCssModal';
import EditCustomHeaderModal from 'flarum/components/EditCustomHeaderModal';
import EditCustomFooterModal from 'flarum/components/EditCustomFooterModal';
import UploadImageButton from 'flarum/components/UploadImageButton';
import saveSettings from 'flarum/utils/saveSettings';
import Page from './Page';
import Button from '../../common/components/Button';
import Switch from '../../common/components/Switch';
import EditCustomCssModal from './EditCustomCssModal';
import EditCustomHeaderModal from './EditCustomHeaderModal';
import EditCustomFooterModal from './EditCustomFooterModal';
import UploadImageButton from './UploadImageButton';
import saveSettings from '../utils/saveSettings';
export default class AppearancePage extends Page {
init() {

View File

@ -1,11 +1,11 @@
import Page from 'flarum/components/Page';
import FieldSet from 'flarum/components/FieldSet';
import Select from 'flarum/components/Select';
import Button from 'flarum/components/Button';
import Alert from 'flarum/components/Alert';
import saveSettings from 'flarum/utils/saveSettings';
import ItemList from 'flarum/utils/ItemList';
import Switch from 'flarum/components/Switch';
import Page from './Page';
import FieldSet from '../../common/components/FieldSet';
import Select from '../../common/components/Select';
import Button from '../../common/components/Button';
import Alert from '../../common/components/Alert';
import saveSettings from '../utils/saveSettings';
import ItemList from '../../common/utils/ItemList';
import Switch from '../../common/components/Switch';
export default class BasicsPage extends Page {
init() {

View File

@ -1,5 +1,5 @@
import Page from 'flarum/components/Page';
import StatusWidget from 'flarum/components/StatusWidget';
import Page from './Page';
import StatusWidget from './StatusWidget';
export default class DashboardPage extends Page {
view() {

View File

@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/
import Component from 'flarum/Component';
import Component from '../../common/Component';
export default class Widget extends Component {
view() {

View File

@ -1,4 +1,4 @@
import SettingsModal from 'flarum/components/SettingsModal';
import SettingsModal from './SettingsModal';
export default class EditCustomCssModal extends SettingsModal {
className() {

View File

@ -1,4 +1,4 @@
import SettingsModal from 'flarum/components/SettingsModal';
import SettingsModal from './SettingsModal';
export default class EditCustomFooterModal extends SettingsModal {
className() {

View File

@ -1,4 +1,4 @@
import SettingsModal from 'flarum/components/SettingsModal';
import SettingsModal from './SettingsModal';
export default class EditCustomHeaderModal extends SettingsModal {
className() {

View File

@ -1,7 +1,7 @@
import Modal from 'flarum/components/Modal';
import Button from 'flarum/components/Button';
import Badge from 'flarum/components/Badge';
import Group from 'flarum/models/Group';
import Modal from '../../common/components/Modal';
import Button from '../../common/components/Button';
import Badge from '../../common/components/Badge';
import Group from '../../common/models/Group';
/**
* The `EditGroupModal` component shows a modal dialog which allows the user

View File

@ -1,13 +1,13 @@
import Page from 'flarum/components/Page';
import LinkButton from 'flarum/components/LinkButton';
import Button from 'flarum/components/Button';
import Dropdown from 'flarum/components/Dropdown';
import Separator from 'flarum/components/Separator';
import AddExtensionModal from 'flarum/components/AddExtensionModal';
import LoadingModal from 'flarum/components/LoadingModal';
import ItemList from 'flarum/utils/ItemList';
import icon from 'flarum/helpers/icon';
import listItems from 'flarum/helpers/listItems';
import Page from './Page';
import LinkButton from '../../common/components/LinkButton';
import Button from '../../common/components/Button';
import Dropdown from '../../common/components/Dropdown';
import Separator from '../../common/components/Separator';
import AddExtensionModal from './AddExtensionModal';
import LoadingModal from './LoadingModal';
import ItemList from '../../common/utils/ItemList';
import icon from '../../common/helpers/icon';
import listItems from '../../common/helpers/listItems';
export default class ExtensionsPage extends Page {
view() {

View File

@ -1,6 +1,6 @@
import Component from 'flarum/Component';
import ItemList from 'flarum/utils/ItemList';
import listItems from 'flarum/helpers/listItems';
import Component from '../../common/Component';
import ItemList from '../../common/utils/ItemList';
import listItems from '../../common/helpers/listItems';
/**
* The `HeaderPrimary` component displays primary header controls. On the

View File

@ -1,7 +1,7 @@
import Component from 'flarum/Component';
import SessionDropdown from 'flarum/components/SessionDropdown';
import ItemList from 'flarum/utils/ItemList';
import listItems from 'flarum/helpers/listItems';
import Component from '../../common/Component';
import SessionDropdown from './SessionDropdown';
import ItemList from '../../common/utils/ItemList';
import listItems from '../../common/helpers/listItems';
/**
* The `HeaderSecondary` component displays secondary header controls.

View File

@ -1,4 +1,4 @@
import Modal from 'flarum/components/Modal';
import Modal from '../../common/components/Modal';
export default class LoadingModal extends Modal {
isDismissible() {

View File

@ -1,8 +1,8 @@
import Page from 'flarum/components/Page';
import FieldSet from 'flarum/components/FieldSet';
import Button from 'flarum/components/Button';
import Alert from 'flarum/components/Alert';
import saveSettings from 'flarum/utils/saveSettings';
import Page from './Page';
import FieldSet from '../../common/components/FieldSet';
import Button from '../../common/components/Button';
import Alert from '../../common/components/Alert';
import saveSettings from '../utils/saveSettings';
export default class MailPage extends Page {
init() {

View File

@ -1,4 +1,4 @@
import Component from 'flarum/Component';
import Component from '../../common/Component';
/**
* The `Page` component

View File

@ -1,9 +1,9 @@
import Dropdown from 'flarum/components/Dropdown';
import Button from 'flarum/components/Button';
import Separator from 'flarum/components/Separator';
import Group from 'flarum/models/Group';
import Badge from 'flarum/components/Badge';
import GroupBadge from 'flarum/components/GroupBadge';
import Dropdown from '../../common/components/Dropdown';
import Button from '../../common/components/Button';
import Separator from '../../common/components/Separator';
import Group from '../../common/models/Group';
import Badge from '../../common/components/Badge';
import GroupBadge from '../../common/components/GroupBadge';
function badgeForId(id) {
const group = app.store.getById('groups', id);

View File

@ -1,9 +1,9 @@
import Component from 'flarum/Component';
import PermissionDropdown from 'flarum/components/PermissionDropdown';
import SettingDropdown from 'flarum/components/SettingDropdown';
import Button from 'flarum/components/Button';
import ItemList from 'flarum/utils/ItemList';
import icon from 'flarum/helpers/icon';
import Component from '../../common/Component';
import PermissionDropdown from './PermissionDropdown';
import SettingDropdown from './SettingDropdown';
import Button from '../../common/components/Button';
import ItemList from '../../common/utils/ItemList';
import icon from '../../common/helpers/icon';
export default class PermissionGrid extends Component {
init() {

View File

@ -1,9 +1,9 @@
import Page from 'flarum/components/Page';
import GroupBadge from 'flarum/components/GroupBadge';
import EditGroupModal from 'flarum/components/EditGroupModal';
import Group from 'flarum/models/Group';
import icon from 'flarum/helpers/icon';
import PermissionGrid from 'flarum/components/PermissionGrid';
import Page from './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';
export default class PermissionsPage extends Page {
view() {

View File

@ -1,8 +1,8 @@
import avatar from 'flarum/helpers/avatar';
import username from 'flarum/helpers/username';
import Dropdown from 'flarum/components/Dropdown';
import Button from 'flarum/components/Button';
import ItemList from 'flarum/utils/ItemList';
import avatar from '../../common/helpers/avatar';
import username from '../../common/helpers/username';
import Dropdown from '../../common/components/Dropdown';
import Button from '../../common/components/Button';
import ItemList from '../../common/utils/ItemList';
/**
* The `SessionDropdown` component shows a button with the current user's

View File

@ -1,6 +1,6 @@
import SelectDropdown from 'flarum/components/SelectDropdown';
import Button from 'flarum/components/Button';
import saveSettings from 'flarum/utils/saveSettings';
import SelectDropdown from '../../common/components/SelectDropdown';
import Button from '../../common/components/Button';
import saveSettings from '../utils/saveSettings';
export default class SettingDropdown extends SelectDropdown {
static initProps(props) {

View File

@ -1,6 +1,6 @@
import Modal from 'flarum/components/Modal';
import Button from 'flarum/components/Button';
import saveSettings from 'flarum/utils/saveSettings';
import Modal from '../../common/components/Modal';
import Button from '../../common/components/Button';
import saveSettings from '../utils/saveSettings';
export default class SettingsModal extends Modal {
init() {

View File

@ -7,10 +7,10 @@
* file that was distributed with this source code.
*/
import DashboardWidget from 'flarum/components/DashboardWidget';
import icon from 'flarum/helpers/icon';
import listItems from 'flarum/helpers/listItems';
import ItemList from 'flarum/utils/ItemList';
import DashboardWidget from './DashboardWidget';
import icon from '../../common/helpers/icon';
import listItems from '../../common/helpers/listItems';
import ItemList from '../../common/utils/ItemList';
export default class StatusWidget extends DashboardWidget {
className() {

View File

@ -1,4 +1,4 @@
import Button from 'flarum/components/Button';
import Button from '../../common/components/Button';
export default class UploadImageButton extends Button {
init() {

View File

@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/
import Component from 'flarum/Component';
import Component from '../../common/Component';
export default class DashboardWidget extends Component {
view() {

18
js/src/admin/index.js Normal file
View File

@ -0,0 +1,18 @@
import AdminApplication from './AdminApplication';
const app = new AdminApplication();
// Backwards compatibility
window.app = app;
export { app };
// Export public API
// Export compat API
import compat from './compat';
compat.app = app;
export { compat };

View File

@ -1,12 +1,12 @@
import DashboardPage from 'flarum/components/DashboardPage';
import BasicsPage from 'flarum/components/BasicsPage';
import PermissionsPage from 'flarum/components/PermissionsPage';
import AppearancePage from 'flarum/components/AppearancePage';
import ExtensionsPage from 'flarum/components/ExtensionsPage';
import MailPage from 'flarum/components/MailPage';
import DashboardPage from './components/DashboardPage';
import BasicsPage from './components/BasicsPage';
import PermissionsPage from './components/PermissionsPage';
import AppearancePage from './components/AppearancePage';
import ExtensionsPage from './components/ExtensionsPage';
import MailPage from './components/MailPage';
/**
* The `routes` initializer defines the admin app's routes.
* The `routes` initializer defines the forum app's routes.
*
* @param {App} app
*/

View File

@ -1,118 +1,184 @@
import ItemList from 'flarum/utils/ItemList';
import Alert from 'flarum/components/Alert';
import Button from 'flarum/components/Button';
import RequestErrorModal from 'flarum/components/RequestErrorModal';
import ConfirmPasswordModal from 'flarum/components/ConfirmPasswordModal';
import Translator from 'flarum/Translator';
import extract from 'flarum/utils/extract';
import patchMithril from 'flarum/utils/patchMithril';
import RequestError from 'flarum/utils/RequestError';
import { extend } from 'flarum/extend';
import ItemList from './utils/ItemList';
import Alert from './components/Alert';
import Button from './components/Button';
import ModalManager from './components/ModalManager';
import AlertManager from './components/AlertManager';
import RequestErrorModal from './components/RequestErrorModal';
import Translator from './Translator';
import Store from './Store';
import Session from './Session';
import extract from './utils/extract';
import Drawer from './utils/Drawer';
import mapRoutes from './utils/mapRoutes';
import RequestError from './utils/RequestError';
import ScrollListener from './utils/ScrollListener';
import { extend } from './extend';
import Forum from './models/Forum';
import User from './models/User';
import Discussion from './models/Discussion';
import Post from './models/Post';
import Group from './models/Group';
import Notification from './models/Notification';
import { flattenDeep } from 'lodash-es';
/**
* The `App` class provides a container for an application, as well as various
* utilities for the rest of the app to use.
*/
export default class App {
constructor() {
patchMithril(window);
/**
* The forum model for this application.
*
* @type {Forum}
* @public
*/
this.forum = null;
/**
* A map of routes, keyed by a unique route name. Each route is an object
* containing the following properties:
*
* - `path` The path that the route is accessed at.
* - `component` The Mithril component to render when this route is active.
*
* @example
* app.routes.discussion = {path: '/d/:id', component: DiscussionPage.component()};
*
* @type {Object}
* @public
*/
this.routes = {};
/**
* An ordered list of initializers to bootstrap the application.
*
* @type {ItemList}
* @public
*/
this.initializers = new ItemList();
/**
* The app's session.
*
* @type {Session}
* @public
*/
this.session = null;
/**
* The app's translator.
*
* @type {Translator}
* @public
*/
this.translator = new Translator();
/**
* The app's data store.
*
* @type {Store}
* @public
*/
this.store = null;
/**
* A local cache that can be used to store data at the application level, so
* that is persists between different routes.
*
* @type {Object}
* @public
*/
this.cache = {};
/**
* Whether or not the app has been booted.
*
* @type {Boolean}
* @public
*/
this.booted = false;
/**
* An Alert that was shown as a result of an AJAX request error. If present,
* it will be dismissed on the next successful request.
*
* @type {null|Alert}
* @private
*/
this.requestError = null;
this.title = '';
this.titleCount = 0;
}
export default class Application {
/**
* Boot the application by running all of the registered initializers.
* The forum model for this application.
*
* @type {Forum}
* @public
*/
boot(data) {
this.data = data;
forum = null;
this.translator.locale = data.locale;
/**
* A map of routes, keyed by a unique route name. Each route is an object
* containing the following properties:
*
* - `path` The path that the route is accessed at.
* - `component` The Mithril component to render when this route is active.
*
* @example
* app.routes.discussion = {path: '/d/:id', component: DiscussionPage.component()};
*
* @type {Object}
* @public
*/
routes = {};
/**
* An ordered list of initializers to bootstrap the application.
*
* @type {ItemList}
* @public
*/
initializers = new ItemList();
/**
* The app's session.
*
* @type {Session}
* @public
*/
session = null;
/**
* The app's translator.
*
* @type {Translator}
* @public
*/
translator = new Translator();
/**
* The app's data store.
*
* @type {Store}
* @public
*/
store = new Store({
forums: Forum,
users: User,
discussions: Discussion,
posts: Post,
groups: Group,
notifications: Notification
});
/**
* A local cache that can be used to store data at the application level, so
* that is persists between different routes.
*
* @type {Object}
* @public
*/
cache = {};
/**
* Whether or not the app has been booted.
*
* @type {Boolean}
* @public
*/
booted = false;
/**
* An Alert that was shown as a result of an AJAX request error. If present,
* it will be dismissed on the next successful request.
*
* @type {null|Alert}
* @private
*/
requestError = null;
data;
title = '';
titleCount = 0;
load(payload) {
this.data = payload;
this.translator.locale = payload.locale;
}
boot() {
this.initializers.toArray().forEach(initializer => initializer(this));
this.store.pushPayload({data: this.data.resources});
this.forum = this.store.getById('forums', 1);
this.session = new Session(
this.store.getById('users', this.data.session.userId),
this.data.session.csrfToken
);
this.mount();
}
bootExtensions(extensions) {
Object.keys(extensions).forEach(name => {
const extension = extensions[name];
const extenders = flattenDeep(extension.extend);
for (const extender of extenders) {
extender.extend(this, { name, exports: extension });
}
});
}
mount() {
this.modal = m.mount(document.getElementById('modal'), <ModalManager/>);
this.alerts = m.mount(document.getElementById('alerts'), <AlertManager/>);
this.drawer = new Drawer();
const basePath = this.forum.attribute('basePath');
m.route(
document.getElementById('content'),
basePath + '/',
mapRoutes(this.routes, basePath)
);
// Add a class to the body which indicates that the page has been scrolled
// down.
new ScrollListener(top => {
const $app = $('#app');
const offset = $app.offset().top;
$app
.toggleClass('affix', top >= offset)
.toggleClass('scrolled', top > offset);
}).start();
$(() => {
$('body').addClass('ontouchstart' in window ? 'touch' : 'no-touch');
});
}
/**
@ -124,6 +190,7 @@ export default class App {
preloadedDocument() {
if (this.data.document) {
const results = this.store.pushPayload(this.data.document);
this.data.document = null;
return results;

View File

@ -1,7 +1,7 @@
import User from 'flarum/models/User';
import username from 'flarum/helpers/username';
import extractText from 'flarum/utils/extractText';
import extract from 'flarum/utils/extract';
import User from './models/User';
import username from './helpers/username';
import extractText from './utils/extractText';
import extract from './utils/extract';
/**
* Translator with the same API as Symfony's.

127
js/src/common/compat.js Normal file
View File

@ -0,0 +1,127 @@
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 SubtreeRetainer from './utils/SubtreeRetainer';
import extract from './utils/extract';
import ScrollListener from './utils/ScrollListener';
import stringToColor from './utils/stringToColor';
import patchMithril from './utils/patchMithril';
import classList from './utils/classList';
import extractText from './utils/extractText';
import formatNumber from './utils/formatNumber';
import mapRoutes from './utils/mapRoutes';
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 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 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';
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/patchMithril': patchMithril,
'utils/classList': classList,
'utils/extractText': extractText,
'utils/formatNumber': formatNumber,
'utils/mapRoutes': mapRoutes,
'models/Notification': Notification,
'models/User': User,
'models/Post': Post,
'models/Discussion': Discussion,
'models/Group': Group,
'models/Forum': Forum,
'Component': Component,
'Translator': Translator,
'components/AlertManager': AlertManager,
'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/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
};

View File

@ -1,7 +1,7 @@
import Component from 'flarum/Component';
import Button from 'flarum/components/Button';
import listItems from 'flarum/helpers/listItems';
import extract from 'flarum/utils/extract';
import Component from '../Component';
import Button from './Button';
import listItems from '../helpers/listItems';
import extract from '../utils/extract';
/**
* The `Alert` component represents an alert box, which contains a message,

View File

@ -1,5 +1,5 @@
import Component from 'flarum/Component';
import Alert from 'flarum/components/Alert';
import Component from '../Component';
import Alert from './Alert';
/**
* The `AlertManager` component provides an area in which `Alert` components can

View File

@ -1,6 +1,6 @@
import Component from 'flarum/Component';
import icon from 'flarum/helpers/icon';
import extract from 'flarum/utils/extract';
import Component from '../Component';
import icon from '../helpers/icon';
import extract from '../utils/extract';
/**
* The `Badge` component represents a user/discussion badge, indicating some

View File

@ -1,8 +1,8 @@
import Component from 'flarum/Component';
import icon from 'flarum/helpers/icon';
import extract from 'flarum/utils/extract';
import extractText from 'flarum/utils/extractText';
import LoadingIndicator from 'flarum/components/LoadingIndicator';
import Component from '../Component';
import icon from '../helpers/icon';
import extract from '../utils/extract';
import extractText from '../utils/extractText';
import LoadingIndicator from './LoadingIndicator';
/**
* The `Button` component defines an element which, when clicked, performs an

View File

@ -1,6 +1,6 @@
import Component from 'flarum/Component';
import LoadingIndicator from 'flarum/components/LoadingIndicator';
import icon from 'flarum/helpers/icon';
import Component from '../Component';
import LoadingIndicator from './LoadingIndicator';
import icon from '../helpers/icon';
/**
* The `Checkbox` component defines a checkbox input.

View File

@ -1,6 +1,6 @@
import Component from 'flarum/Component';
import icon from 'flarum/helpers/icon';
import listItems from 'flarum/helpers/listItems';
import Component from '../Component';
import icon from '../helpers/icon';
import listItems from '../helpers/listItems';
/**
* The `Dropdown` component displays a button which, when clicked, shows a

View File

@ -1,5 +1,5 @@
import Component from 'flarum/Component';
import listItems from 'flarum/helpers/listItems';
import Component from '../Component';
import listItems from '../helpers/listItems';
/**
* The `FieldSet` component defines a collection of fields, displayed in a list

View File

@ -1,4 +1,4 @@
import Badge from 'flarum/components/Badge';
import Badge from './Badge';
export default class GroupBadge extends Badge {
static initProps(props) {

View File

@ -1,4 +1,4 @@
import Button from 'flarum/components/Button';
import Button from './Button';
/**
* The `LinkButton` component defines a `Button` which links to a route.

View File

@ -1,4 +1,5 @@
import Component from 'flarum/Component';
import Component from '../Component';
import { Spinner } from 'spin.js';
/**
* The `LoadingIndicator` component displays a loading spinner with spin.js. It
@ -19,9 +20,17 @@ export default class LoadingIndicator extends Component {
}
config() {
const size = this.props.size || 'small';
const options = { zIndex: 'auto', color: this.$().css('color') };
$.fn.spin.presets[size].zIndex = 'auto';
this.$().spin(size);
switch (this.props.size) {
case 'large':
Object.assign(options, { lines: 10, length: 8, width: 4, radius: 8 });
break;
default:
Object.assign(options, { lines: 8, length: 4, width: 3, radius: 5 });
}
new Spinner(options).spin(this.element);
}
}

View File

@ -1,6 +1,6 @@
import Component from 'flarum/Component';
import Alert from 'flarum/components/Alert';
import Button from 'flarum/components/Button';
import Component from '../Component';
import Alert from './Alert';
import Button from './Button';
/**
* The `Modal` component displays a modal dialog, wrapped in a form. Subclasses

View File

@ -1,5 +1,5 @@
import Component from 'flarum/Component';
import Modal from 'flarum/components/Modal';
import Component from '../Component';
import Modal from './Modal';
/**
* The `ModalManager` component manages a modal dialog. Only one modal dialog

View File

@ -1,6 +1,6 @@
import Component from 'flarum/Component';
import Button from 'flarum/components/Button';
import LinkButton from 'flarum/components/LinkButton';
import Component from '../Component';
import Button from './Button';
import LinkButton from './LinkButton';
/**
* The `Navigation` component displays a set of navigation buttons. Typically

View File

@ -1,4 +1,4 @@
import Component from 'flarum/Component';
import Component from '../Component';
/**
* The `Placeholder` component displays a muted text with some call to action,

View File

@ -1,4 +1,4 @@
import Modal from 'flarum/components/Modal';
import Modal from './Modal';
export default class RequestErrorModal extends Modal {
className() {

View File

@ -1,5 +1,5 @@
import Component from 'flarum/Component';
import icon from 'flarum/helpers/icon';
import Component from '../Component';
import icon from '../helpers/icon';
/**
* The `Select` component displays a <select> input, surrounded with some extra

View File

@ -1,5 +1,5 @@
import Dropdown from 'flarum/components/Dropdown';
import icon from 'flarum/helpers/icon';
import Dropdown from './Dropdown';
import icon from '../helpers/icon';
/**
* The `SelectDropdown` component is the same as a `Dropdown`, except the toggle

View File

@ -1,4 +1,4 @@
import Component from 'flarum/Component';
import Component from '../Component';
/**
* The `Separator` component defines a menu separator item.

View File

@ -1,6 +1,6 @@
import Dropdown from 'flarum/components/Dropdown';
import Button from 'flarum/components/Button';
import icon from 'flarum/helpers/icon';
import Dropdown from './Dropdown';
import Button from './Button';
import icon from '../helpers/icon';
/**
* The `SplitDropdown` component is similar to `Dropdown`, but the first child

View File

@ -1,4 +1,4 @@
import Checkbox from 'flarum/components/Checkbox';
import Checkbox from './Checkbox';
/**
* The `Switch` component is a `Checkbox`, but with a switch display instead of

View File

@ -0,0 +1,41 @@
export default class Routes {
type;
attributes = [];
hasOnes = [];
hasManys = [];
constructor(type, model = null) {
this.type = type;
this.model = model;
}
attribute(name) {
this.attributes.push(name);
return this;
}
hasOne(type) {
this.hasOnes.push(type);
return this;
}
hasMany(type) {
this.hasManys.push(type);
return this;
}
extend(app, extension) {
if (this.model) {
app.store.models[this.type] = this.model;
}
const model = app.store.models[this.type];
this.attributes.forEach(name => model.prototype[name] = model.attribute(name));
this.hasOnes.forEach(name => model.prototype[name] = model.hasOne(name));
this.hasManys.forEach(name => model.prototype[name] = model.hasMany(name));
}
}

View File

@ -0,0 +1,13 @@
export default class PostTypes {
postComponents = {};
add(name, component) {
this.postComponents[name] = component;
return this;
}
extend(app, extension) {
Object.assign(app.postComponents, this.postComponents);
}
}

View File

@ -0,0 +1,13 @@
export default class Routes {
routes = {};
add(name, path, component) {
this.routes[name] = { path, component };
return this;
}
extend(app, extension) {
Object.assign(app.routes, this.routes);
}
}

View File

@ -0,0 +1,3 @@
export { default as Model } from './Model';
export { default as PostTypes } from './PostTypes';
export { default as Routes } from './Routes';

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