mirror of
https://github.com/flarum/core.git
synced 2025-08-17 22:01:44 +02:00
Merge branch 'master' into psr-7
Conflicts: src/Api/Actions/Discussions/IndexAction.php src/Api/Actions/SerializeAction.php src/Core/Formatter/FormatterManager.php src/Extend/ForumAssets.php src/Forum/Actions/IndexAction.php src/Forum/ForumServiceProvider.php
This commit is contained in:
@@ -84,15 +84,11 @@ class IndexAction extends SerializeCollectionAction
|
||||
$load = array_merge($request->include, ['state']);
|
||||
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $load);
|
||||
|
||||
if (($total = $results->getTotal()) !== null) {
|
||||
$document->addMeta('total', $total);
|
||||
}
|
||||
|
||||
static::addPaginationLinks(
|
||||
$document,
|
||||
$request,
|
||||
$this->url->toRoute('flarum.api.discussions.index'),
|
||||
$total ?: $results->areMoreResults()
|
||||
$results->areMoreResults()
|
||||
);
|
||||
|
||||
return $results->getDiscussions();
|
||||
|
@@ -93,7 +93,7 @@ class ShowAction extends SerializeResourceAction
|
||||
|
||||
$discussion = $this->discussions->findOrFail($request->get('id'), $user);
|
||||
|
||||
$discussion->posts_ids = $discussion->posts()->whereCan($user, 'view')->get(['id'])->fetch('id')->all();
|
||||
$discussion->posts_ids = $discussion->visiblePosts($user)->lists('id');
|
||||
|
||||
if (in_array('posts', $request->include)) {
|
||||
$length = strlen($prefix = 'posts.');
|
||||
|
29
src/Api/Actions/Forum/ShowAction.php
Normal file
29
src/Api/Actions/Forum/ShowAction.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php namespace Flarum\Api\Actions\Forum;
|
||||
|
||||
use Flarum\Core\Models\Forum;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Flarum\Api\JsonApiResponse;
|
||||
|
||||
class ShowAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* The name of the serializer class to output results with.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $serializer = 'Flarum\Api\Serializers\ForumSerializer';
|
||||
|
||||
/**
|
||||
* Get the forum, ready to be serialized and assigned to the JsonApi
|
||||
* response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Flarum\Api\JsonApiResponse $response
|
||||
* @return \Flarum\Core\Models\Forum
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, JsonApiResponse $response)
|
||||
{
|
||||
return app('flarum.forum');
|
||||
}
|
||||
}
|
@@ -1,11 +1,12 @@
|
||||
<?php namespace Flarum\Api\Actions;
|
||||
|
||||
use Flarum\Api\Events\WillRespond;
|
||||
use Flarum\Api\Request;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Flarum\Api\JsonApiResponse;
|
||||
use Tobscure\JsonApi\Criteria;
|
||||
use Tobscure\JsonApi\Document;
|
||||
use Tobscure\JsonApi\SerializerInterface;
|
||||
use Tobscure\JsonApi\Criteria;
|
||||
|
||||
abstract class SerializeAction extends JsonApiAction
|
||||
{
|
||||
@@ -68,15 +69,18 @@ abstract class SerializeAction extends JsonApiAction
|
||||
public function respond(Request $request)
|
||||
{
|
||||
$request = static::buildJsonApiRequest($request);
|
||||
|
||||
$document = new Document();
|
||||
|
||||
$data = $this->data($request, $document);
|
||||
|
||||
$serializer = new static::$serializer($request->actor, $request->include, $request->link);
|
||||
|
||||
$document->setData($this->serialize($serializer, $data));
|
||||
$response = new JsonApiResponse($document);
|
||||
|
||||
return new JsonApiResponse($document);
|
||||
event(new WillRespond($this, $data, $request, $response));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -29,6 +29,8 @@ class ShowAction extends SerializeResourceAction
|
||||
'groups' => true
|
||||
];
|
||||
|
||||
public static $link = [];
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
|
20
src/Api/Events/WillRespond.php
Normal file
20
src/Api/Events/WillRespond.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Api\Events;
|
||||
|
||||
class WillRespond
|
||||
{
|
||||
public $action;
|
||||
|
||||
public $data;
|
||||
|
||||
public $request;
|
||||
|
||||
public $response;
|
||||
|
||||
public function __construct($action, &$data, $request, $response)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->data = &$data;
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ use Tobscure\JsonApi\SerializerAbstract;
|
||||
use Flarum\Api\Events\SerializeAttributes;
|
||||
use Flarum\Api\Events\SerializeRelationship;
|
||||
use Flarum\Support\Actor;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
@@ -54,7 +55,7 @@ abstract class BaseSerializer extends SerializerAbstract
|
||||
$data = $relation($model, $include);
|
||||
} else {
|
||||
if ($include) {
|
||||
$data = !is_null($model->$relation) ? $model->$relation : $model->$relation()->getResults();
|
||||
$data = $model->getRelation($relation);
|
||||
} elseif ($many) {
|
||||
$relationIds = $relation.'_ids';
|
||||
$data = $model->$relationIds ?: $model->$relation()->get(['id'])->fetch('id')->all();
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
class DiscussionBasicSerializer extends BaseSerializer
|
||||
{
|
||||
protected static $relationships = [];
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
|
31
src/Api/Serializers/ForumSerializer.php
Normal file
31
src/Api/Serializers/ForumSerializer.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class ForumSerializer extends BaseSerializer
|
||||
{
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'forums';
|
||||
|
||||
protected function id($forum)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Forum model for JSON output.
|
||||
*
|
||||
* @param Forum $forum The Forum model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes($forum)
|
||||
{
|
||||
$attributes = [
|
||||
'title' => $forum->title
|
||||
];
|
||||
|
||||
return $this->extendAttributes($forum, $attributes);
|
||||
}
|
||||
}
|
@@ -2,6 +2,8 @@
|
||||
|
||||
class PostBasicSerializer extends BaseSerializer
|
||||
{
|
||||
protected static $relationships = [];
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
|
60
src/Assets/AssetManager.php
Normal file
60
src/Assets/AssetManager.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php namespace Flarum\Assets;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class AssetManager
|
||||
{
|
||||
protected $less;
|
||||
|
||||
protected $js;
|
||||
|
||||
public function __construct(CompilerInterface $js, CompilerInterface $less)
|
||||
{
|
||||
$this->js = $js;
|
||||
$this->less = $less;
|
||||
}
|
||||
|
||||
public function addFile($file)
|
||||
{
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
|
||||
switch ($ext) {
|
||||
case 'js':
|
||||
$this->js->addFile($file);
|
||||
break;
|
||||
|
||||
case 'css':
|
||||
case 'less':
|
||||
$this->less->addFile($file);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException('Unsupported asset type: '.$ext);
|
||||
}
|
||||
}
|
||||
|
||||
public function addFiles(array $files)
|
||||
{
|
||||
array_walk($files, [$this, 'addFile']);
|
||||
}
|
||||
|
||||
public function addLess($string)
|
||||
{
|
||||
$this->less->addString($string);
|
||||
}
|
||||
|
||||
public function addJs($strings)
|
||||
{
|
||||
$this->js->addString($string);
|
||||
}
|
||||
|
||||
public function getCssFile()
|
||||
{
|
||||
return $this->less->getFile();
|
||||
}
|
||||
|
||||
public function getJsFile()
|
||||
{
|
||||
return $this->js->getFile();
|
||||
}
|
||||
}
|
10
src/Assets/CompilerInterface.php
Normal file
10
src/Assets/CompilerInterface.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php namespace Flarum\Assets;
|
||||
|
||||
interface CompilerInterface
|
||||
{
|
||||
public function addFile($file);
|
||||
|
||||
public function addString($string);
|
||||
|
||||
public function getFile();
|
||||
}
|
9
src/Assets/JsCompiler.php
Normal file
9
src/Assets/JsCompiler.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php namespace Flarum\Assets;
|
||||
|
||||
class JsCompiler extends RevisionCompiler
|
||||
{
|
||||
public function format($string)
|
||||
{
|
||||
return $string.";\n";
|
||||
}
|
||||
}
|
26
src/Assets/LessCompiler.php
Normal file
26
src/Assets/LessCompiler.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php namespace Flarum\Assets;
|
||||
|
||||
use Less_Parser;
|
||||
|
||||
class LessCompiler extends RevisionCompiler
|
||||
{
|
||||
public function compile()
|
||||
{
|
||||
ini_set('xdebug.max_nesting_level', 200);
|
||||
|
||||
$parser = new Less_Parser([
|
||||
'compress' => true,
|
||||
'cache_dir' => storage_path().'/less'
|
||||
]);
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
$parser->parseFile($file);
|
||||
}
|
||||
|
||||
foreach ($this->strings as $string) {
|
||||
$parser->parse($string);
|
||||
}
|
||||
|
||||
return $parser->getCss();
|
||||
}
|
||||
}
|
95
src/Assets/RevisionCompiler.php
Normal file
95
src/Assets/RevisionCompiler.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php namespace Flarum\Assets;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RevisionCompiler implements CompilerInterface
|
||||
{
|
||||
protected $files = [];
|
||||
|
||||
protected $strings = [];
|
||||
|
||||
public function __construct($path, $filename)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
public function addFile($file)
|
||||
{
|
||||
$this->files[] = $file;
|
||||
}
|
||||
|
||||
public function addString($string)
|
||||
{
|
||||
$this->strings[] = $string;
|
||||
}
|
||||
|
||||
public function getFile()
|
||||
{
|
||||
if (! ($revision = $this->getRevision())) {
|
||||
$revision = Str::quickRandom();
|
||||
$this->putRevision($revision);
|
||||
}
|
||||
|
||||
$lastModTime = 0;
|
||||
foreach ($this->files as $file) {
|
||||
$lastModTime = max($lastModTime, filemtime($file));
|
||||
}
|
||||
|
||||
$ext = pathinfo($this->filename, PATHINFO_EXTENSION);
|
||||
$file = $this->path.'/'.substr_replace($this->filename, '-'.$revision, -strlen($ext) - 1, 0);
|
||||
|
||||
if (! file_exists($file)
|
||||
|| filemtime($file) < $lastModTime) {
|
||||
file_put_contents($file, $this->compile());
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
protected function format($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
protected function compile()
|
||||
{
|
||||
$output = '';
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
$output .= $this->format(file_get_contents($file));
|
||||
}
|
||||
|
||||
foreach ($this->strings as $string) {
|
||||
$output .= $this->format($string);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
protected function getRevisionFile()
|
||||
{
|
||||
return $this->path.'/rev-manifest.json';
|
||||
}
|
||||
|
||||
protected function getRevision()
|
||||
{
|
||||
if (file_exists($file = $this->getRevisionFile())) {
|
||||
$manifest = json_decode(file_get_contents($file), true);
|
||||
return array_get($manifest, $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
protected function putRevision($revision)
|
||||
{
|
||||
if (file_exists($file = $this->getRevisionFile())) {
|
||||
$manifest = json_decode(file_get_contents($file), true);
|
||||
} else {
|
||||
$manifest = [];
|
||||
}
|
||||
|
||||
$manifest[$this->filename] = $revision;
|
||||
|
||||
return file_put_contents($this->getRevisionFile(), json_encode($manifest));
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@ class ConsoleServiceProvider extends ServiceProvider
|
||||
{
|
||||
$this->commands('Flarum\Console\InstallCommand');
|
||||
$this->commands('Flarum\Console\SeedCommand');
|
||||
$this->commands('Flarum\Console\GenerateExtensionCommand');
|
||||
}
|
||||
|
||||
public function register()
|
||||
|
127
src/Console/GenerateExtensionCommand.php
Normal file
127
src/Console/GenerateExtensionCommand.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php namespace Flarum\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
class GenerateExtensionCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'flarum:extension';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate a Flarum extension skeleton.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
{
|
||||
do {
|
||||
$name = $this->ask('Extension name (<vendor>-<name>):');
|
||||
} while (! preg_match('/^([a-z0-9]+)-([a-z0-9-]+)$/i', $name, $match));
|
||||
|
||||
list(, $vendor, $package) = $match;
|
||||
|
||||
do {
|
||||
$title = $this->ask('Title:');
|
||||
} while (! $title);
|
||||
|
||||
$description = $this->ask('Description:');
|
||||
|
||||
$authorName = $this->ask('Author name:');
|
||||
|
||||
$authorEmail = $this->ask('Author email:');
|
||||
|
||||
$license = $this->ask('License:');
|
||||
|
||||
$this->info('Generating extension skeleton for "'.$name.'"...');
|
||||
|
||||
$dir = public_path().'/extensions/'.$name;
|
||||
|
||||
$replacements = [
|
||||
'{{namespace}}' => ucfirst($vendor).'\\'.ucfirst($package),
|
||||
'{{escapedNamespace}}' => ucfirst($vendor).'\\\\'.ucfirst($package),
|
||||
'{{classPrefix}}' => ucfirst($package),
|
||||
'{{name}}' => $name
|
||||
];
|
||||
|
||||
$this->copyStub($dir, $replacements);
|
||||
|
||||
rename($dir.'/src/ServiceProvider.php', $dir.'/src/'.ucfirst($package).'ServiceProvider.php');
|
||||
|
||||
$manifest = [
|
||||
'name' => $name,
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'tags' => [],
|
||||
'version' => '0.1.0',
|
||||
'author' => [
|
||||
'name' => $authorName,
|
||||
'email' => $authorEmail
|
||||
],
|
||||
'license' => $license,
|
||||
'require' => [
|
||||
'php' => '>=5.4.0',
|
||||
'flarum' => '>0.1.0'
|
||||
]
|
||||
];
|
||||
|
||||
file_put_contents($dir.'/flarum.json', json_encode($manifest, JSON_PRETTY_PRINT));
|
||||
|
||||
passthru("cd $dir; composer install; cd js; npm install; gulp");
|
||||
|
||||
$this->info('Extension "'.$name.'" generated!');
|
||||
}
|
||||
|
||||
protected function copyStub($destination, $replacements = [])
|
||||
{
|
||||
$this->recursiveCopy(__DIR__.'/../../stubs/extension', $destination, $replacements);
|
||||
}
|
||||
|
||||
protected function recursiveCopy($src, $dst, $replacements = [])
|
||||
{
|
||||
$dir = opendir($src);
|
||||
@mkdir($dst);
|
||||
|
||||
while (($file = readdir($dir)) !== false) {
|
||||
if ($file != '.' && $file != '..') {
|
||||
if (is_dir($src.'/'.$file)) {
|
||||
$this->recursiveCopy($src.'/'.$file, $dst.'/'.$file, $replacements);
|
||||
}
|
||||
else {
|
||||
$contents = file_get_contents($src.'/'.$file);
|
||||
$contents = str_replace(array_keys($replacements), array_values($replacements), $contents);
|
||||
|
||||
file_put_contents($dst.'/'.$file, $contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($dir);
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@ use Flarum\Core\Events\RegisterUserGambits;
|
||||
use Flarum\Extend\Permission;
|
||||
use Flarum\Extend\ActivityType;
|
||||
use Flarum\Extend\NotificationType;
|
||||
use Flarum\Extend\Locale;
|
||||
|
||||
class CoreServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -54,6 +55,17 @@ class CoreServiceProvider extends ServiceProvider
|
||||
(new ActivityType('Flarum\Core\Activity\StartedDiscussionActivity', 'Flarum\Api\Serializers\PostBasicSerializer')),
|
||||
(new ActivityType('Flarum\Core\Activity\JoinedActivity', 'Flarum\Api\Serializers\UserBasicSerializer'))
|
||||
);
|
||||
|
||||
foreach (['en'] as $locale) {
|
||||
$dir = __DIR__.'/../../locale/'.$locale;
|
||||
|
||||
$this->extend(
|
||||
(new Locale($locale))
|
||||
->translations($dir.'/translations.yml')
|
||||
->config($dir.'/config.php')
|
||||
->js($dir.'/config.js')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +82,8 @@ class CoreServiceProvider extends ServiceProvider
|
||||
|
||||
$this->app->singleton('flarum.formatter', 'Flarum\Core\Formatter\FormatterManager');
|
||||
|
||||
$this->app->singleton('flarum.localeManager', 'Flarum\Locale\LocaleManager');
|
||||
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\DiscussionRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentDiscussionRepository'
|
||||
@@ -169,71 +183,78 @@ class CoreServiceProvider extends ServiceProvider
|
||||
$this->extend(
|
||||
new Permission('forum.view'),
|
||||
new Permission('forum.startDiscussion'),
|
||||
new Permission('discussion.rename'),
|
||||
new Permission('discussion.delete'),
|
||||
new Permission('discussion.reply'),
|
||||
new Permission('post.edit'),
|
||||
new Permission('post.delete')
|
||||
new Permission('discussion.editPosts'),
|
||||
new Permission('discussion.deletePosts'),
|
||||
new Permission('discussion.rename'),
|
||||
new Permission('discussion.delete')
|
||||
);
|
||||
|
||||
Forum::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission('forum.'.$permission);
|
||||
Forum::allow('*', function ($forum, $user, $action) {
|
||||
if ($user->hasPermission('forum.'.$action)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Post::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission('post'.$permission);
|
||||
Post::allow('*', function ($post, $user, $action) {
|
||||
if ($user->hasPermission('post.'.$action)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Grant view access to a post only if the user can also view the
|
||||
// discussion which the post is in. Also, the if the post is hidden,
|
||||
// the user must have edit permissions too.
|
||||
Post::grantPermission('view', function ($grant) {
|
||||
$grant->whereCan('view', 'discussion');
|
||||
});
|
||||
|
||||
Post::demandPermission('view', function ($demand) {
|
||||
$demand->whereNull('hide_user_id')
|
||||
->orWhereCan('edit');
|
||||
});
|
||||
|
||||
// Allow a user to edit their own post, unless it has been hidden by
|
||||
// someone else.
|
||||
Post::grantPermission('edit', function ($grant, $user) {
|
||||
$grant->where('user_id', $user->id)
|
||||
->where(function ($query) use ($user) {
|
||||
// When fetching a discussion's posts: if the user doesn't have permission
|
||||
// to moderate the discussion, then they can't see posts that have been
|
||||
// hidden by someone other than themself.
|
||||
Discussion::scopeVisiblePosts(function ($query, User $user, Discussion $discussion) {
|
||||
if (! $discussion->can($user, 'editPosts')) {
|
||||
$query->where(function ($query) use ($user) {
|
||||
$query->whereNull('hide_user_id')
|
||||
->orWhere('hide_user_id', $user->id);
|
||||
});
|
||||
// @todo add limitations to time etc. according to a config setting
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
User::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission('user.'.$permission);
|
||||
Post::allow('view', function ($post, $user) {
|
||||
if (! $post->hide_user_id || $post->can($user, 'edit')) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Grant view access to a user if the user can view the forum.
|
||||
User::grantPermission('view', function ($grant, $user) {
|
||||
$grant->whereCan('view', 'forum');
|
||||
// A post is allowed to be edited if the user has permission to moderate
|
||||
// the discussion which it's in, or if they are the author and the post
|
||||
// hasn't been deleted by someone else.
|
||||
Post::allow('edit', function ($post, $user) {
|
||||
if ($post->discussion->can($user, 'editPosts') ||
|
||||
($post->user_id == $user->id && (! $post->hide_user_id || $post->hide_user_id == $user->id))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Allow a user to edit their own account.
|
||||
User::grantPermission(['edit', 'delete'], function ($grant, $user) {
|
||||
$grant->where('id', $user->id);
|
||||
User::allow('*', function ($discussion, $user, $action) {
|
||||
if ($user->hasPermission('user.'.$action)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Discussion::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission('discussion.'.$permission);
|
||||
User::allow(['edit', 'delete'], function ($user, $actor) {
|
||||
if ($user->id == $actor->id) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Grant view access to a discussion if the user can view the forum.
|
||||
Discussion::grantPermission('view', function ($grant, $user) {
|
||||
$grant->whereCan('view', 'forum');
|
||||
Discussion::allow('*', function ($discussion, $user, $action) {
|
||||
if ($user->hasPermission('discussion.'.$action)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Allow a user to rename their own discussion.
|
||||
Discussion::grantPermission('rename', function ($grant, $user) {
|
||||
$grant->where('start_user_id', $user->id);
|
||||
// @todo add limitations to time etc. according to a config setting
|
||||
Discussion::allow('rename', function ($discussion, $user) {
|
||||
if ($discussion->start_user_id == $user->id) {
|
||||
return true;
|
||||
// @todo add limitations to time etc. according to a config setting
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,8 @@ class FormatterManager
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public $config;
|
||||
|
||||
/**
|
||||
* Create a new formatter manager instance.
|
||||
*
|
||||
@@ -23,6 +25,17 @@ class FormatterManager
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
// Studio does not yet merge autoload_files...
|
||||
// https://github.com/franzliedke/studio/commit/4f0f4314db4ed3e36c869a5f79b855c97bdd1be7
|
||||
require __DIR__.'/../../../vendor/ezyang/htmlpurifier/library/HTMLPurifier.composer.php';
|
||||
|
||||
$this->config = HTMLPurifier_Config::createDefault();
|
||||
$this->config->set('Core.Encoding', 'UTF-8');
|
||||
$this->config->set('Core.EscapeInvalidTags', true);
|
||||
$this->config->set('HTML.Doctype', 'HTML 4.01 Strict');
|
||||
$this->config->set('HTML.Allowed', 'p,em,strong,a[href|title],ul,ol,li,code,pre,blockquote,h1,h2,h3,h4,h5,h6,br,hr,img[src|alt]');
|
||||
$this->config->set('HTML.Nofollow', true);
|
||||
}
|
||||
|
||||
public function add($name, $formatter, $priority = 0)
|
||||
@@ -66,18 +79,7 @@ class FormatterManager
|
||||
$text = $formatter->beforePurification($text, $post);
|
||||
}
|
||||
|
||||
// Studio does not yet merge autoload_files...
|
||||
// https://github.com/franzliedke/studio/commit/4f0f4314db4ed3e36c869a5f79b855c97bdd1be7
|
||||
require __DIR__.'/../../../vendor/ezyang/htmlpurifier/library/HTMLPurifier.composer.php';
|
||||
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
$config->set('Core.Encoding', 'UTF-8');
|
||||
$config->set('Core.EscapeInvalidTags', true);
|
||||
$config->set('HTML.Doctype', 'HTML 4.01 Strict');
|
||||
$config->set('HTML.Allowed', 'p,em,strong,a[href|title],ul,ol,li,code,pre,blockquote,h1,h2,h3,h4,h5,h6,br,hr');
|
||||
$config->set('HTML.Nofollow', true);
|
||||
|
||||
$purifier = new HTMLPurifier($config);
|
||||
$purifier = new HTMLPurifier($this->config);
|
||||
|
||||
$text = $purifier->purify($text);
|
||||
|
||||
|
@@ -20,9 +20,8 @@ class EditDiscussionCommandHandler
|
||||
$user = $command->user;
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $user);
|
||||
|
||||
$discussion->assertCan($user, 'edit');
|
||||
|
||||
if (isset($command->data['title'])) {
|
||||
$discussion->assertCan($user, 'rename');
|
||||
$discussion->rename($command->data['title'], $user);
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Tobscure\Permissible\Permissible;
|
||||
use Flarum\Core\Support\EventGenerator;
|
||||
use Flarum\Core\Support\Locked;
|
||||
use Flarum\Core\Support\VisibleScope;
|
||||
use Flarum\Core\Events\DiscussionWasDeleted;
|
||||
use Flarum\Core\Events\DiscussionWasStarted;
|
||||
use Flarum\Core\Events\DiscussionWasRenamed;
|
||||
@@ -9,7 +10,8 @@ use Flarum\Core\Models\User;
|
||||
|
||||
class Discussion extends Model
|
||||
{
|
||||
use Permissible;
|
||||
use Locked;
|
||||
use VisibleScope;
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
@@ -28,6 +30,8 @@ class Discussion extends Model
|
||||
'last_post_number' => 'integer'
|
||||
];
|
||||
|
||||
protected static $relationships = [];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
@@ -213,6 +217,24 @@ class Discussion extends Model
|
||||
return $this->hasMany('Flarum\Core\Models\Post');
|
||||
}
|
||||
|
||||
protected static $visiblePostsScopes = [];
|
||||
|
||||
public static function scopeVisiblePosts($scope)
|
||||
{
|
||||
static::$visiblePostsScopes[] = $scope;
|
||||
}
|
||||
|
||||
public function visiblePosts(User $user)
|
||||
{
|
||||
$query = $this->posts();
|
||||
|
||||
foreach (static::$visiblePostsScopes as $scope) {
|
||||
$scope($query, $user, $this);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's comments.
|
||||
*
|
||||
@@ -295,9 +317,8 @@ class Discussion extends Model
|
||||
*/
|
||||
public function stateFor(User $user)
|
||||
{
|
||||
$loadedState = array_get($this->relations, 'state');
|
||||
if ($loadedState && $loadedState->user_id === $user->id) {
|
||||
return $loadedState;
|
||||
if ($this->isRelationLoaded('state')) {
|
||||
return $this->relations['state'];
|
||||
}
|
||||
|
||||
$state = $this->state($user)->first();
|
||||
|
@@ -1,11 +1,13 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Tobscure\Permissible\Permissible;
|
||||
use Flarum\Core\Support\Locked;
|
||||
use Flarum\Core;
|
||||
|
||||
class Forum extends Model
|
||||
{
|
||||
use Permissible;
|
||||
use Locked;
|
||||
|
||||
protected static $relationships = [];
|
||||
|
||||
public function getTitleAttribute()
|
||||
{
|
||||
|
@@ -169,37 +169,22 @@ class Model extends Eloquent
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the user has permission to view this model, throwing an
|
||||
* exception if they don't.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return void
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function assertVisibleTo(User $user)
|
||||
public function isRelationLoaded($relation)
|
||||
{
|
||||
if (! $this->can($user, 'view')) {
|
||||
throw new ModelNotFoundException;
|
||||
}
|
||||
return array_key_exists($relation, $this->relations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the user has a certain permission for this model, throwing
|
||||
* an exception if they don't.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param string $permission
|
||||
* @return void
|
||||
*
|
||||
* @throws \Flarum\Core\Exceptions\PermissionDeniedException
|
||||
*/
|
||||
public function assertCan(User $user, $permission)
|
||||
public function getRelation($relation)
|
||||
{
|
||||
if (! $this->can($user, $permission)) {
|
||||
throw new PermissionDeniedException;
|
||||
if (isset($this->$relation)) {
|
||||
return $this->$relation;
|
||||
}
|
||||
|
||||
if (! $this->isRelationLoaded($relation)) {
|
||||
$this->relations[$relation] = $this->$relation()->getResults();
|
||||
}
|
||||
|
||||
return $this->relations[$relation];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Tobscure\Permissible\Permissible;
|
||||
use Flarum\Core\Events\PostWasDeleted;
|
||||
use Flarum\Core\Support\Locked;
|
||||
|
||||
class Post extends Model
|
||||
{
|
||||
use Permissible;
|
||||
use Locked;
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Illuminate\Contracts\Hashing\Hasher;
|
||||
use Tobscure\Permissible\Permissible;
|
||||
use Flarum\Core\Formatter\FormatterManager;
|
||||
use Flarum\Core\Events\UserWasDeleted;
|
||||
use Flarum\Core\Events\UserWasRegistered;
|
||||
@@ -13,10 +12,13 @@ use Flarum\Core\Events\UserAvatarWasChanged;
|
||||
use Flarum\Core\Events\UserWasActivated;
|
||||
use Flarum\Core\Events\UserEmailWasConfirmed;
|
||||
use Flarum\Core\Events\UserEmailChangeWasRequested;
|
||||
use Flarum\Core\Support\Locked;
|
||||
use Flarum\Core\Support\VisibleScope;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
use Permissible;
|
||||
use Locked;
|
||||
use VisibleScope;
|
||||
|
||||
/**
|
||||
* The text formatter instance.
|
||||
@@ -185,6 +187,7 @@ class User extends Model
|
||||
public function changeBio($bio)
|
||||
{
|
||||
$this->bio = $bio;
|
||||
$this->bio_html = null;
|
||||
|
||||
$this->raise(new UserBioWasChanged($this));
|
||||
|
||||
@@ -199,7 +202,7 @@ class User extends Model
|
||||
*/
|
||||
public function getBioHtmlAttribute($value)
|
||||
{
|
||||
if (! $value) {
|
||||
if ($value === null) {
|
||||
$this->bio_html = $value = static::formatBio($this->bio);
|
||||
$this->save();
|
||||
}
|
||||
@@ -309,9 +312,13 @@ class User extends Model
|
||||
return true;
|
||||
}
|
||||
|
||||
$count = $this->permissions()->where('permission', $permission)->count();
|
||||
static $permissions;
|
||||
|
||||
return (bool) $count;
|
||||
if (!$permissions) {
|
||||
$permissions = $this->permissions()->get();
|
||||
}
|
||||
|
||||
return (bool) $permissions->contains('permission', $permission);
|
||||
}
|
||||
|
||||
public function getUnreadNotificationsCount()
|
||||
|
@@ -30,7 +30,7 @@ class EloquentDiscussionRepository implements DiscussionRepositoryInterface
|
||||
{
|
||||
$query = Discussion::where('id', $id);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->firstOrFail();
|
||||
return $this->scopeVisibleTo($query, $user)->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,10 +54,10 @@ class EloquentDiscussionRepository implements DiscussionRepositoryInterface
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function scopeVisibleForUser(Builder $query, User $user = null)
|
||||
protected function scopeVisibleTo(Builder $query, User $user = null)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$query->whereCan($user, 'view');
|
||||
$query->whereVisibleTo($user);
|
||||
}
|
||||
|
||||
return $query;
|
||||
|
@@ -1,10 +1,32 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Flarum\Core\Models\Post;
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Search\Discussions\Fulltext\DriverInterface;
|
||||
|
||||
// TODO: In some cases, the use of a post repository incurs extra query expense,
|
||||
// because for every post retrieved we need to check if the discussion it's in
|
||||
// is visible. Especially when retrieving a discussion's posts, we can end up
|
||||
// with an inefficient chain of queries like this:
|
||||
// 1. Api\Discussions\ShowAction: get discussion (will exit if not visible)
|
||||
// 2. Discussion@visiblePosts: get discussion tags (for post visibility purposes)
|
||||
// 3. Discussion@visiblePosts: get post IDs
|
||||
// 4. EloquentPostRepository@getIndexForNumber: get discussion
|
||||
// 5. EloquentPostRepository@getIndexForNumber: get discussion tags (for post visibility purposes)
|
||||
// 6. EloquentPostRepository@getIndexForNumber: get post index for number
|
||||
// 7. EloquentPostRepository@findWhere: get post IDs for discussion to check for discussion visibility
|
||||
// 8. EloquentPostRepository@findWhere: get post IDs in visible discussions
|
||||
// 9. EloquentPostRepository@findWhere: get posts
|
||||
// 10. EloquentPostRepository@findWhere: eager load discussion onto posts
|
||||
// 11. EloquentPostRepository@findWhere: get discussion tags to filter visible posts
|
||||
// 12. Api\Discussions\ShowAction: eager load users
|
||||
// 13. Api\Discussions\ShowAction: eager load groups
|
||||
// 14. Api\Discussions\ShowAction: eager load mentions
|
||||
// 14. Serializers\DiscussionSerializer: load discussion-user state
|
||||
|
||||
class EloquentPostRepository implements PostRepositoryInterface
|
||||
{
|
||||
protected $fulltext;
|
||||
@@ -26,9 +48,13 @@ class EloquentPostRepository implements PostRepositoryInterface
|
||||
*/
|
||||
public function findOrFail($id, User $user = null)
|
||||
{
|
||||
$query = Post::where('id', $id);
|
||||
$posts = $this->findByIds([$id], $user);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->firstOrFail();
|
||||
if (! count($posts)) {
|
||||
throw new ModelNotFoundException;
|
||||
}
|
||||
|
||||
return $posts->first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +78,9 @@ class EloquentPostRepository implements PostRepositoryInterface
|
||||
$query->orderBy($field, $order);
|
||||
}
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->get();
|
||||
$ids = $query->lists('id');
|
||||
|
||||
return $this->findByIds($ids, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,9 +93,11 @@ class EloquentPostRepository implements PostRepositoryInterface
|
||||
*/
|
||||
public function findByIds(array $ids, User $user = null)
|
||||
{
|
||||
$query = Post::whereIn('id', (array) $ids);
|
||||
$ids = $this->filterDiscussionVisibleTo($ids, $user);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->get();
|
||||
$posts = Post::with('discussion')->whereIn('id', (array) $ids)->get();
|
||||
|
||||
return $this->filterVisibleTo($posts, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,13 +112,17 @@ class EloquentPostRepository implements PostRepositoryInterface
|
||||
{
|
||||
$ids = $this->fulltext->match($string);
|
||||
|
||||
$ids = $this->filterDiscussionVisibleTo($ids, $user);
|
||||
|
||||
$query = Post::select('id', 'discussion_id')->whereIn('id', $ids);
|
||||
|
||||
foreach ($ids as $id) {
|
||||
$query->orderByRaw('id != ?', [$id]);
|
||||
}
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->get();
|
||||
$posts = $query->get();
|
||||
|
||||
return $this->filterVisibleTo($posts, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +137,8 @@ class EloquentPostRepository implements PostRepositoryInterface
|
||||
*/
|
||||
public function getIndexForNumber($discussionId, $number, User $user = null)
|
||||
{
|
||||
$query = Post::where('discussion_id', $discussionId)
|
||||
$query = Discussion::find($discussionId)
|
||||
->visiblePosts($user)
|
||||
->where('time', '<', function ($query) use ($discussionId, $number) {
|
||||
$query->select('time')
|
||||
->from('posts')
|
||||
@@ -116,22 +151,32 @@ class EloquentPostRepository implements PostRepositoryInterface
|
||||
->orderByRaw('ABS(CAST(number AS SIGNED) - '.(int) $number.')');
|
||||
});
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->count();
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include records that are visible to a user.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function scopeVisibleForUser(Builder $query, User $user = null)
|
||||
protected function filterDiscussionVisibleTo($ids, $user)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$query->whereCan($user, 'view');
|
||||
// For each post ID, we need to make sure that the discussion it's in
|
||||
// is visible to the user.
|
||||
if ($user) {
|
||||
$ids = Discussion::join('posts', 'discussions.id', '=', 'posts.discussion_id')
|
||||
->whereIn('posts.id', $ids)
|
||||
->whereVisibleTo($user)
|
||||
->get(['posts.id'])
|
||||
->lists('id');
|
||||
}
|
||||
|
||||
return $query;
|
||||
return $ids;
|
||||
}
|
||||
|
||||
protected function filterVisibleTo($posts, $user)
|
||||
{
|
||||
if ($user) {
|
||||
$posts = $posts->filter(function ($post) use ($user) {
|
||||
return $post->can($user, 'view');
|
||||
});
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ class EloquentUserRepository implements UserRepositoryInterface
|
||||
{
|
||||
$query = User::where('id', $id);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->firstOrFail();
|
||||
return $this->scopeVisibleTo($query, $user)->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ class EloquentUserRepository implements UserRepositoryInterface
|
||||
{
|
||||
$query = User::where('username', 'like', $username);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->pluck('id');
|
||||
return $this->scopeVisibleTo($query, $user)->pluck('id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +85,7 @@ class EloquentUserRepository implements UserRepositoryInterface
|
||||
->orderByRaw('username = ? desc', [$string])
|
||||
->orderByRaw('username like ? desc', [$string.'%']);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->lists('id');
|
||||
return $this->scopeVisibleTo($query, $user)->lists('id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,10 +95,10 @@ class EloquentUserRepository implements UserRepositoryInterface
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function scopeVisibleForUser(Builder $query, User $user = null)
|
||||
protected function scopeVisibleTo(Builder $query, User $user = null)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$query->whereCan($user, 'view');
|
||||
$query->whereVisibleTo($user);
|
||||
}
|
||||
|
||||
return $query;
|
||||
|
@@ -6,13 +6,10 @@ class DiscussionSearchResults
|
||||
|
||||
protected $areMoreResults;
|
||||
|
||||
protected $total;
|
||||
|
||||
public function __construct($discussions, $areMoreResults, $total)
|
||||
public function __construct($discussions, $areMoreResults)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
$this->areMoreResults = $areMoreResults;
|
||||
$this->total = $total;
|
||||
}
|
||||
|
||||
public function getDiscussions()
|
||||
@@ -20,11 +17,6 @@ class DiscussionSearchResults
|
||||
return $this->discussions;
|
||||
}
|
||||
|
||||
public function getTotal()
|
||||
{
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
public function areMoreResults()
|
||||
{
|
||||
return $this->areMoreResults;
|
||||
|
@@ -59,12 +59,10 @@ class DiscussionSearcher implements SearcherInterface
|
||||
public function search(DiscussionSearchCriteria $criteria, $limit = null, $offset = 0, $load = [])
|
||||
{
|
||||
$this->user = $criteria->user;
|
||||
$this->query = $this->discussions->query()->whereCan($criteria->user, 'view');
|
||||
$this->query = $this->discussions->query()->whereVisibleTo($criteria->user);
|
||||
|
||||
$this->gambits->apply($criteria->query, $this);
|
||||
|
||||
$total = $this->query->count();
|
||||
|
||||
$sort = $criteria->sort ?: $this->defaultSort;
|
||||
|
||||
foreach ($sort as $field => $order) {
|
||||
@@ -112,6 +110,6 @@ class DiscussionSearcher implements SearcherInterface
|
||||
Discussion::setStateUser($this->user);
|
||||
$discussions->load($load);
|
||||
|
||||
return new DiscussionSearchResults($discussions, $areMoreResults, $total);
|
||||
return new DiscussionSearchResults($discussions, $areMoreResults);
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ class ConfigTableSeeder extends Seeder
|
||||
'welcome_message' => 'Flarum is now at a point where you can have basic conversations, so here is a little demo for you to break.',
|
||||
'welcome_title' => 'Welcome to Flarum Demo Forum',
|
||||
'extensions_enabled' => '[]',
|
||||
'locale' => 'en',
|
||||
'theme_primary_color' => '#536F90',
|
||||
'theme_secondary_color' => '#536F90',
|
||||
'theme_dark_mode' => false,
|
||||
|
@@ -27,8 +27,8 @@ class PermissionsTableSeeder extends Seeder
|
||||
// Moderators can edit + delete stuff and suspend users
|
||||
[4, 'discussion.delete'],
|
||||
[4, 'discussion.rename'],
|
||||
[4, 'post.delete'],
|
||||
[4, 'post.edit'],
|
||||
[4, 'discussion.editPosts'],
|
||||
[4, 'discussion.deletePosts'],
|
||||
[4, 'user.suspend'],
|
||||
|
||||
];
|
||||
|
57
src/Core/Support/Locked.php
Normal file
57
src/Core/Support/Locked.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php namespace Flarum\Core\Support;
|
||||
|
||||
use Flarum\Core\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Models\User;
|
||||
use Closure;
|
||||
|
||||
trait Locked
|
||||
{
|
||||
protected static $conditions = [];
|
||||
|
||||
protected static function getConditions($action)
|
||||
{
|
||||
$conditions = isset(static::$conditions[$action]) ? static::$conditions[$action] : [];
|
||||
$all = isset(static::$conditions['*']) ? static::$conditions['*'] : [];
|
||||
|
||||
return array_merge($conditions, $all);
|
||||
}
|
||||
|
||||
public static function allow($action, Closure $condition)
|
||||
{
|
||||
foreach ((array) $action as $action) {
|
||||
if (! isset(static::$conditions[$action])) {
|
||||
static::$conditions[$action] = [];
|
||||
}
|
||||
|
||||
static::$conditions[$action][] = $condition;
|
||||
}
|
||||
}
|
||||
|
||||
public function can(User $user, $action)
|
||||
{
|
||||
foreach ($this->getConditions($action) as $condition) {
|
||||
$can = $condition($this, $user, $action);
|
||||
|
||||
if ($can !== null) {
|
||||
return $can;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the user has a certain permission for this model, throwing
|
||||
* an exception if they don't.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param string $permission
|
||||
* @return void
|
||||
*
|
||||
* @throws \Flarum\Core\Exceptions\PermissionDeniedException
|
||||
*/
|
||||
public function assertCan(User $user, $action)
|
||||
{
|
||||
if (! $this->can($user, $action)) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
}
|
||||
}
|
20
src/Core/Support/VisibleScope.php
Normal file
20
src/Core/Support/VisibleScope.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Core\Support;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
trait VisibleScope
|
||||
{
|
||||
protected static $visibleScopes = [];
|
||||
|
||||
public static function scopeVisible($scope)
|
||||
{
|
||||
static::$visibleScopes[] = $scope;
|
||||
}
|
||||
|
||||
public function scopeWhereVisibleTo($query, User $user)
|
||||
{
|
||||
foreach (static::$visibleScopes as $scope) {
|
||||
$scope($query, $user);
|
||||
}
|
||||
}
|
||||
}
|
18
src/Extend/AdminTranslations.php
Normal file
18
src/Extend/AdminTranslations.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php namespace Flarum\Extend;
|
||||
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class AdminTranslations implements ExtenderInterface
|
||||
{
|
||||
protected $keys;
|
||||
|
||||
public function __construct($keys)
|
||||
{
|
||||
$this->keys = $keys;
|
||||
}
|
||||
|
||||
public function extend(Container $container)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
28
src/Extend/ApiLink.php
Normal file
28
src/Extend/ApiLink.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php namespace Flarum\Extend;
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
class ApiLink implements ExtenderInterface
|
||||
{
|
||||
protected $actions;
|
||||
|
||||
protected $relationships;
|
||||
|
||||
public function __construct($actions, $relationships)
|
||||
{
|
||||
$this->actions = $actions;
|
||||
$this->relationships = $relationships;
|
||||
}
|
||||
|
||||
public function extend(Application $app)
|
||||
{
|
||||
foreach ((array) $this->actions as $action) {
|
||||
$parts = explode('.', $action);
|
||||
$class = 'Flarum\Api\Actions\\'.ucfirst($parts[0]).'\\'.ucfirst($parts[1]).'Action';
|
||||
|
||||
foreach ((array) $this->relationships as $relationship) {
|
||||
$class::$link[] = $relationship;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,7 +14,7 @@ class ForumAssets implements ExtenderInterface
|
||||
public function extend(Container $container)
|
||||
{
|
||||
$container->make('events')->listen('Flarum\Forum\Events\RenderView', function ($event) {
|
||||
$event->assets->addFile($this->files);
|
||||
$event->assets->addFiles($this->files);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
19
src/Extend/ForumTranslations.php
Normal file
19
src/Extend/ForumTranslations.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php namespace Flarum\Extend;
|
||||
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Flarum\Forum\Actions\IndexAction;
|
||||
|
||||
class ForumTranslations implements ExtenderInterface
|
||||
{
|
||||
protected $keys;
|
||||
|
||||
public function __construct($keys)
|
||||
{
|
||||
$this->keys = $keys;
|
||||
}
|
||||
|
||||
public function extend(Container $container)
|
||||
{
|
||||
IndexAction::$translations = array_merge(IndexAction::$translations, $this->keys);
|
||||
}
|
||||
}
|
57
src/Extend/Locale.php
Normal file
57
src/Extend/Locale.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php namespace Flarum\Extend;
|
||||
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class Locale implements ExtenderInterface
|
||||
{
|
||||
protected $locale;
|
||||
|
||||
protected $translations;
|
||||
|
||||
protected $config;
|
||||
|
||||
protected $js;
|
||||
|
||||
public function __construct($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
}
|
||||
|
||||
public function translations($translations)
|
||||
{
|
||||
$this->translations = $translations;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function config($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function js($js)
|
||||
{
|
||||
$this->js = $js;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(Container $container)
|
||||
{
|
||||
$manager = $container->make('flarum.localeManager');
|
||||
|
||||
if ($this->translations) {
|
||||
$manager->addTranslations($this->locale, $this->translations);
|
||||
}
|
||||
|
||||
if ($this->config) {
|
||||
$manager->addConfig($this->locale, $this->config);
|
||||
}
|
||||
|
||||
if ($this->js) {
|
||||
$manager->addJsFile($this->locale, $this->js);
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,17 +7,19 @@ class Relationship implements ExtenderInterface
|
||||
{
|
||||
protected $parent;
|
||||
|
||||
protected $type;
|
||||
|
||||
protected $name;
|
||||
|
||||
protected $type;
|
||||
|
||||
protected $child;
|
||||
|
||||
public function __construct($parent, $type, $name, $child = null)
|
||||
protected $table;
|
||||
|
||||
public function __construct($parent, $name, $type, $child = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->type = $type;
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->child = $child;
|
||||
}
|
||||
|
||||
@@ -30,6 +32,8 @@ class Relationship implements ExtenderInterface
|
||||
return call_user_func($this->type, $model);
|
||||
} elseif ($this->type === 'belongsTo') {
|
||||
return $model->belongsTo($this->child, null, null, $this->name);
|
||||
} elseif ($this->type === 'belongsToMany') {
|
||||
return $model->belongsToMany($this->child, $this->table, null, null, $this->name);
|
||||
} else {
|
||||
// @todo
|
||||
}
|
||||
|
@@ -1,10 +1,14 @@
|
||||
<?php namespace Flarum\Forum\Actions;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Assets\AssetManager;
|
||||
use Flarum\Assets\JsCompiler;
|
||||
use Flarum\Assets\LessCompiler;
|
||||
use Flarum\Core;
|
||||
use Flarum\Forum\Events\RenderView;
|
||||
use Flarum\Locale\JsCompiler as LocaleJsCompiler;
|
||||
use Flarum\Support\Actor;
|
||||
use Flarum\Support\HtmlAction;
|
||||
use Flarum\Forum\Events\RenderView;
|
||||
use Illuminate\Database\DatabaseManager;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
@@ -19,6 +23,8 @@ class IndexAction extends HtmlAction
|
||||
|
||||
protected $database;
|
||||
|
||||
public static $translations = [];
|
||||
|
||||
// TODO: DatabaseManager should be ConnectionInterface
|
||||
public function __construct(Client $apiClient, Actor $actor, DatabaseManager $database, SessionInterface $session)
|
||||
{
|
||||
@@ -35,12 +41,23 @@ class IndexAction extends HtmlAction
|
||||
$session = [];
|
||||
$alert = $this->session->get('alert');
|
||||
|
||||
$response = $this->apiClient->send('Flarum\Api\Actions\Forum\ShowAction');
|
||||
|
||||
$data = [$response->data];
|
||||
if (isset($response->included)) {
|
||||
$data = array_merge($data, $response->included);
|
||||
}
|
||||
|
||||
if (($user = $this->actor->getUser()) && $user->exists) {
|
||||
$session = [
|
||||
'userId' => $user->id,
|
||||
'token' => $request->getCookieParams()['flarum_remember'],
|
||||
];
|
||||
|
||||
// TODO: calling on the API here results in an extra query to get
|
||||
// the user + their groups, when we already have this information on
|
||||
// $this->actor. Can we simply run the CurrentUserSerializer
|
||||
// manually?
|
||||
$response = $this->apiClient->send('Flarum\Api\Actions\Users\ShowAction', ['id' => $user->id]);
|
||||
|
||||
$data = [$response->data];
|
||||
@@ -57,23 +74,63 @@ class IndexAction extends HtmlAction
|
||||
->with('session', $session)
|
||||
->with('alert', $alert);
|
||||
|
||||
$assetManager = app('flarum.forum.assetManager');
|
||||
$root = __DIR__.'/../../..';
|
||||
$assetManager->addFile([
|
||||
$root.'/js/forum/dist/app.js',
|
||||
$root.'/less/forum/app.less'
|
||||
]);
|
||||
$assetManager->addLess('
|
||||
@fl-primary-color: '.Core::config('theme_primary_color').';
|
||||
@fl-secondary-color: '.Core::config('theme_secondary_color').';
|
||||
@fl-dark-mode: '.(Core::config('theme_dark_mode') ? 'true' : 'false').';
|
||||
@fl-colored_header: '.(Core::config('theme_colored_header') ? 'true' : 'false').';
|
||||
');
|
||||
$public = public_path().'/assets';
|
||||
|
||||
event(new RenderView($view, $assetManager, $this));
|
||||
$assets = new AssetManager(
|
||||
new JsCompiler($public, 'forum.js'),
|
||||
new LessCompiler($public, 'forum.css')
|
||||
);
|
||||
|
||||
$assets->addFile($root.'/js/forum/dist/app.js');
|
||||
$assets->addFile($root.'/less/forum/app.less');
|
||||
|
||||
$variables = [
|
||||
'fl-primary-color' => Core::config('theme_primary_color', '#000'),
|
||||
'fl-secondary-color' => Core::config('theme_secondary_color', '#000'),
|
||||
'fl-dark-mode' => Core::config('theme_dark_mode') ? 'true' : 'false',
|
||||
'fl-colored-header' => Core::config('theme_colored_header') ? 'true' : 'false'
|
||||
];
|
||||
foreach ($variables as $name => $value) {
|
||||
$assets->addLess("@$name: $value;");
|
||||
}
|
||||
|
||||
$locale = $user->locale ?: Core::config('locale', 'en');
|
||||
|
||||
$localeManager = app('flarum.localeManager');
|
||||
$translations = $localeManager->getTranslations($locale);
|
||||
$jsFiles = $localeManager->getJsFiles($locale);
|
||||
|
||||
$localeCompiler = new LocaleJsCompiler($public, 'locale-'.$locale.'.js');
|
||||
$localeCompiler->setTranslations(static::filterTranslations($translations));
|
||||
array_walk($jsFiles, [$localeCompiler, 'addFile']);
|
||||
|
||||
event(new RenderView($view, $assets, $this));
|
||||
|
||||
return $view
|
||||
->with('styles', $assetManager->getCSSFiles())
|
||||
->with('scripts', $assetManager->getJSFiles());
|
||||
->with('styles', [$assets->getCssFile()])
|
||||
->with('scripts', [$assets->getJsFile(), $localeCompiler->getFile()]);
|
||||
}
|
||||
|
||||
protected static function filterTranslations($translations)
|
||||
{
|
||||
$filtered = [];
|
||||
|
||||
foreach (static::$translations as $key) {
|
||||
$parts = explode('.', $key);
|
||||
$level = &$filtered;
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (! isset($level[$part])) {
|
||||
$level[$part] = [];
|
||||
}
|
||||
|
||||
$level = &$level[$part];
|
||||
}
|
||||
|
||||
$level = array_get($translations, $key);
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
<?php namespace Flarum\Forum;
|
||||
|
||||
use Flarum\Extend\ForumTranslations;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Support\AssetManager;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Flarum\Support\ServiceProvider;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class ForumServiceProvider extends ServiceProvider
|
||||
@@ -43,6 +44,12 @@ class ForumServiceProvider extends ServiceProvider
|
||||
]);
|
||||
|
||||
$this->routes();
|
||||
|
||||
$this->extend(
|
||||
new ForumTranslations([
|
||||
//
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
protected function routes()
|
||||
|
24
src/Locale/JsCompiler.php
Normal file
24
src/Locale/JsCompiler.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php namespace Flarum\Locale;
|
||||
|
||||
use Flarum\Assets\RevisionCompiler;
|
||||
|
||||
class JsCompiler extends RevisionCompiler
|
||||
{
|
||||
protected $translations = [];
|
||||
|
||||
public function setTranslations(array $translations)
|
||||
{
|
||||
$this->translations = $translations;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
$output = "var app = require('flarum/app')['default']; app.translator.translations = ".json_encode($this->translations).";";
|
||||
|
||||
foreach ($this->files as $filename) {
|
||||
$output .= file_get_contents($filename);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
65
src/Locale/LocaleManager.php
Normal file
65
src/Locale/LocaleManager.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php namespace Flarum\Locale;
|
||||
|
||||
class LocaleManager
|
||||
{
|
||||
protected $translations = [];
|
||||
|
||||
protected $js = [];
|
||||
|
||||
protected $config = [];
|
||||
|
||||
public function addTranslations($locale, $translations)
|
||||
{
|
||||
if (! isset($this->translations[$locale])) {
|
||||
$this->translations[$locale] = [];
|
||||
}
|
||||
|
||||
$this->translations[$locale][] = $translations;
|
||||
}
|
||||
|
||||
public function addJsFile($locale, $js)
|
||||
{
|
||||
if (! isset($this->js[$locale])) {
|
||||
$this->js[$locale] = [];
|
||||
}
|
||||
|
||||
$this->js[$locale][] = $js;
|
||||
}
|
||||
|
||||
public function addConfig($locale, $config)
|
||||
{
|
||||
if (! isset($this->config[$locale])) {
|
||||
$this->config[$locale] = [];
|
||||
}
|
||||
|
||||
$this->config[$locale][] = $config;
|
||||
}
|
||||
|
||||
public function getTranslations($locale)
|
||||
{
|
||||
$files = array_get($this->translations, $locale, []);
|
||||
|
||||
$parts = explode('-', $locale);
|
||||
|
||||
if (count($parts) > 1) {
|
||||
$files = array_merge(array_get($this->translations, $parts[0], []), $files);
|
||||
}
|
||||
|
||||
$compiler = new TranslationCompiler($locale, $files);
|
||||
|
||||
return $compiler->getTranslations();
|
||||
}
|
||||
|
||||
public function getJsFiles($locale)
|
||||
{
|
||||
$files = array_get($this->js, $locale, []);
|
||||
|
||||
$parts = explode('-', $locale);
|
||||
|
||||
if (count($parts) > 1) {
|
||||
$files = array_merge(array_get($this->js, $parts[0], []), $files);
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
}
|
29
src/Locale/TranslationCompiler.php
Normal file
29
src/Locale/TranslationCompiler.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php namespace Flarum\Locale;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class TranslationCompiler
|
||||
{
|
||||
protected $locale;
|
||||
|
||||
protected $filenames;
|
||||
|
||||
public function __construct($locale, array $filenames)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
$this->filenames = $filenames;
|
||||
}
|
||||
|
||||
public function getTranslations()
|
||||
{
|
||||
// @todo caching
|
||||
|
||||
$translations = [];
|
||||
|
||||
foreach ($this->filenames as $filename) {
|
||||
$translations = array_replace_recursive($translations, Yaml::parse(file_get_contents($filename)));
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
}
|
42
src/Locale/Translator.php
Normal file
42
src/Locale/Translator.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php namespace Flarum\Locale;
|
||||
|
||||
use Closure;
|
||||
|
||||
class Translator
|
||||
{
|
||||
protected $translations;
|
||||
|
||||
protected $plural;
|
||||
|
||||
public function __construct(array $translations, Closure $plural)
|
||||
{
|
||||
$this->translations = $translations;
|
||||
$this->plural = $plural;
|
||||
}
|
||||
|
||||
public function plural($count)
|
||||
{
|
||||
$callback = $this->plural;
|
||||
|
||||
return $callback($count);
|
||||
}
|
||||
|
||||
public function translate($key, array $input = [])
|
||||
{
|
||||
$translation = array_get($this->translations, $key);
|
||||
|
||||
if (is_array($translation) && isset($input['count'])) {
|
||||
$translation = $translation[$this->plural($input['count'])];
|
||||
}
|
||||
|
||||
if (is_string($translation)) {
|
||||
foreach ($input as $k => $v) {
|
||||
$translation = str_replace('{'.$k.'}', $v, $translation);
|
||||
}
|
||||
|
||||
return $translation;
|
||||
} else {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,166 +0,0 @@
|
||||
<?php namespace Flarum\Support;
|
||||
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Str;
|
||||
use Cache;
|
||||
use Less_Parser;
|
||||
use Closure;
|
||||
|
||||
class AssetManager
|
||||
{
|
||||
protected $files = [
|
||||
'css' => [],
|
||||
'js' => [],
|
||||
'less' => []
|
||||
];
|
||||
|
||||
protected $less = [];
|
||||
|
||||
protected $publicPath;
|
||||
|
||||
protected $name;
|
||||
|
||||
protected $storage;
|
||||
|
||||
public function __construct(Filesystem $storage, $publicPath, $name)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
$this->publicPath = $publicPath;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function addFile($files)
|
||||
{
|
||||
foreach ((array) $files as $file) {
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
$this->files[$ext][] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
public function addLess($strings)
|
||||
{
|
||||
foreach ((array) $strings as $string) {
|
||||
$this->less[] = $string;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getAssetDirectory()
|
||||
{
|
||||
$dir = $this->publicPath;
|
||||
if (! $this->storage->isDirectory($dir)) {
|
||||
$this->storage->makeDirectory($dir);
|
||||
}
|
||||
return $dir;
|
||||
}
|
||||
|
||||
protected function getRevisionFile()
|
||||
{
|
||||
return $this->getAssetDirectory().'/'.$this->name;
|
||||
}
|
||||
|
||||
protected function getRevision()
|
||||
{
|
||||
if (file_exists($file = $this->getRevisionFile())) {
|
||||
return file_get_contents($file);
|
||||
}
|
||||
}
|
||||
|
||||
protected function putRevision($revision)
|
||||
{
|
||||
return file_put_contents($this->getRevisionFile(), $revision);
|
||||
}
|
||||
|
||||
protected function getFiles($type, Closure $callback)
|
||||
{
|
||||
$dir = $this->getAssetDirectory();
|
||||
|
||||
if (! ($revision = $this->getRevision())) {
|
||||
$revision = Str::quickRandom();
|
||||
$this->putRevision($revision);
|
||||
}
|
||||
|
||||
$lastModTime = 0;
|
||||
foreach ($this->files[$type] as $file) {
|
||||
$lastModTime = max($lastModTime, filemtime($file));
|
||||
}
|
||||
$debug = 0;
|
||||
// $debug = 1;
|
||||
|
||||
if (! file_exists($file = $dir.'/'.$this->name.'-'.$revision.'.'.$type)
|
||||
|| filemtime($file) < $lastModTime
|
||||
|| $debug) {
|
||||
$this->storage->put($file, $callback());
|
||||
}
|
||||
|
||||
return [$file];
|
||||
}
|
||||
|
||||
public function clearCache()
|
||||
{
|
||||
if ($revision = $this->getRevision()) {
|
||||
$dir = $this->getAssetDirectory();
|
||||
foreach (['css', 'js'] as $type) {
|
||||
@unlink($dir.'/'.$this->name.'-'.$revision.'.'.$type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getCSSFiles()
|
||||
{
|
||||
return $this->getFiles('css', function () {
|
||||
return $this->compileCSS();
|
||||
});
|
||||
}
|
||||
|
||||
public function getJSFiles()
|
||||
{
|
||||
return $this->getFiles('js', function () {
|
||||
return $this->compileJS();
|
||||
});
|
||||
}
|
||||
|
||||
public function compileLess()
|
||||
{
|
||||
ini_set('xdebug.max_nesting_level', 200);
|
||||
|
||||
$parser = new Less_Parser(['compress' => true, 'cache_dir' => storage_path().'/less']);
|
||||
|
||||
$css = [];
|
||||
$dir = $this->getAssetDirectory();
|
||||
foreach ($this->files['less'] as $file) {
|
||||
$parser->parseFile($file);
|
||||
}
|
||||
|
||||
foreach ($this->less as $less) {
|
||||
$parser->parse($less);
|
||||
}
|
||||
|
||||
return $parser->getCss();
|
||||
}
|
||||
|
||||
public function compileCSS()
|
||||
{
|
||||
$css = $this->compileLess();
|
||||
|
||||
foreach ($this->files['css'] as $file) {
|
||||
$css .= $this->storage->get($file);
|
||||
}
|
||||
|
||||
// minify
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
public function compileJS()
|
||||
{
|
||||
$js = '';
|
||||
|
||||
foreach ($this->files['js'] as $file) {
|
||||
$js .= $this->storage->get($file).';';
|
||||
}
|
||||
|
||||
// minify
|
||||
|
||||
return $js;
|
||||
}
|
||||
}
|
@@ -21,11 +21,12 @@ class ExtensionsServiceProvider extends ServiceProvider
|
||||
$providers = [];
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
if (file_exists($file = base_path().'/extensions/'.$extension.'/bootstrap.php')) {
|
||||
if (file_exists($file = public_path().'/extensions/'.$extension.'/bootstrap.php') ||
|
||||
file_exists($file = base_path().'/extensions/'.$extension.'/bootstrap.php')) {
|
||||
$providers[$extension] = require $file;
|
||||
}
|
||||
}
|
||||
|
||||
// @todo store $providers somewhere so that extensions can talk to each other
|
||||
// @todo store $providers somewhere (in Core?) so that extensions can talk to each other
|
||||
}
|
||||
}
|
||||
|
@@ -17,8 +17,14 @@ class ServiceProvider extends IlluminateServiceProvider
|
||||
|
||||
public function extend()
|
||||
{
|
||||
foreach (func_get_args() as $extender) {
|
||||
$extender->extend($this->app);
|
||||
// @todo don't support func_get_args
|
||||
foreach (func_get_args() as $extenders) {
|
||||
if (! is_array($extenders)) {
|
||||
$extenders = [$extenders];
|
||||
}
|
||||
foreach ($extenders as $extender) {
|
||||
$extender->extend($this->app);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user