1
0
mirror of https://github.com/flarum/core.git synced 2025-08-15 12:54:47 +02:00

Compare commits

...

29 Commits

Author SHA1 Message Date
Daniel Klabbers
17e9bccc15 changelog for v1.0.4 2021-06-28 12:22:48 +02:00
Ian Morland
4a5b84d2e7 Remove LIKE and raw from viewUserList rename 2021-06-28 11:58:23 +02:00
Ian Morland
557fc2cd39 Include updating of scoped tag permissions
Addresses https://github.com/flarum/core/issues/2924

The rename `viewDiscussions` migration introduced for Flarum 1.0 does not take tag scoped permissions into account
e92c267cde/migrations/2021_05_10_000000_rename_permissions.php (L17)

This adds a new migration to additionally rename `tagX.viewDiscussions` to `tagX.viewForum`

Tested locally on an upgrade from core `beta.16` to `1.0.3`
2021-06-28 11:58:23 +02:00
Daniel Klabbers
e92c267cde update version constant for the next release 2021-06-22 23:38:47 +02:00
Daniel Klabbers
f959a69530 changelog entry for laravel filesystem issue 2021-06-22 23:15:25 +02:00
Daniel Klabbers
4e246779f4 changelog so far for 1.0.3 2021-06-22 23:15:25 +02:00
Daniel Klabbers
5b0f5aeaa0 updated foundation version 2021-06-22 23:15:25 +02:00
Daniel Klabbers
6e92af8b00 Fixes issue with Laravel 8.48 filesystem changes
The FilesystemManager has changed to also allow to override
the config while resolving a filesystem.

This PR adds the argument and applies it if provided.
2021-06-22 23:07:41 +02:00
flarum-bot
1cf9491fe6 Bundled output for commit 3fcc7bd3b9
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2021-06-21 22:16:28 +00:00
ctml91
3fcc7bd3b9 use display name for avatar color gen 2021-06-22 00:14:37 +02:00
Daniel Klabbers
4acff91f80 allows replacing maintenance mode handler using ioc 2021-06-22 00:10:41 +02:00
Daniël Klabbers
a0152ffb18 Dw/huntr fix path traversal (#2931)
* Fix Huntr vuln with possible directory traversal
* Use `active_url` in Laravel validator
2021-06-21 10:14:15 +02:00
David Wheatley
d1e38558c5 Fix image avatar alignment in notifications (#2906) 2021-06-11 12:13:57 +01:00
Daniël Klabbers
0cca808275 minor improvements to the security policy 2021-06-10 21:56:30 +02:00
Daniël Klabbers
5ee5f82e3d huntr.dev as first point for security vuln (#2918)
* huntr.dev as first point for security vuln

* add badge for huntr.dev
2021-06-10 16:26:40 +02:00
Daniël Klabbers
9077fef5b2 clean up of composer.json, added funding and more support links 2021-06-08 01:58:37 +02:00
Daniël Klabbers
93cebec0be remove tidelift, we stopped doing that 2021-06-08 01:54:11 +02:00
Daniël Klabbers
a4a81c0ec2 Remove [forum] prefix in some mails
fixes #2515
2021-06-08 01:28:04 +02:00
David Wheatley
50dcfdb2a6 Mark typings as generated code (#2886) 2021-06-07 13:12:43 +01:00
flarum-bot
8149397850 Bundled output for commit 1ced907e52
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2021-06-06 01:50:56 +00:00
David Wheatley
1ced907e52 npm audit fix 2021-06-06 02:47:58 +01:00
David Wheatley
17c5a40740 Update changelog 2021-06-06 02:44:32 +01:00
David Wheatley
440bed81b8 Fix XSS vulnerability 2021-06-06 02:41:48 +01:00
David Wheatley
eeb8fe1443 Update version constant to 1.0.2 2021-06-06 02:09:03 +01:00
Daniel Klabbers
11b1ab5932 update version constant for 1.0.2-dev 2021-06-02 09:10:01 +02:00
Daniel Klabbers
6f34c43dc1 v1.0.1 changelog and constant 2021-06-02 09:05:36 +02:00
luceos
8ced9eef45 Apply fixes from StyleCI
[ci skip] [skip ci]
2021-05-31 14:08:49 +00:00
Daniel Klabbers
8af52153e4 ref #2890, no longer using process and dump 2021-05-31 16:08:15 +02:00
Daniel Klabbers
2c5e5f13dd Revert "Revert "Squash core migrations (#2842)""
This reverts commit 16f3ae9d1e.
2021-05-31 15:49:20 +02:00
30 changed files with 852 additions and 95 deletions

1
.gitattributes vendored
View File

@@ -12,5 +12,6 @@ tests export-ignore
js/dist/* -diff
js/dist/* linguist-generated
js/dist-typings/* linguist-generated
* text=auto eol=lf

1
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,2 @@
github: flarum
open_collective: flarum
tidelift: packagist/flarum/core

14
.github/SECURITY.md vendored
View File

@@ -1,13 +1,13 @@
# Security Policy
## Supported Versions
## Versions
We will only patch security vulnerabilities in the stable 1.x release.
Due to the nature of our project - being open source - we have decided to patch only the latest major release (currently v1.x) for security vulnerabilities.
## Reporting a Vulnerability
## How to disclose
If you discover a security vulnerability within Flarum, please send an email to security@flarum.org so we can address it promptly.
Please use [huntr.dev](https://huntr.dev/) for security issues that affect our project. If you believe you have found a vulnerability, please disclose it via [this form](https://huntr.dev/bounties/disclose/?target=https://github.com/flarum/core).
We will get back to you as time allows.
Discussions may commence internally, so you may not hear back immediately.
When reporting a vulnerability, please provide your GitHub username (if available), so that we can invite you to collaborate on a [security advisory on GitHub](https://help.github.com/en/articles/about-maintainer-security-advisories).
This will enable us to **review** the vulnerability, **fix** it promptly, and **reward** you for your efforts.
If you have any questions about the process, feel free to reach out to security@huntr.dev or security@flarum.org.

View File

@@ -1,5 +1,35 @@
# Changelog
## [1.0.4](https://github.com/flarum/core/compare/v1.0.3...v1.0.4)
### Fixed
- Upgrade to v1.0 resets the "view" permission on all tags (https://github.com/flarum/core/pull/2941)
## [1.0.3](https://github.com/flarum/core/compare/v1.0.2...v1.0.3)
### Changed
- Removed [forum] prefix from Request Password and Email Confirmation emails ([a4a81c0](https://github.com/flarum/core/commit/a4a81c0ec237476cd6e7ca00c1ed9465493af476))
- Adopt huntr.dev for handling our security vulnerability reports (https://github.com/flarum/core/pull/2918)
- Maintenance handler can now be replaced through the service container (ioc) ([4acff91](https://github.com/flarum/core/commit/4acff91f8063fcced9bf8c9a76fbb510d06823c0))
- The colors on the auto generated avatars are now based on the Display Name of the user (https://github.com/flarum/core/pull/2873)
### Fixed
- Avatar in notifications list are incorrectly aligned (https://github.com/flarum/core/pull/2906)
- FilesystemManager is not compatible with upstream Laravel implementation (https://github.com/flarum/core/pull/2936)
## [1.0.2](https://github.com/flarum/core/compare/v1.0.1...v1.0.2)
### Fixed
- Critical XSS vulnerability
## [1.0.1](https://github.com/flarum/core/compare/v1.0.0...v1.0.1)
### Fixed
- Installation fails on environments without proc_* functions enabled or mysql client binary (https://github.com/flarum/core/issues/2890)
## [1.0.0](https://github.com/flarum/core/compare/v0.1.0-beta.16...v1.0.0)
### Added

View File

@@ -5,6 +5,7 @@
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/packagist/dt/flarum/core" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/github/v/release/flarum/core?sort=semver" alt="Latest Version"></a>
<a href="https://packagist.org/packages/flarum/core"><img src="https://img.shields.io/packagist/l/flarum/core" alt="License"></a>
<a href="https://huntr.dev/bounties/disclose/?target=https://github.com/flarum/core"><img src="https://cdn.huntr.dev/huntr_security_badge_mono.svg" alt="huntr"></a>
<a href="https://github.styleci.io/repos/28257573"><img src="https://github.styleci.io/repos/28257573/shield?style=flat" alt="StyleCI"></a>
</p>

View File

@@ -14,10 +14,26 @@
"homepage": "https://flarum.org/team"
}
],
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/flarum"
},
{
"type": "github",
"url": "https://github.com/sponsors/flarum"
},
{
"type": "other",
"url": "https://flarum.org/donate"
}
],
"support": {
"issues": "https://github.com/flarum/core/issues",
"source": "https://github.com/flarum/core",
"docs": "https://flarum.org/docs/"
"docs": "https://docs.flarum.org",
"forum": "https://discuss.flarum.org",
"chat": "https://flarum.org/chat"
},
"require": {
"php": ">=7.3",

2
js/dist/admin.js generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/admin.js.map generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/forum.js generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/forum.js.map generated vendored

File diff suppressed because one or more lines are too long

12
js/package-lock.json generated
View File

@@ -7552,9 +7552,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/ws": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==",
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"dev": true,
"engines": {
"node": ">=8.3.0"
@@ -13723,9 +13723,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==",
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"dev": true,
"requires": {}
},

View File

@@ -48,12 +48,23 @@ export default class Translator {
// future there should be a hook here to inspect the user and change the
// translation key. This will allow a gender property to determine which
// translation key is used.
if ('user' in parameters) {
const user = extract(parameters, 'user');
if (!parameters.username) parameters.username = username(user);
}
return parameters;
const escapedParameters: TranslatorParameters = {};
for (const param in parameters) {
const paramValue = parameters[param];
if (typeof paramValue === 'string') escapedParameters[param] = <>{parameters[param]}</>;
else escapedParameters[param] = parameters[param];
}
return escapedParameters;
}
trans(id: string, parameters: TranslatorParameters = {}) {

View File

@@ -35,11 +35,11 @@ Object.assign(User.prototype, {
canDelete: Model.attribute('canDelete'),
avatarColor: null,
color: computed('username', 'avatarUrl', 'avatarColor', function (username, avatarUrl, avatarColor) {
color: computed('displayName', 'avatarUrl', 'avatarColor', function (displayName, avatarUrl, avatarColor) {
// If we've already calculated and cached the dominant color of the user's
// avatar, then we can return that in RGB format. If we haven't, we'll want
// to calculate it. Unless the user doesn't have an avatar, in which case
// we generate a color from their username.
// we generate a color from their display name.
if (avatarColor) {
return 'rgb(' + avatarColor.join(', ') + ')';
} else if (avatarUrl) {
@@ -47,7 +47,7 @@ Object.assign(User.prototype, {
return '';
}
return '#' + stringToColor(username);
return '#' + stringToColor(displayName);
}),
/**

View File

@@ -136,6 +136,14 @@
.Avatar--size(24px);
grid-area: avatar;
}
// Since images don't have baselines, aligning against the baseline won't work.
// Instead we need to do some manual hackery to fix then, otherwise they won't
// be correctly vertically aligned.
img.Avatar {
align-self: flex-start;
margin-top: -2px;
}
&-icon {
font-size: 14px;

View File

@@ -14,23 +14,23 @@ return [
$db = $schema->getConnection();
$db->table('group_permission')
->where('permission', 'LIKE', 'viewDiscussions')
->where('permission', 'LIKE', '%viewDiscussions')
->update(['permission' => $db->raw("REPLACE(permission, 'viewDiscussions', 'viewForum')")]);
$db->table('group_permission')
->where('permission', 'LIKE', 'viewUserList')
->update(['permission' => $db->raw("REPLACE(permission, 'viewUserList', 'searchUsers')")]);
->where('permission', 'viewUserList')
->update(['permission' => 'searchUsers']);
},
'down' => function (Builder $schema) {
$db = $schema->getConnection();
$db->table('group_permission')
->where('permission', 'LIKE', 'viewForum')
->where('permission', 'LIKE', '%viewForum')
->update(['permission' => $db->raw("REPLACE(permission, 'viewForum', 'viewDiscussions')")]);
$db->table('group_permission')
->where('permission', 'LIKE', 'searchUsers')
->update(['permission' => $db->raw("REPLACE(permission, 'searchUsers', 'viewUserList')")]);
->where('permission', 'searchUsers')
->update(['permission' => 'viewUserList']);
}
];

367
migrations/install.dump Normal file
View File

@@ -0,0 +1,367 @@
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `db_prefix_access_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_access_tokens` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`token` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`last_activity_at` datetime NOT NULL,
`created_at` datetime NOT NULL,
`type` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`title` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`last_ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`last_user_agent` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `db_prefix_access_tokens_token_unique` (`token`),
KEY `db_prefix_access_tokens_user_id_foreign` (`user_id`),
KEY `db_prefix_access_tokens_type_index` (`type`),
CONSTRAINT `db_prefix_access_tokens_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_api_keys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_api_keys` (
`key` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`allowed_ips` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`scopes` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_id` int(10) unsigned DEFAULT NULL,
`created_at` datetime NOT NULL,
`last_activity_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `db_prefix_api_keys_key_unique` (`key`),
KEY `db_prefix_api_keys_user_id_foreign` (`user_id`),
CONSTRAINT `db_prefix_api_keys_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_discussion_user`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_discussion_user` (
`user_id` int(10) unsigned NOT NULL,
`discussion_id` int(10) unsigned NOT NULL,
`last_read_at` datetime DEFAULT NULL,
`last_read_post_number` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`user_id`,`discussion_id`),
KEY `db_prefix_discussion_user_discussion_id_foreign` (`discussion_id`),
CONSTRAINT `db_prefix_discussion_user_discussion_id_foreign` FOREIGN KEY (`discussion_id`) REFERENCES `db_prefix_discussions` (`id`) ON DELETE CASCADE,
CONSTRAINT `db_prefix_discussion_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_discussions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_discussions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
`comment_count` int(11) NOT NULL DEFAULT 1,
`participant_count` int(10) unsigned NOT NULL DEFAULT 0,
`post_number_index` int(10) unsigned NOT NULL DEFAULT 0,
`created_at` datetime NOT NULL,
`user_id` int(10) unsigned DEFAULT NULL,
`first_post_id` int(10) unsigned DEFAULT NULL,
`last_posted_at` datetime DEFAULT NULL,
`last_posted_user_id` int(10) unsigned DEFAULT NULL,
`last_post_id` int(10) unsigned DEFAULT NULL,
`last_post_number` int(10) unsigned DEFAULT NULL,
`hidden_at` datetime DEFAULT NULL,
`hidden_user_id` int(10) unsigned DEFAULT NULL,
`slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`is_private` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `db_prefix_discussions_hidden_user_id_foreign` (`hidden_user_id`),
KEY `db_prefix_discussions_first_post_id_foreign` (`first_post_id`),
KEY `db_prefix_discussions_last_post_id_foreign` (`last_post_id`),
KEY `db_prefix_discussions_last_posted_at_index` (`last_posted_at`),
KEY `db_prefix_discussions_last_posted_user_id_index` (`last_posted_user_id`),
KEY `db_prefix_discussions_created_at_index` (`created_at`),
KEY `db_prefix_discussions_user_id_index` (`user_id`),
KEY `db_prefix_discussions_comment_count_index` (`comment_count`),
KEY `db_prefix_discussions_participant_count_index` (`participant_count`),
KEY `db_prefix_discussions_hidden_at_index` (`hidden_at`),
FULLTEXT KEY `title` (`title`),
CONSTRAINT `db_prefix_discussions_first_post_id_foreign` FOREIGN KEY (`first_post_id`) REFERENCES `db_prefix_posts` (`id`) ON DELETE SET NULL,
CONSTRAINT `db_prefix_discussions_hidden_user_id_foreign` FOREIGN KEY (`hidden_user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE SET NULL,
CONSTRAINT `db_prefix_discussions_last_post_id_foreign` FOREIGN KEY (`last_post_id`) REFERENCES `db_prefix_posts` (`id`) ON DELETE SET NULL,
CONSTRAINT `db_prefix_discussions_last_posted_user_id_foreign` FOREIGN KEY (`last_posted_user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE SET NULL,
CONSTRAINT `db_prefix_discussions_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_email_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_email_tokens` (
`token` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`token`),
KEY `db_prefix_email_tokens_user_id_foreign` (`user_id`),
CONSTRAINT `db_prefix_email_tokens_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_group_permission`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_group_permission` (
`group_id` int(10) unsigned NOT NULL,
`permission` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`group_id`,`permission`),
CONSTRAINT `db_prefix_group_permission_group_id_foreign` FOREIGN KEY (`group_id`) REFERENCES `db_prefix_groups` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_group_user`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_group_user` (
`user_id` int(10) unsigned NOT NULL,
`group_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`group_id`),
KEY `db_prefix_group_user_group_id_foreign` (`group_id`),
CONSTRAINT `db_prefix_group_user_group_id_foreign` FOREIGN KEY (`group_id`) REFERENCES `db_prefix_groups` (`id`) ON DELETE CASCADE,
CONSTRAINT `db_prefix_group_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_groups`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_groups` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name_singular` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`name_plural` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`color` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`icon` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`is_hidden` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_login_providers`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_login_providers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`provider` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`identifier` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` datetime DEFAULT NULL,
`last_login_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `db_prefix_login_providers_provider_identifier_unique` (`provider`,`identifier`),
KEY `db_prefix_login_providers_user_id_foreign` (`user_id`),
CONSTRAINT `db_prefix_login_providers_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_migrations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`extension` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_notifications`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_notifications` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`from_user_id` int(10) unsigned DEFAULT NULL,
`type` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`subject_id` int(10) unsigned DEFAULT NULL,
`data` blob DEFAULT NULL,
`created_at` datetime NOT NULL,
`is_deleted` tinyint(1) NOT NULL DEFAULT 0,
`read_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `db_prefix_notifications_from_user_id_foreign` (`from_user_id`),
KEY `db_prefix_notifications_user_id_index` (`user_id`),
CONSTRAINT `db_prefix_notifications_from_user_id_foreign` FOREIGN KEY (`from_user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE SET NULL,
CONSTRAINT `db_prefix_notifications_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_password_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_password_tokens` (
`token` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`token`),
KEY `db_prefix_password_tokens_user_id_foreign` (`user_id`),
CONSTRAINT `db_prefix_password_tokens_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_post_user`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_post_user` (
`post_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`post_id`,`user_id`),
KEY `db_prefix_post_user_user_id_foreign` (`user_id`),
CONSTRAINT `db_prefix_post_user_post_id_foreign` FOREIGN KEY (`post_id`) REFERENCES `db_prefix_posts` (`id`) ON DELETE CASCADE,
CONSTRAINT `db_prefix_post_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_posts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`discussion_id` int(10) unsigned NOT NULL,
`number` int(10) unsigned DEFAULT NULL,
`created_at` datetime NOT NULL,
`user_id` int(10) unsigned DEFAULT NULL,
`type` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`content` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ' ',
`edited_at` datetime DEFAULT NULL,
`edited_user_id` int(10) unsigned DEFAULT NULL,
`hidden_at` datetime DEFAULT NULL,
`hidden_user_id` int(10) unsigned DEFAULT NULL,
`ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`is_private` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `db_prefix_posts_discussion_id_number_unique` (`discussion_id`,`number`),
KEY `db_prefix_posts_edited_user_id_foreign` (`edited_user_id`),
KEY `db_prefix_posts_hidden_user_id_foreign` (`hidden_user_id`),
KEY `db_prefix_posts_discussion_id_number_index` (`discussion_id`,`number`),
KEY `db_prefix_posts_discussion_id_created_at_index` (`discussion_id`,`created_at`),
KEY `db_prefix_posts_user_id_created_at_index` (`user_id`,`created_at`),
FULLTEXT KEY `content` (`content`),
CONSTRAINT `db_prefix_posts_discussion_id_foreign` FOREIGN KEY (`discussion_id`) REFERENCES `db_prefix_discussions` (`id`) ON DELETE CASCADE,
CONSTRAINT `db_prefix_posts_edited_user_id_foreign` FOREIGN KEY (`edited_user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE SET NULL,
CONSTRAINT `db_prefix_posts_hidden_user_id_foreign` FOREIGN KEY (`hidden_user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE SET NULL,
CONSTRAINT `db_prefix_posts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `db_prefix_users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_registration_tokens`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_registration_tokens` (
`token` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`provider` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`identifier` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_attributes` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_settings`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_settings` (
`key` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `db_prefix_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `db_prefix_users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
`is_email_confirmed` tinyint(1) NOT NULL DEFAULT 0,
`password` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`avatar_url` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`preferences` blob DEFAULT NULL,
`joined_at` datetime DEFAULT NULL,
`last_seen_at` datetime DEFAULT NULL,
`marked_all_as_read_at` datetime DEFAULT NULL,
`read_notifications_at` datetime DEFAULT NULL,
`discussion_count` int(10) unsigned NOT NULL DEFAULT 0,
`comment_count` int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `db_prefix_users_username_unique` (`username`),
UNIQUE KEY `db_prefix_users_email_unique` (`email`),
KEY `db_prefix_users_joined_at_index` (`joined_at`),
KEY `db_prefix_users_last_seen_at_index` (`last_seen_at`),
KEY `db_prefix_users_discussion_count_index` (`discussion_count`),
KEY `db_prefix_users_comment_count_index` (`comment_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO `db_prefix_migrations` VALUES (1,'2015_02_24_000000_create_access_tokens_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (2,'2015_02_24_000000_create_api_keys_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (3,'2015_02_24_000000_create_config_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (4,'2015_02_24_000000_create_discussions_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (5,'2015_02_24_000000_create_email_tokens_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (6,'2015_02_24_000000_create_groups_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (7,'2015_02_24_000000_create_notifications_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (8,'2015_02_24_000000_create_password_tokens_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (9,'2015_02_24_000000_create_permissions_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (10,'2015_02_24_000000_create_posts_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (11,'2015_02_24_000000_create_users_discussions_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (12,'2015_02_24_000000_create_users_groups_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (13,'2015_02_24_000000_create_users_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (14,'2015_09_15_000000_create_auth_tokens_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (15,'2015_09_20_224327_add_hide_to_discussions',NULL);
INSERT INTO `db_prefix_migrations` VALUES (16,'2015_09_22_030432_rename_notification_read_time',NULL);
INSERT INTO `db_prefix_migrations` VALUES (17,'2015_10_07_130531_rename_config_to_settings',NULL);
INSERT INTO `db_prefix_migrations` VALUES (18,'2015_10_24_194000_add_ip_address_to_posts',NULL);
INSERT INTO `db_prefix_migrations` VALUES (19,'2015_12_05_042721_change_access_tokens_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (20,'2015_12_17_194247_change_settings_value_column_to_text',NULL);
INSERT INTO `db_prefix_migrations` VALUES (21,'2016_02_04_095452_add_slug_to_discussions',NULL);
INSERT INTO `db_prefix_migrations` VALUES (22,'2017_04_07_114138_add_is_private_to_discussions',NULL);
INSERT INTO `db_prefix_migrations` VALUES (23,'2017_04_07_114138_add_is_private_to_posts',NULL);
INSERT INTO `db_prefix_migrations` VALUES (24,'2018_01_11_093900_change_access_tokens_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (25,'2018_01_11_094000_change_access_tokens_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (26,'2018_01_11_095000_change_api_keys_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (27,'2018_01_11_101800_rename_auth_tokens_to_registration_tokens',NULL);
INSERT INTO `db_prefix_migrations` VALUES (28,'2018_01_11_102000_change_registration_tokens_rename_id_to_token',NULL);
INSERT INTO `db_prefix_migrations` VALUES (29,'2018_01_11_102100_change_registration_tokens_created_at_to_datetime',NULL);
INSERT INTO `db_prefix_migrations` VALUES (30,'2018_01_11_120604_change_posts_table_to_innodb',NULL);
INSERT INTO `db_prefix_migrations` VALUES (31,'2018_01_11_155200_change_discussions_rename_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (32,'2018_01_11_155300_change_discussions_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (33,'2018_01_15_071700_rename_users_discussions_to_discussion_user',NULL);
INSERT INTO `db_prefix_migrations` VALUES (34,'2018_01_15_071800_change_discussion_user_rename_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (35,'2018_01_15_071900_change_discussion_user_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (36,'2018_01_15_072600_change_email_tokens_rename_id_to_token',NULL);
INSERT INTO `db_prefix_migrations` VALUES (37,'2018_01_15_072700_change_email_tokens_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (38,'2018_01_15_072800_change_email_tokens_created_at_to_datetime',NULL);
INSERT INTO `db_prefix_migrations` VALUES (39,'2018_01_18_130400_rename_permissions_to_group_permission',NULL);
INSERT INTO `db_prefix_migrations` VALUES (40,'2018_01_18_130500_change_group_permission_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (41,'2018_01_18_130600_rename_users_groups_to_group_user',NULL);
INSERT INTO `db_prefix_migrations` VALUES (42,'2018_01_18_130700_change_group_user_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (43,'2018_01_18_133000_change_notifications_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (44,'2018_01_18_133100_change_notifications_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (45,'2018_01_18_134400_change_password_tokens_rename_id_to_token',NULL);
INSERT INTO `db_prefix_migrations` VALUES (46,'2018_01_18_134500_change_password_tokens_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (47,'2018_01_18_134600_change_password_tokens_created_at_to_datetime',NULL);
INSERT INTO `db_prefix_migrations` VALUES (48,'2018_01_18_135000_change_posts_rename_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (49,'2018_01_18_135100_change_posts_add_foreign_keys',NULL);
INSERT INTO `db_prefix_migrations` VALUES (50,'2018_01_30_112238_add_fulltext_index_to_discussions_title',NULL);
INSERT INTO `db_prefix_migrations` VALUES (51,'2018_01_30_220100_create_post_user_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (52,'2018_01_30_222900_change_users_rename_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (55,'2018_09_15_041340_add_users_indicies',NULL);
INSERT INTO `db_prefix_migrations` VALUES (56,'2018_09_15_041828_add_discussions_indicies',NULL);
INSERT INTO `db_prefix_migrations` VALUES (57,'2018_09_15_043337_add_notifications_indices',NULL);
INSERT INTO `db_prefix_migrations` VALUES (58,'2018_09_15_043621_add_posts_indices',NULL);
INSERT INTO `db_prefix_migrations` VALUES (59,'2018_09_22_004100_change_registration_tokens_columns',NULL);
INSERT INTO `db_prefix_migrations` VALUES (60,'2018_09_22_004200_create_login_providers_table',NULL);
INSERT INTO `db_prefix_migrations` VALUES (61,'2018_10_08_144700_add_shim_prefix_to_group_icons',NULL);
INSERT INTO `db_prefix_migrations` VALUES (62,'2019_10_12_195349_change_posts_add_discussion_foreign_key',NULL);
INSERT INTO `db_prefix_migrations` VALUES (63,'2020_03_19_134512_change_discussions_default_comment_count',NULL);
INSERT INTO `db_prefix_migrations` VALUES (64,'2020_04_21_130500_change_permission_groups_add_is_hidden',NULL);
INSERT INTO `db_prefix_migrations` VALUES (65,'2021_03_02_040000_change_access_tokens_add_type',NULL);
INSERT INTO `db_prefix_migrations` VALUES (66,'2021_03_02_040500_change_access_tokens_add_id',NULL);
INSERT INTO `db_prefix_migrations` VALUES (67,'2021_03_02_041000_change_access_tokens_add_title_ip_agent',NULL);
INSERT INTO `db_prefix_migrations` VALUES (68,'2021_04_18_040500_change_migrations_add_id_primary_key',NULL);
INSERT INTO `db_prefix_migrations` VALUES (69,'2021_04_18_145100_change_posts_content_column_to_mediumtext',NULL);

View File

@@ -64,6 +64,8 @@ class ConsoleServiceProvider extends AbstractServiceProvider
ResetCommand::class,
ScheduleListCommand::class,
ScheduleRunCommand::class
// Used internally to create DB dumps before major releases.
// \Flarum\Database\Console\GenerateDumpCommand::class
];
});

View File

@@ -0,0 +1,87 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Database\Console;
use Flarum\Console\AbstractCommand;
use Flarum\Foundation\Paths;
use Illuminate\Database\Connection;
class GenerateDumpCommand extends AbstractCommand
{
/**
* @var Connection
*/
protected $connection;
/**
* @var Paths
*/
protected $paths;
/**
* @param Connection $connection
* @param Paths $paths
*/
public function __construct(Connection $connection, Paths $paths)
{
$this->connection = $connection;
$this->paths = $paths;
parent::__construct();
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('schema:dump')
->setDescription('Dump DB schema');
}
/**
* {@inheritdoc}
*/
protected function fire()
{
$dumpPath = __DIR__.'/../../../migrations/install.dump';
/** @var Connection */
$connection = resolve('db.connection');
$connection
->getSchemaState()
->withMigrationTable($connection->getTablePrefix().'migrations')
->handleOutputUsing(function ($type, $buffer) {
$this->output->write($buffer);
})
->dump($connection, $dumpPath);
// We need to remove any data migrations, as those won't be captured
// in the schema dump, and must be run separately.
$coreDataMigrations = [
'2018_07_21_000000_seed_default_groups',
'2018_07_21_000100_seed_default_group_permissions',
];
$newDump = [];
$dump = file($dumpPath);
foreach ($dump as $line) {
foreach ($coreDataMigrations as $excludeMigrationId) {
if (strpos($line, $excludeMigrationId) !== false) {
continue 2;
}
}
$newDump[] = $line;
}
file_put_contents($dumpPath, implode($newDump));
}
}

View File

@@ -88,22 +88,6 @@ class DatabaseMigrationRepository implements MigrationRepositoryInterface
$query->delete();
}
/**
* Create the migration repository data store.
*
* @return void
*/
public function createRepository()
{
$schema = $this->connection->getSchemaBuilder();
$schema->create($this->table, function ($table) {
$table->increments('id');
$table->string('migration');
$table->string('extension')->nullable();
});
}
/**
* Determine if the migration repository exists.
*

View File

@@ -37,13 +37,6 @@ interface MigrationRepositoryInterface
*/
public function delete($file, $extension = null);
/**
* Create the migration repository data store.
*
* @return void
*/
public function createRepository();
/**
* Determine if the migration repository exists.
*

View File

@@ -12,8 +12,9 @@ namespace Flarum\Database;
use Exception;
use Flarum\Extension\Extension;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Schema\Builder;
use Illuminate\Database\MySqlConnection;
use Illuminate\Filesystem\Filesystem;
use InvalidArgumentException;
use Symfony\Component\Console\Output\OutputInterface;
/**
@@ -35,19 +36,16 @@ class Migrator
*/
protected $files;
/**
* The database schema builder instance.
*
* @var Builder
*/
protected $schemaBuilder;
/**
* The output interface implementation.
*
* @var OutputInterface
*/
protected $output;
/**
* @var ConnectionInterface|MySqlConnection
*/
protected $connection;
/**
* Create a new migrator instance.
@@ -64,7 +62,11 @@ class Migrator
$this->files = $files;
$this->repository = $repository;
$this->schemaBuilder = $connection->getSchemaBuilder();
if (! ($connection instanceof MySqlConnection)) {
throw new InvalidArgumentException('Only MySQL connections are supported');
}
$this->connection = $connection;
// Workaround for https://github.com/laravel/framework/issues/1186
$connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
@@ -197,7 +199,7 @@ class Migrator
protected function runClosureMigration($migration, $direction = 'up')
{
if (is_array($migration) && array_key_exists($direction, $migration)) {
call_user_func($migration[$direction], $this->schemaBuilder);
call_user_func($migration[$direction], $this->connection->getSchemaBuilder());
} else {
throw new Exception('Migration file should contain an array with up/down.');
}
@@ -245,6 +247,42 @@ class Migrator
}
}
/**
* Initialize the Flarum database from a schema dump.
*
* @param string $path to the directory containing the dump.
*/
public function installFromSchema(string $path)
{
$schemaPath = "$path/install.dump";
$startTime = microtime(true);
$dump = file_get_contents($schemaPath);
$this->connection->getSchemaBuilder()->disableForeignKeyConstraints();
foreach (explode(';', $dump) as $statement) {
$statement = trim($statement);
if (empty($statement) || substr($statement, 0, 2) === '/*') {
continue;
}
$statement = str_replace(
'db_prefix_',
$this->connection->getTablePrefix(),
$statement
);
$this->connection->statement($statement);
}
$this->connection->getSchemaBuilder()->enableForeignKeyConstraints();
$runTime = number_format((microtime(true) - $startTime) * 1000, 2);
$this->note('<info>Loaded stored database schema.</info> ('.$runTime.'ms)');
}
/**
* Set the output implementation that should be used by the console.
*
@@ -271,16 +309,6 @@ class Migrator
}
}
/**
* Get the migration repository instance.
*
* @return MigrationRepositoryInterface
*/
public function getRepository()
{
return $this->repository;
}
/**
* Determine if the migration repository exists.
*
@@ -290,14 +318,4 @@ class Migrator
{
return $this->repository->repositoryExists();
}
/**
* Get the file system instance.
*
* @return \Illuminate\Filesystem\Filesystem
*/
public function getFilesystem()
{
return $this->files;
}
}

View File

@@ -35,16 +35,16 @@ class FilesystemManager extends LaravelFilesystemManager
/**
* @inheritDoc
*/
protected function resolve($name): Filesystem
protected function resolve($name, $config = null): Filesystem
{
$driver = $this->getDriver($name);
$localConfig = $this->getLocalConfig($name);
$localConfig = $config ?? $this->getLocalConfig($name);
if (empty($localConfig)) {
throw new InvalidArgumentException("Disk [{$name}] has not been declared. Use the Filesystem extender to do this.");
}
$driver = $config['driver'] ?? $this->getDriver($name);
if ($driver === 'local') {
return $this->createLocalDriver($localConfig);
}

View File

@@ -21,7 +21,7 @@ class Application
*
* @var string
*/
const VERSION = '1.0.1-dev';
const VERSION = '1.0.4';
/**
* The IoC container for the Flarum application.

View File

@@ -48,7 +48,7 @@ class InstalledApp implements AppInterface
public function getRequestHandler()
{
if ($this->config->inMaintenanceMode()) {
return new MaintenanceModeHandler();
return $this->container->make('flarum.maintenance.handler');
} elseif ($this->needsUpdate()) {
return $this->getUpdaterHandler();
}

View File

@@ -105,6 +105,7 @@ class InstalledSite implements SiteInterface
$container->alias('flarum.config', Config::class);
$container->instance('flarum.debug', $this->config->inDebugMode());
$container->instance('config', $config = $this->getIlluminateConfig($laravel));
$container->instance('flarum.maintenance.handler', new MaintenanceModeHandler);
$this->registerLogger($container);
$this->registerCache($container);

View File

@@ -42,7 +42,7 @@ class RunMigrations implements Step
{
$migrator = $this->getMigrator();
$migrator->getRepository()->createRepository();
$migrator->installFromSchema($this->path);
$migrator->run($this->path);
}

View File

@@ -21,8 +21,10 @@ use Flarum\User\UserValidator;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Validation\Factory;
use Illuminate\Validation\ValidationException;
use Intervention\Image\ImageManager;
use InvalidArgumentException;
class RegisterUserHandler
{
@@ -36,12 +38,16 @@ class RegisterUserHandler
/**
* @var UserValidator
*/
protected $validator;
protected $userValidator;
/**
* @var AvatarUploader
*/
protected $avatarUploader;
/**
* @var Factory
*/
private $validator;
/**
* @param Dispatcher $events
@@ -49,12 +55,13 @@ class RegisterUserHandler
* @param UserValidator $validator
* @param AvatarUploader $avatarUploader
*/
public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, AvatarUploader $avatarUploader)
public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $userValidator, AvatarUploader $avatarUploader, Factory $validator)
{
$this->events = $events;
$this->settings = $settings;
$this->validator = $validator;
$this->userValidator = $userValidator;
$this->avatarUploader = $avatarUploader;
$this->validator = $validator;
}
/**
@@ -101,7 +108,7 @@ class RegisterUserHandler
new Saving($user, $actor, $data)
);
$this->validator->assertValid(array_merge($user->getAttributes(), compact('password')));
$this->userValidator->assertValid(array_merge($user->getAttributes(), compact('password')));
$user->save();
@@ -134,8 +141,25 @@ class RegisterUserHandler
);
}
/**
* @throws InvalidArgumentException
*/
private function uploadAvatarFromUrl(User $user, string $url)
{
$urlValidator = $this->validator->make(compact('url'), [
'url' => 'required|active_url',
]);
if ($urlValidator->fails()) {
throw new InvalidArgumentException('Provided avatar URL must be a valid URI.', 503);
}
$scheme = parse_url($url, PHP_URL_SCHEME);
if (! in_array($scheme, ['http', 'https'])) {
throw new InvalidArgumentException("Provided avatar URL must have scheme http or https. Scheme provided was $scheme.", 503);
}
$image = (new ImageManager)->make($url);
$this->avatarUploader->upload($user, $image);

View File

@@ -110,7 +110,7 @@ class RequestPasswordResetHandler
];
$body = $this->translator->trans('core.email.reset_password.body', $data);
$subject = '['.$data['forum'].'] '.$this->translator->trans('core.email.reset_password.subject');
$subject = $this->translator->trans('core.email.reset_password.subject');
$this->queue->push(new SendRawEmailJob($user->email, $subject, $body));

View File

@@ -52,7 +52,7 @@ class EmailConfirmationMailer
$data = $this->getEmailData($event->user, $email);
$body = $this->translator->trans('core.email.confirm_email.body', $data);
$subject = '['.$data['forum'].'] '.$this->translator->trans('core.email.confirm_email.subject');
$subject = $this->translator->trans('core.email.confirm_email.subject');
$this->queue->push(new SendRawEmailJob($email, $subject, $body));
}

View File

@@ -12,6 +12,7 @@ namespace Flarum\Tests\integration\api\users;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\RegistrationToken;
use Flarum\User\User;
class CreateTest extends TestCase
@@ -168,4 +169,218 @@ class CreateTest extends TestCase
$settings->set('allow_sign_up', true);
}
/**
* @test
*/
public function cannot_create_user_with_invalid_avatar_uri_scheme()
{
// Boot app
$this->app();
$regTokens = [];
// Add registration tokens that should cause a failure
$regTokens[] = [
'token' => RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'file://localhost/etc/passwd'
], []),
'scheme' => 'file'
];
$regTokens[] = [
'token' => RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'ftp://localhost/image.png'
], []),
'scheme' => 'ftp'
];
// Test each reg token
foreach ($regTokens as $regToken) {
$regToken['token']->saveOrFail();
// Call the registration endpoint
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'json' => [
'data' => [
'attributes' => [
'token' => $regToken['token']->token,
],
]
],
]
)->withAttribute('bypassCsrfToken', true)
);
// The response body should contain details about the invalid URI
$body = (string) $response->getBody();
$this->assertJson($body);
$decodedBody = json_decode($body, true);
$this->assertEquals(500, $response->getStatusCode());
$firstError = $decodedBody['errors'][0];
// Check that the error is an invalid URI
$this->assertStringStartsWith('InvalidArgumentException: Provided avatar URL must have scheme http or https. Scheme provided was '.$regToken['scheme'].'.', $firstError['detail']);
}
}
/**
* @test
*/
public function cannot_create_user_with_invalid_avatar_uri()
{
// Boot app
$this->app();
$regTokens = [];
// Add registration tokens that should cause a failure
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://127.0.0.1/image.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://192.168.0.1/image.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => '../image.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test',
'email' => 'test@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'image.png'
], []);
// Test each reg token
foreach ($regTokens as $regToken) {
$regToken->saveOrFail();
// Call the registration endpoint
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'json' => [
'data' => [
'attributes' => [
'token' => $regToken->token,
],
]
],
]
)->withAttribute('bypassCsrfToken', true)
);
// The response body should contain details about the invalid URI
$body = (string) $response->getBody();
$this->assertJson($body);
$decodedBody = json_decode($body, true);
$this->assertEquals(500, $response->getStatusCode());
$firstError = $decodedBody['errors'][0];
// Check that the error is an invalid URI
$this->assertStringStartsWith('InvalidArgumentException: Provided avatar URL must be a valid URI.', $firstError['detail']);
}
}
/**
* @test
*/
public function can_create_user_with_valid_avatar_uri()
{
// Boot app
$this->app();
$regTokens = [];
// Add registration tokens that should work fine
$regTokens[] = RegistrationToken::generate('flarum', '1', [
'username' => 'test1',
'email' => 'test1@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://via.placeholder.com/150.png'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '2', [
'username' => 'test2',
'email' => 'test2@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://via.placeholder.com/150.jpg'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '3', [
'username' => 'test3',
'email' => 'test3@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'https://via.placeholder.com/150.gif'
], []);
$regTokens[] = RegistrationToken::generate('flarum', '4', [
'username' => 'test4',
'email' => 'test4@machine.local',
'is_email_confirmed' => 1,
'avatar_url' => 'http://via.placeholder.com/150.png'
], []);
/**
* Test each reg token.
*
* @var RegistrationToken $regToken
*/
foreach ($regTokens as $regToken) {
$regToken->saveOrFail();
// Call the registration endpoint
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'json' => [
'data' => [
'attributes' => [
'token' => $regToken->token,
],
]
],
]
)->withAttribute('bypassCsrfToken', true)
);
$this->assertEquals(201, $response->getStatusCode());
$user = User::where('username', $regToken->user_attributes['username'])->firstOrFail();
$this->assertEquals($regToken->user_attributes['is_email_confirmed'], $user->is_email_confirmed);
$this->assertEquals($regToken->user_attributes['username'], $user->username);
$this->assertEquals($regToken->user_attributes['email'], $user->email);
}
}
}