Unverified Commit b8ad9fe5 authored by daniel's avatar daniel Committed by GitHub
Browse files

Merge pull request #1136 from pixelfed/frontend-ui-refactor

v0.9.0
parents 9f5d31ec 7b9b6b27
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Redis;
class Installer extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'install';
/**
* The console command description.
*
* @var string
*/
protected $description = 'CLI Installer';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->welcome();
}
protected function welcome()
{
$this->info(' ____ _ ______ __ ');
$this->info(' / __ \(_) _____ / / __/__ ____/ / ');
$this->info(' / /_/ / / |/_/ _ \/ / /_/ _ \/ __ / ');
$this->info(' / ____/ /> </ __/ / __/ __/ /_/ / ');
$this->info(' /_/ /_/_/|_|\___/_/_/ \___/\__,_/ ');
$this->info(' ');
$this->info(' Welcome to the Pixelfed Installer!');
$this->info(' ');
$this->info(' ');
$this->info('Pixelfed version: ' . config('pixelfed.version'));
$this->line(' ');
$this->info('Scanning system...');
$this->preflightCheck();
}
protected function preflightCheck()
{
$this->line(' ');
$this->info('Checking for installed dependencies...');
$redis = Redis::connection();
if($redis->ping()) {
$this->info('- Found redis!');
} else {
$this->error('- Redis not found, aborting installation');
exit;
}
$this->checkPhpDependencies();
$this->checkPermissions();
$this->envCheck();
}
protected function checkPhpDependencies()
{
$extensions = [
'bcmath',
'ctype',
'curl',
'json',
'mbstring',
'openssl'
];
$this->line('');
$this->info('Checking for required php extensions...');
foreach($extensions as $ext) {
if(extension_loaded($ext) == false) {
$this->error("- {$ext} extension not found, aborting installation");
exit;
} else {
$this->info("- {$ext} extension found!");
}
}
}
protected function checkPermissions()
{
$this->line('');
$this->info('Checking for proper filesystem permissions...');
$paths = [
base_path('bootstrap'),
base_path('storage')
];
foreach($paths as $path) {
if(is_writeable($path) == false) {
$this->error("- Invalid permission found! Aborting installation.");
$this->error(" Please make the following path writeable by the web server:");
$this->error(" $path");
exit;
} else {
$this->info("- Found valid permissions for {$path}");
}
}
}
protected function envCheck()
{
if(!file_exists(base_path('.env'))) {
$this->line('');
$this->info('No .env configuration file found. We will create one now!');
$this->createEnv();
} else {
$confirm = $this->confirm('Found .env file, do you want to overwrite it?');
if(!$confirm) {
$this->info('Cancelling installation.');
exit;
}
$confirm = $this->confirm('Are you really sure you want to overwrite it?');
if(!$confirm) {
$this->info('Cancelling installation.');
exit;
}
$this->error('Warning ... if you did not backup your .env before its overwritten it will be permanently deleted.');
$confirm = $this->confirm('The application may be installed already, are you really sure you want to overwrite it?');
if(!$confirm) {
$this->info('Cancelling installation.');
exit;
}
}
$this->postInstall();
}
protected function createEnv()
{
$this->line('');
// copy env
$name = $this->ask('Site name [ex: Pixelfed]');
$domain = $this->ask('Site Domain [ex: pixelfed.com]');
$tls = $this->choice('Use HTTPS/TLS?', ['https', 'http'], 0);
$dbDrive = $this->choice('Select database driver', ['mysql', 'pgsql'/*, 'sqlite', 'sqlsrv'*/], 0);
$ws = $this->choice('Select cache driver', ["apc", "array", "database", "file", "memcached", "redis"], 5);
}
protected function postInstall()
{
$this->callSilent('config:cache');
//$this->call('route:cache');
$this->info('Pixelfed has been successfully installed!');
}
}
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use App\User;
class NewMention implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
protected $user;
protected $data;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(User $user, $data)
{
$this->user = $user;
$this->data = $data;
}
public function broadcastAs()
{
return 'notification.new.mention';
}
public function broadcastOn()
{
return new PrivateChannel('App.User.' . $this->user->id);
}
public function broadcastWith()
{
return ['id' => $this->user->id];
}
public function via()
{
return 'broadcast';
}
}
<?php
namespace App\Events\Notification;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use App\Status;
use App\Transformer\Api\StatusTransformer;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
class NewPublicPost implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
protected $status;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Status $status)
{
$this->status = $status;
}
public function broadcastAs()
{
return 'status';
}
public function broadcastOn()
{
return new Channel('firehost.public');
}
public function broadcastWith()
{
$resource = new Fractal\Resource\Item($this->status, new StatusTransformer());
$res = $this->fractal->createData($resource)->toArray();
return [
'entity' => $res
];
}
public function via()
{
return 'broadcast';
}
}
......@@ -128,7 +128,7 @@ class AccountController extends Controller
}
}
public function fetchNotifications($id)
public function fetchNotifications(int $id)
{
$key = config('cache.prefix').":user.{$id}.notifications";
$redis = Redis::connection();
......@@ -167,14 +167,14 @@ class AccountController extends Controller
public function mute(Request $request)
{
$this->validate($request, [
'type' => 'required|string',
'type' => 'required|alpha_dash',
'item' => 'required|integer|min:1',
]);
$user = Auth::user()->profile;
$type = $request->input('type');
$item = $request->input('item');
$action = "{$type}.mute";
$action = $type . '.mute';
if (!in_array($action, $this->filters)) {
return abort(406);
......@@ -211,17 +211,71 @@ class AccountController extends Controller
return redirect()->back();
}
public function unmute(Request $request)
{
$this->validate($request, [
'type' => 'required|alpha_dash',
'item' => 'required|integer|min:1',
]);
$user = Auth::user()->profile;
$type = $request->input('type');
$item = $request->input('item');
$action = $type . '.mute';
if (!in_array($action, $this->filters)) {
return abort(406);
}
$filterable = [];
switch ($type) {
case 'user':
$profile = Profile::findOrFail($item);
if ($profile->id == $user->id) {
return abort(403);
}
$class = get_class($profile);
$filterable['id'] = $profile->id;
$filterable['type'] = $class;
break;
default:
abort(400);
break;
}
$filter = UserFilter::whereUserId($user->id)
->whereFilterableId($filterable['id'])
->whereFilterableType($filterable['type'])
->whereFilterType('mute')
->first();
if($filter) {
$filter->delete();
}
$pid = $user->id;
Cache::forget("user:filter:list:$pid");
Cache::forget("feature:discover:people:$pid");
Cache::forget("feature:discover:posts:$pid");
if($request->wantsJson()) {
return response()->json([200]);
} else {
return redirect()->back();
}
}
public function block(Request $request)
{
$this->validate($request, [
'type' => 'required|string',
'type' => 'required|alpha_dash',
'item' => 'required|integer|min:1',
]);
$user = Auth::user()->profile;
$type = $request->input('type');
$item = $request->input('item');
$action = "{$type}.block";
$action = $type.'.block';
if (!in_array($action, $this->filters)) {
return abort(406);
}
......@@ -259,6 +313,56 @@ class AccountController extends Controller
return redirect()->back();
}
public function unblock(Request $request)
{
$this->validate($request, [
'type' => 'required|alpha_dash',
'item' => 'required|integer|min:1',
]);
$user = Auth::user()->profile;
$type = $request->input('type');
$item = $request->input('item');
$action = $type . '.block';
if (!in_array($action, $this->filters)) {
return abort(406);
}
$filterable = [];
switch ($type) {
case 'user':
$profile = Profile::findOrFail($item);
if ($profile->id == $user->id) {
return abort(403);
}
$class = get_class($profile);
$filterable['id'] = $profile->id;
$filterable['type'] = $class;
break;
default:
abort(400);
break;
}
$filter = UserFilter::whereUserId($user->id)
->whereFilterableId($filterable['id'])
->whereFilterableType($filterable['type'])
->whereFilterType('block')
->first();
if($filter) {
$filter->delete();
}
$pid = $user->id;
Cache::forget("user:filter:list:$pid");
Cache::forget("feature:discover:people:$pid");
Cache::forget("feature:discover:posts:$pid");
return redirect()->back();
}
public function followRequests(Request $request)
{
$pid = Auth::user()->profile->id;
......
......@@ -31,6 +31,10 @@ class ApiController extends BaseApiController
'media_types' => config('pixelfed.media_types'),
'enforce_account_limit' => config('pixelfed.enforce_account_limit')
],
'activitypub' => [
'enabled' => config('pixelfed.activitypub_enabled'),
'remote_follow' => config('pixelfed.remote_follow_enabled')
]
];
});
......
......@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use DB;
use Cache;
use App\Comment;
......@@ -58,14 +59,21 @@ class CommentController extends Controller
Cache::forget('transform:status:'.$status->url());
$autolink = Autolink::create()->autolink($comment);
$reply = new Status();
$reply->profile_id = $profile->id;
$reply->caption = e($comment);
$reply->rendered = $autolink;
$reply->in_reply_to_id = $status->id;
$reply->in_reply_to_profile_id = $status->profile_id;
$reply->save();
$reply = DB::transaction(function() use($comment, $status, $profile) {
$autolink = Autolink::create()->autolink($comment);
$reply = new Status();
$reply->profile_id = $profile->id;
$reply->caption = e($comment);
$reply->rendered = $autolink;
$reply->in_reply_to_id = $status->id;
$reply->in_reply_to_profile_id = $status->profile_id;
$reply->save();
$status->reply_count++;
$status->save();
return $reply;
});
NewStatusPipeline::dispatch($reply, false);
CommentPipeline::dispatch($status, $reply);
......
......@@ -82,9 +82,10 @@ trait Instagram
->whereStage(1)
->firstOrFail();
$limit = config('pixelfed.import.instagram.limits.posts');
foreach ($media as $k => $v) {
$original = $v->getClientOriginalName();
if(strlen($original) < 32 || $k > 100) {
if(strlen($original) < 32 || $k > $limit) {
continue;
}
$storagePath = "import/{$job->uuid}";
......@@ -105,7 +106,6 @@ trait Instagram
$job->save();
});
return redirect($job->url());
return view('settings.import.instagram.step-one', compact('profile', 'job'));
}
public function instagramStepTwo(Request $request, $uuid)
......@@ -148,6 +148,7 @@ trait Instagram
{
$profile = Auth::user()->profile;
$job = ImportJob::whereProfileId($profile->id)
->whereService('instagram')
->whereNull('completed_at')
->whereUuid($uuid)
->whereStage(3)
......@@ -159,14 +160,21 @@ trait Instagram
{
$profile = Auth::user()->profile;
$job = ImportJob::whereProfileId($profile->id)
try {
$import = ImportJob::whereProfileId($profile->id)
->where('uuid', $uuid)
->whereNotNull('media_json')
->whereNull('completed_at')
->whereUuid($uuid)
->whereStage(3)
->firstOrFail();
ImportInstagram::dispatch($import);
} catch (Exception $e) {
\Log::info($e);
}
ImportInstagram::dispatchNow($job);
return redirect($profile->url());
return redirect(route('settings'))->with(['status' => [
'Import successful! It may take a few minutes to finish.'
]]);
}
}
......@@ -108,6 +108,7 @@ class PublicApiController extends Controller
'max_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
'limit' => 'nullable|integer|min:5|max:50'
]);
$limit = $request->limit ?? 10;
$profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
$status = Status::whereProfileId($profile->id)->whereCommentsDisabled(false)->findOrFail($postId);
......@@ -116,7 +117,7 @@ class PublicApiController extends Controller
if($request->filled('min_id')) {
$replies = $status->comments()
->whereNull('reblog_of_id')
->select('id', 'caption', 'rendered', 'profile_id', 'in_reply_to_id', 'created_at')
->select('id', 'caption', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
->where('id', '>=', $request->min_id)
->orderBy('id', 'desc')
->paginate($limit);
......@@ -124,7 +125,7 @@ class PublicApiController extends Controller
if($request->filled('max_id')) {
$replies = $status->comments()
->whereNull('reblog_of_id')
->select('id', 'caption', 'rendered', 'profile_id', 'in_reply_to_id', 'created_at')
->select('id', 'caption', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
->where('id', '<=', $request->max_id)
->orderBy('id', 'desc')
->paginate($limit);
......@@ -132,7 +133,7 @@ class PublicApiController extends Controller
} else {
$replies = $status->comments()
->whereNull('reblog_of_id')
->select('id', 'caption', 'rendered', 'profile_id', 'in_reply_to_id', 'created_at')
->select('id', 'caption', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
->orderBy('id', 'desc')
->paginate($limit);
}
......@@ -180,8 +181,8 @@ class PublicApiController extends Controller
if(!$user) {
abort(403);
} else {
$follows = $profile->followedBy(Auth::user()->profile);
if($follows == false && $profile->id !== $user->profile->id) {
$follows = $profile->followedBy($user->profile);
if($follows == false && $profile->id !== $user->profile->id && $user->is_admin == false) {
abort(404);
}
}
......@@ -357,8 +358,6 @@ class PublicApiController extends Controller
'created_at',
'updated_at'
)->whereIn('type', ['photo', 'photo:album', 'video', 'video:album'])
->whereLocal(true)
->whereNull('uri')
->where('id', $dir, $id)
->whereIn('profile_id', $following)
->whereNotIn('profile_id', $filtered)
......@@ -386,8 +385,6 @@ class PublicApiController extends Controller
'created_at',
'updated_at'
)->whereIn('type', ['photo', 'photo:album', 'video', 'video:album'])
->whereLocal(true)
->whereNull('uri')
->whereIn('profile_id', $following)
->whereNotIn('profile_id', $filtered)
->whereNull('in_reply_to_id')
......@@ -453,14 +450,18 @@ class PublicApiController extends Controller
'is_nsfw',
'scope',
'local',
'reply_count',
'comments_disabled',
'created_at',