mirror of
https://github.com/flarum/core.git
synced 2025-08-24 00:53:27 +02:00
Compare commits
142 Commits
v0.1.0-bet
...
next-front
Author | SHA1 | Date | |
---|---|---|---|
|
fd3f484eaf | ||
|
8b3971f202 | ||
|
5b68b80e73 | ||
|
ef4c9d4f8a | ||
|
4585002118 | ||
|
5451aac693 | ||
|
01b56eecdb | ||
|
bcdcb8c20e | ||
|
d6c99eccdb | ||
|
01cb8ab79d | ||
|
877aed215b | ||
|
57570d960e | ||
|
04c4806f6f | ||
|
4c0339c30e | ||
|
e64dc4ea45 | ||
|
305076814f | ||
|
e31edd29d2 | ||
|
23b423c6ce | ||
|
1af1f472f9 | ||
|
7c86f7a34c | ||
|
14e49269d6 | ||
|
7837fff107 | ||
|
3dfa6bc8cb | ||
|
e47fe288fa | ||
|
03e30d7d4f | ||
|
9836ff6c54 | ||
|
bb1c655c90 | ||
|
bf20fe595a | ||
|
b5db57156b | ||
|
a7d3bdf244 | ||
|
c82f0bde61 | ||
|
986102c1d3 | ||
|
2140619c0b | ||
|
2f714a01ed | ||
|
231d018de5 | ||
|
5d62231004 | ||
|
123c3a93f5 | ||
|
c1eec2b261 | ||
|
60d3d6ef99 | ||
|
7862bd32dd | ||
|
92b555a246 | ||
|
687ec6a199 | ||
|
f788a0a972 | ||
|
e7bec9fe29 | ||
|
57da4e24cb | ||
|
7d1a22bcb5 | ||
|
8cc117d89d | ||
|
31ef02dc2c | ||
|
95c9ff9243 | ||
|
9718f54683 | ||
|
bb1e3278de | ||
|
bbcc33b5b5 | ||
|
30076547e5 | ||
|
7b710d5898 | ||
|
d02b5c9db7 | ||
|
cd70819fd5 | ||
|
20b4619e75 | ||
|
fdec2fd094 | ||
|
d7e4ae09b3 | ||
|
fcfc1b2a37 | ||
|
01eba18164 | ||
|
0dcf7d6aa9 | ||
|
015967a76c | ||
|
3cd59e12f5 | ||
|
26d07699e9 | ||
|
b7d6ba4893 | ||
|
d3753d94ae | ||
|
9349ed13fc | ||
|
91ace15f6d | ||
|
7c1b0bfcf2 | ||
|
542bae6277 | ||
|
275c14ee7f | ||
|
bccc970231 | ||
|
da6f79b34a | ||
|
a3cbec25db | ||
|
2225fdec72 | ||
|
6a532ec14e | ||
|
9416d16ebb | ||
|
d6857b0fe5 | ||
|
2c7e7f5b39 | ||
|
b5b18dd436 | ||
|
4778ae5f74 | ||
|
0936a630ef | ||
|
ec8ae6e03b | ||
|
9ffdeff608 | ||
|
8540932638 | ||
|
974f45e4e8 | ||
|
32ac48c6a9 | ||
|
af5b86806a | ||
|
aeef45b3cd | ||
|
8aa70de765 | ||
|
076a71c621 | ||
|
06c32b668d | ||
|
7af4b8d45f | ||
|
cbba325a87 | ||
|
b7d7e8b18a | ||
|
1031826a3d | ||
|
3612ca7aca | ||
|
c2ee84a115 | ||
|
060745ecb7 | ||
|
dd209b1747 | ||
|
aeb0a411b9 | ||
|
1ebb8bf39a | ||
|
fcdf36b3d0 | ||
|
ab912ba1ad | ||
|
4b8eb5d6e4 | ||
|
0e20949eb0 | ||
|
b2c691a03d | ||
|
dde0de046a | ||
|
7a9795fbc3 | ||
|
f30fac6a94 | ||
|
1fb8092987 | ||
|
ea6b943dbd | ||
|
b9918e6c40 | ||
|
b3e1a023c2 | ||
|
96926a180a | ||
|
f3bdc163fa | ||
|
0df6eee10f | ||
|
258a4b352d | ||
|
24580ced7a | ||
|
8e90d9f9e2 | ||
|
af36ef3fa9 | ||
|
eef63745e6 | ||
|
c702e911b3 | ||
|
73d2ee825b | ||
|
9f99610542 | ||
|
1192867c4f | ||
|
b048498b84 | ||
|
81f7a39a31 | ||
|
ea12bbaf48 | ||
|
c8122a7879 | ||
|
1a5d7a337d | ||
|
c29ea98d48 | ||
|
3702ffa998 | ||
|
58f9c22375 | ||
|
939a1e9ca8 | ||
|
736f22a31a | ||
|
592dd6a927 | ||
|
2c5aa138cd | ||
|
53fd7b66b4 | ||
|
a1a22aa4ce | ||
|
05c9ce335e |
7
.gitattributes
vendored
7
.gitattributes
vendored
@@ -2,3 +2,10 @@
|
||||
.gitignore export-ignore
|
||||
.gitmodules export-ignore
|
||||
.travis.yml export-ignore
|
||||
.editorconfig export-ignore
|
||||
.styleci.yml export-ignore
|
||||
|
||||
phpunit.xml export-ignore
|
||||
tests export-ignore
|
||||
|
||||
js/*/dist/*.js -diff
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ Thumbs.db
|
||||
tests/_output/*
|
||||
.vagrant
|
||||
.idea/*
|
||||
node_modules
|
||||
bower_components
|
@@ -5,6 +5,7 @@ enabled:
|
||||
|
||||
disabled:
|
||||
- align_double_arrow
|
||||
- blank_line_after_opening_tag
|
||||
- multiline_array_trailing_comma
|
||||
- new_with_braces
|
||||
- phpdoc_align
|
||||
|
@@ -1,9 +1,9 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- hhvm
|
||||
|
||||
matrix:
|
||||
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2016 Toby Zerner
|
||||
Copyright (c) 2014-2017 Toby Zerner
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -20,7 +20,7 @@
|
||||
"docs": "http://flarum.org/docs"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"php": ">=5.6.0",
|
||||
"dflydev/fig-cookies": "^1.0.2",
|
||||
"doctrine/dbal": "^2.5",
|
||||
"components/font-awesome": "^4.6",
|
||||
@@ -50,10 +50,10 @@
|
||||
"symfony/http-foundation": "^2.7",
|
||||
"symfony/translation": "^2.7",
|
||||
"symfony/yaml": "^2.7",
|
||||
"s9e/text-formatter": "^0.6.1",
|
||||
"s9e/text-formatter": "^0.8.1",
|
||||
"tobscure/json-api": "^0.3.0",
|
||||
"zendframework/zend-diactoros": "^1.1",
|
||||
"zendframework/zend-stratigility": "^1.1"
|
||||
"zendframework/zend-stratigility": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^0.9.4",
|
||||
|
1
js/.gitignore
vendored
1
js/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
bower_components
|
2
js/admin/.gitignore
vendored
2
js/admin/.gitignore
vendored
@@ -1 +1 @@
|
||||
node_modules
|
||||
dist
|
@@ -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'
|
||||
});
|
23804
js/admin/dist/app.js
vendored
23804
js/admin/dist/app.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.1",
|
||||
"flarum-gulp": "^0.2.0"
|
||||
}
|
||||
}
|
53
js/admin/src/AdminApplication.tsx
Normal file
53
js/admin/src/AdminApplication.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import * as m from 'mithril';
|
||||
import Application from './lib/Application';
|
||||
import routes from './routes';
|
||||
import Nav from './components/Nav';
|
||||
|
||||
export default class AdminApplication extends Application {
|
||||
/**
|
||||
* A map of extension names to their settings callbacks.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
extensionSettings = {};
|
||||
|
||||
/**
|
||||
* Construct a list of permissions required to have the given permission.
|
||||
*
|
||||
* @param {String} permission
|
||||
* @return {Array}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
mount() {
|
||||
m.route.prefix('#');
|
||||
|
||||
super.mount();
|
||||
|
||||
m.mount(document.getElementById('nav'), <Nav/>);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
registerDefaultRoutes(router) {
|
||||
routes(router);
|
||||
}
|
||||
}
|
@@ -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;
|
@@ -1,30 +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 'flarum/components/Modal';
|
||||
|
||||
export default class AddExtensionModal extends Modal {
|
||||
className() {
|
||||
return 'AddExtensionModal Modal--small';
|
||||
}
|
||||
|
||||
title() {
|
||||
return 'Add Extension';
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
40
js/admin/src/components/AddExtensionModal.tsx
Normal file
40
js/admin/src/components/AddExtensionModal.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 flarum from 'flarum';
|
||||
import Modal from 'flarum/lib/components/Modal';
|
||||
|
||||
export default class AddExtensionModal extends Modal {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
className() {
|
||||
return super.className() + ' AddExtensionModal Modal--small';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
title() {
|
||||
return flarum.translator.trans('admin.add_extension.title');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
content() {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<p>{flarum.translator.trans('admin.add_extension.temporary_text')}</p>
|
||||
<p>{flarum.translator.trans('admin.add_extension.install_text', {a: <a href="https://discuss.flarum.org/t/extensions" target="_blank"/>})}</p>
|
||||
<p>{flarum.translator.trans('admin.add_extension.developer_text', {a: <a href="http://flarum.org/docs/extend" target="_blank"/>})}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,24 +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 'flarum/components/LinkButton';
|
||||
|
||||
export default class AdminLinkButton extends LinkButton {
|
||||
getButtonContent() {
|
||||
const content = super.getButtonContent();
|
||||
|
||||
content.push(
|
||||
<div className="AdminLinkButton-description">
|
||||
{this.props.description}
|
||||
</div>
|
||||
);
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
33
js/admin/src/components/AdminLinkButton.tsx
Normal file
33
js/admin/src/components/AdminLinkButton.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import LinkButton from 'flarum/lib/components/LinkButton';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default class AdminLinkButton extends LinkButton {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
className() {
|
||||
return super.className() + ' AdminLinkButton';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
content() {
|
||||
const content = super.content();
|
||||
|
||||
content.push(<div className="AdminLinkButton-description">{this.attrs.description}</div>);
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
@@ -1,79 +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 Component from 'flarum/Component';
|
||||
import AdminLinkButton from 'flarum/components/AdminLinkButton';
|
||||
import SelectDropdown from 'flarum/components/SelectDropdown';
|
||||
|
||||
import ItemList from 'flarum/utils/ItemList';
|
||||
|
||||
export default class AdminNav extends Component {
|
||||
view() {
|
||||
return (
|
||||
<SelectDropdown
|
||||
className="AdminNav App-titleControl"
|
||||
buttonClassName="Button"
|
||||
children={this.items().toArray()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list of links to show in the admin navigation.
|
||||
*
|
||||
* @return {ItemList}
|
||||
*/
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('dashboard', AdminLinkButton.component({
|
||||
href: app.route('dashboard'),
|
||||
icon: 'bar-chart',
|
||||
children: app.translator.trans('core.admin.nav.dashboard_button'),
|
||||
description: app.translator.trans('core.admin.nav.dashboard_text')
|
||||
}));
|
||||
|
||||
items.add('basics', AdminLinkButton.component({
|
||||
href: app.route('basics'),
|
||||
icon: 'pencil',
|
||||
children: app.translator.trans('core.admin.nav.basics_button'),
|
||||
description: app.translator.trans('core.admin.nav.basics_text')
|
||||
}));
|
||||
|
||||
items.add('mail', AdminLinkButton.component({
|
||||
href: app.route('mail'),
|
||||
icon: 'envelope',
|
||||
children: app.translator.trans('core.admin.nav.email_button'),
|
||||
description: app.translator.trans('core.admin.nav.email_text')
|
||||
}));
|
||||
|
||||
items.add('permissions', AdminLinkButton.component({
|
||||
href: app.route('permissions'),
|
||||
icon: 'key',
|
||||
children: app.translator.trans('core.admin.nav.permissions_button'),
|
||||
description: app.translator.trans('core.admin.nav.permissions_text')
|
||||
}));
|
||||
|
||||
items.add('appearance', AdminLinkButton.component({
|
||||
href: app.route('appearance'),
|
||||
icon: 'paint-brush',
|
||||
children: app.translator.trans('core.admin.nav.appearance_button'),
|
||||
description: app.translator.trans('core.admin.nav.appearance_text')
|
||||
}));
|
||||
|
||||
items.add('extensions', AdminLinkButton.component({
|
||||
href: app.route('extensions'),
|
||||
icon: 'puzzle-piece',
|
||||
children: app.translator.trans('core.admin.nav.extensions_button'),
|
||||
description: app.translator.trans('core.admin.nav.extensions_text')
|
||||
}));
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
52
js/admin/src/components/AdminNav.tsx
Normal file
52
js/admin/src/components/AdminNav.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import Component from 'flarum/lib/Component';
|
||||
import ItemList from 'flarum/lib/utils/ItemList';
|
||||
import AdminLinkButton from 'flarum/components/AdminLinkButton';
|
||||
import SelectDropdown from 'flarum/components/SelectDropdown';
|
||||
|
||||
function addLink(items, route, icon) {
|
||||
items.add(route, <AdminLinkButton
|
||||
href={flarum.router.to(route)}
|
||||
icon={icon}
|
||||
children={flarum.translator.trans(`admin.nav.${route}_button`)}
|
||||
description={flarum.translator.trans(`admin.nav.${route}_text`)}/>);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default class Nav extends Component {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
view() {
|
||||
return <SelectDropdown className="Nav" buttonClassName="Button" children={this.items().toArray()}/>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list of links to show in the admin navigation.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @public
|
||||
*/
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
addLink(items, 'dashboard', 'bar-chart'));
|
||||
addLink(items, 'basics', 'pencil'));
|
||||
addLink(items, 'mail', 'envelope'));
|
||||
addLink(items, 'permissions', 'key'));
|
||||
addLink(items, 'appearance', 'paint-brush'));
|
||||
addLink(items, 'extensions', 'puzzle-piece'));
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
@@ -28,8 +28,8 @@ export default class AppearancePage extends Page {
|
||||
</div>
|
||||
|
||||
<div className="AppearancePage-colors-input">
|
||||
<input className="FormControl" placeholder="#aaaaaa" value={this.primaryColor()} onchange={m.withAttr('value', this.primaryColor)}/>
|
||||
<input className="FormControl" placeholder="#aaaaaa" value={this.secondaryColor()} onchange={m.withAttr('value', this.secondaryColor)}/>
|
||||
<input className="FormControl" type="color" placeholder="#aaaaaa" value={this.primaryColor()} onchange={m.withAttr('value', this.primaryColor)}/>
|
||||
<input className="FormControl" type="color" placeholder="#aaaaaa" value={this.secondaryColor()} onchange={m.withAttr('value', this.secondaryColor)}/>
|
||||
</div>
|
||||
|
||||
{Switch.component({
|
@@ -5,6 +5,7 @@ import Button from 'flarum/components/Button';
|
||||
import Alert from 'flarum/components/Alert';
|
||||
import saveSettings from 'flarum/utils/saveSettings';
|
||||
import ItemList from 'flarum/utils/ItemList';
|
||||
import Switch from 'flarum/components/Switch';
|
||||
|
||||
export default class BasicsPage extends Page {
|
||||
init() {
|
||||
@@ -16,6 +17,7 @@ export default class BasicsPage extends Page {
|
||||
'forum_title',
|
||||
'forum_description',
|
||||
'default_locale',
|
||||
'show_language_selector',
|
||||
'default_route',
|
||||
'welcome_title',
|
||||
'welcome_message'
|
||||
@@ -23,13 +25,15 @@ export default class BasicsPage extends Page {
|
||||
this.values = {};
|
||||
|
||||
const settings = app.data.settings;
|
||||
this.fields.forEach(key => this.values[key] = m.prop(settings[key]));
|
||||
this.fields.forEach(key => this.values[key] = m.prop(settings[key] || false));
|
||||
|
||||
this.localeOptions = {};
|
||||
const locales = app.data.locales;
|
||||
for (const i in locales) {
|
||||
this.localeOptions[i] = `${locales[i]} (${i})`;
|
||||
}
|
||||
|
||||
if (typeof this.values.show_language_selector() !== "number") this.values.show_language_selector(1);
|
||||
}
|
||||
|
||||
view() {
|
||||
@@ -60,12 +64,21 @@ export default class BasicsPage extends Page {
|
||||
children: [
|
||||
Select.component({
|
||||
options: this.localeOptions,
|
||||
value: this.values.default_locale(),
|
||||
onchange: this.values.default_locale
|
||||
})
|
||||
]
|
||||
})
|
||||
: ''}
|
||||
|
||||
{Switch.component({
|
||||
state: this.values.show_language_selector(),
|
||||
onchange: this.values.show_language_selector,
|
||||
children: app.translator.trans('core.admin.basics.show_language_selector_label'),
|
||||
})}
|
||||
|
||||
<br/>
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.basics.home_page_heading'),
|
||||
className: 'BasicsPage-homePage',
|
25
js/admin/src/components/Header.tsx
Normal file
25
js/admin/src/components/Header.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import Component from 'flarum/Component';
|
||||
import ItemList from 'flarum/utils/ItemList';
|
||||
import SessionDropdown from './SessionDropdown';
|
||||
|
||||
export default class Header extends Component {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
view() {
|
||||
return this.items().toVnodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the header contents.
|
||||
*
|
||||
* @return {ItemList}
|
||||
*/
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('session', <SessionDropdown/>);
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
import Component from 'flarum/Component';
|
||||
import SessionDropdown from 'flarum/components/SessionDropdown';
|
||||
import ItemList from 'flarum/utils/ItemList';
|
||||
import listItems from 'flarum/helpers/listItems';
|
||||
|
||||
/**
|
||||
* The `HeaderSecondary` component displays secondary header controls.
|
||||
*/
|
||||
export default class HeaderSecondary extends Component {
|
||||
view() {
|
||||
return (
|
||||
<ul className="Header-controls">
|
||||
{listItems(this.items().toArray())}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
config(isInitialized, context) {
|
||||
// Since this component is 'above' the content of the page (that is, it is a
|
||||
// part of the global UI that persists between routes), we will flag the DOM
|
||||
// to be retained across route changes.
|
||||
context.retain = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
*/
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('session', SessionDropdown.component());
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
@@ -91,6 +91,13 @@ export default class PermissionGrid extends Component {
|
||||
allowGuest: true
|
||||
}, 100);
|
||||
|
||||
items.add('viewUserList', {
|
||||
icon: 'users',
|
||||
label: app.translator.trans('core.admin.permissions.view_user_list_label'),
|
||||
permission: 'viewUserList',
|
||||
allowGuest: true
|
||||
}, 100);
|
||||
|
||||
items.add('signUp', {
|
||||
icon: 'user-plus',
|
||||
label: app.translator.trans('core.admin.permissions.sign_up_label'),
|
9
js/admin/src/index.ts
Normal file
9
js/admin/src/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import AdminApplication from './AdminApplication';
|
||||
|
||||
export const app = new AdminApplication();
|
||||
|
||||
export const extensions = [];
|
||||
|
||||
// Export public API
|
||||
// export { default as Extend } from './Extend';
|
||||
// export { IndexPage, DicsussionList } from './components';
|
@@ -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');
|
||||
}
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
import DashboardPage from 'flarum/components/DashboardPage';
|
||||
import BasicsPage from 'flarum/components/BasicsPage';
|
||||
import PermissionsPage from 'flarum/components/PermissionsPage';
|
||||
import AppearancePage from 'flarum/components/AppearancePage';
|
||||
import ExtensionsPage from 'flarum/components/ExtensionsPage';
|
||||
import MailPage from 'flarum/components/MailPage';
|
||||
|
||||
/**
|
||||
* The `routes` initializer defines the admin app's routes.
|
||||
*
|
||||
* @param {App} app
|
||||
*/
|
||||
export default function(app) {
|
||||
app.routes = {
|
||||
'dashboard': {path: '/', component: DashboardPage.component()},
|
||||
'basics': {path: '/basics', component: BasicsPage.component()},
|
||||
'permissions': {path: '/permissions', component: PermissionsPage.component()},
|
||||
'appearance': {path: '/appearance', component: AppearancePage.component()},
|
||||
'extensions': {path: '/extensions', component: ExtensionsPage.component()},
|
||||
'mail': {path: '/mail', component: MailPage.component()}
|
||||
};
|
||||
}
|
1
js/admin/src/lib
Symbolic link
1
js/admin/src/lib
Symbolic link
@@ -0,0 +1 @@
|
||||
/Users/toby/Projects/Flarum/app/packages/core/js/lib
|
15
js/admin/src/routes.ts
Normal file
15
js/admin/src/routes.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
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';
|
||||
|
||||
export default function(router) {
|
||||
router.add('dashboard', '/', DashboardPage);
|
||||
router.add('basics', '/basics', BasicsPage);
|
||||
router.add('permissions', '/permissions', PermissionsPage);
|
||||
router.add('appearance', '/appearance', AppearancePage);
|
||||
router.add('extensions', '/extensions', ExtensionsPage);
|
||||
router.add('mail', '/mail', MailPage);
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
export default function saveSettings(settings) {
|
||||
const oldSettings = JSON.parse(JSON.stringify(app.data.settings));
|
||||
|
||||
Object.assign(app.data.settings, settings);
|
||||
|
||||
return app.request({
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('apiUrl') + '/settings',
|
||||
data: settings
|
||||
}).catch(error => {
|
||||
app.data.settings = oldSettings;
|
||||
throw error;
|
||||
});
|
||||
}
|
22
js/admin/src/utils/saveSettings.tsx
Normal file
22
js/admin/src/utils/saveSettings.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import flarum from 'flarum';
|
||||
|
||||
/**
|
||||
* Make a request to save the given settings to the database.
|
||||
*
|
||||
* @param {Object} settings
|
||||
* @return {Promise}
|
||||
*/
|
||||
export default function saveSettings(settings) {
|
||||
const oldSettings = JSON.parse(JSON.stringify(flarum.data.settings));
|
||||
|
||||
Object.assign(flarum.data.settings, settings);
|
||||
|
||||
return flarum.ajax.request({
|
||||
method: 'POST',
|
||||
url: flarum.forum.apiUrl + '/settings',
|
||||
data: settings
|
||||
}).catch(error => {
|
||||
flarum.data.settings = oldSettings;
|
||||
throw error;
|
||||
});
|
||||
}
|
23
js/admin/tsconfig.json
Normal file
23
js/admin/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": false,
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"jsxFactory": "m",
|
||||
"declaration": true,
|
||||
"lib": ["dom", "es2015"],
|
||||
"types": [
|
||||
"mithril",
|
||||
"classnames"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
30
js/admin/webpack.config.js
Normal file
30
js/admin/webpack.config.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const path = require('path');
|
||||
const { CheckerPlugin } = require('awesome-typescript-loader');
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve(__dirname, 'src/index.tsx'),
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx']
|
||||
},
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
library: 'flarum',
|
||||
libraryTarget: 'var'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
loader: 'awesome-typescript-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CheckerPlugin()
|
||||
],
|
||||
externals: {
|
||||
mithril: 'm'
|
||||
}
|
||||
};
|
@@ -1,16 +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",
|
||||
"fastclick": "~1.0.6",
|
||||
"autolink": "~1.0.0",
|
||||
"m.attrs.bidi": "tobscure/m.attrs.bidi"
|
||||
}
|
||||
}
|
2
js/forum/.gitignore
vendored
2
js/forum/.gitignore
vendored
@@ -1 +1 @@
|
||||
node_modules
|
||||
dist
|
@@ -1,35 +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 + '/autolink/autolink-min.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 + '/fastclick/lib/fastclick.js'
|
||||
],
|
||||
modules: {
|
||||
'flarum': [
|
||||
'src/**/*.js',
|
||||
'../lib/**/*.js'
|
||||
]
|
||||
},
|
||||
outputFile: 'dist/app.js'
|
||||
});
|
32117
js/forum/dist/app.js
vendored
32117
js/forum/dist/app.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.1",
|
||||
"flarum-gulp": "^0.2.0"
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
86
js/forum/src/ForumApplication.tsx
Normal file
86
js/forum/src/ForumApplication.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import Application from './lib/Application';
|
||||
import routes from './routes';
|
||||
import Search from './components/Search';
|
||||
|
||||
export default class ForumApplication extends Application {
|
||||
/**
|
||||
* The page's search component instance.
|
||||
*
|
||||
* @type {SearchBox}
|
||||
*/
|
||||
search = new Search();
|
||||
|
||||
/**
|
||||
* A map of notification types to their components.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
notificationComponents = {};
|
||||
|
||||
/**
|
||||
* A map of post types to their components.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
postComponents = {};
|
||||
|
||||
//app.postComponents.comment = CommentPost;
|
||||
//app.postComponents.discussionRenamed = DiscussionRenamedPost;
|
||||
|
||||
// app.notificationComponents.discussionRenamed = DiscussionRenamedNotification;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
registerDefaultRoutes(router) {
|
||||
routes(router);
|
||||
}
|
||||
|
||||
// TODO: work out where to put these helper functions
|
||||
// /**
|
||||
// * 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();
|
||||
// }
|
||||
// }
|
||||
}
|
@@ -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;
|
@@ -150,7 +150,7 @@ class Composer extends Component {
|
||||
const composer = this;
|
||||
|
||||
$(element).css('cursor', 'row-resize')
|
||||
.bind('dragstart mousedown', e => e.preventDefault())
|
||||
.on('dragstart mousedown', e => e.preventDefault())
|
||||
.mousedown(function(e) {
|
||||
composer.mouseStart = e.clientY;
|
||||
composer.heightStart = composer.$().height();
|
@@ -15,6 +15,7 @@ export default class EditUserModal extends Modal {
|
||||
|
||||
this.username = m.prop(user.username() || '');
|
||||
this.email = m.prop(user.email() || '');
|
||||
this.isActivated = m.prop(user.isActivated() || false);
|
||||
this.setPassword = m.prop(false);
|
||||
this.password = m.prop(user.password() || '');
|
||||
this.groups = {};
|
||||
@@ -37,22 +38,32 @@ export default class EditUserModal extends Modal {
|
||||
<div className="Modal-body">
|
||||
<div className="Form">
|
||||
<div className="Form-group">
|
||||
<label>Username</label>
|
||||
<label>{app.translator.trans('core.forum.edit_user.username_heading')}</label>
|
||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.username_label'))}
|
||||
bidi={this.username} />
|
||||
</div>
|
||||
|
||||
{app.session.user !== this.props.user ? [
|
||||
<div className="Form-group">
|
||||
<label>Email</label>
|
||||
<label>{app.translator.trans('core.forum.edit_user.email_heading')}</label>
|
||||
<div>
|
||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))}
|
||||
bidi={this.email} />
|
||||
</div>
|
||||
{!this.isActivated() ? (
|
||||
<div>
|
||||
{Button.component({
|
||||
className: 'Button Button--block',
|
||||
children: app.translator.trans('core.forum.edit_user.activate_button'),
|
||||
loading: this.loading,
|
||||
onclick: this.activate.bind(this)
|
||||
})}
|
||||
</div>
|
||||
) : ''}
|
||||
</div>,
|
||||
|
||||
<div className="Form-group">
|
||||
<label>Password</label>
|
||||
<label>{app.translator.trans('core.forum.edit_user.password_heading')}</label>
|
||||
<div>
|
||||
<label className="checkbox">
|
||||
<input type="checkbox" checked={this.setPassword()} onchange={e => {
|
||||
@@ -61,7 +72,7 @@ export default class EditUserModal extends Modal {
|
||||
if (e.target.checked) this.$('[name=password]').select();
|
||||
m.redraw.strategy('none');
|
||||
}}/>
|
||||
Set new password
|
||||
{app.translator.trans('core.forum.edit_user.set_password_label')}
|
||||
</label>
|
||||
{this.setPassword() ? (
|
||||
<input className="FormControl" type="password" name="password" placeholder={extractText(app.translator.trans('core.forum.edit_user.password_label'))}
|
||||
@@ -72,7 +83,7 @@ export default class EditUserModal extends Modal {
|
||||
] : ''}
|
||||
|
||||
<div className="Form-group EditUserModal-groups">
|
||||
<label>Groups</label>
|
||||
<label>{app.translator.trans('core.forum.edit_user.groups_heading')}</label>
|
||||
<div>
|
||||
{Object.keys(this.groups)
|
||||
.map(id => app.store.getById('groups', id))
|
||||
@@ -100,6 +111,24 @@ export default class EditUserModal extends Modal {
|
||||
);
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.loading = true;
|
||||
const data = {
|
||||
username: this.username(),
|
||||
isActivated: true,
|
||||
};
|
||||
this.props.user.save(data, {errorHandler: this.onerror.bind(this)})
|
||||
.then(() => {
|
||||
this.isActivated(true);
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
data() {
|
||||
const groups = Object.keys(this.groups)
|
||||
.filter(id => this.groups[id]())
|
@@ -1,33 +0,0 @@
|
||||
import Component from 'flarum/Component';
|
||||
import ItemList from 'flarum/utils/ItemList';
|
||||
import listItems from 'flarum/helpers/listItems';
|
||||
|
||||
/**
|
||||
* The `HeaderPrimary` component displays primary header controls. On the
|
||||
* default skin, these are shown just to the right of the forum title.
|
||||
*/
|
||||
export default class HeaderPrimary extends Component {
|
||||
view() {
|
||||
return (
|
||||
<ul className="Header-controls">
|
||||
{listItems(this.items().toArray())}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
config(isInitialized, context) {
|
||||
// Since this component is 'above' the content of the page (that is, it is a
|
||||
// part of the global UI that persists between routes), we will flag the DOM
|
||||
// to be retained across route changes.
|
||||
context.retain = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
*/
|
||||
items() {
|
||||
return new ItemList();
|
||||
}
|
||||
}
|
@@ -39,7 +39,7 @@ export default class HeaderSecondary extends Component {
|
||||
|
||||
items.add('search', app.search.render(), 30);
|
||||
|
||||
if (Object.keys(app.data.locales).length > 1) {
|
||||
if (app.forum.attribute("showLanguageSelector") && Object.keys(app.data.locales).length > 1) {
|
||||
const locales = [];
|
||||
|
||||
for (const locale in app.data.locales) {
|
@@ -236,7 +236,13 @@ export default class IndexPage extends Page {
|
||||
title: app.translator.trans('core.forum.index.refresh_tooltip'),
|
||||
icon: 'refresh',
|
||||
className: 'Button Button--icon',
|
||||
onclick: () => app.cache.discussionList.refresh()
|
||||
onclick: () => {
|
||||
app.cache.discussionList.refresh();
|
||||
if (app.session.user) {
|
||||
app.store.find('users', app.session.user.id());
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@@ -4,6 +4,7 @@ import SignUpModal from 'flarum/components/SignUpModal';
|
||||
import Alert from 'flarum/components/Alert';
|
||||
import Button from 'flarum/components/Button';
|
||||
import LogInButtons from 'flarum/components/LogInButtons';
|
||||
import Switch from 'flarum/components/Switch';
|
||||
import extractText from 'flarum/utils/extractText';
|
||||
|
||||
/**
|
||||
@@ -11,7 +12,7 @@ import extractText from 'flarum/utils/extractText';
|
||||
*
|
||||
* ### Props
|
||||
*
|
||||
* - `email`
|
||||
* - `identification`
|
||||
* - `password`
|
||||
*/
|
||||
export default class LogInModal extends Modal {
|
||||
@@ -19,11 +20,11 @@ export default class LogInModal extends Modal {
|
||||
super.init();
|
||||
|
||||
/**
|
||||
* The value of the email input.
|
||||
* The value of the identification input.
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
this.email = m.prop(this.props.email || '');
|
||||
this.identification = m.prop(this.props.identification || '');
|
||||
|
||||
/**
|
||||
* The value of the password input.
|
||||
@@ -31,6 +32,13 @@ export default class LogInModal extends Modal {
|
||||
* @type {Function}
|
||||
*/
|
||||
this.password = m.prop(this.props.password || '');
|
||||
|
||||
/**
|
||||
* The value of the remember me input.
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
this.remember = m.prop(this.props.remember && true);
|
||||
}
|
||||
|
||||
className() {
|
||||
@@ -48,8 +56,8 @@ export default class LogInModal extends Modal {
|
||||
|
||||
<div className="Form Form--centered">
|
||||
<div className="Form-group">
|
||||
<input className="FormControl" name="email" type="text" placeholder={extractText(app.translator.trans('core.forum.log_in.username_or_email_placeholder'))}
|
||||
bidi={this.email}
|
||||
<input className="FormControl" name="identification" type="text" placeholder={extractText(app.translator.trans('core.forum.log_in.username_or_email_placeholder'))}
|
||||
bidi={this.identification}
|
||||
disabled={this.loading} />
|
||||
</div>
|
||||
|
||||
@@ -58,6 +66,15 @@ export default class LogInModal extends Modal {
|
||||
bidi={this.password}
|
||||
disabled={this.loading} />
|
||||
</div>
|
||||
|
||||
<div className="Form-group">
|
||||
{Switch.component({
|
||||
children: app.translator.trans('core.forum.log_in.remember_me_label'),
|
||||
disabled: this.loading,
|
||||
onchange: this.remember,
|
||||
state: this.remember()
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="Form-group">
|
||||
{Button.component({
|
||||
@@ -90,7 +107,7 @@ export default class LogInModal extends Modal {
|
||||
* @public
|
||||
*/
|
||||
forgotPassword() {
|
||||
const email = this.email();
|
||||
const email = this.identification();
|
||||
const props = email.indexOf('@') !== -1 ? {email} : undefined;
|
||||
|
||||
app.modal.show(new ForgotPasswordModal(props));
|
||||
@@ -104,14 +121,14 @@ export default class LogInModal extends Modal {
|
||||
*/
|
||||
signUp() {
|
||||
const props = {password: this.password()};
|
||||
const email = this.email();
|
||||
props[email.indexOf('@') !== -1 ? 'email' : 'username'] = email;
|
||||
const identification = this.identification();
|
||||
props[identification.indexOf('@') !== -1 ? 'email' : 'username'] = identification;
|
||||
|
||||
app.modal.show(new SignUpModal(props));
|
||||
}
|
||||
|
||||
onready() {
|
||||
this.$('[name=' + (this.email() ? 'password' : 'email') + ']').select();
|
||||
this.$('[name=' + (this.identification() ? 'password' : 'identification') + ']').select();
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
@@ -119,13 +136,15 @@ export default class LogInModal extends Modal {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
const email = this.email();
|
||||
const identification = this.identification();
|
||||
const password = this.password();
|
||||
const remember = this.remember();
|
||||
|
||||
app.session.login(email, password, {errorHandler: this.onerror.bind(this)}).then(
|
||||
() => window.location.reload(),
|
||||
this.loaded.bind(this)
|
||||
);
|
||||
app.session.login({identification, password, remember}, {errorHandler: this.onerror.bind(this)})
|
||||
.then(
|
||||
() => window.location.reload(),
|
||||
this.loaded.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
onerror(error) {
|
@@ -89,12 +89,12 @@ export default class NotificationGrid extends Component {
|
||||
config(isInitialized) {
|
||||
if (isInitialized) return;
|
||||
|
||||
this.$('thead .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function(e) {
|
||||
this.$('thead .NotificationGrid-groupToggle').on('mouseenter mouseleave', function(e) {
|
||||
const i = parseInt($(this).index(), 10) + 1;
|
||||
$(this).parents('table').find('td:nth-child(' + i + ')').toggleClass('highlighted', e.type === 'mouseenter');
|
||||
});
|
||||
|
||||
this.$('tbody .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function(e) {
|
||||
this.$('tbody .NotificationGrid-groupToggle').on('mouseenter mouseleave', function(e) {
|
||||
$(this).parent().find('td').toggleClass('highlighted', e.type === 'mouseenter');
|
||||
});
|
||||
}
|
@@ -258,12 +258,12 @@ export default class PostStreamScrubber extends Component {
|
||||
// When any part of the whole scrollbar is clicked, we want to jump to
|
||||
// that position.
|
||||
this.$('.Scrubber-scrollbar')
|
||||
.bind('click', this.onclick.bind(this))
|
||||
.click(this.onclick.bind(this))
|
||||
|
||||
// Now we want to make the scrollbar handle draggable. Let's start by
|
||||
// preventing default browser events from messing things up.
|
||||
.css({ cursor: 'pointer', 'user-select': 'none' })
|
||||
.bind('dragstart mousedown touchstart', e => e.preventDefault());
|
||||
.on('dragstart mousedown touchstart', e => e.preventDefault());
|
||||
|
||||
// When the mouse is pressed on the scrollbar handle, we capture some
|
||||
// information about its current position. We will store this
|
||||
@@ -275,7 +275,7 @@ export default class PostStreamScrubber extends Component {
|
||||
|
||||
this.$('.Scrubber-handle')
|
||||
.css('cursor', 'move')
|
||||
.bind('mousedown touchstart', this.onmousedown.bind(this))
|
||||
.on('mousedown touchstart', this.onmousedown.bind(this))
|
||||
|
||||
// Exempt the scrollbar handle from the 'jump to' click event.
|
||||
.click(e => e.stopPropagation());
|
67
js/forum/src/components/RenameDiscussionModal.tsx
Normal file
67
js/forum/src/components/RenameDiscussionModal.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import Modal from 'flarum/components/Modal';
|
||||
import Button from 'flarum/components/Button';
|
||||
|
||||
/**
|
||||
* The 'RenameDiscussionModal' displays a modal dialog with an input to rename a discussion
|
||||
*/
|
||||
export default class RenameDiscussionModal extends Modal {
|
||||
init() {
|
||||
super.init();
|
||||
|
||||
this.discussion = this.props.discussion;
|
||||
this.currentTitle = this.props.currentTitle;
|
||||
this.newTitle = m.prop(this.currentTitle);
|
||||
}
|
||||
|
||||
className() {
|
||||
return 'RenameDiscussionModal Modal--small';
|
||||
}
|
||||
|
||||
title() {
|
||||
return app.translator.trans('core.forum.rename_discussion.title');
|
||||
}
|
||||
|
||||
content() {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<div className="Form">
|
||||
<div className="Form-group">
|
||||
<input className="FormControl title" placeholder={this.currentTitle} bidi={this.newTitle} />
|
||||
</div>
|
||||
<div className="Form-group">
|
||||
{Button.component({
|
||||
className: 'Button Button--primary',
|
||||
type: 'submit',
|
||||
loading: this.loading,
|
||||
children: app.translator.trans('core.forum.rename_discussion.submit_button')
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.loading = true;
|
||||
|
||||
const title = this.newTitle;
|
||||
const currentTitle = this.currentTitle;
|
||||
|
||||
// If the title is different to what it was before, then save it. After the
|
||||
// save has completed, update the post stream as there will be a new post
|
||||
// indicating that the discussion was renamed.
|
||||
if (title && title !== currentTitle) {
|
||||
return this.discussion.save({title}).then(() => {
|
||||
if (app.viewingDiscussion(this.discussion)) {
|
||||
app.current.stream.update();
|
||||
}
|
||||
m.redraw();
|
||||
this.hide();
|
||||
});
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user