mirror of
https://github.com/flarum/core.git
synced 2025-08-13 20:04:24 +02:00
Compare commits
23 Commits
ds/1820-se
...
v0.1.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
fd371c1203 | ||
|
d72416cd8a | ||
|
937ff1a0d5 | ||
|
097a87dbb6 | ||
|
7794546845 | ||
|
c43cc874ee | ||
|
33cf94c192 | ||
|
036e519865 | ||
|
9386c91af9 | ||
|
8306cef963 | ||
|
51ea326959 | ||
|
15bed971e6 | ||
|
c896cd8696 | ||
|
54ac83d0b6 | ||
|
1592cd1013 | ||
|
6e8884f190 | ||
|
df8f73bd3d | ||
|
3f0f89afb1 | ||
|
f0f301c5f4 | ||
|
3045bde167 | ||
|
ee7a4627d8 | ||
|
b9fb92d49a | ||
|
b5accca957 |
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Lint code
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: Lint JS code with Prettier
|
||||
name: JS / Prettier
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,5 +1,50 @@
|
||||
# Changelog
|
||||
|
||||
## [0.1.0-beta.13](https://github.com/flarum/core/compare/v0.1.0-beta.12...v0.1.0-beta.13)
|
||||
|
||||
### Added
|
||||
- Middleware extender (#2017, #2063, #2084)
|
||||
- Console extender (#2057)
|
||||
- CSRF extender (#2095)
|
||||
- Event extender (#2097)
|
||||
- Mail extender (#2012)
|
||||
- Model extender (#2100)
|
||||
- Show discussion start user as html class on post
|
||||
- PHPUnit 8 compatibility.
|
||||
- Composer 2 compatibility
|
||||
- Permission groups can now be hidden (#2129)
|
||||
- Confirmation popup when hiding or deleting posts (#2135)
|
||||
|
||||
### Changed
|
||||
- Updated less.php dependency version to 3.0.
|
||||
- All notifications now processed through the queue (#1931)
|
||||
- Updated JS dependencies
|
||||
- Simplified uploads, removing need to store intermediate files (#2117)
|
||||
- Improved date handling for dates older than 1 year (#2034)
|
||||
- Linting and automatic formatting for JS (#2099)
|
||||
- Translation files from Language Packs are only loaded for extensions that are enabled (#2020)
|
||||
|
||||
### Fixed
|
||||
- Users can no longer restore discussions hidden by others (#2037)
|
||||
- Issues of the Modal not showing or auto hiding (#2080)
|
||||
- Extensions page in admin showning columns incorrectly (#2111)
|
||||
- Non dismissable modals can be dismissed using the ESC key (#1917)
|
||||
- New post injected above unread sticky (#1868)
|
||||
- New discussions not visible to users when using Pusher (#2077)
|
||||
- Icons on admin permissions page (#2016, #2018)
|
||||
- Notification bubble contrast on mobile with colored header (#2109)
|
||||
- PostStreamScrubber click jumps back to first position (#1945)
|
||||
- Loading state of Switch toggle component is hard to see (#2039, #1491)
|
||||
- Allowing permission check to use class name based gate checks (#1977)
|
||||
|
||||
### Removed
|
||||
- Zend compatibility bridge (#2010)
|
||||
- SES mail support (#2011)
|
||||
- Backward compatibility dropped for mail drivers
|
||||
- Support for PHP 7.1
|
||||
- Deprecated Flarum\Util\Str helper class
|
||||
- Deprecated ConfigureMiddleware event
|
||||
|
||||
## [0.1.0-beta.12](https://github.com/flarum/core/compare/v0.1.0-beta.11.1...v0.1.0-beta.12)
|
||||
|
||||
### Added
|
||||
|
4
js/dist/admin.js
vendored
4
js/dist/admin.js
vendored
File diff suppressed because one or more lines are too long
2
js/dist/admin.js.map
vendored
2
js/dist/admin.js.map
vendored
File diff suppressed because one or more lines are too long
4
js/dist/forum.js
vendored
4
js/dist/forum.js
vendored
File diff suppressed because one or more lines are too long
2
js/dist/forum.js.map
vendored
2
js/dist/forum.js.map
vendored
File diff suppressed because one or more lines are too long
3
js/package-lock.json
generated
3
js/package-lock.json
generated
@@ -4943,8 +4943,7 @@
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
"resolved": ""
|
||||
},
|
||||
"tapable": {
|
||||
"version": "1.1.3",
|
||||
|
@@ -3,6 +3,7 @@ import Button from '../../common/components/Button';
|
||||
import Badge from '../../common/components/Badge';
|
||||
import Group from '../../common/models/Group';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import Switch from '../../common/components/Switch';
|
||||
|
||||
/**
|
||||
* The `EditGroupModal` component shows a modal dialog which allows the user
|
||||
@@ -16,6 +17,7 @@ export default class EditGroupModal extends Modal {
|
||||
this.namePlural = m.prop(this.group.namePlural() || '');
|
||||
this.icon = m.prop(this.group.icon() || '');
|
||||
this.color = m.prop(this.group.color() || '');
|
||||
this.isHidden = m.prop(this.group.isHidden() || false);
|
||||
}
|
||||
|
||||
className() {
|
||||
@@ -89,6 +91,18 @@ export default class EditGroupModal extends Modal {
|
||||
10
|
||||
);
|
||||
|
||||
items.add(
|
||||
'hidden',
|
||||
<div className="Form-group">
|
||||
{Switch.component({
|
||||
state: !!Number(this.isHidden()),
|
||||
children: app.translator.trans('core.admin.edit_group.hide_label'),
|
||||
onchange: this.isHidden,
|
||||
})}
|
||||
</div>,
|
||||
10
|
||||
);
|
||||
|
||||
items.add(
|
||||
'submit',
|
||||
<div className="Form-group">
|
||||
@@ -118,6 +132,7 @@ export default class EditGroupModal extends Modal {
|
||||
namePlural: this.namePlural(),
|
||||
color: this.color(),
|
||||
icon: this.icon(),
|
||||
isHidden: this.isHidden(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -112,6 +112,16 @@ export default class PermissionGrid extends Component {
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'viewHiddenGroups',
|
||||
{
|
||||
icon: 'fas fa-users',
|
||||
label: app.translator.trans('core.admin.permissions.view_hidden_groups_label'),
|
||||
permission: 'viewHiddenGroups',
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'viewUserList',
|
||||
{
|
||||
|
@@ -7,6 +7,7 @@ Object.assign(Group.prototype, {
|
||||
namePlural: Model.attribute('namePlural'),
|
||||
color: Model.attribute('color'),
|
||||
icon: Model.attribute('icon'),
|
||||
isHidden: Model.attribute('isHidden'),
|
||||
});
|
||||
|
||||
Group.ADMINISTRATOR_ID = '1';
|
||||
|
@@ -2,6 +2,7 @@ import EditPostComposer from '../components/EditPostComposer';
|
||||
import Button from '../../common/components/Button';
|
||||
import Separator from '../../common/components/Separator';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import extractText from '../../common/utils/extractText';
|
||||
|
||||
/**
|
||||
* The `PostControls` utility constructs a list of buttons for a post which
|
||||
@@ -145,6 +146,7 @@ export default {
|
||||
* @return {Promise}
|
||||
*/
|
||||
hideAction() {
|
||||
if (!confirm(extractText(app.translator.trans('core.forum.post_controls.hide_confirmation')))) return;
|
||||
this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user });
|
||||
|
||||
return this.save({ isHidden: true }).then(() => m.redraw());
|
||||
@@ -167,6 +169,7 @@ export default {
|
||||
* @return {Promise}
|
||||
*/
|
||||
deleteAction(context) {
|
||||
if (!confirm(extractText(app.translator.trans('core.forum.post_controls.delete_confirmation')))) return;
|
||||
if (context) context.loading = true;
|
||||
|
||||
return this.delete()
|
||||
|
@@ -0,0 +1,14 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
|
||||
return Migration::addColumns('groups', [
|
||||
'is_hidden' => ['boolean', 'default' => false]
|
||||
]);
|
@@ -26,6 +26,8 @@ class ListGroupsController extends AbstractListController
|
||||
*/
|
||||
protected function data(ServerRequestInterface $request, Document $document)
|
||||
{
|
||||
return Group::all();
|
||||
$actor = $request->getAttribute('actor');
|
||||
|
||||
return Group::whereVisibleTo($actor)->get();
|
||||
}
|
||||
}
|
||||
|
@@ -45,6 +45,10 @@ class BasicUserSerializer extends AbstractSerializer
|
||||
*/
|
||||
protected function groups($user)
|
||||
{
|
||||
return $this->hasMany($user, GroupSerializer::class);
|
||||
if ($this->getActor()->can('viewHiddenGroups')) {
|
||||
return $this->hasMany($user, GroupSerializer::class);
|
||||
}
|
||||
|
||||
return $this->hasMany($user, GroupSerializer::class, 'visibleGroups');
|
||||
}
|
||||
}
|
||||
|
@@ -85,7 +85,7 @@ class ForumSerializer extends AbstractSerializer
|
||||
|
||||
if ($this->actor->can('administrate')) {
|
||||
$attributes['adminUrl'] = $this->url->to('admin')->base();
|
||||
$attributes['version'] = $this->app->version();
|
||||
$attributes['version'] = Application::VERSION;
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
|
@@ -52,6 +52,7 @@ class GroupSerializer extends AbstractSerializer
|
||||
'namePlural' => $this->translateGroupName($group->name_plural),
|
||||
'color' => $group->color,
|
||||
'icon' => $group->icon,
|
||||
'isHidden' => $group->is_hidden
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -9,9 +9,9 @@
|
||||
|
||||
namespace Flarum\Console\Event;
|
||||
|
||||
use Flarum\Foundation\Application;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Application as ConsoleApplication;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
@@ -19,22 +19,22 @@ use Symfony\Component\Console\Application as ConsoleApplication;
|
||||
class Configuring
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
* @var Container
|
||||
*/
|
||||
public $app;
|
||||
|
||||
/**
|
||||
* @var ConsoleApplication
|
||||
* @var Application
|
||||
*/
|
||||
public $console;
|
||||
|
||||
/**
|
||||
* @param Application $app
|
||||
* @param ConsoleApplication $console
|
||||
* @param Container $container
|
||||
* @param Application $console
|
||||
*/
|
||||
public function __construct(Application $app, ConsoleApplication $console)
|
||||
public function __construct(Container $container, Application $console)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->app = $container;
|
||||
$this->console = $console;
|
||||
}
|
||||
|
||||
|
@@ -10,12 +10,12 @@
|
||||
namespace Flarum\Console;
|
||||
|
||||
use Flarum\Console\Event\Configuring;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Foundation\SiteInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Symfony\Component\Console\Application as ConsoleApplication;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
@@ -33,7 +33,7 @@ class Server
|
||||
{
|
||||
$app = $this->site->bootApp();
|
||||
|
||||
$console = new ConsoleApplication('Flarum', Application::VERSION);
|
||||
$console = new Application('Flarum', \Flarum\Foundation\Application::VERSION);
|
||||
|
||||
foreach ($app->getConsoleCommands() as $command) {
|
||||
$console->add($command);
|
||||
@@ -47,29 +47,28 @@ class Server
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private function extend(ConsoleApplication $console)
|
||||
private function extend(Application $console)
|
||||
{
|
||||
$app = Application::getInstance();
|
||||
$container = \Illuminate\Container\Container::getInstance();
|
||||
|
||||
$this->handleErrors($app, $console);
|
||||
$this->handleErrors($container, $console);
|
||||
|
||||
$events = $app->make(Dispatcher::class);
|
||||
|
||||
$events->dispatch(new Configuring($app, $console));
|
||||
$events = $container->make(Dispatcher::class);
|
||||
$events->dispatch(new Configuring($container, $console));
|
||||
}
|
||||
|
||||
private function handleErrors(Application $app, ConsoleApplication $console)
|
||||
private function handleErrors(Container $container, Application $console)
|
||||
{
|
||||
$dispatcher = new EventDispatcher();
|
||||
|
||||
$dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event) use ($app) {
|
||||
$dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event) use ($container) {
|
||||
/** @var Registry $registry */
|
||||
$registry = $app->make(Registry::class);
|
||||
$registry = $container->make(Registry::class);
|
||||
|
||||
$error = $registry->handle($event->getError());
|
||||
|
||||
/** @var Reporter[] $reporters */
|
||||
$reporters = $app->tagged(Reporter::class);
|
||||
$reporters = $container->tagged(Reporter::class);
|
||||
|
||||
if ($error->shouldBeReported()) {
|
||||
foreach ($reporters as $reporter) {
|
||||
|
@@ -14,6 +14,7 @@ use Flarum\Event\ConfigureModelDefaultAttributes;
|
||||
use Flarum\Event\GetModelRelationship;
|
||||
use Illuminate\Database\Eloquent\Model as Eloquent;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Arr;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
@@ -46,6 +47,12 @@ abstract class AbstractModel extends Eloquent
|
||||
*/
|
||||
protected $afterDeleteCallbacks = [];
|
||||
|
||||
public static $customRelations = [];
|
||||
|
||||
public static $dateAttributes = [];
|
||||
|
||||
public static $defaults = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -71,13 +78,20 @@ abstract class AbstractModel extends Eloquent
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$defaults = [];
|
||||
$this->attributes = [];
|
||||
|
||||
foreach (array_merge(array_reverse(class_parents($this)), [static::class]) as $class) {
|
||||
$this->attributes = array_merge($this->attributes, Arr::get(static::$defaults, $class, []));
|
||||
}
|
||||
|
||||
// Deprecated in beta 13, remove in beta 14.
|
||||
static::$dispatcher->dispatch(
|
||||
new ConfigureModelDefaultAttributes($this, $defaults)
|
||||
new ConfigureModelDefaultAttributes($this, $this->attributes)
|
||||
);
|
||||
|
||||
$this->attributes = $defaults;
|
||||
$this->attributes = array_map(function ($item) {
|
||||
return is_callable($item) ? $item() : $item;
|
||||
}, $this->attributes);
|
||||
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
@@ -89,19 +103,17 @@ abstract class AbstractModel extends Eloquent
|
||||
*/
|
||||
public function getDates()
|
||||
{
|
||||
static $dates = [];
|
||||
static::$dispatcher->dispatch(
|
||||
new ConfigureModelDates($this, $this->dates)
|
||||
);
|
||||
|
||||
$class = get_class($this);
|
||||
$dates = $this->dates;
|
||||
|
||||
if (! isset($dates[$class])) {
|
||||
static::$dispatcher->dispatch(
|
||||
new ConfigureModelDates($this, $this->dates)
|
||||
);
|
||||
|
||||
$dates[$class] = $this->dates;
|
||||
foreach (array_merge(array_reverse(class_parents($this)), [static::class]) as $class) {
|
||||
$dates = array_merge($dates, Arr::get(static::$dateAttributes, $class, []));
|
||||
}
|
||||
|
||||
return $dates[$class];
|
||||
return $dates;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,6 +151,14 @@ abstract class AbstractModel extends Eloquent
|
||||
*/
|
||||
protected function getCustomRelation($name)
|
||||
{
|
||||
foreach (array_merge([static::class], class_parents($this)) as $class) {
|
||||
$relation = Arr::get(static::$customRelations, $class.".$name", null);
|
||||
if (! is_null($relation)) {
|
||||
return $relation($this);
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated, remove in beta 14
|
||||
return static::$dispatcher->until(
|
||||
new GetModelRelationship($this, $name)
|
||||
);
|
||||
|
@@ -14,21 +14,29 @@ use Flarum\Database\Migrator;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
class MigrateCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @param Application $application
|
||||
*/
|
||||
public function __construct(Application $application)
|
||||
public function __construct(Container $container, Application $application)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->app = $application;
|
||||
|
||||
parent::__construct();
|
||||
@@ -58,16 +66,16 @@ class MigrateCommand extends AbstractCommand
|
||||
|
||||
public function upgrade()
|
||||
{
|
||||
$this->app->bind(Builder::class, function ($app) {
|
||||
return $app->make(ConnectionInterface::class)->getSchemaBuilder();
|
||||
$this->container->bind(Builder::class, function ($container) {
|
||||
return $container->make(ConnectionInterface::class)->getSchemaBuilder();
|
||||
});
|
||||
|
||||
$migrator = $this->app->make(Migrator::class);
|
||||
$migrator = $this->container->make(Migrator::class);
|
||||
$migrator->setOutput($this->output);
|
||||
|
||||
$migrator->run(__DIR__.'/../../../migrations');
|
||||
|
||||
$extensions = $this->app->make(ExtensionManager::class);
|
||||
$extensions = $this->container->make(ExtensionManager::class);
|
||||
$extensions->getMigrator()->setOutput($this->output);
|
||||
|
||||
foreach ($extensions->getEnabledExtensions() as $name => $extension) {
|
||||
@@ -78,11 +86,11 @@ class MigrateCommand extends AbstractCommand
|
||||
}
|
||||
}
|
||||
|
||||
$this->app->make(SettingsRepositoryInterface::class)->set('version', $this->app->version());
|
||||
$this->container->make(SettingsRepositoryInterface::class)->set('version', Application::VERSION);
|
||||
|
||||
$this->info('Publishing assets...');
|
||||
|
||||
$this->app->make('files')->copyDirectory(
|
||||
$this->container->make('files')->copyDirectory(
|
||||
$this->app->vendorPath().'/components/font-awesome/webfonts',
|
||||
$this->app->publicPath().'/assets/fonts'
|
||||
);
|
||||
|
@@ -12,6 +12,8 @@ namespace Flarum\Event;
|
||||
use Flarum\Database\AbstractModel;
|
||||
|
||||
/**
|
||||
* @deprecated in beta 13, removed in beta 14
|
||||
*
|
||||
* The `ConfigureModelDates` event is called to retrieve a list of fields for a model
|
||||
* that should be converted into date objects.
|
||||
*/
|
||||
|
@@ -11,6 +11,9 @@ namespace Flarum\Event;
|
||||
|
||||
use Flarum\Database\AbstractModel;
|
||||
|
||||
/**
|
||||
* @deprecated in beta 13, removed in beta 14
|
||||
*/
|
||||
class ConfigureModelDefaultAttributes
|
||||
{
|
||||
/**
|
||||
|
@@ -12,6 +12,8 @@ namespace Flarum\Event;
|
||||
use Flarum\Database\AbstractModel;
|
||||
|
||||
/**
|
||||
* @deprecated beta 13, use the Model extender instead.
|
||||
*
|
||||
* The `GetModelRelationship` event is called to retrieve Relation object for a
|
||||
* model. Listeners should return an Eloquent Relation object.
|
||||
*/
|
||||
|
@@ -11,13 +11,20 @@ namespace Flarum\Extend;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use SplFileInfo;
|
||||
|
||||
class LanguagePack implements ExtenderInterface, LifecycleInterface
|
||||
{
|
||||
private const CORE_LOCALE_FILES = [
|
||||
'core',
|
||||
'validation',
|
||||
];
|
||||
|
||||
private $path;
|
||||
|
||||
/**
|
||||
@@ -49,13 +56,13 @@ class LanguagePack implements ExtenderInterface, LifecycleInterface
|
||||
|
||||
$container->resolving(
|
||||
LocaleManager::class,
|
||||
function (LocaleManager $locales) use ($extension, $locale, $title) {
|
||||
$this->registerLocale($locales, $extension, $locale, $title);
|
||||
function (LocaleManager $locales, Container $container) use ($extension, $locale, $title) {
|
||||
$this->registerLocale($container, $locales, $extension, $locale, $title);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function registerLocale(LocaleManager $locales, Extension $extension, $locale, $title)
|
||||
private function registerLocale(Container $container, LocaleManager $locales, Extension $extension, $locale, $title)
|
||||
{
|
||||
$locales->addLocale($locale, $title);
|
||||
|
||||
@@ -76,12 +83,41 @@ class LanguagePack implements ExtenderInterface, LifecycleInterface
|
||||
}
|
||||
|
||||
foreach (new DirectoryIterator($directory) as $file) {
|
||||
if ($file->isFile() && in_array($file->getExtension(), ['yml', 'yaml'])) {
|
||||
if ($this->shouldLoad($file, $container)) {
|
||||
$locales->addTranslations($locale, $file->getPathname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldLoad(SplFileInfo $file, Container $container)
|
||||
{
|
||||
if (! $file->isFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are only interested in YAML files
|
||||
if (! in_array($file->getExtension(), ['yml', 'yaml'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some language packs include translations for many extensions
|
||||
// from the ecosystems. For performance reasons, we should only
|
||||
// load those that belong to core, or extensions that are enabled.
|
||||
// To identify them, we compare the filename (without the YAML
|
||||
// extension) with the list of known names and all extension IDs.
|
||||
$slug = $file->getBasename(".{$file->getExtension()}");
|
||||
|
||||
if (in_array($slug, self::CORE_LOCALE_FILES, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var ExtensionManager $extensions */
|
||||
static $extensions;
|
||||
$extensions = $extensions ?? $container->make(ExtensionManager::class);
|
||||
|
||||
return $extensions->isEnabled($slug);
|
||||
}
|
||||
|
||||
public function onEnable(Container $container, Extension $extension)
|
||||
{
|
||||
$container->make('flarum.locales')->clearCache();
|
||||
|
182
src/Extend/Model.php
Normal file
182
src/Extend/Model.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?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\Extend;
|
||||
|
||||
use Flarum\Database\AbstractModel;
|
||||
use Flarum\Extension\Extension;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Model implements ExtenderInterface
|
||||
{
|
||||
private $modelClass;
|
||||
|
||||
/**
|
||||
* @param string $modelClass The ::class attribute of the model you are modifying.
|
||||
* This model should extend from \Flarum\Database\AbstractModel.
|
||||
*/
|
||||
public function __construct(string $modelClass)
|
||||
{
|
||||
$this->modelClass = $modelClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attribute to be treated as a date.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return self
|
||||
*/
|
||||
public function dateAttribute(string $attribute)
|
||||
{
|
||||
Arr::set(
|
||||
AbstractModel::$dateAttributes,
|
||||
$this->modelClass,
|
||||
array_merge(
|
||||
Arr::get(AbstractModel::$dateAttributes, $this->modelClass, []),
|
||||
[$attribute]
|
||||
)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a default value for a given attribute, which can be an explicit value, or a closure.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return self
|
||||
*/
|
||||
public function default(string $attribute, $value)
|
||||
{
|
||||
Arr::set(AbstractModel::$defaults, "$this->modelClass.$attribute", $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a simple belongsTo relationship from this model to another model.
|
||||
* This represents an inverse one-to-one or inverse one-to-many relationship.
|
||||
* For more complex relationships, use the ->relationship method.
|
||||
*
|
||||
* @param string $name: The name of the relation. This doesn't have to be anything in particular,
|
||||
* but has to be unique from other relation names for this model, and should
|
||||
* work as the name of a method.
|
||||
* @param string $related: The ::class attribute of the model, which should extend \Flarum\Database\AbstractModel.
|
||||
* @param string $foreignKey: The foreign key attribute of the parent model.
|
||||
* @param string $ownerKey: The primary key attribute of the parent model.
|
||||
* @return self
|
||||
*/
|
||||
public function belongsTo(string $name, string $related, string $foreignKey = null, string $ownerKey = null)
|
||||
{
|
||||
return $this->relationship($name, function (AbstractModel $model) use ($related, $foreignKey, $ownerKey, $name) {
|
||||
return $model->belongsTo($related, $foreignKey, $ownerKey, $name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a simple belongsToMany relationship from this model to another model.
|
||||
* This represents a many-to-many relationship.
|
||||
* For more complex relationships, use the ->relationship method.
|
||||
*
|
||||
* @param string $name: The name of the relation. This doesn't have to be anything in particular,
|
||||
* but has to be unique from other relation names for this model, and should
|
||||
* work as the name of a method.
|
||||
* @param string $related: The ::class attribute of the model, which should extend \Flarum\Database\AbstractModel.
|
||||
* @param string $table: The intermediate table for this relation
|
||||
* @param string $foreignPivotKey: The foreign key attribute of the parent model.
|
||||
* @param string $relatedPivotKey: The associated key attribute of the relation.
|
||||
* @param string $parentKey: The key name of the parent model.
|
||||
* @param string $relatedKey: The key name of the related model.
|
||||
* @return self
|
||||
*/
|
||||
public function belongsToMany(
|
||||
string $name,
|
||||
string $related,
|
||||
string $table = null,
|
||||
string $foreignPivotKey = null,
|
||||
string $relatedPivotKey = null,
|
||||
string $parentKey = null,
|
||||
string $relatedKey = null
|
||||
) {
|
||||
return $this->relationship($name, function (AbstractModel $model) use ($related, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey, $name) {
|
||||
return $model->belongsToMany($related, $table, $foreignPivotKey, $relatedPivotKey, $parentKey, $relatedKey, $name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a simple hasOne relationship from this model to another model.
|
||||
* This represents a one-to-one relationship.
|
||||
* For more complex relationships, use the ->relationship method.
|
||||
*
|
||||
* @param string $name: The name of the relation. This doesn't have to be anything in particular,
|
||||
* but has to be unique from other relation names for this model, and should
|
||||
* work as the name of a method.
|
||||
* @param string $related: The ::class attribute of the model, which should extend \Flarum\Database\AbstractModel.
|
||||
* @param string $foreignKey: The foreign key attribute of the parent model.
|
||||
* @param string $localKey: The primary key attribute of the parent model.
|
||||
* @return self
|
||||
*/
|
||||
public function hasOne(string $name, string $related, string $foreignKey = null, string $localKey = null)
|
||||
{
|
||||
return $this->relationship($name, function (AbstractModel $model) use ($related, $foreignKey, $localKey) {
|
||||
return $model->hasOne($related, $foreignKey, $localKey);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a simple hasMany relationship from this model to another model.
|
||||
* This represents a one-to-many relationship.
|
||||
* For more complex relationships, use the ->relationship method.
|
||||
*
|
||||
* @param string $name: The name of the relation. This doesn't have to be anything in particular,
|
||||
* but has to be unique from other relation names for this model, and should
|
||||
* work as the name of a method.
|
||||
* @param string $related: The ::class attribute of the model, which should extend \Flarum\Database\AbstractModel.
|
||||
* @param string $foreignKey: The foreign key attribute of the parent model.
|
||||
* @param string $localKey: The primary key attribute of the parent model.
|
||||
* @return self
|
||||
*/
|
||||
public function hasMany(string $name, string $related, string $foreignKey = null, string $localKey = null)
|
||||
{
|
||||
return $this->relationship($name, function (AbstractModel $model) use ($related, $foreignKey, $localKey) {
|
||||
return $model->hasMany($related, $foreignKey, $localKey);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a relationship from this model to another model.
|
||||
*
|
||||
* @param string $name: The name of the relation. This doesn't have to be anything in particular,
|
||||
* but has to be unique from other relation names for this model, and should
|
||||
* work as the name of a method.
|
||||
* @param callable $callable
|
||||
*
|
||||
* The callable can be a closure or invokable class, and should accept:
|
||||
* - $instance: An instance of this model.
|
||||
*
|
||||
* The callable should return:
|
||||
* - $relationship: A Laravel Relationship object. See relevant methods of models
|
||||
* like \Flarum\User\User for examples of how relationships should be returned.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function relationship(string $name, callable $callable)
|
||||
{
|
||||
Arr::set(AbstractModel::$customRelations, "$this->modelClass.$name", $callable);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
// Nothing needed here.
|
||||
}
|
||||
}
|
@@ -31,6 +31,8 @@ class ExtensionManager
|
||||
|
||||
protected $app;
|
||||
|
||||
protected $container;
|
||||
|
||||
protected $migrator;
|
||||
|
||||
/**
|
||||
@@ -51,12 +53,14 @@ class ExtensionManager
|
||||
public function __construct(
|
||||
SettingsRepositoryInterface $config,
|
||||
Application $app,
|
||||
Container $container,
|
||||
Migrator $migrator,
|
||||
Dispatcher $dispatcher,
|
||||
Filesystem $filesystem
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->app = $app;
|
||||
$this->container = $container;
|
||||
$this->migrator = $migrator;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->filesystem = $filesystem;
|
||||
@@ -73,12 +77,20 @@ class ExtensionManager
|
||||
// Load all packages installed by composer.
|
||||
$installed = json_decode($this->filesystem->get($this->app->vendorPath().'/composer/installed.json'), true);
|
||||
|
||||
// Composer 2.0 changes the structure of the installed.json manifest
|
||||
$installed = $installed['packages'] ?? $installed;
|
||||
|
||||
foreach ($installed as $package) {
|
||||
if (Arr::get($package, 'type') != 'flarum-extension' || empty(Arr::get($package, 'name'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = isset($package['install-path'])
|
||||
? $this->getExtensionsDir().'/composer/'.$package['install-path']
|
||||
: $this->getExtensionsDir().'/'.Arr::get($package, 'name');
|
||||
|
||||
// Instantiates an Extension object using the package path and composer.json file.
|
||||
$extension = new Extension($this->getExtensionsDir().'/'.Arr::get($package, 'name'), $package);
|
||||
$extension = new Extension($path, $package);
|
||||
|
||||
// Per default all extensions are installed if they are registered in composer.
|
||||
$extension->setInstalled(true);
|
||||
@@ -130,7 +142,7 @@ class ExtensionManager
|
||||
|
||||
$this->setEnabled($enabled);
|
||||
|
||||
$extension->enable($this->app);
|
||||
$extension->enable($this->container);
|
||||
|
||||
$this->dispatcher->dispatch(new Enabled($extension));
|
||||
}
|
||||
@@ -156,7 +168,7 @@ class ExtensionManager
|
||||
|
||||
$this->setEnabled($enabled);
|
||||
|
||||
$extension->disable($this->app);
|
||||
$extension->disable($this->container);
|
||||
|
||||
$this->dispatcher->dispatch(new Disabled($extension));
|
||||
}
|
||||
@@ -227,7 +239,7 @@ class ExtensionManager
|
||||
*/
|
||||
public function migrate(Extension $extension, $direction = 'up')
|
||||
{
|
||||
$this->app->bind(Builder::class, function ($container) {
|
||||
$this->container->bind(Builder::class, function ($container) {
|
||||
return $container->make(ConnectionInterface::class)->getSchemaBuilder();
|
||||
});
|
||||
|
||||
|
@@ -23,7 +23,7 @@ class Application extends Container implements ApplicationContract
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '0.1.0-beta.13-dev';
|
||||
const VERSION = '0.1.0-beta.13';
|
||||
|
||||
/**
|
||||
* The base path for the Flarum installation.
|
||||
|
@@ -12,10 +12,12 @@ namespace Flarum\Frontend\Content;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Frontend\Compiler\CompilerInterface;
|
||||
use Flarum\Frontend\Document;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class Assets
|
||||
{
|
||||
protected $container;
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
@@ -23,14 +25,15 @@ class Assets
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
public function __construct(Application $app)
|
||||
public function __construct(Container $container, Application $app)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public function forFrontend(string $name)
|
||||
{
|
||||
$this->assets = $this->app->make('flarum.assets.'.$name);
|
||||
$this->assets = $this->container->make('flarum.assets.'.$name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@@ -54,7 +54,8 @@ class CreateGroupHandler
|
||||
Arr::get($data, 'attributes.nameSingular'),
|
||||
Arr::get($data, 'attributes.namePlural'),
|
||||
Arr::get($data, 'attributes.color'),
|
||||
Arr::get($data, 'attributes.icon')
|
||||
Arr::get($data, 'attributes.icon'),
|
||||
Arr::get($data, 'attributes.isHidden', false)
|
||||
);
|
||||
|
||||
$this->events->dispatch(
|
||||
|
@@ -74,6 +74,10 @@ class EditGroupHandler
|
||||
$group->icon = $attributes['icon'];
|
||||
}
|
||||
|
||||
if (isset($attributes['isHidden'])) {
|
||||
$group->is_hidden = $attributes['isHidden'];
|
||||
}
|
||||
|
||||
$this->events->dispatch(
|
||||
new Saving($group, $actor, $data)
|
||||
);
|
||||
|
@@ -23,6 +23,7 @@ use Flarum\User\User;
|
||||
* @property string $name_plural
|
||||
* @property string|null $color
|
||||
* @property string|null $icon
|
||||
* @property bool $is_hidden
|
||||
* @property \Illuminate\Database\Eloquent\Collection $users
|
||||
* @property \Illuminate\Database\Eloquent\Collection $permissions
|
||||
*/
|
||||
@@ -72,9 +73,10 @@ class Group extends AbstractModel
|
||||
* @param string $namePlural
|
||||
* @param string $color
|
||||
* @param string $icon
|
||||
* @param bool $isHidden
|
||||
* @return static
|
||||
*/
|
||||
public static function build($nameSingular, $namePlural, $color, $icon)
|
||||
public static function build($nameSingular, $namePlural, $color = null, $icon = null, bool $isHidden = false): self
|
||||
{
|
||||
$group = new static;
|
||||
|
||||
@@ -82,6 +84,7 @@ class Group extends AbstractModel
|
||||
$group->name_plural = $namePlural;
|
||||
$group->color = $color;
|
||||
$group->icon = $icon;
|
||||
$group->is_hidden = $isHidden;
|
||||
|
||||
$group->raise(new Created($group));
|
||||
|
||||
|
@@ -11,6 +11,7 @@ namespace Flarum\Group;
|
||||
|
||||
use Flarum\User\AbstractPolicy;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class GroupPolicy extends AbstractPolicy
|
||||
{
|
||||
@@ -30,4 +31,15 @@ class GroupPolicy extends AbstractPolicy
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $actor
|
||||
* @param Builder $query
|
||||
*/
|
||||
public function find(User $actor, Builder $query)
|
||||
{
|
||||
if ($actor->cannot('viewHiddenGroups')) {
|
||||
$query->where('is_hidden', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -89,14 +89,22 @@ class EnableBundledExtensions implements Step
|
||||
private function loadExtensions()
|
||||
{
|
||||
$json = file_get_contents("$this->vendorPath/composer/installed.json");
|
||||
$installed = json_decode($json, true);
|
||||
|
||||
return (new Collection(json_decode($json, true)))
|
||||
// Composer 2.0 changes the structure of the installed.json manifest
|
||||
$installed = $installed['packages'] ?? $installed;
|
||||
|
||||
return (new Collection($installed))
|
||||
->filter(function ($package) {
|
||||
return Arr::get($package, 'type') == 'flarum-extension';
|
||||
})->filter(function ($package) {
|
||||
return ! empty(Arr::get($package, 'name'));
|
||||
})->map(function ($package) {
|
||||
$extension = new Extension($this->vendorPath.'/'.Arr::get($package, 'name'), $package);
|
||||
$path = isset($package['install-path'])
|
||||
? "$this->vendorPath/composer/".$package['install-path']
|
||||
: $this->vendorPath.'/'.Arr::get($package, 'name');
|
||||
|
||||
$extension = new Extension($path, $package);
|
||||
$extension->setVersion(Arr::get($package, 'version'));
|
||||
|
||||
return $extension;
|
||||
|
@@ -51,19 +51,6 @@ class DiscussionRenamedBlueprint implements BlueprintInterface
|
||||
return ['postNumber' => (int) $this->post->number];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return [
|
||||
'type' => static::getType(),
|
||||
'from_user_id' => $this->post->user ? $this->post->user->id : null,
|
||||
'subject_id' => $this->post->discussion ? $this->post->discussion->id : null,
|
||||
'data' => json_encode(['postNumber' => (int) $this->post->number]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@@ -35,7 +35,7 @@ abstract class AbstractPolicy
|
||||
*/
|
||||
public function getPermission(GetPermission $event)
|
||||
{
|
||||
if (! $event->model instanceof $this->model) {
|
||||
if (! $event->model instanceof $this->model && $event->model !== $this->model) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -606,6 +606,11 @@ class User extends AbstractModel
|
||||
return $this->belongsToMany(Group::class);
|
||||
}
|
||||
|
||||
public function visibleGroups()
|
||||
{
|
||||
return $this->belongsToMany(Group::class)->where('is_hidden', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the user's notifications.
|
||||
*
|
||||
|
@@ -9,15 +9,37 @@
|
||||
|
||||
namespace Flarum\Tests\integration\api\groups;
|
||||
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class ListTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->prepareDatabase([
|
||||
'users' => [
|
||||
$this->adminUser(),
|
||||
$this->normalUser(),
|
||||
],
|
||||
'groups' => [
|
||||
$this->adminGroup(),
|
||||
$this->hiddenGroup()
|
||||
],
|
||||
'group_user' => [
|
||||
['user_id' => 1, 'group_id' => 1],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shows_index_for_guest()
|
||||
public function shows_limited_index_for_guest()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/groups')
|
||||
@@ -26,6 +48,35 @@ class ListTest extends TestCase
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertEquals(Group::count(), count($data['data']));
|
||||
$this->assertEquals(['1'], Arr::pluck($data['data'], 'id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shows_index_for_admin()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/groups', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertEquals(['1', '10'], Arr::pluck($data['data'], 'id'));
|
||||
}
|
||||
|
||||
protected function hiddenGroup(): array
|
||||
{
|
||||
return [
|
||||
'id' => 10,
|
||||
'name_singular' => 'Hidden',
|
||||
'name_plural' => 'Ninjas',
|
||||
'color' => null,
|
||||
'icon' => 'fas fa-wrench',
|
||||
'is_hidden' => 1
|
||||
];
|
||||
}
|
||||
}
|
||||
|
425
tests/integration/extenders/ModelTest.php
Normal file
425
tests/integration/extenders/ModelTest.php
Normal file
@@ -0,0 +1,425 @@
|
||||
<?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\Tests\integration\extenders;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Extend;
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\Post\AbstractEventPost;
|
||||
use Flarum\Post\CommentPost;
|
||||
use Flarum\Post\DiscussionRenamedPost;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
use Flarum\User\User;
|
||||
|
||||
class ModelTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
protected function prepDb()
|
||||
{
|
||||
$this->prepareDatabase([
|
||||
'users' => [
|
||||
$this->adminUser(),
|
||||
$this->normalUser(),
|
||||
],
|
||||
'discussions' => []
|
||||
]);
|
||||
}
|
||||
|
||||
protected function prepPostsHierarchy()
|
||||
{
|
||||
$this->prepareDatabase([
|
||||
'users' => [
|
||||
$this->normalUser(),
|
||||
],
|
||||
'discussions' => [
|
||||
['id' => 1, 'title' => 'Discussion with post', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 1, 'comment_count' => 1, 'is_private' => 0],
|
||||
],
|
||||
'posts' => [
|
||||
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_does_not_exist_by_default()
|
||||
{
|
||||
$this->prepDB();
|
||||
|
||||
$user = User::find(1);
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$user->customRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_hasOne_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasOne('customRelation', Discussion::class, 'user_id')
|
||||
);
|
||||
|
||||
$this->prepDB();
|
||||
|
||||
$user = User::find(1);
|
||||
|
||||
$this->assertEquals([], $user->customRelation()->get()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_hasMany_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('customRelation', Discussion::class, 'user_id')
|
||||
);
|
||||
|
||||
$this->prepDB();
|
||||
|
||||
$user = User::find(1);
|
||||
|
||||
$this->assertEquals([], $user->customRelation()->get()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_belongsTo_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->belongsTo('customRelation', Discussion::class, 'user_id')
|
||||
);
|
||||
|
||||
$this->prepDB();
|
||||
|
||||
$user = User::find(1);
|
||||
|
||||
$this->assertEquals([], $user->customRelation()->get()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->relationship('customRelation', function (User $user) {
|
||||
return $user->hasMany(Discussion::class, 'user_id');
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDB();
|
||||
|
||||
$user = User::find(1);
|
||||
|
||||
$this->assertEquals([], $user->customRelation()->get()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_exists_and_can_return_instances_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->relationship('customRelation', function (User $user) {
|
||||
return $user->hasMany(Discussion::class, 'user_id');
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDB();
|
||||
$this->prepareDatabase([
|
||||
'discussions' => [
|
||||
['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'first_post_id' => 1, 'comment_count' => 1]
|
||||
]
|
||||
]);
|
||||
|
||||
$user = User::find(1);
|
||||
|
||||
$this->assertNotEquals([], $user->customRelation()->get()->toArray());
|
||||
$this->assertContains(json_encode(__CLASS__), json_encode($user->customRelation()->get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_is_inherited_to_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Post::class))
|
||||
->belongsTo('ancestor', Discussion::class, 'discussion_id')
|
||||
);
|
||||
|
||||
$this->prepPostsHierarchy();
|
||||
|
||||
$post = CommentPost::find(1);
|
||||
|
||||
$this->assertInstanceOf(Discussion::class, $post->ancestor);
|
||||
$this->assertEquals(1, $post->ancestor->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_prioritizes_child_classes_within_2_parent_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Post::class))
|
||||
->belongsTo('ancestor', User::class, 'user_id'),
|
||||
(new Extend\Model(AbstractEventPost::class))
|
||||
->belongsTo('ancestor', Discussion::class, 'discussion_id')
|
||||
);
|
||||
|
||||
$this->prepPostsHierarchy();
|
||||
|
||||
$post = DiscussionRenamedPost::find(1);
|
||||
|
||||
$this->assertInstanceOf(Discussion::class, $post->ancestor);
|
||||
$this->assertEquals(1, $post->ancestor->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_prioritizes_child_classes_within_child_class_and_immediate_parent()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(AbstractEventPost::class))
|
||||
->belongsTo('ancestor', Discussion::class, 'discussion_id'),
|
||||
(new Extend\Model(DiscussionRenamedPost::class))
|
||||
->belongsTo('ancestor', User::class, 'user_id')
|
||||
);
|
||||
|
||||
$this->prepPostsHierarchy();
|
||||
$post = DiscussionRenamedPost::find(1);
|
||||
|
||||
$this->assertInstanceOf(User::class, $post->ancestor);
|
||||
$this->assertEquals(2, $post->ancestor->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_does_not_exist_if_added_to_unrelated_model()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->relationship('customRelation', function (User $user) {
|
||||
return $user->hasMany(Discussion::class, 'user_id');
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDB();
|
||||
$this->prepareDatabase([
|
||||
'groups' => [
|
||||
$this->adminGroup()
|
||||
]
|
||||
]);
|
||||
|
||||
$group = Group::find(1);
|
||||
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$group->customRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_default_attribute_doesnt_exist_if_not_set()
|
||||
{
|
||||
$group = new Group;
|
||||
|
||||
$this->app();
|
||||
|
||||
$this->assertNotEquals('Custom Default', $group->name_singular);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_default_attribute_works_if_set()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Group::class))
|
||||
->default('name_singular', 'Custom Default')
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$group = new Group;
|
||||
|
||||
$this->assertEquals('Custom Default', $group->name_singular);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_default_attribute_evaluated_at_runtime_if_callable()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Group::class))
|
||||
->default('counter', function () {
|
||||
static $counter = 0;
|
||||
|
||||
return ++$counter;
|
||||
})
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$group1 = new Group;
|
||||
$group2 = new Group;
|
||||
|
||||
$this->assertEquals(1, $group1->counter);
|
||||
$this->assertEquals(2, $group2->counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_default_attribute_is_inherited_to_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Post::class))
|
||||
->default('answer', 42)
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$post = new CommentPost;
|
||||
|
||||
$this->assertEquals(42, $post->answer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_default_attribute_inheritance_prioritizes_child_class()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Post::class))
|
||||
->default('answer', 'dont do this'),
|
||||
(new Extend\Model(AbstractEventPost::class))
|
||||
->default('answer', 42),
|
||||
(new Extend\Model(DiscussionRenamedPost::class))
|
||||
->default('answer', 'ni!')
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$post = new CustomPost;
|
||||
|
||||
$this->assertEquals(42, $post->answer);
|
||||
|
||||
$commentPost = new DiscussionRenamedPost;
|
||||
|
||||
$this->assertEquals('ni!', $commentPost->answer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_default_attribute_doesnt_work_if_set_on_unrelated_model()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Group::class))
|
||||
->default('name_singular', 'Custom Default')
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$user = new User;
|
||||
|
||||
$this->assertNotEquals('Custom Default', $user->name_singular);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_date_attribute_doesnt_exist_by_default()
|
||||
{
|
||||
$post = new Post;
|
||||
|
||||
$this->app();
|
||||
|
||||
$this->assertNotContains('custom', $post->getDates());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_date_attribute_can_be_set()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Post::class))
|
||||
->dateAttribute('custom')
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$post = new Post;
|
||||
|
||||
$this->assertContains('custom', $post->getDates());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_date_attribute_is_inherited_to_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Post::class))
|
||||
->dateAttribute('custom')
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$post = new CommentPost;
|
||||
|
||||
$this->assertContains('custom', $post->getDates());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_date_attribute_doesnt_work_if_set_on_unrelated_model()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(Post::class))
|
||||
->dateAttribute('custom')
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$discussion = new Discussion;
|
||||
|
||||
$this->assertNotContains('custom', $discussion->getDates());
|
||||
}
|
||||
}
|
||||
|
||||
class CustomPost extends AbstractEventPost
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $type = 'customPost';
|
||||
}
|
64
tests/unit/User/AbstractPolicyTest.php
Normal file
64
tests/unit/User/AbstractPolicyTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?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\Tests\unit\User;
|
||||
|
||||
use Flarum\Event\GetPermission;
|
||||
use Flarum\Tests\unit\TestCase;
|
||||
use Flarum\User\AbstractPolicy;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Events\Dispatcher;
|
||||
use Mockery as m;
|
||||
|
||||
class AbstractPolicyTest extends TestCase
|
||||
{
|
||||
private $policy;
|
||||
private $dispatcher;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->policy = m::mock(CustomUserPolicy::class)->makePartial();
|
||||
$this->dispatcher = new Dispatcher();
|
||||
$this->dispatcher->subscribe($this->policy);
|
||||
User::setEventDispatcher($this->dispatcher);
|
||||
}
|
||||
|
||||
public function test_policy_can_be_called_with_object()
|
||||
{
|
||||
$this->policy->shouldReceive('edit')->andReturn(true);
|
||||
|
||||
$allowed = $this->dispatcher->until(new GetPermission(new User(), 'edit', new User()));
|
||||
|
||||
$this->assertTrue($allowed);
|
||||
}
|
||||
|
||||
public function test_policy_can_be_called_with_class()
|
||||
{
|
||||
$this->policy->shouldReceive('create')->andReturn(true);
|
||||
|
||||
$allowed = $this->dispatcher->until(new GetPermission(new User(), 'create', User::class));
|
||||
|
||||
$this->assertTrue($allowed);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomUserPolicy extends AbstractPolicy
|
||||
{
|
||||
protected $model = User::class;
|
||||
|
||||
public function create(User $actor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function edit(User $actor, User $user)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user