mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-21 22:11:26 +00:00
Apply fixes from StyleCI
This commit is contained in:
parent
75de27f482
commit
b8abbdd90f
261 changed files with 3368 additions and 3231 deletions
|
@ -8,11 +8,11 @@ class Comment extends Model
|
||||||
{
|
{
|
||||||
public function profile()
|
public function profile()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class);
|
return $this->belongsTo(Profile::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function status()
|
public function status()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Status::class);
|
return $this->belongsTo(Status::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use App\Media;
|
|
||||||
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
||||||
|
use App\Media;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class CatchUnoptimizedMedia extends Command
|
class CatchUnoptimizedMedia extends Command
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,7 @@ class CatchUnoptimizedMedia extends Command
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$medias = Media::whereNull('processed_at')->take(50)->get();
|
$medias = Media::whereNull('processed_at')->take(50)->get();
|
||||||
foreach($medias as $media) {
|
foreach ($medias as $media) {
|
||||||
ImageOptimize::dispatch($media);
|
ImageOptimize::dispatch($media);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\{Follower, Profile};
|
use App\Follower;
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use App\Jobs\FollowPipeline\FollowPipeline;
|
use App\Jobs\FollowPipeline\FollowPipeline;
|
||||||
|
use App\Profile;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class SeedFollows extends Command
|
class SeedFollows extends Command
|
||||||
{
|
{
|
||||||
|
@ -41,12 +42,12 @@ class SeedFollows extends Command
|
||||||
{
|
{
|
||||||
$limit = 10000;
|
$limit = 10000;
|
||||||
|
|
||||||
for ($i=0; $i < $limit; $i++) {
|
for ($i = 0; $i < $limit; $i++) {
|
||||||
try {
|
try {
|
||||||
$actor = Profile::inRandomOrder()->firstOrFail();
|
$actor = Profile::inRandomOrder()->firstOrFail();
|
||||||
$target = Profile::inRandomOrder()->firstOrFail();
|
$target = Profile::inRandomOrder()->firstOrFail();
|
||||||
|
|
||||||
$follow = new Follower;
|
$follow = new Follower();
|
||||||
$follow->profile_id = $actor->id;
|
$follow->profile_id = $actor->id;
|
||||||
$follow->following_id = $target->id;
|
$follow->following_id = $target->id;
|
||||||
$follow->save();
|
$follow->save();
|
||||||
|
|
|
@ -19,7 +19,8 @@ class Kernel extends ConsoleKernel
|
||||||
/**
|
/**
|
||||||
* Define the application's command schedule.
|
* Define the application's command schedule.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function schedule(Schedule $schedule)
|
protected function schedule(Schedule $schedule)
|
||||||
|
|
|
@ -8,8 +8,9 @@ class EmailVerification extends Model
|
||||||
{
|
{
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
$base = config('app.url');
|
$base = config('app.url');
|
||||||
$path = '/i/confirm-email/' . $this->user_token . '/' . $this->random_token;
|
$path = '/i/confirm-email/'.$this->user_token.'/'.$this->random_token;
|
||||||
return "{$base}{$path}";
|
|
||||||
|
return "{$base}{$path}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,11 @@
|
||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
use App\User;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use App\UserSetting;
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use App\{User, UserSetting};
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class AuthLoginEvent
|
class AuthLoginEvent
|
||||||
{
|
{
|
||||||
|
@ -27,8 +24,8 @@ class AuthLoginEvent
|
||||||
|
|
||||||
public function handle(User $user)
|
public function handle(User $user)
|
||||||
{
|
{
|
||||||
if(empty($user->settings)) {
|
if (empty($user->settings)) {
|
||||||
$settings = new UserSetting;
|
$settings = new UserSetting();
|
||||||
$settings->user_id = $user->id;
|
$settings->user_id = $user->id;
|
||||||
$settings->save();
|
$settings->save();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ class Handler extends ExceptionHandler
|
||||||
/**
|
/**
|
||||||
* Report or log an exception.
|
* Report or log an exception.
|
||||||
*
|
*
|
||||||
* @param \Exception $exception
|
* @param \Exception $exception
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function report(Exception $exception)
|
public function report(Exception $exception)
|
||||||
|
@ -40,8 +41,9 @@ class Handler extends ExceptionHandler
|
||||||
/**
|
/**
|
||||||
* Render an exception into an HTTP response.
|
* Render an exception into an HTTP response.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param \Exception $exception
|
* @param \Exception $exception
|
||||||
|
*
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function render($request, Exception $exception)
|
public function render($request, Exception $exception)
|
||||||
|
|
|
@ -8,30 +8,32 @@ class Follower extends Model
|
||||||
{
|
{
|
||||||
public function actor()
|
public function actor()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function target()
|
public function target()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'following_id', 'id');
|
return $this->belongsTo(Profile::class, 'following_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function profile()
|
public function profile()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'following_id', 'id');
|
return $this->belongsTo(Profile::class, 'following_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toText()
|
public function toText()
|
||||||
{
|
{
|
||||||
$actorName = $this->actor->username;
|
$actorName = $this->actor->username;
|
||||||
return "{$actorName} " . __('notification.startedFollowingYou');
|
|
||||||
|
return "{$actorName} ".__('notification.startedFollowingYou');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toHtml()
|
public function toHtml()
|
||||||
{
|
{
|
||||||
$actorName = $this->actor->username;
|
$actorName = $this->actor->username;
|
||||||
$actorUrl = $this->actor->url();
|
$actorUrl = $this->actor->url();
|
||||||
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> " .
|
|
||||||
|
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
|
||||||
__('notification.startedFollowingYou');
|
__('notification.startedFollowingYou');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,11 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Hashtag extends Model
|
class Hashtag extends Model
|
||||||
{
|
{
|
||||||
public $fillable = ['name','slug'];
|
public $fillable = ['name', 'slug'];
|
||||||
|
|
||||||
public function posts()
|
public function posts()
|
||||||
{
|
{
|
||||||
return $this->hasManyThrough(
|
return $this->hasManyThrough(
|
||||||
Status::class,
|
Status::class,
|
||||||
StatusHashtag::class,
|
StatusHashtag::class,
|
||||||
'hashtag_id',
|
'hashtag_id',
|
||||||
|
@ -22,7 +22,6 @@ class Hashtag extends Model
|
||||||
|
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
return config('routes.hashtag.base') . $this->slug;
|
return config('routes.hashtag.base').$this->slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,77 +2,78 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use App\EmailVerification;
|
||||||
use Carbon\Carbon;
|
|
||||||
use App\Mail\ConfirmEmail;
|
use App\Mail\ConfirmEmail;
|
||||||
use Auth, DB, Cache, Mail, Redis;
|
use App\Notification;
|
||||||
use App\{
|
use App\Profile;
|
||||||
EmailVerification,
|
use App\User;
|
||||||
Notification,
|
use App\UserFilter;
|
||||||
Profile,
|
use Auth;
|
||||||
User,
|
use Cache;
|
||||||
UserFilter
|
use Carbon\Carbon;
|
||||||
};
|
use Illuminate\Http\Request;
|
||||||
|
use Mail;
|
||||||
|
use Redis;
|
||||||
|
|
||||||
class AccountController extends Controller
|
class AccountController extends Controller
|
||||||
{
|
{
|
||||||
protected $filters = [
|
protected $filters = [
|
||||||
'user.mute',
|
'user.mute',
|
||||||
'user.block'
|
'user.block',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function notifications(Request $request)
|
public function notifications(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'page' => 'nullable|min:1|max:3',
|
'page' => 'nullable|min:1|max:3',
|
||||||
'a' => 'nullable|alpha_dash',
|
'a' => 'nullable|alpha_dash',
|
||||||
]);
|
]);
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$action = $request->input('a');
|
$action = $request->input('a');
|
||||||
$timeago = Carbon::now()->subMonths(6);
|
$timeago = Carbon::now()->subMonths(6);
|
||||||
if($action && in_array($action, ['comment', 'follow', 'mention'])) {
|
if ($action && in_array($action, ['comment', 'follow', 'mention'])) {
|
||||||
$notifications = Notification::whereProfileId($profile->id)
|
$notifications = Notification::whereProfileId($profile->id)
|
||||||
->whereAction($action)
|
->whereAction($action)
|
||||||
->whereDate('created_at', '>', $timeago)
|
->whereDate('created_at', '>', $timeago)
|
||||||
->orderBy('id','desc')
|
->orderBy('id', 'desc')
|
||||||
->simplePaginate(30);
|
->simplePaginate(30);
|
||||||
} else {
|
} else {
|
||||||
$notifications = Notification::whereProfileId($profile->id)
|
$notifications = Notification::whereProfileId($profile->id)
|
||||||
->whereDate('created_at', '>', $timeago)
|
->whereDate('created_at', '>', $timeago)
|
||||||
->orderBy('id','desc')
|
->orderBy('id', 'desc')
|
||||||
->simplePaginate(30);
|
->simplePaginate(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('account.activity', compact('profile', 'notifications'));
|
return view('account.activity', compact('profile', 'notifications'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function followingActivity(Request $request)
|
public function followingActivity(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'page' => 'nullable|min:1|max:3',
|
'page' => 'nullable|min:1|max:3',
|
||||||
'a' => 'nullable|alpha_dash',
|
'a' => 'nullable|alpha_dash',
|
||||||
]);
|
]);
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$action = $request->input('a');
|
$action = $request->input('a');
|
||||||
$timeago = Carbon::now()->subMonths(1);
|
$timeago = Carbon::now()->subMonths(1);
|
||||||
$following = $profile->following->pluck('id');
|
$following = $profile->following->pluck('id');
|
||||||
$notifications = Notification::whereIn('actor_id', $following)
|
$notifications = Notification::whereIn('actor_id', $following)
|
||||||
->where('profile_id', '!=', $profile->id)
|
->where('profile_id', '!=', $profile->id)
|
||||||
->whereDate('created_at', '>', $timeago)
|
->whereDate('created_at', '>', $timeago)
|
||||||
->orderBy('notifications.id','desc')
|
->orderBy('notifications.id', 'desc')
|
||||||
->simplePaginate(30);
|
->simplePaginate(30);
|
||||||
|
|
||||||
return view('account.following', compact('profile', 'notifications'));
|
return view('account.following', compact('profile', 'notifications'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verifyEmail(Request $request)
|
public function verifyEmail(Request $request)
|
||||||
{
|
{
|
||||||
return view('account.verify_email');
|
return view('account.verify_email');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendVerifyEmail(Request $request)
|
public function sendVerifyEmail(Request $request)
|
||||||
|
@ -82,19 +83,18 @@ class AccountController extends Controller
|
||||||
->where('created_at', '>', $timeLimit)->count();
|
->where('created_at', '>', $timeLimit)->count();
|
||||||
$exists = EmailVerification::whereUserId(Auth::id())->count();
|
$exists = EmailVerification::whereUserId(Auth::id())->count();
|
||||||
|
|
||||||
if($recentAttempt == 1 && $exists == 1) {
|
if ($recentAttempt == 1 && $exists == 1) {
|
||||||
return redirect()->back()->with('error', 'A verification email has already been sent recently. Please check your email, or try again later.');
|
return redirect()->back()->with('error', 'A verification email has already been sent recently. Please check your email, or try again later.');
|
||||||
} elseif ($recentAttempt == 0 && $exists !== 0) {
|
} elseif ($recentAttempt == 0 && $exists !== 0) {
|
||||||
// Delete old verification and send new one.
|
// Delete old verification and send new one.
|
||||||
EmailVerification::whereUserId(Auth::id())->delete();
|
EmailVerification::whereUserId(Auth::id())->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$user = User::whereNull('email_verified_at')->find(Auth::id());
|
$user = User::whereNull('email_verified_at')->find(Auth::id());
|
||||||
$utoken = hash('sha512', $user->id);
|
$utoken = hash('sha512', $user->id);
|
||||||
$rtoken = str_random(40);
|
$rtoken = str_random(40);
|
||||||
|
|
||||||
$verify = new EmailVerification;
|
$verify = new EmailVerification();
|
||||||
$verify->user_id = $user->id;
|
$verify->user_id = $user->id;
|
||||||
$verify->email = $user->email;
|
$verify->email = $user->email;
|
||||||
$verify->user_token = $utoken;
|
$verify->user_token = $utoken;
|
||||||
|
@ -112,55 +112,56 @@ class AccountController extends Controller
|
||||||
->where('random_token', $randomToken)
|
->where('random_token', $randomToken)
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
if(Auth::id() === $verify->user_id) {
|
if (Auth::id() === $verify->user_id) {
|
||||||
$user = User::find(Auth::id());
|
$user = User::find(Auth::id());
|
||||||
$user->email_verified_at = Carbon::now();
|
$user->email_verified_at = Carbon::now();
|
||||||
$user->save();
|
$user->save();
|
||||||
return redirect('/');
|
|
||||||
|
return redirect('/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetchNotifications($id)
|
public function fetchNotifications($id)
|
||||||
{
|
{
|
||||||
$key = config('cache.prefix') . ":user.{$id}.notifications";
|
$key = config('cache.prefix').":user.{$id}.notifications";
|
||||||
$redis = Redis::connection();
|
$redis = Redis::connection();
|
||||||
$notifications = $redis->lrange($key, 0, 30);
|
$notifications = $redis->lrange($key, 0, 30);
|
||||||
if(empty($notifications)) {
|
if (empty($notifications)) {
|
||||||
$notifications = Notification::whereProfileId($id)
|
$notifications = Notification::whereProfileId($id)
|
||||||
->orderBy('id','desc')->take(30)->get();
|
->orderBy('id', 'desc')->take(30)->get();
|
||||||
} else {
|
} else {
|
||||||
$notifications = $this->hydrateNotifications($notifications);
|
$notifications = $this->hydrateNotifications($notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $notifications;
|
return $notifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hydrateNotifications($keys)
|
public function hydrateNotifications($keys)
|
||||||
{
|
{
|
||||||
$prefix = 'notification.';
|
$prefix = 'notification.';
|
||||||
$notifications = collect([]);
|
$notifications = collect([]);
|
||||||
foreach($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$notifications->push(Cache::get("{$prefix}{$key}"));
|
$notifications->push(Cache::get("{$prefix}{$key}"));
|
||||||
}
|
}
|
||||||
return $notifications;
|
|
||||||
|
return $notifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function messages()
|
public function messages()
|
||||||
{
|
{
|
||||||
return view('account.messages');
|
return view('account.messages');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function showMessage(Request $request, $id)
|
public function showMessage(Request $request, $id)
|
||||||
{
|
{
|
||||||
return view('account.message');
|
return view('account.message');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mute(Request $request)
|
public function mute(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'type' => 'required|string',
|
'type' => 'required|string',
|
||||||
'item' => 'required|integer|min:1'
|
'item' => 'required|integer|min:1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = Auth::user()->profile;
|
$user = Auth::user()->profile;
|
||||||
|
@ -168,15 +169,15 @@ class AccountController extends Controller
|
||||||
$item = $request->input('item');
|
$item = $request->input('item');
|
||||||
$action = "{$type}.mute";
|
$action = "{$type}.mute";
|
||||||
|
|
||||||
if(!in_array($action, $this->filters)) {
|
if (!in_array($action, $this->filters)) {
|
||||||
return abort(406);
|
return abort(406);
|
||||||
}
|
}
|
||||||
$filterable = [];
|
$filterable = [];
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'user':
|
case 'user':
|
||||||
$profile = Profile::findOrFail($item);
|
$profile = Profile::findOrFail($item);
|
||||||
if($profile->id == $user->id) {
|
if ($profile->id == $user->id) {
|
||||||
return abort(403);
|
return abort(403);
|
||||||
}
|
}
|
||||||
$class = get_class($profile);
|
$class = get_class($profile);
|
||||||
$filterable['id'] = $profile->id;
|
$filterable['id'] = $profile->id;
|
||||||
|
@ -184,34 +185,33 @@ class AccountController extends Controller
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
# code...
|
// code...
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$filter = UserFilter::firstOrCreate([
|
$filter = UserFilter::firstOrCreate([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'filterable_id' => $filterable['id'],
|
'filterable_id' => $filterable['id'],
|
||||||
'filterable_type' => $filterable['type'],
|
'filterable_type' => $filterable['type'],
|
||||||
'filter_type' => 'mute'
|
'filter_type' => 'mute',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function block(Request $request)
|
public function block(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'type' => 'required|string',
|
'type' => 'required|string',
|
||||||
'item' => 'required|integer|min:1'
|
'item' => 'required|integer|min:1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = Auth::user()->profile;
|
$user = Auth::user()->profile;
|
||||||
$type = $request->input('type');
|
$type = $request->input('type');
|
||||||
$item = $request->input('item');
|
$item = $request->input('item');
|
||||||
$action = "{$type}.block";
|
$action = "{$type}.block";
|
||||||
if(!in_array($action, $this->filters)) {
|
if (!in_array($action, $this->filters)) {
|
||||||
return abort(406);
|
return abort(406);
|
||||||
}
|
}
|
||||||
$filterable = [];
|
$filterable = [];
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
|
@ -223,19 +223,17 @@ class AccountController extends Controller
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
# code...
|
// code...
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$filter = UserFilter::firstOrCreate([
|
$filter = UserFilter::firstOrCreate([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'filterable_id' => $filterable['id'],
|
'filterable_id' => $filterable['id'],
|
||||||
'filterable_type' => $filterable['type'],
|
'filterable_type' => $filterable['type'],
|
||||||
'filter_type' => 'block'
|
'filter_type' => 'block',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,84 +2,83 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use App\Report;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use App\{Comment, Like, Media, Profile, Report, Status, User};
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
|
|
||||||
trait AdminReportController
|
trait AdminReportController
|
||||||
{
|
{
|
||||||
public function updateReport(Request $request, $id)
|
public function updateReport(Request $request, $id)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'action' => 'required|string'
|
'action' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$action = $request->input('action');
|
$action = $request->input('action');
|
||||||
|
|
||||||
$actions = [
|
$actions = [
|
||||||
'ignore',
|
'ignore',
|
||||||
'cw',
|
'cw',
|
||||||
'unlist',
|
'unlist',
|
||||||
'delete',
|
'delete',
|
||||||
'shadowban',
|
'shadowban',
|
||||||
'ban'
|
'ban',
|
||||||
];
|
];
|
||||||
|
|
||||||
if(!in_array($action, $actions)) {
|
if (!in_array($action, $actions)) {
|
||||||
return abort(403);
|
return abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$report = Report::findOrFail($id);
|
$report = Report::findOrFail($id);
|
||||||
|
|
||||||
$this->handleReportAction($report, $action);
|
$this->handleReportAction($report, $action);
|
||||||
|
|
||||||
return response()->json(['msg'=> 'Success']);
|
return response()->json(['msg'=> 'Success']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleReportAction(Report $report, $action)
|
public function handleReportAction(Report $report, $action)
|
||||||
{
|
{
|
||||||
$item = $report->reported();
|
$item = $report->reported();
|
||||||
$report->admin_seen = Carbon::now();
|
$report->admin_seen = Carbon::now();
|
||||||
|
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'ignore':
|
case 'ignore':
|
||||||
$report->not_interested = true;
|
$report->not_interested = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'cw':
|
case 'cw':
|
||||||
$item->is_nsfw = true;
|
$item->is_nsfw = true;
|
||||||
$item->save();
|
$item->save();
|
||||||
$report->nsfw = true;
|
$report->nsfw = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'unlist':
|
case 'unlist':
|
||||||
$item->visibility = 'unlisted';
|
$item->visibility = 'unlisted';
|
||||||
$item->save();
|
$item->save();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
// Todo: fire delete job
|
// Todo: fire delete job
|
||||||
$report->admin_seen = null;
|
$report->admin_seen = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'shadowban':
|
case 'shadowban':
|
||||||
// Todo: fire delete job
|
// Todo: fire delete job
|
||||||
$report->admin_seen = null;
|
$report->admin_seen = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ban':
|
case 'ban':
|
||||||
// Todo: fire delete job
|
// Todo: fire delete job
|
||||||
$report->admin_seen = null;
|
$report->admin_seen = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$report->admin_seen = null;
|
$report->admin_seen = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$report->save();
|
$report->save();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,43 +2,48 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Media;
|
||||||
|
use App\Status;
|
||||||
|
use App\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\{Comment, Like, Media, Profile, Status, User};
|
|
||||||
|
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
return $this->middleware('admin');
|
return $this->middleware('admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function home()
|
public function home()
|
||||||
{
|
{
|
||||||
return view('admin.home');
|
return view('admin.home');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function users(Request $request)
|
public function users(Request $request)
|
||||||
{
|
{
|
||||||
$users = User::orderBy('id', 'desc')->paginate(10);
|
$users = User::orderBy('id', 'desc')->paginate(10);
|
||||||
return view('admin.users.home', compact('users'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return view('admin.users.home', compact('users'));
|
||||||
|
}
|
||||||
|
|
||||||
public function statuses(Request $request)
|
public function statuses(Request $request)
|
||||||
{
|
{
|
||||||
$statuses = Status::orderBy('id', 'desc')->paginate(10);
|
$statuses = Status::orderBy('id', 'desc')->paginate(10);
|
||||||
return view('admin.statuses.home', compact('statuses'));
|
|
||||||
|
return view('admin.statuses.home', compact('statuses'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showStatus(Request $request, $id)
|
public function showStatus(Request $request, $id)
|
||||||
{
|
{
|
||||||
$status = Status::findOrFail($id);
|
$status = Status::findOrFail($id);
|
||||||
return view('admin.statuses.show', compact('status'));
|
|
||||||
|
return view('admin.statuses.show', compact('status'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function media(Request $request)
|
public function media(Request $request)
|
||||||
{
|
{
|
||||||
$media = Status::whereHas('media')->orderby('id', 'desc')->paginate(12);
|
$media = Status::whereHas('media')->orderby('id', 'desc')->paginate(12);
|
||||||
return view('admin.media.home', compact('media'));
|
|
||||||
|
return view('admin.media.home', compact('media'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,23 +2,17 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use Auth, Cache;
|
use App\Avatar;
|
||||||
use App\{
|
|
||||||
Avatar,
|
|
||||||
Like,
|
|
||||||
Profile,
|
|
||||||
Status
|
|
||||||
};
|
|
||||||
use League\Fractal;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Controllers\AvatarController;
|
use App\Http\Controllers\AvatarController;
|
||||||
use App\Util\Webfinger\Webfinger;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Transformer\Api\{
|
|
||||||
AccountTransformer,
|
|
||||||
StatusTransformer
|
|
||||||
};
|
|
||||||
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
||||||
|
use App\Profile;
|
||||||
|
use App\Transformer\Api\AccountTransformer;
|
||||||
|
use App\Transformer\Api\StatusTransformer;
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use League\Fractal;
|
||||||
use League\Fractal\Serializer\ArraySerializer;
|
use League\Fractal\Serializer\ArraySerializer;
|
||||||
|
|
||||||
class BaseApiController extends Controller
|
class BaseApiController extends Controller
|
||||||
|
@ -35,8 +29,9 @@ class BaseApiController extends Controller
|
||||||
public function accounts(Request $request, $id)
|
public function accounts(Request $request, $id)
|
||||||
{
|
{
|
||||||
$profile = Profile::findOrFail($id);
|
$profile = Profile::findOrFail($id);
|
||||||
$resource = new Fractal\Resource\Item($profile, new AccountTransformer);
|
$resource = new Fractal\Resource\Item($profile, new AccountTransformer());
|
||||||
$res = $this->fractal->createData($resource)->toArray();
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,8 +39,9 @@ class BaseApiController extends Controller
|
||||||
{
|
{
|
||||||
$profile = Profile::findOrFail($id);
|
$profile = Profile::findOrFail($id);
|
||||||
$followers = $profile->followers;
|
$followers = $profile->followers;
|
||||||
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer);
|
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer());
|
||||||
$res = $this->fractal->createData($resource)->toArray();
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +49,9 @@ class BaseApiController extends Controller
|
||||||
{
|
{
|
||||||
$profile = Profile::findOrFail($id);
|
$profile = Profile::findOrFail($id);
|
||||||
$following = $profile->following;
|
$following = $profile->following;
|
||||||
$resource = new Fractal\Resource\Collection($following, new AccountTransformer);
|
$resource = new Fractal\Resource\Collection($following, new AccountTransformer());
|
||||||
$res = $this->fractal->createData($resource)->toArray();
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,17 +59,18 @@ class BaseApiController extends Controller
|
||||||
{
|
{
|
||||||
$profile = Profile::findOrFail($id);
|
$profile = Profile::findOrFail($id);
|
||||||
$statuses = $profile->statuses()->orderBy('id', 'desc')->paginate(20);
|
$statuses = $profile->statuses()->orderBy('id', 'desc')->paginate(20);
|
||||||
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer);
|
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer());
|
||||||
$res = $this->fractal->createData($resource)->toArray();
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function followSuggestions(Request $request)
|
public function followSuggestions(Request $request)
|
||||||
{
|
{
|
||||||
$followers = Auth::user()->profile->recommendFollowers();
|
$followers = Auth::user()->profile->recommendFollowers();
|
||||||
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer);
|
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer());
|
||||||
$res = $this->fractal->createData($resource)->toArray();
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,33 +79,34 @@ class BaseApiController extends Controller
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'upload' => 'required|mimes:jpeg,png,gif|max:2000',
|
'upload' => 'required|mimes:jpeg,png,gif|max:2000',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$profile = $user->profile;
|
$profile = $user->profile;
|
||||||
$file = $request->file('upload');
|
$file = $request->file('upload');
|
||||||
$path = (new AvatarController())->getPath($user, $file);
|
$path = (new AvatarController())->getPath($user, $file);
|
||||||
$dir = $path['root'];
|
$dir = $path['root'];
|
||||||
$name = $path['name'];
|
$name = $path['name'];
|
||||||
$public = $path['storage'];
|
$public = $path['storage'];
|
||||||
$currentAvatar = storage_path('app/'.$profile->avatar->media_path);
|
$currentAvatar = storage_path('app/'.$profile->avatar->media_path);
|
||||||
$loc = $request->file('upload')->storeAs($public, $name);
|
$loc = $request->file('upload')->storeAs($public, $name);
|
||||||
|
|
||||||
$avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
|
$avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
|
||||||
$opath = $avatar->media_path;
|
$opath = $avatar->media_path;
|
||||||
$avatar->media_path = "$public/$name";
|
$avatar->media_path = "$public/$name";
|
||||||
$avatar->thumb_path = null;
|
$avatar->thumb_path = null;
|
||||||
$avatar->change_count = ++$avatar->change_count;
|
$avatar->change_count = ++$avatar->change_count;
|
||||||
$avatar->last_processed_at = null;
|
$avatar->last_processed_at = null;
|
||||||
$avatar->save();
|
$avatar->save();
|
||||||
|
|
||||||
Cache::forget("avatar:{$profile->id}");
|
Cache::forget("avatar:{$profile->id}");
|
||||||
AvatarOptimize::dispatch($user->profile, $currentAvatar);
|
AvatarOptimize::dispatch($user->profile, $currentAvatar);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'code' => 200,
|
'code' => 200,
|
||||||
'msg' => 'Avatar successfully updated'
|
'msg' => 'Avatar successfully updated',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Auth, Cache;
|
|
||||||
use App\{Like, Status};
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Http\Controllers\Api\BaseApiController;
|
use App\Http\Controllers\Api\BaseApiController;
|
||||||
|
use App\Like;
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class ApiController extends BaseApiController
|
class ApiController extends BaseApiController
|
||||||
{
|
{
|
||||||
|
|
||||||
public function hydrateLikes(Request $request)
|
public function hydrateLikes(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
|
@ -18,7 +18,7 @@ class ApiController extends BaseApiController
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$res = Cache::remember('api:like-ids:user:'.$profile->id, 1440, function() use ($profile) {
|
$res = Cache::remember('api:like-ids:user:'.$profile->id, 1440, function () use ($profile) {
|
||||||
return Like::whereProfileId($profile->id)
|
return Like::whereProfileId($profile->id)
|
||||||
->orderBy('id', 'desc')
|
->orderBy('id', 'desc')
|
||||||
->take(1000)
|
->take(1000)
|
||||||
|
@ -30,6 +30,5 @@ class ApiController extends BaseApiController
|
||||||
|
|
||||||
public function loadMoreComments(Request $request)
|
public function loadMoreComments(Request $request)
|
||||||
{
|
{
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\{AccountLog, User};
|
use App\AccountLog;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\User;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
|
|
||||||
class LoginController extends Controller
|
class LoginController extends Controller
|
||||||
|
@ -41,17 +42,18 @@ class LoginController extends Controller
|
||||||
/**
|
/**
|
||||||
* Validate the user login request.
|
* Validate the user login request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function validateLogin($request)
|
public function validateLogin($request)
|
||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
$this->username() => 'required|string',
|
$this->username() => 'required|string',
|
||||||
'password' => 'required|string',
|
'password' => 'required|string',
|
||||||
];
|
];
|
||||||
|
|
||||||
if(config('pixelfed.recaptcha')) {
|
if (config('pixelfed.recaptcha')) {
|
||||||
$rules['g-recaptcha-response'] = 'required|recaptcha';
|
$rules['g-recaptcha-response'] = 'required|recaptcha';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,13 +63,14 @@ class LoginController extends Controller
|
||||||
/**
|
/**
|
||||||
* The user has been authenticated.
|
* The user has been authenticated.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param mixed $user
|
* @param mixed $user
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
protected function authenticated($request, $user)
|
protected function authenticated($request, $user)
|
||||||
{
|
{
|
||||||
$log = new AccountLog;
|
$log = new AccountLog();
|
||||||
$log->user_id = $user->id;
|
$log->user_id = $user->id;
|
||||||
$log->item_id = $user->id;
|
$log->item_id = $user->id;
|
||||||
$log->item_type = 'App\User';
|
$log->item_type = 'App\User';
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use App\User;
|
use App\User;
|
||||||
use App\Util\Lexer\RestrictedNames;
|
use App\Util\Lexer\RestrictedNames;
|
||||||
use App\Http\Controllers\Controller;
|
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
|
||||||
|
|
||||||
class RegisterController extends Controller
|
class RegisterController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,8 @@ class RegisterController extends Controller
|
||||||
/**
|
/**
|
||||||
* Get a validator for an incoming registration request.
|
* Get a validator for an incoming registration request.
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
|
*
|
||||||
* @return \Illuminate\Contracts\Validation\Validator
|
* @return \Illuminate\Contracts\Validation\Validator
|
||||||
*/
|
*/
|
||||||
protected function validator(array $data)
|
protected function validator(array $data)
|
||||||
|
@ -57,21 +58,21 @@ class RegisterController extends Controller
|
||||||
'min:2',
|
'min:2',
|
||||||
'max:15',
|
'max:15',
|
||||||
'unique:users',
|
'unique:users',
|
||||||
function($attribute, $value, $fail) {
|
function ($attribute, $value, $fail) {
|
||||||
if(!ctype_alpha($value[0])) {
|
if (!ctype_alpha($value[0])) {
|
||||||
return $fail($attribute . ' is invalid. Username must be alpha-numeric and start with a letter.');
|
return $fail($attribute.' is invalid. Username must be alpha-numeric and start with a letter.');
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
$rules = [
|
$rules = [
|
||||||
'name' => 'required|string|max:' . config('pixelfed.max_name_length'),
|
'name' => 'required|string|max:'.config('pixelfed.max_name_length'),
|
||||||
'username' => $usernameRules,
|
'username' => $usernameRules,
|
||||||
'email' => 'required|string|email|max:255|unique:users',
|
'email' => 'required|string|email|max:255|unique:users',
|
||||||
'password' => 'required|string|min:6|confirmed',
|
'password' => 'required|string|min:6|confirmed',
|
||||||
];
|
];
|
||||||
|
|
||||||
if(config('pixelfed.recaptcha')) {
|
if (config('pixelfed.recaptcha')) {
|
||||||
$rules['g-recaptcha-response'] = 'required|recaptcha';
|
$rules['g-recaptcha-response'] = 'required|recaptcha';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,15 +82,16 @@ class RegisterController extends Controller
|
||||||
/**
|
/**
|
||||||
* Create a new user instance after a valid registration.
|
* Create a new user instance after a valid registration.
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
|
*
|
||||||
* @return \App\User
|
* @return \App\User
|
||||||
*/
|
*/
|
||||||
protected function create(array $data)
|
protected function create(array $data)
|
||||||
{
|
{
|
||||||
return User::create([
|
return User::create([
|
||||||
'name' => $data['name'],
|
'name' => $data['name'],
|
||||||
'username' => $data['username'],
|
'username' => $data['username'],
|
||||||
'email' => $data['email'],
|
'email' => $data['email'],
|
||||||
'password' => Hash::make($data['password']),
|
'password' => Hash::make($data['password']),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -98,7 +100,7 @@ class RegisterController extends Controller
|
||||||
{
|
{
|
||||||
$restricted = RestrictedNames::get();
|
$restricted = RestrictedNames::get();
|
||||||
|
|
||||||
if(in_array($username, $restricted)) {
|
if (in_array($username, $restricted)) {
|
||||||
return abort(403);
|
return abort(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +108,7 @@ class RegisterController extends Controller
|
||||||
public function openRegistrationCheck()
|
public function openRegistrationCheck()
|
||||||
{
|
{
|
||||||
$openRegistration = config('pixelfed.open_registration');
|
$openRegistration = config('pixelfed.open_registration');
|
||||||
if(false == $openRegistration) {
|
if (false == $openRegistration) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Auth, Cache, Log, Storage;
|
|
||||||
use App\Avatar;
|
use App\Avatar;
|
||||||
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Storage;
|
||||||
|
|
||||||
class AvatarController extends Controller
|
class AvatarController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -17,31 +19,33 @@ class AvatarController extends Controller
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'avatar' => 'required|mimes:jpeg,png|max:2000'
|
'avatar' => 'required|mimes:jpeg,png|max:2000',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$profile = $user->profile;
|
$profile = $user->profile;
|
||||||
$file = $request->file('avatar');
|
$file = $request->file('avatar');
|
||||||
$path = $this->getPath($user, $file);
|
$path = $this->getPath($user, $file);
|
||||||
$dir = $path['root'];
|
$dir = $path['root'];
|
||||||
$name = $path['name'];
|
$name = $path['name'];
|
||||||
$public = $path['storage'];
|
$public = $path['storage'];
|
||||||
$currentAvatar = storage_path('app/'.$profile->avatar->media_path);
|
$currentAvatar = storage_path('app/'.$profile->avatar->media_path);
|
||||||
$loc = $request->file('avatar')->storeAs($public, $name);
|
$loc = $request->file('avatar')->storeAs($public, $name);
|
||||||
|
|
||||||
$avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
|
$avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
|
||||||
$opath = $avatar->media_path;
|
$opath = $avatar->media_path;
|
||||||
$avatar->media_path = "$public/$name";
|
$avatar->media_path = "$public/$name";
|
||||||
$avatar->thumb_path = null;
|
$avatar->thumb_path = null;
|
||||||
$avatar->change_count = ++$avatar->change_count;
|
$avatar->change_count = ++$avatar->change_count;
|
||||||
$avatar->last_processed_at = null;
|
$avatar->last_processed_at = null;
|
||||||
$avatar->save();
|
$avatar->save();
|
||||||
|
|
||||||
Cache::forget("avatar:{$profile->id}");
|
Cache::forget("avatar:{$profile->id}");
|
||||||
AvatarOptimize::dispatch($user->profile, $currentAvatar);
|
AvatarOptimize::dispatch($user->profile, $currentAvatar);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('status', 'Avatar updated successfully. It may take a few minutes to update across the site.');
|
return redirect()->back()->with('status', 'Avatar updated successfully. It may take a few minutes to update across the site.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,15 +58,15 @@ class AvatarController extends Controller
|
||||||
$path = $this->buildPath($id);
|
$path = $this->buildPath($id);
|
||||||
$dir = storage_path('app/'.$path);
|
$dir = storage_path('app/'.$path);
|
||||||
$this->checkDir($dir);
|
$this->checkDir($dir);
|
||||||
$name = 'avatar.' . $file->guessExtension();
|
$name = 'avatar.'.$file->guessExtension();
|
||||||
$res = ['root' => 'storage/app/' . $path, 'name' => $name, 'storage' => $path];
|
$res = ['root' => 'storage/app/'.$path, 'name' => $name, 'storage' => $path];
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkDir($path)
|
public function checkDir($path)
|
||||||
{
|
{
|
||||||
if(!is_dir($path)) {
|
if (!is_dir($path)) {
|
||||||
mkdir($path);
|
mkdir($path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,25 +75,26 @@ class AvatarController extends Controller
|
||||||
{
|
{
|
||||||
$padded = str_pad($id, 12, 0, STR_PAD_LEFT);
|
$padded = str_pad($id, 12, 0, STR_PAD_LEFT);
|
||||||
$parts = str_split($padded, 3);
|
$parts = str_split($padded, 3);
|
||||||
foreach($parts as $k => $part) {
|
foreach ($parts as $k => $part) {
|
||||||
if($k == 0) {
|
if ($k == 0) {
|
||||||
$prefix = storage_path('app/public/avatars/'.$parts[0]);
|
$prefix = storage_path('app/public/avatars/'.$parts[0]);
|
||||||
$this->checkDir($prefix);
|
$this->checkDir($prefix);
|
||||||
}
|
}
|
||||||
if($k == 1) {
|
if ($k == 1) {
|
||||||
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1]);
|
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1]);
|
||||||
$this->checkDir($prefix);
|
$this->checkDir($prefix);
|
||||||
}
|
}
|
||||||
if($k == 2) {
|
if ($k == 2) {
|
||||||
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2]);
|
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2]);
|
||||||
$this->checkDir($prefix);
|
$this->checkDir($prefix);
|
||||||
}
|
}
|
||||||
if($k == 3) {
|
if ($k == 3) {
|
||||||
$avatarpath = 'public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2].'/'.$parts[3];
|
$avatarpath = 'public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2].'/'.$parts[3];
|
||||||
$prefix = storage_path('app/'.$avatarpath);
|
$prefix = storage_path('app/'.$avatarpath);
|
||||||
$this->checkDir($prefix);
|
$this->checkDir($prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $avatarpath;
|
return $avatarpath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Bookmark;
|
||||||
|
use App\Status;
|
||||||
use Auth;
|
use Auth;
|
||||||
use App\{Bookmark, Profile, Status};
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class BookmarkController extends Controller
|
class BookmarkController extends Controller
|
||||||
|
@ -16,7 +17,7 @@ class BookmarkController extends Controller
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'item' => 'required|integer|min:1'
|
'item' => 'required|integer|min:1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
|
@ -26,17 +27,16 @@ class BookmarkController extends Controller
|
||||||
['status_id' => $status->id], ['profile_id' => $profile->id]
|
['status_id' => $status->id], ['profile_id' => $profile->id]
|
||||||
);
|
);
|
||||||
|
|
||||||
if(!$bookmark->wasRecentlyCreated) {
|
if (!$bookmark->wasRecentlyCreated) {
|
||||||
$bookmark->delete();
|
$bookmark->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
if($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
$response = ['code' => 200, 'msg' => 'Bookmark saved!'];
|
$response = ['code' => 200, 'msg' => 'Bookmark saved!'];
|
||||||
} else {
|
} else {
|
||||||
$response = redirect()->back();
|
$response = redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,61 +2,66 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use App\Comment;
|
||||||
use App\Jobs\CommentPipeline\CommentPipeline;
|
use App\Jobs\CommentPipeline\CommentPipeline;
|
||||||
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
||||||
use Auth, Hashids;
|
use App\Profile;
|
||||||
use App\{Comment, Profile, Status};
|
use App\Status;
|
||||||
|
use Auth;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class CommentController extends Controller
|
class CommentController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function show(Request $request, $username, int $id, int $cid)
|
public function show(Request $request, $username, int $id, int $cid)
|
||||||
{
|
{
|
||||||
$user = Profile::whereUsername($username)->firstOrFail();
|
$user = Profile::whereUsername($username)->firstOrFail();
|
||||||
$status = Status::whereProfileId($user->id)->whereInReplyToId($id)->findOrFail($cid);
|
$status = Status::whereProfileId($user->id)->whereInReplyToId($id)->findOrFail($cid);
|
||||||
return view('status.reply', compact('user', 'status'));
|
|
||||||
|
return view('status.reply', compact('user', 'status'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showAll(Request $request, $username, int $id)
|
public function showAll(Request $request, $username, int $id)
|
||||||
{
|
{
|
||||||
$user = Profile::whereUsername($username)->firstOrFail();
|
$user = Profile::whereUsername($username)->firstOrFail();
|
||||||
$status = Status::whereProfileId($user->id)->findOrFail($id);
|
$status = Status::whereProfileId($user->id)->findOrFail($id);
|
||||||
$replies = Status::whereInReplyToId($id)->paginate(40);
|
$replies = Status::whereInReplyToId($id)->paginate(40);
|
||||||
return view('status.comments', compact('user', 'status', 'replies'));
|
|
||||||
|
return view('status.comments', compact('user', 'status', 'replies'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
if(Auth::check() === false) { abort(403); }
|
if (Auth::check() === false) {
|
||||||
$this->validate($request, [
|
abort(403);
|
||||||
|
}
|
||||||
|
$this->validate($request, [
|
||||||
'item' => 'required|integer',
|
'item' => 'required|integer',
|
||||||
'comment' => 'required|string|max:500'
|
'comment' => 'required|string|max:500',
|
||||||
]);
|
]);
|
||||||
$comment = $request->input('comment');
|
$comment = $request->input('comment');
|
||||||
$statusId = $request->item;
|
$statusId = $request->item;
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$profile = $user->profile;
|
$profile = $user->profile;
|
||||||
$status = Status::findOrFail($statusId);
|
$status = Status::findOrFail($statusId);
|
||||||
|
|
||||||
$reply = new Status();
|
$reply = new Status();
|
||||||
$reply->profile_id = $profile->id;
|
$reply->profile_id = $profile->id;
|
||||||
$reply->caption = e($comment);
|
$reply->caption = e($comment);
|
||||||
$reply->rendered = $comment;
|
$reply->rendered = $comment;
|
||||||
$reply->in_reply_to_id = $status->id;
|
$reply->in_reply_to_id = $status->id;
|
||||||
$reply->in_reply_to_profile_id = $status->profile_id;
|
$reply->in_reply_to_profile_id = $status->profile_id;
|
||||||
$reply->save();
|
$reply->save();
|
||||||
|
|
||||||
NewStatusPipeline::dispatch($reply, false);
|
NewStatusPipeline::dispatch($reply, false);
|
||||||
CommentPipeline::dispatch($status, $reply);
|
CommentPipeline::dispatch($status, $reply);
|
||||||
|
|
||||||
if($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
$response = ['code' => 200, 'msg' => 'Comment saved', 'username' => $profile->username, 'url' => $reply->url(), 'profile' => $profile->url(), 'comment' => $reply->caption];
|
$response = ['code' => 200, 'msg' => 'Comment saved', 'username' => $profile->username, 'url' => $reply->url(), 'profile' => $profile->url(), 'comment' => $reply->caption];
|
||||||
} else {
|
} else {
|
||||||
$response = redirect($status->url());
|
$response = redirect($status->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,57 +2,60 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use App\Follower;
|
||||||
use App\{Hashtag, Follower, Profile, Status, StatusHashtag};
|
use App\Hashtag;
|
||||||
|
use App\Profile;
|
||||||
|
use App\Status;
|
||||||
use Auth;
|
use Auth;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class DiscoverController extends Controller
|
class DiscoverController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function home()
|
public function home()
|
||||||
{
|
{
|
||||||
$pid = Auth::user()->profile->id;
|
$pid = Auth::user()->profile->id;
|
||||||
|
|
||||||
$following = Follower::whereProfileId($pid)
|
$following = Follower::whereProfileId($pid)
|
||||||
->pluck('following_id');
|
->pluck('following_id');
|
||||||
|
|
||||||
$people = Profile::inRandomOrder()
|
$people = Profile::inRandomOrder()
|
||||||
->where('id', '!=', $pid)
|
->where('id', '!=', $pid)
|
||||||
->whereNotIn('id', $following)
|
->whereNotIn('id', $following)
|
||||||
->take(3)
|
->take(3)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$posts = Status::whereHas('media')
|
$posts = Status::whereHas('media')
|
||||||
->where('profile_id', '!=', $pid)
|
->where('profile_id', '!=', $pid)
|
||||||
->whereNotIn('profile_id', $following)
|
->whereNotIn('profile_id', $following)
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('created_at', 'desc')
|
||||||
->simplePaginate(21);
|
->simplePaginate(21);
|
||||||
|
|
||||||
return view('discover.home', compact('people', 'posts'));
|
return view('discover.home', compact('people', 'posts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showTags(Request $request, $hashtag)
|
public function showTags(Request $request, $hashtag)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'page' => 'nullable|integer|min:1|max:10'
|
'page' => 'nullable|integer|min:1|max:10',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$tag = Hashtag::with('posts')
|
$tag = Hashtag::with('posts')
|
||||||
->withCount('posts')
|
->withCount('posts')
|
||||||
->whereSlug($hashtag)
|
->whereSlug($hashtag)
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
$posts = $tag->posts()
|
$posts = $tag->posts()
|
||||||
->whereIsNsfw(false)
|
->whereIsNsfw(false)
|
||||||
->whereVisibility('public')
|
->whereVisibility('public')
|
||||||
->has('media')
|
->has('media')
|
||||||
->orderBy('id','desc')
|
->orderBy('id', 'desc')
|
||||||
->simplePaginate(12);
|
->simplePaginate(12);
|
||||||
|
|
||||||
return view('discover.tags.show', compact('tag', 'posts'));
|
return view('discover.tags.show', compact('tag', 'posts'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,162 +2,165 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Auth, Cache;
|
use App\Jobs\InboxPipeline\InboxWorker;
|
||||||
|
use App\Jobs\RemoteFollowPipeline\RemoteFollowPipeline;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use Carbon\Carbon;
|
use App\Transformer\ActivityPub\ProfileOutbox;
|
||||||
use League\Fractal;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Util\Lexer\Nickname;
|
use App\Util\Lexer\Nickname;
|
||||||
use App\Util\Webfinger\Webfinger;
|
use App\Util\Webfinger\Webfinger;
|
||||||
use App\Transformer\ActivityPub\{
|
use Auth;
|
||||||
ProfileOutbox,
|
use Cache;
|
||||||
ProfileTransformer
|
use Carbon\Carbon;
|
||||||
};
|
use Illuminate\Http\Request;
|
||||||
use App\Jobs\RemoteFollowPipeline\RemoteFollowPipeline;
|
use League\Fractal;
|
||||||
use App\Jobs\InboxPipeline\InboxWorker;
|
|
||||||
|
|
||||||
class FederationController extends Controller
|
class FederationController extends Controller
|
||||||
{
|
{
|
||||||
public function authCheck()
|
public function authCheck()
|
||||||
{
|
{
|
||||||
if(!Auth::check()) {
|
if (!Auth::check()) {
|
||||||
return abort(403);
|
return abort(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorizeFollow(Request $request)
|
public function authorizeFollow(Request $request)
|
||||||
{
|
{
|
||||||
$this->authCheck();
|
$this->authCheck();
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'acct' => 'required|string|min:3|max:255'
|
'acct' => 'required|string|min:3|max:255',
|
||||||
]);
|
]);
|
||||||
$acct = $request->input('acct');
|
$acct = $request->input('acct');
|
||||||
$nickname = Nickname::normalizeProfileUrl($acct);
|
$nickname = Nickname::normalizeProfileUrl($acct);
|
||||||
return view('federation.authorizefollow', compact('acct', 'nickname'));
|
|
||||||
|
return view('federation.authorizefollow', compact('acct', 'nickname'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remoteFollow()
|
public function remoteFollow()
|
||||||
{
|
{
|
||||||
$this->authCheck();
|
$this->authCheck();
|
||||||
return view('federation.remotefollow');
|
|
||||||
|
return view('federation.remotefollow');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remoteFollowStore(Request $request)
|
public function remoteFollowStore(Request $request)
|
||||||
{
|
{
|
||||||
$this->authCheck();
|
$this->authCheck();
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'url' => 'required|string'
|
'url' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if(config('pixelfed.remote_follow_enabled') !== true) {
|
if (config('pixelfed.remote_follow_enabled') !== true) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$follower = Auth::user()->profile;
|
$follower = Auth::user()->profile;
|
||||||
$url = $request->input('url');
|
$url = $request->input('url');
|
||||||
|
|
||||||
RemoteFollowPipeline::dispatch($follower, $url);
|
RemoteFollowPipeline::dispatch($follower, $url);
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function nodeinfoWellKnown()
|
public function nodeinfoWellKnown()
|
||||||
{
|
{
|
||||||
$res = [
|
$res = [
|
||||||
'links' => [
|
'links' => [
|
||||||
[
|
[
|
||||||
'href' => config('pixelfed.nodeinfo.url'),
|
'href' => config('pixelfed.nodeinfo.url'),
|
||||||
'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0'
|
'rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
return response()->json($res);
|
|
||||||
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function nodeinfo()
|
public function nodeinfo()
|
||||||
{
|
{
|
||||||
$res = Cache::remember('api:nodeinfo', 60, function() {
|
$res = Cache::remember('api:nodeinfo', 60, function () {
|
||||||
return [
|
return [
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'nodeName' => config('app.name'),
|
'nodeName' => config('app.name'),
|
||||||
'software' => [
|
'software' => [
|
||||||
'homepage' => 'https://pixelfed.org',
|
'homepage' => 'https://pixelfed.org',
|
||||||
'github' => 'https://github.com/pixelfed',
|
'github' => 'https://github.com/pixelfed',
|
||||||
'follow' => 'https://mastodon.social/@pixelfed'
|
'follow' => 'https://mastodon.social/@pixelfed',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'openRegistrations' => config('pixelfed.open_registration'),
|
'openRegistrations' => config('pixelfed.open_registration'),
|
||||||
'protocols' => [
|
'protocols' => [
|
||||||
'activitypub'
|
'activitypub',
|
||||||
],
|
],
|
||||||
'services' => [
|
'services' => [
|
||||||
'inbound' => [],
|
'inbound' => [],
|
||||||
'outbound' => []
|
'outbound' => [],
|
||||||
],
|
],
|
||||||
'software' => [
|
'software' => [
|
||||||
'name' => 'pixelfed',
|
'name' => 'pixelfed',
|
||||||
'version' => config('pixelfed.version')
|
'version' => config('pixelfed.version'),
|
||||||
],
|
],
|
||||||
'usage' => [
|
'usage' => [
|
||||||
'localPosts' => \App\Status::whereLocal(true)->whereHas('media')->count(),
|
'localPosts' => \App\Status::whereLocal(true)->whereHas('media')->count(),
|
||||||
'localComments' => \App\Status::whereLocal(true)->whereNotNull('in_reply_to_id')->count(),
|
'localComments' => \App\Status::whereLocal(true)->whereNotNull('in_reply_to_id')->count(),
|
||||||
'users' => [
|
'users' => [
|
||||||
'total' => \App\User::count(),
|
'total' => \App\User::count(),
|
||||||
'activeHalfyear' => \App\User::where('updated_at', '>', Carbon::now()->subMonths(6)->toDateTimeString())->count(),
|
'activeHalfyear' => \App\User::where('updated_at', '>', Carbon::now()->subMonths(6)->toDateTimeString())->count(),
|
||||||
'activeMonth' => \App\User::where('updated_at', '>', Carbon::now()->subMonths(1)->toDateTimeString())->count(),
|
'activeMonth' => \App\User::where('updated_at', '>', Carbon::now()->subMonths(1)->toDateTimeString())->count(),
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'version' => '2.0'
|
'version' => '2.0',
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
public function webfinger(Request $request)
|
public function webfinger(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, ['resource'=>'required|string|min:3|max:255']);
|
$this->validate($request, ['resource'=>'required|string|min:3|max:255']);
|
||||||
|
|
||||||
$hash = hash('sha256', $request->input('resource'));
|
$hash = hash('sha256', $request->input('resource'));
|
||||||
|
|
||||||
$webfinger = Cache::remember('api:webfinger:'.$hash, 1440, function() use($request) {
|
$webfinger = Cache::remember('api:webfinger:'.$hash, 1440, function () use ($request) {
|
||||||
$resource = $request->input('resource');
|
$resource = $request->input('resource');
|
||||||
$parsed = Nickname::normalizeProfileUrl($resource);
|
$parsed = Nickname::normalizeProfileUrl($resource);
|
||||||
$username = $parsed['username'];
|
$username = $parsed['username'];
|
||||||
$user = Profile::whereUsername($username)->firstOrFail();
|
$user = Profile::whereUsername($username)->firstOrFail();
|
||||||
return (new Webfinger($user))->generate();
|
|
||||||
});
|
return (new Webfinger($user))->generate();
|
||||||
return response()->json($webfinger, 200, [], JSON_PRETTY_PRINT);
|
});
|
||||||
|
|
||||||
|
return response()->json($webfinger, 200, [], JSON_PRETTY_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userOutbox(Request $request, $username)
|
public function userOutbox(Request $request, $username)
|
||||||
{
|
{
|
||||||
if(config('pixelfed.activitypub_enabled') == false) {
|
if (config('pixelfed.activitypub_enabled') == false) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Profile::whereNull('remote_url')->whereUsername($username)->firstOrFail();
|
$user = Profile::whereNull('remote_url')->whereUsername($username)->firstOrFail();
|
||||||
$timeline = $user->statuses()->orderBy('created_at','desc')->paginate(10);
|
$timeline = $user->statuses()->orderBy('created_at', 'desc')->paginate(10);
|
||||||
$fractal = new Fractal\Manager();
|
$fractal = new Fractal\Manager();
|
||||||
$resource = new Fractal\Resource\Item($user, new ProfileOutbox);
|
$resource = new Fractal\Resource\Item($user, new ProfileOutbox());
|
||||||
$res = $fractal->createData($resource)->toArray();
|
$res = $fractal->createData($resource)->toArray();
|
||||||
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
|
|
||||||
|
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userInbox(Request $request, $username)
|
public function userInbox(Request $request, $username)
|
||||||
{
|
{
|
||||||
if(config('pixelfed.activitypub_enabled') == false) {
|
if (config('pixelfed.activitypub_enabled') == false) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$mimes = [
|
$mimes = [
|
||||||
'application/activity+json',
|
'application/activity+json',
|
||||||
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||||
];
|
];
|
||||||
if(!in_array($request->header('Content-Type'), $mimes)) {
|
if (!in_array($request->header('Content-Type'), $mimes)) {
|
||||||
abort(500, 'Invalid request');
|
abort(500, 'Invalid request');
|
||||||
}
|
}
|
||||||
$profile = Profile::whereUsername($username)->firstOrFail();
|
$profile = Profile::whereUsername($username)->firstOrFail();
|
||||||
InboxWorker::dispatch($request, $profile, $request->all());
|
InboxWorker::dispatch($request, $profile, $request->all());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,41 +2,41 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Auth;
|
use App\Follower;
|
||||||
use App\{Follower, Profile};
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Jobs\FollowPipeline\FollowPipeline;
|
use App\Jobs\FollowPipeline\FollowPipeline;
|
||||||
|
use App\Profile;
|
||||||
|
use Auth;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class FollowerController extends Controller
|
class FollowerController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'item' => 'required|integer',
|
'item' => 'required|integer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = Auth::user()->profile;
|
$user = Auth::user()->profile;
|
||||||
$target = Profile::where('id', '!=', $user->id)->findOrFail($request->input('item'));
|
$target = Profile::where('id', '!=', $user->id)->findOrFail($request->input('item'));
|
||||||
|
|
||||||
$isFollowing = Follower::whereProfileId($user->id)->whereFollowingId($target->id)->count();
|
$isFollowing = Follower::whereProfileId($user->id)->whereFollowingId($target->id)->count();
|
||||||
|
|
||||||
if($isFollowing == 0) {
|
if ($isFollowing == 0) {
|
||||||
$follower = new Follower;
|
$follower = new Follower();
|
||||||
$follower->profile_id = $user->id;
|
$follower->profile_id = $user->id;
|
||||||
$follower->following_id = $target->id;
|
$follower->following_id = $target->id;
|
||||||
$follower->save();
|
$follower->save();
|
||||||
FollowPipeline::dispatch($follower);
|
FollowPipeline::dispatch($follower);
|
||||||
} else {
|
} else {
|
||||||
$follower = Follower::whereProfileId($user->id)->whereFollowingId($target->id)->firstOrFail();
|
$follower = Follower::whereProfileId($user->id)->whereFollowingId($target->id)->firstOrFail();
|
||||||
$follower->delete();
|
$follower->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return redirect()->back();
|
||||||
return redirect()->back();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class HashtagController extends Controller
|
class HashtagController extends Controller
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,55 +2,58 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Auth, Cache, Hashids;
|
|
||||||
use App\{Like, Profile, Status, User};
|
|
||||||
use App\Jobs\LikePipeline\LikePipeline;
|
use App\Jobs\LikePipeline\LikePipeline;
|
||||||
|
use App\Like;
|
||||||
|
use App\Status;
|
||||||
|
use App\User;
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class LikeController extends Controller
|
class LikeController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'item' => 'required|integer',
|
'item' => 'required|integer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$status = Status::withCount('likes')->findOrFail($request->input('item'));
|
$status = Status::withCount('likes')->findOrFail($request->input('item'));
|
||||||
|
|
||||||
$count = $status->likes_count;
|
$count = $status->likes_count;
|
||||||
|
|
||||||
if($status->likes()->whereProfileId($profile->id)->count() !== 0) {
|
if ($status->likes()->whereProfileId($profile->id)->count() !== 0) {
|
||||||
$like = Like::whereProfileId($profile->id)->whereStatusId($status->id)->firstOrFail();
|
$like = Like::whereProfileId($profile->id)->whereStatusId($status->id)->firstOrFail();
|
||||||
$like->forceDelete();
|
$like->forceDelete();
|
||||||
$count--;
|
$count--;
|
||||||
} else {
|
} else {
|
||||||
$like = new Like;
|
$like = new Like();
|
||||||
$like->profile_id = $profile->id;
|
$like->profile_id = $profile->id;
|
||||||
$like->status_id = $status->id;
|
$like->status_id = $status->id;
|
||||||
$like->save();
|
$like->save();
|
||||||
$count++;
|
$count++;
|
||||||
LikePipeline::dispatch($like);
|
LikePipeline::dispatch($like);
|
||||||
}
|
}
|
||||||
|
|
||||||
$likes = Like::whereProfileId($profile->id)
|
$likes = Like::whereProfileId($profile->id)
|
||||||
->orderBy('id', 'desc')
|
->orderBy('id', 'desc')
|
||||||
->take(1000)
|
->take(1000)
|
||||||
->pluck('status_id');
|
->pluck('status_id');
|
||||||
|
|
||||||
Cache::put('api:like-ids:user:'.$profile->id, $likes, 1440);
|
Cache::put('api:like-ids:user:'.$profile->id, $likes, 1440);
|
||||||
|
|
||||||
if($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
$response = ['code' => 200, 'msg' => 'Like saved', 'count' => $count];
|
$response = ['code' => 200, 'msg' => 'Like saved', 'count' => $count];
|
||||||
} else {
|
} else {
|
||||||
$response = redirect($status->url());
|
$response = redirect($status->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class MediaController extends Controller
|
class MediaController extends Controller
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class NotificationController extends Controller
|
class NotificationController extends Controller
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,133 +2,135 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Follower;
|
||||||
|
use App\Profile;
|
||||||
|
use App\Transformer\ActivityPub\ProfileTransformer;
|
||||||
|
use App\User;
|
||||||
|
use Auth;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Auth, Cache;
|
|
||||||
use App\{Follower, Profile, User};
|
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
use App\Util\Lexer\Nickname;
|
|
||||||
use App\Util\Webfinger\Webfinger;
|
|
||||||
use App\Transformer\ActivityPub\{
|
|
||||||
ProfileOutbox,
|
|
||||||
ProfileTransformer
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProfileController extends Controller
|
class ProfileController extends Controller
|
||||||
{
|
{
|
||||||
public function show(Request $request, $username)
|
public function show(Request $request, $username)
|
||||||
{
|
{
|
||||||
$user = Profile::whereUsername($username)->firstOrFail();
|
$user = Profile::whereUsername($username)->firstOrFail();
|
||||||
if($user->remote_url) {
|
if ($user->remote_url) {
|
||||||
$settings = new \StdClass;
|
$settings = new \StdClass();
|
||||||
$settings->crawlable = false;
|
$settings->crawlable = false;
|
||||||
} else {
|
} else {
|
||||||
$settings = User::whereUsername($username)->firstOrFail()->settings;
|
$settings = User::whereUsername($username)->firstOrFail()->settings;
|
||||||
}
|
|
||||||
|
|
||||||
if($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
|
|
||||||
return $this->showActivityPub($request, $user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($user->is_private == true) {
|
|
||||||
$can_access = $this->privateProfileCheck($user);
|
|
||||||
if($can_access !== true) {
|
|
||||||
abort(403);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// TODO: refactor this mess
|
if ($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
|
||||||
$owner = Auth::check() && Auth::id() === $user->user_id;
|
return $this->showActivityPub($request, $user);
|
||||||
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
}
|
||||||
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
|
||||||
$timeline = $user->statuses()
|
if ($user->is_private == true) {
|
||||||
|
$can_access = $this->privateProfileCheck($user);
|
||||||
|
if ($can_access !== true) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: refactor this mess
|
||||||
|
$owner = Auth::check() && Auth::id() === $user->user_id;
|
||||||
|
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
||||||
|
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
||||||
|
$timeline = $user->statuses()
|
||||||
->whereHas('media')
|
->whereHas('media')
|
||||||
->whereNull('in_reply_to_id')
|
->whereNull('in_reply_to_id')
|
||||||
->whereNull('reblog_of_id')
|
->whereNull('reblog_of_id')
|
||||||
->orderBy('created_at','desc')
|
->orderBy('created_at', 'desc')
|
||||||
->withCount(['comments', 'likes'])
|
->withCount(['comments', 'likes'])
|
||||||
->simplePaginate(21);
|
->simplePaginate(21);
|
||||||
|
|
||||||
return view('profile.show', compact('user', 'settings', 'owner', 'is_following', 'is_admin', 'timeline'));
|
return view('profile.show', compact('user', 'settings', 'owner', 'is_following', 'is_admin', 'timeline'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function permalinkRedirect(Request $request, $username)
|
public function permalinkRedirect(Request $request, $username)
|
||||||
{
|
{
|
||||||
$user = Profile::whereUsername($username)->firstOrFail();
|
$user = Profile::whereUsername($username)->firstOrFail();
|
||||||
$settings = User::whereUsername($username)->firstOrFail()->settings;
|
$settings = User::whereUsername($username)->firstOrFail()->settings;
|
||||||
|
|
||||||
if($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
|
if ($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
|
||||||
return $this->showActivityPub($request, $user);
|
return $this->showActivityPub($request, $user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($user->url());
|
return redirect($user->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function privateProfileCheck(Profile $profile)
|
protected function privateProfileCheck(Profile $profile)
|
||||||
{
|
{
|
||||||
if(Auth::check() === false) {
|
if (Auth::check() === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$follower_ids = (array) $profile->followers()->pluck('followers.profile_id');
|
$follower_ids = (array) $profile->followers()->pluck('followers.profile_id');
|
||||||
$pid = Auth::user()->profile->id;
|
$pid = Auth::user()->profile->id;
|
||||||
if(!in_array($pid, $follower_ids) && $pid !== $profile->id) {
|
if (!in_array($pid, $follower_ids) && $pid !== $profile->id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showActivityPub(Request $request, $user)
|
public function showActivityPub(Request $request, $user)
|
||||||
{
|
{
|
||||||
$fractal = new Fractal\Manager();
|
$fractal = new Fractal\Manager();
|
||||||
$resource = new Fractal\Resource\Item($user, new ProfileTransformer);
|
$resource = new Fractal\Resource\Item($user, new ProfileTransformer());
|
||||||
$res = $fractal->createData($resource)->toArray();
|
$res = $fractal->createData($resource)->toArray();
|
||||||
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
|
|
||||||
|
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showAtomFeed(Request $request, $user)
|
public function showAtomFeed(Request $request, $user)
|
||||||
{
|
{
|
||||||
$profile = Profile::whereUsername($user)->firstOrFail();
|
$profile = Profile::whereUsername($user)->firstOrFail();
|
||||||
$items = $profile->statuses()->orderBy('created_at', 'desc')->take(10)->get();
|
$items = $profile->statuses()->orderBy('created_at', 'desc')->take(10)->get();
|
||||||
return response()->view('atom.user', compact('profile', 'items'))
|
|
||||||
|
return response()->view('atom.user', compact('profile', 'items'))
|
||||||
->header('Content-Type', 'application/atom+xml');
|
->header('Content-Type', 'application/atom+xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function followers(Request $request, $username)
|
public function followers(Request $request, $username)
|
||||||
{
|
{
|
||||||
$profile = Profile::whereUsername($username)->firstOrFail();
|
$profile = Profile::whereUsername($username)->firstOrFail();
|
||||||
// TODO: fix $profile/$user mismatch in profile & follower templates
|
// TODO: fix $profile/$user mismatch in profile & follower templates
|
||||||
$user = $profile;
|
$user = $profile;
|
||||||
$owner = Auth::check() && Auth::id() === $user->user_id;
|
$owner = Auth::check() && Auth::id() === $user->user_id;
|
||||||
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
||||||
$followers = $profile->followers()->orderBy('created_at','desc')->simplePaginate(12);
|
$followers = $profile->followers()->orderBy('created_at', 'desc')->simplePaginate(12);
|
||||||
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
||||||
return view('profile.followers', compact('user', 'profile', 'followers', 'owner', 'is_following', 'is_admin'));
|
|
||||||
|
return view('profile.followers', compact('user', 'profile', 'followers', 'owner', 'is_following', 'is_admin'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function following(Request $request, $username)
|
public function following(Request $request, $username)
|
||||||
{
|
{
|
||||||
$profile = Profile::whereUsername($username)->firstOrFail();
|
$profile = Profile::whereUsername($username)->firstOrFail();
|
||||||
// TODO: fix $profile/$user mismatch in profile & follower templates
|
// TODO: fix $profile/$user mismatch in profile & follower templates
|
||||||
$user = $profile;
|
$user = $profile;
|
||||||
$owner = Auth::check() && Auth::id() === $user->user_id;
|
$owner = Auth::check() && Auth::id() === $user->user_id;
|
||||||
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
||||||
$following = $profile->following()->orderBy('created_at','desc')->simplePaginate(12);
|
$following = $profile->following()->orderBy('created_at', 'desc')->simplePaginate(12);
|
||||||
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
||||||
return view('profile.following', compact('user', 'profile', 'following', 'owner', 'is_following', 'is_admin'));
|
|
||||||
|
return view('profile.following', compact('user', 'profile', 'following', 'owner', 'is_following', 'is_admin'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function savedBookmarks(Request $request, $username)
|
public function savedBookmarks(Request $request, $username)
|
||||||
{
|
{
|
||||||
if(Auth::check() === false || $username !== Auth::user()->username) {
|
if (Auth::check() === false || $username !== Auth::user()->username) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$user = Auth::user()->profile;
|
$user = Auth::user()->profile;
|
||||||
$settings = User::whereUsername($username)->firstOrFail()->settings;
|
$settings = User::whereUsername($username)->firstOrFail()->settings;
|
||||||
$owner = true;
|
$owner = true;
|
||||||
$following = false;
|
$following = false;
|
||||||
$timeline = $user->bookmarks()->withCount(['likes','comments'])->orderBy('created_at','desc')->simplePaginate(10);
|
$timeline = $user->bookmarks()->withCount(['likes', 'comments'])->orderBy('created_at', 'desc')->simplePaginate(10);
|
||||||
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
||||||
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
||||||
return view('profile.show', compact('user', 'settings', 'owner', 'following', 'timeline', 'is_following', 'is_admin'));
|
|
||||||
|
return view('profile.show', compact('user', 'settings', 'owner', 'following', 'timeline', 'is_following', 'is_admin'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Profile;
|
||||||
|
use App\Report;
|
||||||
|
use App\Status;
|
||||||
|
use App\User;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\{Avatar, Profile, Report, Status, User};
|
|
||||||
|
|
||||||
class ReportController extends Controller
|
class ReportController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -12,95 +15,96 @@ class ReportController extends Controller
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showForm(Request $request)
|
public function showForm(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'type' => 'required|alpha_dash',
|
'type' => 'required|alpha_dash',
|
||||||
'id' => 'required|integer|min:1'
|
'id' => 'required|integer|min:1',
|
||||||
]);
|
]);
|
||||||
return view('report.form');
|
|
||||||
|
return view('report.form');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function notInterestedForm(Request $request)
|
public function notInterestedForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.not-interested');
|
return view('report.not-interested');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function spamForm(Request $request)
|
public function spamForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.spam');
|
return view('report.spam');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function spamCommentForm(Request $request)
|
public function spamCommentForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.spam.comment');
|
return view('report.spam.comment');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function spamPostForm(Request $request)
|
public function spamPostForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.spam.post');
|
return view('report.spam.post');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function spamProfileForm(Request $request)
|
public function spamProfileForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.spam.profile');
|
return view('report.spam.profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sensitiveCommentForm(Request $request)
|
public function sensitiveCommentForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.sensitive.comment');
|
return view('report.sensitive.comment');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sensitivePostForm(Request $request)
|
public function sensitivePostForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.sensitive.post');
|
return view('report.sensitive.post');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sensitiveProfileForm(Request $request)
|
public function sensitiveProfileForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.sensitive.profile');
|
return view('report.sensitive.profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function abusiveCommentForm(Request $request)
|
public function abusiveCommentForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.abusive.comment');
|
return view('report.abusive.comment');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function abusivePostForm(Request $request)
|
public function abusivePostForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.abusive.post');
|
return view('report.abusive.post');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function abusiveProfileForm(Request $request)
|
public function abusiveProfileForm(Request $request)
|
||||||
{
|
{
|
||||||
return view('report.abusive.profile');
|
return view('report.abusive.profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function formStore(Request $request)
|
public function formStore(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'report' => 'required|alpha_dash',
|
'report' => 'required|alpha_dash',
|
||||||
'type' => 'required|alpha_dash',
|
'type' => 'required|alpha_dash',
|
||||||
'id' => 'required|integer|min:1',
|
'id' => 'required|integer|min:1',
|
||||||
'msg' => 'nullable|string|max:150'
|
'msg' => 'nullable|string|max:150',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$reportType = $request->input('report');
|
$reportType = $request->input('report');
|
||||||
$object_id = $request->input('id');
|
$object_id = $request->input('id');
|
||||||
$object_type = $request->input('type');
|
$object_type = $request->input('type');
|
||||||
$msg = $request->input('msg');
|
$msg = $request->input('msg');
|
||||||
$object = null;
|
$object = null;
|
||||||
$types = ['spam', 'sensitive', 'abusive'];
|
$types = ['spam', 'sensitive', 'abusive'];
|
||||||
|
|
||||||
if(!in_array($reportType, $types)) {
|
if (!in_array($reportType, $types)) {
|
||||||
return redirect('/timeline')->with('error', 'Invalid report type');
|
return redirect('/timeline')->with('error', 'Invalid report type');
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($object_type) {
|
switch ($object_type) {
|
||||||
case 'post':
|
case 'post':
|
||||||
$object = Status::findOrFail($object_id);
|
$object = Status::findOrFail($object_id);
|
||||||
$object_type = 'App\Status';
|
$object_type = 'App\Status';
|
||||||
|
@ -115,25 +119,24 @@ class ReportController extends Controller
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($exists !== 0) {
|
if ($exists !== 0) {
|
||||||
return redirect('/timeline')->with('error', 'You have already reported this!');
|
return redirect('/timeline')->with('error', 'You have already reported this!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if($object->profile_id == $profile->id) {
|
if ($object->profile_id == $profile->id) {
|
||||||
return redirect('/timeline')->with('error', 'You cannot report your own content!');
|
return redirect('/timeline')->with('error', 'You cannot report your own content!');
|
||||||
}
|
}
|
||||||
|
|
||||||
$report = new Report;
|
$report = new Report();
|
||||||
$report->profile_id = $profile->id;
|
$report->profile_id = $profile->id;
|
||||||
$report->user_id = Auth::id();
|
$report->user_id = Auth::id();
|
||||||
$report->object_id = $object->id;
|
$report->object_id = $object->id;
|
||||||
$report->object_type = $object_type;
|
$report->object_type = $object_type;
|
||||||
$report->reported_profile_id = $object->profile_id;
|
$report->reported_profile_id = $object->profile_id;
|
||||||
$report->type = $request->input('report');
|
$report->type = $request->input('report');
|
||||||
$report->message = $request->input('msg');
|
$report->message = $request->input('msg');
|
||||||
$report->save();
|
$report->save();
|
||||||
|
|
||||||
return redirect('/timeline')->with('status', 'Report successfully sent!');
|
return redirect('/timeline')->with('status', 'Report successfully sent!');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\{Hashtag, Profile};
|
use App\Hashtag;
|
||||||
|
use App\Profile;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
@ -10,33 +11,34 @@ class SearchController extends Controller
|
||||||
{
|
{
|
||||||
public function searchAPI(Request $request, $tag)
|
public function searchAPI(Request $request, $tag)
|
||||||
{
|
{
|
||||||
$res = Cache::remember('api:search:tag:' . $tag, 1440, function() use($tag) {
|
$res = Cache::remember('api:search:tag:'.$tag, 1440, function () use ($tag) {
|
||||||
$res = Hashtag::where('slug', 'like', '%'.$tag.'%')->get();
|
$res = Hashtag::where('slug', 'like', '%'.$tag.'%')->get();
|
||||||
$tags = $res->map(function($item, $key) {
|
$tags = $res->map(function ($item, $key) {
|
||||||
return [
|
return [
|
||||||
'count' => $item->posts()->count(),
|
'count' => $item->posts()->count(),
|
||||||
'url' => $item->url(),
|
'url' => $item->url(),
|
||||||
'type' => 'hashtag',
|
'type' => 'hashtag',
|
||||||
'value' => $item->name,
|
'value' => $item->name,
|
||||||
'tokens' => explode('-', $item->name),
|
'tokens' => explode('-', $item->name),
|
||||||
'name' => null
|
'name' => null,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
$res = Profile::where('username', 'like', '%'.$tag.'%')->get();
|
$res = Profile::where('username', 'like', '%'.$tag.'%')->get();
|
||||||
$profiles = $res->map(function($item, $key) {
|
$profiles = $res->map(function ($item, $key) {
|
||||||
return [
|
return [
|
||||||
'count' => 0,
|
'count' => 0,
|
||||||
'url' => $item->url(),
|
'url' => $item->url(),
|
||||||
'type' => 'profile',
|
'type' => 'profile',
|
||||||
'value' => $item->username,
|
'value' => $item->username,
|
||||||
'tokens' => [$item->username],
|
'tokens' => [$item->username],
|
||||||
'name' => $item->name
|
'name' => $item->name,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
$tags = $tags->push($profiles[0]);
|
$tags = $tags->push($profiles[0]);
|
||||||
return $tags;
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json($res);
|
return $tags;
|
||||||
|
});
|
||||||
|
|
||||||
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,15 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use App\AccountLog;
|
||||||
use App\{AccountLog, EmailVerification, Media, Profile, User};
|
use App\EmailVerification;
|
||||||
use Auth, DB;
|
use App\Media;
|
||||||
|
use App\Profile;
|
||||||
|
use App\User;
|
||||||
use App\Util\Lexer\PrettyNumber;
|
use App\Util\Lexer\PrettyNumber;
|
||||||
|
use Auth;
|
||||||
|
use DB;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class SettingsController extends Controller
|
class SettingsController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -16,229 +21,237 @@ class SettingsController extends Controller
|
||||||
|
|
||||||
public function home()
|
public function home()
|
||||||
{
|
{
|
||||||
$id = Auth::user()->profile->id;
|
$id = Auth::user()->profile->id;
|
||||||
$storage = [];
|
$storage = [];
|
||||||
$used = Media::whereProfileId($id)->sum('size');
|
$used = Media::whereProfileId($id)->sum('size');
|
||||||
$storage['limit'] = config('pixelfed.max_account_size') * 1024;
|
$storage['limit'] = config('pixelfed.max_account_size') * 1024;
|
||||||
$storage['used'] = $used;
|
$storage['used'] = $used;
|
||||||
$storage['percentUsed'] = ceil($storage['used'] / $storage['limit'] * 100);
|
$storage['percentUsed'] = ceil($storage['used'] / $storage['limit'] * 100);
|
||||||
$storage['limitPretty'] = PrettyNumber::size($storage['limit']);
|
$storage['limitPretty'] = PrettyNumber::size($storage['limit']);
|
||||||
$storage['usedPretty'] = PrettyNumber::size($storage['used']);
|
$storage['usedPretty'] = PrettyNumber::size($storage['used']);
|
||||||
return view('settings.home', compact('storage'));
|
|
||||||
|
return view('settings.home', compact('storage'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function homeUpdate(Request $request)
|
public function homeUpdate(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'name' => 'required|string|max:' . config('pixelfed.max_name_length'),
|
'name' => 'required|string|max:'.config('pixelfed.max_name_length'),
|
||||||
'bio' => 'nullable|string|max:' . config('pixelfed.max_bio_length'),
|
'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'),
|
||||||
'website' => 'nullable|url',
|
'website' => 'nullable|url',
|
||||||
'email' => 'nullable|email'
|
'email' => 'nullable|email',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$changes = false;
|
$changes = false;
|
||||||
$name = $request->input('name');
|
$name = $request->input('name');
|
||||||
$bio = $request->input('bio');
|
$bio = $request->input('bio');
|
||||||
$website = $request->input('website');
|
$website = $request->input('website');
|
||||||
$email = $request->input('email');
|
$email = $request->input('email');
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$profile = $user->profile;
|
$profile = $user->profile;
|
||||||
|
|
||||||
$validate = config('pixelfed.enforce_email_verification');
|
$validate = config('pixelfed.enforce_email_verification');
|
||||||
|
|
||||||
if($user->email != $email) {
|
if ($user->email != $email) {
|
||||||
$changes = true;
|
$changes = true;
|
||||||
$user->email = $email;
|
$user->email = $email;
|
||||||
|
|
||||||
if($validate) {
|
if ($validate) {
|
||||||
$user->email_verified_at = null;
|
$user->email_verified_at = null;
|
||||||
// Prevent old verifications from working
|
// Prevent old verifications from working
|
||||||
EmailVerification::whereUserId($user->id)->delete();
|
EmailVerification::whereUserId($user->id)->delete();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Only allow email to be updated if not yet verified
|
|
||||||
if(!$validate || !$changes && $user->email_verified_at) {
|
|
||||||
if($profile->name != $name) {
|
|
||||||
$changes = true;
|
|
||||||
$user->name = $name;
|
|
||||||
$profile->name = $name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$profile->website || $profile->website != $website) {
|
// Only allow email to be updated if not yet verified
|
||||||
$changes = true;
|
if (!$validate || !$changes && $user->email_verified_at) {
|
||||||
$profile->website = $website;
|
if ($profile->name != $name) {
|
||||||
|
$changes = true;
|
||||||
|
$user->name = $name;
|
||||||
|
$profile->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$profile->website || $profile->website != $website) {
|
||||||
|
$changes = true;
|
||||||
|
$profile->website = $website;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$profile->bio || !$profile->bio != $bio) {
|
||||||
|
$changes = true;
|
||||||
|
$profile->bio = $bio;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$profile->bio || !$profile->bio != $bio) {
|
if ($changes === true) {
|
||||||
$changes = true;
|
$user->save();
|
||||||
$profile->bio = $bio;
|
$profile->save();
|
||||||
|
|
||||||
|
return redirect('/settings/home')->with('status', 'Profile successfully updated!');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if($changes === true) {
|
return redirect('/settings/home');
|
||||||
$user->save();
|
|
||||||
$profile->save();
|
|
||||||
return redirect('/settings/home')->with('status', 'Profile successfully updated!');
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect('/settings/home');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function password()
|
public function password()
|
||||||
{
|
{
|
||||||
return view('settings.password');
|
return view('settings.password');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function passwordUpdate(Request $request)
|
public function passwordUpdate(Request $request)
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'current' => 'required|string',
|
'current' => 'required|string',
|
||||||
'password' => 'required|string',
|
'password' => 'required|string',
|
||||||
'password_confirmation' => 'required|string',
|
'password_confirmation' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$current = $request->input('current');
|
$current = $request->input('current');
|
||||||
$new = $request->input('password');
|
$new = $request->input('password');
|
||||||
$confirm = $request->input('password_confirmation');
|
$confirm = $request->input('password_confirmation');
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
||||||
if(password_verify($current, $user->password) && $new === $confirm) {
|
if (password_verify($current, $user->password) && $new === $confirm) {
|
||||||
$user->password = bcrypt($new);
|
$user->password = bcrypt($new);
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return redirect('/settings/home')->with('status', 'Password successfully updated!');
|
return redirect('/settings/home')->with('status', 'Password successfully updated!');
|
||||||
}
|
}
|
||||||
return redirect('/settings/home')->with('error', 'There was an error with your request!');
|
|
||||||
|
return redirect('/settings/home')->with('error', 'There was an error with your request!');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function email()
|
public function email()
|
||||||
{
|
{
|
||||||
return view('settings.email');
|
return view('settings.email');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function avatar()
|
public function avatar()
|
||||||
{
|
{
|
||||||
return view('settings.avatar');
|
return view('settings.avatar');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function accessibility()
|
public function accessibility()
|
||||||
{
|
{
|
||||||
$settings = Auth::user()->settings;
|
$settings = Auth::user()->settings;
|
||||||
return view('settings.accessibility', compact('settings'));
|
|
||||||
|
return view('settings.accessibility', compact('settings'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function accessibilityStore(Request $request)
|
public function accessibilityStore(Request $request)
|
||||||
{
|
{
|
||||||
$settings = Auth::user()->settings;
|
$settings = Auth::user()->settings;
|
||||||
$fields = [
|
$fields = [
|
||||||
'compose_media_descriptions',
|
'compose_media_descriptions',
|
||||||
'reduce_motion',
|
'reduce_motion',
|
||||||
'optimize_screen_reader',
|
'optimize_screen_reader',
|
||||||
'high_contrast_mode',
|
'high_contrast_mode',
|
||||||
'video_autoplay'
|
'video_autoplay',
|
||||||
];
|
];
|
||||||
foreach($fields as $field) {
|
foreach ($fields as $field) {
|
||||||
$form = $request->input($field);
|
$form = $request->input($field);
|
||||||
if($form == 'on') {
|
if ($form == 'on') {
|
||||||
$settings->{$field} = true;
|
$settings->{$field} = true;
|
||||||
} else {
|
} else {
|
||||||
$settings->{$field} = false;
|
$settings->{$field} = false;
|
||||||
}
|
}
|
||||||
$settings->save();
|
$settings->save();
|
||||||
}
|
}
|
||||||
return redirect(route('settings.accessibility'))->with('status', 'Settings successfully updated!');
|
|
||||||
|
return redirect(route('settings.accessibility'))->with('status', 'Settings successfully updated!');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function notifications()
|
public function notifications()
|
||||||
{
|
{
|
||||||
return view('settings.notifications');
|
return view('settings.notifications');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function privacy()
|
public function privacy()
|
||||||
{
|
{
|
||||||
$settings = Auth::user()->settings;
|
$settings = Auth::user()->settings;
|
||||||
$is_private = Auth::user()->profile->is_private;
|
$is_private = Auth::user()->profile->is_private;
|
||||||
$settings['is_private'] = (bool) $is_private;
|
$settings['is_private'] = (bool) $is_private;
|
||||||
return view('settings.privacy', compact('settings'));
|
|
||||||
|
return view('settings.privacy', compact('settings'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function privacyStore(Request $request)
|
public function privacyStore(Request $request)
|
||||||
{
|
{
|
||||||
$settings = Auth::user()->settings;
|
$settings = Auth::user()->settings;
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$fields = [
|
$fields = [
|
||||||
'is_private',
|
'is_private',
|
||||||
'crawlable',
|
'crawlable',
|
||||||
'show_profile_follower_count',
|
'show_profile_follower_count',
|
||||||
'show_profile_following_count'
|
'show_profile_following_count',
|
||||||
];
|
];
|
||||||
foreach($fields as $field) {
|
foreach ($fields as $field) {
|
||||||
$form = $request->input($field);
|
$form = $request->input($field);
|
||||||
if($field == 'is_private') {
|
if ($field == 'is_private') {
|
||||||
if($form == 'on') {
|
if ($form == 'on') {
|
||||||
$profile->{$field} = true;
|
$profile->{$field} = true;
|
||||||
$settings->show_guests = false;
|
$settings->show_guests = false;
|
||||||
$settings->show_discover = false;
|
$settings->show_discover = false;
|
||||||
$profile->save();
|
$profile->save();
|
||||||
|
} else {
|
||||||
|
$profile->{$field} = false;
|
||||||
|
$profile->save();
|
||||||
|
}
|
||||||
|
} elseif ($field == 'crawlable') {
|
||||||
|
if ($form == 'on') {
|
||||||
|
$settings->{$field} = false;
|
||||||
|
} else {
|
||||||
|
$settings->{$field} = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$profile->{$field} = false;
|
if ($form == 'on') {
|
||||||
$profile->save();
|
$settings->{$field} = true;
|
||||||
|
} else {
|
||||||
|
$settings->{$field} = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} elseif($field == 'crawlable') {
|
$settings->save();
|
||||||
if($form == 'on') {
|
}
|
||||||
$settings->{$field} = false;
|
|
||||||
} else {
|
return redirect(route('settings.privacy'))->with('status', 'Settings successfully updated!');
|
||||||
$settings->{$field} = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if($form == 'on') {
|
|
||||||
$settings->{$field} = true;
|
|
||||||
} else {
|
|
||||||
$settings->{$field} = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$settings->save();
|
|
||||||
}
|
|
||||||
return redirect(route('settings.privacy'))->with('status', 'Settings successfully updated!');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function security()
|
public function security()
|
||||||
{
|
{
|
||||||
$sessions = DB::table('sessions')
|
$sessions = DB::table('sessions')
|
||||||
->whereUserId(Auth::id())
|
->whereUserId(Auth::id())
|
||||||
->limit(20)
|
->limit(20)
|
||||||
->get();
|
->get();
|
||||||
$activity = AccountLog::whereUserId(Auth::id())
|
$activity = AccountLog::whereUserId(Auth::id())
|
||||||
->orderBy('created_at','desc')
|
->orderBy('created_at', 'desc')
|
||||||
->limit(50)
|
->limit(50)
|
||||||
->get();
|
->get();
|
||||||
return view('settings.security', compact('sessions', 'activity'));
|
|
||||||
|
return view('settings.security', compact('sessions', 'activity'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applications()
|
public function applications()
|
||||||
{
|
{
|
||||||
return view('settings.applications');
|
return view('settings.applications');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataExport()
|
public function dataExport()
|
||||||
{
|
{
|
||||||
return view('settings.dataexport');
|
return view('settings.dataexport');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataImport()
|
public function dataImport()
|
||||||
{
|
{
|
||||||
return view('settings.import.home');
|
return view('settings.import.home');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataImportInstagram()
|
public function dataImportInstagram()
|
||||||
{
|
{
|
||||||
return view('settings.import.instagram.home');
|
return view('settings.import.instagram.home');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function developers()
|
public function developers()
|
||||||
{
|
{
|
||||||
return view('settings.developers');
|
return view('settings.developers');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,24 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App, Auth, Cache;
|
use App;
|
||||||
use Illuminate\Http\Request;
|
use App\Follower;
|
||||||
use App\{Follower, Profile, Status, User};
|
use App\Profile;
|
||||||
|
use App\Status;
|
||||||
|
use App\User;
|
||||||
use App\Util\Lexer\PrettyNumber;
|
use App\Util\Lexer\PrettyNumber;
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class SiteController extends Controller
|
class SiteController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function home()
|
public function home()
|
||||||
{
|
{
|
||||||
if(Auth::check()) {
|
if (Auth::check()) {
|
||||||
return $this->homeTimeline();
|
return $this->homeTimeline();
|
||||||
} else {
|
} else {
|
||||||
return $this->homeGuest();
|
return $this->homeGuest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,40 +30,44 @@ class SiteController extends Controller
|
||||||
|
|
||||||
public function homeTimeline()
|
public function homeTimeline()
|
||||||
{
|
{
|
||||||
// TODO: Use redis for timelines
|
// TODO: Use redis for timelines
|
||||||
$following = Follower::whereProfileId(Auth::user()->profile->id)->pluck('following_id');
|
$following = Follower::whereProfileId(Auth::user()->profile->id)->pluck('following_id');
|
||||||
$following->push(Auth::user()->profile->id);
|
$following->push(Auth::user()->profile->id);
|
||||||
$timeline = Status::whereIn('profile_id', $following)
|
$timeline = Status::whereIn('profile_id', $following)
|
||||||
->whereHas('media')
|
->whereHas('media')
|
||||||
->orderBy('id','desc')
|
->orderBy('id', 'desc')
|
||||||
->withCount(['comments', 'likes', 'shares'])
|
->withCount(['comments', 'likes', 'shares'])
|
||||||
->simplePaginate(20);
|
->simplePaginate(20);
|
||||||
$type = 'personal';
|
$type = 'personal';
|
||||||
return view('timeline.template', compact('timeline', 'type'));
|
|
||||||
|
return view('timeline.template', compact('timeline', 'type'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function changeLocale(Request $request, $locale)
|
public function changeLocale(Request $request, $locale)
|
||||||
{
|
{
|
||||||
if(!App::isLocale($locale)) {
|
if (!App::isLocale($locale)) {
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
App::setLocale($locale);
|
App::setLocale($locale);
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function about()
|
public function about()
|
||||||
{
|
{
|
||||||
$res = Cache::remember('site:page:about', 15, function() {
|
$res = Cache::remember('site:page:about', 15, function () {
|
||||||
$statuses = Status::whereHas('media')
|
$statuses = Status::whereHas('media')
|
||||||
->whereNull('in_reply_to_id')
|
->whereNull('in_reply_to_id')
|
||||||
->whereNull('reblog_of_id')
|
->whereNull('reblog_of_id')
|
||||||
->count();
|
->count();
|
||||||
$statusCount = PrettyNumber::convert($statuses);
|
$statusCount = PrettyNumber::convert($statuses);
|
||||||
$userCount = PrettyNumber::convert(User::count());
|
$userCount = PrettyNumber::convert(User::count());
|
||||||
$remoteCount = PrettyNumber::convert(Profile::whereNotNull('remote_url')->count());
|
$remoteCount = PrettyNumber::convert(Profile::whereNotNull('remote_url')->count());
|
||||||
$adminContact = User::whereIsAdmin(true)->first();
|
$adminContact = User::whereIsAdmin(true)->first();
|
||||||
return view('site.about')->with(compact('statusCount', 'userCount', 'remoteCount', 'adminContact'))->render();
|
|
||||||
|
return view('site.about')->with(compact('statusCount', 'userCount', 'remoteCount', 'adminContact'))->render();
|
||||||
});
|
});
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,18 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Auth, Cache;
|
|
||||||
use League\Fractal;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Vinkla\Hashids\Facades\Hashids;
|
|
||||||
use App\{Media, Profile, Status, User};
|
|
||||||
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
||||||
|
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
||||||
|
use App\Jobs\StatusPipeline\StatusDelete;
|
||||||
|
use App\Media;
|
||||||
|
use App\Profile;
|
||||||
|
use App\Status;
|
||||||
use App\Transformer\ActivityPub\StatusTransformer;
|
use App\Transformer\ActivityPub\StatusTransformer;
|
||||||
use App\Jobs\StatusPipeline\{NewStatusPipeline, StatusDelete};
|
use App\User;
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use League\Fractal;
|
||||||
|
|
||||||
class StatusController extends Controller
|
class StatusController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -21,8 +25,8 @@ class StatusController extends Controller
|
||||||
->withCount(['likes', 'comments', 'media'])
|
->withCount(['likes', 'comments', 'media'])
|
||||||
->findOrFail($id);
|
->findOrFail($id);
|
||||||
|
|
||||||
if($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
|
if ($request->wantsJson() && config('pixelfed.activitypub_enabled')) {
|
||||||
return $this->showActivityPub($request, $status);
|
return $this->showActivityPub($request, $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
$template = $this->detectTemplate($status);
|
$template = $this->detectTemplate($status);
|
||||||
|
@ -34,25 +38,28 @@ class StatusController extends Controller
|
||||||
|
|
||||||
protected function detectTemplate($status)
|
protected function detectTemplate($status)
|
||||||
{
|
{
|
||||||
$template = Cache::rememberForever('template:status:type:'.$status->id, function () use($status) {
|
$template = Cache::rememberForever('template:status:type:'.$status->id, function () use ($status) {
|
||||||
$template = 'status.show.photo';
|
$template = 'status.show.photo';
|
||||||
if(!$status->media_path && $status->in_reply_to_id) {
|
if (!$status->media_path && $status->in_reply_to_id) {
|
||||||
$template = 'status.reply';
|
$template = 'status.reply';
|
||||||
}
|
}
|
||||||
if($status->media->count() > 1) {
|
if ($status->media->count() > 1) {
|
||||||
$template = 'status.show.album';
|
$template = 'status.show.album';
|
||||||
}
|
}
|
||||||
if($status->viewType() == 'video') {
|
if ($status->viewType() == 'video') {
|
||||||
$template = 'status.show.video';
|
$template = 'status.show.video';
|
||||||
}
|
}
|
||||||
return $template;
|
|
||||||
|
return $template;
|
||||||
});
|
});
|
||||||
|
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function compose()
|
public function compose()
|
||||||
{
|
{
|
||||||
$this->authCheck();
|
$this->authCheck();
|
||||||
|
|
||||||
return view('status.compose');
|
return view('status.compose');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,27 +70,27 @@ class StatusController extends Controller
|
||||||
|
|
||||||
$size = Media::whereUserId($user->id)->sum('size') / 1000;
|
$size = Media::whereUserId($user->id)->sum('size') / 1000;
|
||||||
$limit = (int) config('pixelfed.max_account_size');
|
$limit = (int) config('pixelfed.max_account_size');
|
||||||
if($size >= $limit) {
|
if ($size >= $limit) {
|
||||||
return redirect()->back()->with('error', 'You have exceeded your storage limit. Please click <a href="#">here</a> for more info.');
|
return redirect()->back()->with('error', 'You have exceeded your storage limit. Please click <a href="#">here</a> for more info.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'photo.*' => 'required|mimes:jpeg,png,gif|max:' . config('pixelfed.max_photo_size'),
|
'photo.*' => 'required|mimes:jpeg,png,gif|max:'.config('pixelfed.max_photo_size'),
|
||||||
'caption' => 'string|max:' . config('pixelfed.max_caption_length'),
|
'caption' => 'string|max:'.config('pixelfed.max_caption_length'),
|
||||||
'cw' => 'nullable|string',
|
'cw' => 'nullable|string',
|
||||||
'filter_class' => 'nullable|string',
|
'filter_class' => 'nullable|string',
|
||||||
'filter_name' => 'nullable|string',
|
'filter_name' => 'nullable|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if(count($request->file('photo')) > config('pixelfed.max_album_length')) {
|
if (count($request->file('photo')) > config('pixelfed.max_album_length')) {
|
||||||
return redirect()->back()->with('error', 'Too many files, max limit per post: ' . config('pixelfed.max_album_length'));
|
return redirect()->back()->with('error', 'Too many files, max limit per post: '.config('pixelfed.max_album_length'));
|
||||||
}
|
}
|
||||||
$cw = $request->filled('cw') && $request->cw == 'on' ? true : false;
|
$cw = $request->filled('cw') && $request->cw == 'on' ? true : false;
|
||||||
$monthHash = hash('sha1', date('Y') . date('m'));
|
$monthHash = hash('sha1', date('Y').date('m'));
|
||||||
$userHash = hash('sha1', $user->id . (string) $user->created_at);
|
$userHash = hash('sha1', $user->id.(string) $user->created_at);
|
||||||
$profile = $user->profile;
|
$profile = $user->profile;
|
||||||
|
|
||||||
$status = new Status;
|
$status = new Status();
|
||||||
$status->profile_id = $profile->id;
|
$status->profile_id = $profile->id;
|
||||||
$status->caption = strip_tags($request->caption);
|
$status->caption = strip_tags($request->caption);
|
||||||
$status->is_nsfw = $cw;
|
$status->is_nsfw = $cw;
|
||||||
|
@ -93,23 +100,23 @@ class StatusController extends Controller
|
||||||
$photos = $request->file('photo');
|
$photos = $request->file('photo');
|
||||||
$order = 1;
|
$order = 1;
|
||||||
foreach ($photos as $k => $v) {
|
foreach ($photos as $k => $v) {
|
||||||
$storagePath = "public/m/{$monthHash}/{$userHash}";
|
$storagePath = "public/m/{$monthHash}/{$userHash}";
|
||||||
$path = $v->store($storagePath);
|
$path = $v->store($storagePath);
|
||||||
$hash = \hash_file('sha256', $v);
|
$hash = \hash_file('sha256', $v);
|
||||||
$media = new Media;
|
$media = new Media();
|
||||||
$media->status_id = $status->id;
|
$media->status_id = $status->id;
|
||||||
$media->profile_id = $profile->id;
|
$media->profile_id = $profile->id;
|
||||||
$media->user_id = $user->id;
|
$media->user_id = $user->id;
|
||||||
$media->media_path = $path;
|
$media->media_path = $path;
|
||||||
$media->original_sha256 = $hash;
|
$media->original_sha256 = $hash;
|
||||||
$media->size = $v->getClientSize();
|
$media->size = $v->getClientSize();
|
||||||
$media->mime = $v->getClientMimeType();
|
$media->mime = $v->getClientMimeType();
|
||||||
$media->filter_class = $request->input('filter_class');
|
$media->filter_class = $request->input('filter_class');
|
||||||
$media->filter_name = $request->input('filter_name');
|
$media->filter_name = $request->input('filter_name');
|
||||||
$media->order = $order;
|
$media->order = $order;
|
||||||
$media->save();
|
$media->save();
|
||||||
ImageOptimize::dispatch($media);
|
ImageOptimize::dispatch($media);
|
||||||
$order++;
|
$order++;
|
||||||
}
|
}
|
||||||
|
|
||||||
NewStatusPipeline::dispatch($status);
|
NewStatusPipeline::dispatch($status);
|
||||||
|
@ -121,19 +128,19 @@ class StatusController extends Controller
|
||||||
|
|
||||||
public function delete(Request $request)
|
public function delete(Request $request)
|
||||||
{
|
{
|
||||||
if(!Auth::check()) {
|
if (!Auth::check()) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'type' => 'required|string',
|
'type' => 'required|string',
|
||||||
'item' => 'required|integer|min:1'
|
'item' => 'required|integer|min:1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$status = Status::findOrFail($request->input('item'));
|
$status = Status::findOrFail($request->input('item'));
|
||||||
|
|
||||||
if($status->profile_id === Auth::user()->profile->id || Auth::user()->is_admin == true) {
|
if ($status->profile_id === Auth::user()->profile->id || Auth::user()->is_admin == true) {
|
||||||
StatusDelete::dispatch($status);
|
StatusDelete::dispatch($status);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect(Auth::user()->url());
|
return redirect(Auth::user()->url());
|
||||||
|
@ -153,26 +160,26 @@ class StatusController extends Controller
|
||||||
$exists = Status::whereProfileId(Auth::user()->profile->id)
|
$exists = Status::whereProfileId(Auth::user()->profile->id)
|
||||||
->whereReblogOfId($status->id)
|
->whereReblogOfId($status->id)
|
||||||
->count();
|
->count();
|
||||||
if($exists !== 0) {
|
if ($exists !== 0) {
|
||||||
$shares = Status::whereProfileId(Auth::user()->profile->id)
|
$shares = Status::whereProfileId(Auth::user()->profile->id)
|
||||||
->whereReblogOfId($status->id)
|
->whereReblogOfId($status->id)
|
||||||
->get();
|
->get();
|
||||||
foreach($shares as $share) {
|
foreach ($shares as $share) {
|
||||||
$share->delete();
|
$share->delete();
|
||||||
$count--;
|
$count--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$share = new Status;
|
$share = new Status();
|
||||||
$share->profile_id = $profile->id;
|
$share->profile_id = $profile->id;
|
||||||
$share->reblog_of_id = $status->id;
|
$share->reblog_of_id = $status->id;
|
||||||
$share->save();
|
$share->save();
|
||||||
$count++;
|
$count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
$response = ['code' => 200, 'msg' => 'Share saved', 'count' => $count];
|
$response = ['code' => 200, 'msg' => 'Share saved', 'count' => $count];
|
||||||
} else {
|
} else {
|
||||||
$response = redirect($status->url());
|
$response = redirect($status->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
|
@ -180,10 +187,11 @@ class StatusController extends Controller
|
||||||
|
|
||||||
public function showActivityPub(Request $request, $status)
|
public function showActivityPub(Request $request, $status)
|
||||||
{
|
{
|
||||||
$fractal = new Fractal\Manager();
|
$fractal = new Fractal\Manager();
|
||||||
$resource = new Fractal\Resource\Item($status, new StatusTransformer);
|
$resource = new Fractal\Resource\Item($status, new StatusTransformer());
|
||||||
$res = $fractal->createData($resource)->toArray();
|
$res = $fractal->createData($resource)->toArray();
|
||||||
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
|
|
||||||
|
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Request $request, $username, $id)
|
public function edit(Request $request, $username, $id)
|
||||||
|
@ -193,10 +201,10 @@ class StatusController extends Controller
|
||||||
$status = Status::whereProfileId($user->id)
|
$status = Status::whereProfileId($user->id)
|
||||||
->with(['media'])
|
->with(['media'])
|
||||||
->findOrFail($id);
|
->findOrFail($id);
|
||||||
|
|
||||||
return view('status.edit', compact('user', 'status'));
|
return view('status.edit', compact('user', 'status'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function editStore(Request $request, $username, $id)
|
public function editStore(Request $request, $username, $id)
|
||||||
{
|
{
|
||||||
$this->authCheck();
|
$this->authCheck();
|
||||||
|
@ -206,9 +214,9 @@ class StatusController extends Controller
|
||||||
->findOrFail($id);
|
->findOrFail($id);
|
||||||
|
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'id' => 'required|integer|min:1',
|
'id' => 'required|integer|min:1',
|
||||||
'caption' => 'nullable',
|
'caption' => 'nullable',
|
||||||
'filter' => 'nullable|alpha_dash|max:30'
|
'filter' => 'nullable|alpha_dash|max:30',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$id = $request->input('id');
|
$id = $request->input('id');
|
||||||
|
@ -221,27 +229,27 @@ class StatusController extends Controller
|
||||||
|
|
||||||
$changed = false;
|
$changed = false;
|
||||||
|
|
||||||
if($media->caption != $caption) {
|
if ($media->caption != $caption) {
|
||||||
$media->caption = $caption;
|
$media->caption = $caption;
|
||||||
$changed = true;
|
$changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($media->filter_class != $filter) {
|
if ($media->filter_class != $filter) {
|
||||||
$media->filter_class = $filter;
|
$media->filter_class = $filter;
|
||||||
$changed = true;
|
$changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($changed === true) {
|
if ($changed === true) {
|
||||||
$media->save();
|
$media->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([], 200);
|
return response()->json([], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function authCheck()
|
protected function authCheck()
|
||||||
{
|
{
|
||||||
if(Auth::check() == false)
|
if (Auth::check() == false) {
|
||||||
{
|
abort(403);
|
||||||
abort(403);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use App\Profile;
|
|
||||||
|
|
||||||
class StoryController extends Controller
|
class StoryController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,57 +2,61 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use App\Follower;
|
||||||
|
use App\Profile;
|
||||||
|
use App\Status;
|
||||||
|
use App\User;
|
||||||
|
use App\UserFilter;
|
||||||
use Auth;
|
use Auth;
|
||||||
use App\{Follower, Profile, Status, User, UserFilter};
|
|
||||||
|
|
||||||
class TimelineController extends Controller
|
class TimelineController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function personal()
|
public function personal()
|
||||||
{
|
{
|
||||||
$pid = Auth::user()->profile->id;
|
$pid = Auth::user()->profile->id;
|
||||||
// TODO: Use redis for timelines
|
// TODO: Use redis for timelines
|
||||||
$following = Follower::whereProfileId($pid)->pluck('following_id');
|
$following = Follower::whereProfileId($pid)->pluck('following_id');
|
||||||
$following->push($pid);
|
$following->push($pid);
|
||||||
$filtered = UserFilter::whereUserId($pid)
|
$filtered = UserFilter::whereUserId($pid)
|
||||||
->whereFilterableType('App\Profile')
|
->whereFilterableType('App\Profile')
|
||||||
->whereIn('filter_type', ['mute', 'block'])
|
->whereIn('filter_type', ['mute', 'block'])
|
||||||
->pluck('filterable_id');
|
->pluck('filterable_id');
|
||||||
$timeline = Status::whereIn('profile_id', $following)
|
$timeline = Status::whereIn('profile_id', $following)
|
||||||
->whereNotIn('profile_id', $filtered)
|
->whereNotIn('profile_id', $filtered)
|
||||||
->orderBy('id','desc')
|
->orderBy('id', 'desc')
|
||||||
->withCount(['comments', 'likes'])
|
->withCount(['comments', 'likes'])
|
||||||
->simplePaginate(20);
|
->simplePaginate(20);
|
||||||
$type = 'personal';
|
$type = 'personal';
|
||||||
return view('timeline.template', compact('timeline', 'type'));
|
|
||||||
|
return view('timeline.template', compact('timeline', 'type'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function local()
|
public function local()
|
||||||
{
|
{
|
||||||
// TODO: Use redis for timelines
|
// TODO: Use redis for timelines
|
||||||
// $timeline = Timeline::build()->local();
|
// $timeline = Timeline::build()->local();
|
||||||
$pid = Auth::user()->profile->id;
|
$pid = Auth::user()->profile->id;
|
||||||
|
|
||||||
$filtered = UserFilter::whereUserId($pid)
|
$filtered = UserFilter::whereUserId($pid)
|
||||||
->whereFilterableType('App\Profile')
|
->whereFilterableType('App\Profile')
|
||||||
->whereIn('filter_type', ['mute', 'block'])
|
->whereIn('filter_type', ['mute', 'block'])
|
||||||
->pluck('filterable_id');
|
->pluck('filterable_id');
|
||||||
$private = Profile::whereIsPrivate(true)->pluck('id');
|
$private = Profile::whereIsPrivate(true)->pluck('id');
|
||||||
$filtered = $filtered->merge($private);
|
$filtered = $filtered->merge($private);
|
||||||
$timeline = Status::whereHas('media')
|
$timeline = Status::whereHas('media')
|
||||||
->whereNotIn('profile_id', $filtered)
|
->whereNotIn('profile_id', $filtered)
|
||||||
->whereNull('in_reply_to_id')
|
->whereNull('in_reply_to_id')
|
||||||
->whereNull('reblog_of_id')
|
->whereNull('reblog_of_id')
|
||||||
->withCount(['comments', 'likes'])
|
->withCount(['comments', 'likes'])
|
||||||
->orderBy('id','desc')
|
->orderBy('id', 'desc')
|
||||||
->simplePaginate(20);
|
->simplePaginate(20);
|
||||||
$type = 'local';
|
$type = 'local';
|
||||||
return view('timeline.template', compact('timeline', 'type'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return view('timeline.template', compact('timeline', 'type'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,15 +51,15 @@ class Kernel extends HttpKernel
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $routeMiddleware = [
|
protected $routeMiddleware = [
|
||||||
'admin' => \App\Http\Middleware\Admin::class,
|
'admin' => \App\Http\Middleware\Admin::class,
|
||||||
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
'validemail' => \App\Http\Middleware\EmailVerificationCheck::class,
|
'validemail' => \App\Http\Middleware\EmailVerificationCheck::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,22 @@
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
use Auth, Closure;
|
use Auth;
|
||||||
|
use Closure;
|
||||||
|
|
||||||
class Admin
|
class Admin
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param \Closure $next
|
* @param \Closure $next
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle($request, Closure $next)
|
||||||
{
|
{
|
||||||
if(Auth::check() == false || Auth::user()->is_admin == false) {
|
if (Auth::check() == false || Auth::user()->is_admin == false) {
|
||||||
return redirect(config('app.url'));
|
return redirect(config('app.url'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,26 +2,28 @@
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
use Auth, Closure;
|
use Closure;
|
||||||
|
|
||||||
class EmailVerificationCheck
|
class EmailVerificationCheck
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param \Closure $next
|
* @param \Closure $next
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle($request, Closure $next)
|
||||||
{
|
{
|
||||||
if($request->user() &&
|
if ($request->user() &&
|
||||||
config('pixelfed.enforce_email_verification') &&
|
config('pixelfed.enforce_email_verification') &&
|
||||||
is_null($request->user()->email_verified_at) &&
|
is_null($request->user()->email_verified_at) &&
|
||||||
!$request->is('i/verify-email', 'log*', 'i/confirm-email/*', 'settings/home')
|
!$request->is('i/verify-email', 'log*', 'i/confirm-email/*', 'settings/home')
|
||||||
) {
|
) {
|
||||||
return redirect('/i/verify-email');
|
return redirect('/i/verify-email');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,10 @@ class RedirectIfAuthenticated
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param \Closure $next
|
* @param \Closure $next
|
||||||
* @param string|null $guard
|
* @param string|null $guard
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next, $guard = null)
|
public function handle($request, Closure $next, $guard = null)
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Fideloper\Proxy\TrustProxies as Middleware;
|
use Fideloper\Proxy\TrustProxies as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class TrustProxies extends Middleware
|
class TrustProxies extends Middleware
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
namespace App\Jobs\AvatarPipeline;
|
namespace App\Jobs\AvatarPipeline;
|
||||||
|
|
||||||
use \Carbon\Carbon;
|
use App\Avatar;
|
||||||
use Image as Intervention;
|
use App\Profile;
|
||||||
use App\{Avatar, Profile};
|
use Carbon\Carbon;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Image as Intervention;
|
||||||
|
|
||||||
class AvatarOptimize implements ShouldQueue
|
class AvatarOptimize implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -54,16 +55,15 @@ class AvatarOptimize implements ShouldQueue
|
||||||
$avatar->save();
|
$avatar->save();
|
||||||
$this->deleteOldAvatar($avatar->media_path, $this->current);
|
$this->deleteOldAvatar($avatar->media_path, $this->current);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function deleteOldAvatar($new, $current)
|
protected function deleteOldAvatar($new, $current)
|
||||||
{
|
{
|
||||||
if(storage_path('app/' . $new) == $current) {
|
if (storage_path('app/'.$new) == $current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(is_file($current)) {
|
if (is_file($current)) {
|
||||||
@unlink($current);
|
@unlink($current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,23 +2,24 @@
|
||||||
|
|
||||||
namespace App\Jobs\AvatarPipeline;
|
namespace App\Jobs\AvatarPipeline;
|
||||||
|
|
||||||
use App\{Avatar, Profile};
|
use App\Avatar;
|
||||||
use Illuminate\Bus\Queueable;
|
use App\Profile;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use App\Util\Identicon\Preprocessor\HashPreprocessor;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Bitverse\Identicon\Identicon;
|
|
||||||
use Bitverse\Identicon\Color\Color;
|
use Bitverse\Identicon\Color\Color;
|
||||||
use Bitverse\Identicon\Generator\RingsGenerator;
|
use Bitverse\Identicon\Generator\RingsGenerator;
|
||||||
use App\Util\Identicon\Preprocessor\HashPreprocessor;
|
use Bitverse\Identicon\Identicon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class CreateAvatar implements ShouldQueue
|
class CreateAvatar implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $profile;
|
protected $profile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
@ -44,66 +45,63 @@ class CreateAvatar implements ShouldQueue
|
||||||
|
|
||||||
$identicon = new Identicon(new HashPreprocessor('sha256'), $generator);
|
$identicon = new Identicon(new HashPreprocessor('sha256'), $generator);
|
||||||
|
|
||||||
$hash = $username . str_random(12);
|
$hash = $username.str_random(12);
|
||||||
$icon = $identicon->getIcon($hash);
|
$icon = $identicon->getIcon($hash);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$baseDir = storage_path('app/public/avatars');
|
||||||
|
if (!is_dir($baseDir)) {
|
||||||
|
mkdir($baseDir);
|
||||||
|
}
|
||||||
|
|
||||||
$baseDir = storage_path('app/public/avatars');
|
$prefix = $profile->id;
|
||||||
if(!is_dir($baseDir)) {
|
$padded = str_pad($prefix, 12, 0, STR_PAD_LEFT);
|
||||||
mkdir($baseDir);
|
$parts = str_split($padded, 3);
|
||||||
}
|
foreach ($parts as $k => $part) {
|
||||||
|
if ($k == 0) {
|
||||||
$prefix = $profile->id;
|
$prefix = storage_path('app/public/avatars/'.$parts[0]);
|
||||||
$padded = str_pad($prefix, 12, 0, STR_PAD_LEFT);
|
if (!is_dir($prefix)) {
|
||||||
$parts = str_split($padded, 3);
|
mkdir($prefix);
|
||||||
foreach($parts as $k => $part) {
|
}
|
||||||
if($k == 0) {
|
}
|
||||||
$prefix = storage_path('app/public/avatars/'.$parts[0]);
|
if ($k == 1) {
|
||||||
if(!is_dir($prefix)) {
|
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1]);
|
||||||
mkdir($prefix);
|
if (!is_dir($prefix)) {
|
||||||
|
mkdir($prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($k == 2) {
|
||||||
|
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2]);
|
||||||
|
if (!is_dir($prefix)) {
|
||||||
|
mkdir($prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($k == 3) {
|
||||||
|
$avatarpath = 'public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2].'/'.$parts[3];
|
||||||
|
$prefix = storage_path('app/'.$avatarpath);
|
||||||
|
if (!is_dir($prefix)) {
|
||||||
|
mkdir($prefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if($k == 1) {
|
$dir = storage_path('app/'.$avatarpath);
|
||||||
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1]);
|
//$dir = storage_path('app/public/avatars/'.$prefix);
|
||||||
if(!is_dir($prefix)) {
|
if (!is_dir($dir)) {
|
||||||
mkdir($prefix);
|
mkdir($dir);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if($k == 2) {
|
//$path = 'public/avatars/' . $prefix . '/avatar.svg';
|
||||||
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2]);
|
$path = $avatarpath.'/avatar.svg';
|
||||||
if(!is_dir($prefix)) {
|
$basePath = storage_path('app/'.$path);
|
||||||
mkdir($prefix);
|
file_put_contents($basePath, $icon);
|
||||||
}
|
|
||||||
}
|
|
||||||
if($k == 3) {
|
|
||||||
$avatarpath = 'public/avatars/'.$parts[0].'/'.$parts[1].'/'.$parts[2].'/'.$parts[3];
|
|
||||||
$prefix = storage_path('app/'.$avatarpath);
|
|
||||||
if(!is_dir($prefix)) {
|
|
||||||
mkdir($prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$dir = storage_path('app/'.$avatarpath);
|
|
||||||
//$dir = storage_path('app/public/avatars/'.$prefix);
|
|
||||||
if(!is_dir($dir)) {
|
|
||||||
mkdir($dir);
|
|
||||||
}
|
|
||||||
//$path = 'public/avatars/' . $prefix . '/avatar.svg';
|
|
||||||
$path = $avatarpath . '/avatar.svg';
|
|
||||||
$basePath = storage_path('app/' . $path);
|
|
||||||
file_put_contents($basePath, $icon);
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$avatar = new Avatar;
|
$avatar = new Avatar();
|
||||||
$avatar->profile_id = $profile->id;
|
$avatar->profile_id = $profile->id;
|
||||||
$avatar->media_path = $path;
|
$avatar->media_path = $path;
|
||||||
$avatar->thumb_path = $path;
|
$avatar->thumb_path = $path;
|
||||||
$avatar->change_count = 0;
|
$avatar->change_count = 0;
|
||||||
$avatar->last_processed_at = \Carbon\Carbon::now();
|
$avatar->last_processed_at = \Carbon\Carbon::now();
|
||||||
$avatar->save();
|
$avatar->save();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
|
|
||||||
namespace App\Jobs\CommentPipeline;
|
namespace App\Jobs\CommentPipeline;
|
||||||
|
|
||||||
use Cache, Log, Redis;
|
use App\Notification;
|
||||||
use App\{Like, Notification, Status};
|
use App\Status;
|
||||||
use App\Util\Lexer\Hashtag as HashtagLexer;
|
use Cache;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Log;
|
||||||
|
use Redis;
|
||||||
|
|
||||||
class CommentPipeline implements ShouldQueue
|
class CommentPipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -42,13 +44,12 @@ class CommentPipeline implements ShouldQueue
|
||||||
$target = $status->profile;
|
$target = $status->profile;
|
||||||
$actor = $comment->profile;
|
$actor = $comment->profile;
|
||||||
|
|
||||||
if($actor->id === $target->id) {
|
if ($actor->id === $target->id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$notification = new Notification();
|
||||||
$notification = new Notification;
|
|
||||||
$notification->profile_id = $target->id;
|
$notification->profile_id = $target->id;
|
||||||
$notification->actor_id = $actor->id;
|
$notification->actor_id = $actor->id;
|
||||||
$notification->action = 'comment';
|
$notification->action = 'comment';
|
||||||
|
@ -58,16 +59,14 @@ class CommentPipeline implements ShouldQueue
|
||||||
$notification->item_type = "App\Status";
|
$notification->item_type = "App\Status";
|
||||||
$notification->save();
|
$notification->save();
|
||||||
|
|
||||||
Cache::forever('notification.' . $notification->id, $notification);
|
Cache::forever('notification.'.$notification->id, $notification);
|
||||||
|
|
||||||
$redis = Redis::connection();
|
$redis = Redis::connection();
|
||||||
|
|
||||||
$nkey = config('cache.prefix').':user.' . $target->id . '.notifications';
|
$nkey = config('cache.prefix').':user.'.$target->id.'.notifications';
|
||||||
$redis->lpush($nkey, $notification->id);
|
$redis->lpush($nkey, $notification->id);
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error($e);
|
Log::error($e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
namespace App\Jobs\FollowPipeline;
|
namespace App\Jobs\FollowPipeline;
|
||||||
|
|
||||||
use Cache, Log, Redis;
|
use App\Notification;
|
||||||
use App\{Like, Notification};
|
use Cache;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use App\Jobs\FollowPipeline\FollowDiscover;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Log;
|
||||||
|
use Redis;
|
||||||
|
|
||||||
class FollowPipeline implements ShouldQueue
|
class FollowPipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -39,8 +40,7 @@ class FollowPipeline implements ShouldQueue
|
||||||
$target = $follower->target;
|
$target = $follower->target;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$notification = new Notification();
|
||||||
$notification = new Notification;
|
|
||||||
$notification->profile_id = $target->id;
|
$notification->profile_id = $target->id;
|
||||||
$notification->actor_id = $actor->id;
|
$notification->actor_id = $actor->id;
|
||||||
$notification->action = 'follow';
|
$notification->action = 'follow';
|
||||||
|
@ -50,13 +50,12 @@ class FollowPipeline implements ShouldQueue
|
||||||
$notification->item_type = "App\Profile";
|
$notification->item_type = "App\Profile";
|
||||||
$notification->save();
|
$notification->save();
|
||||||
|
|
||||||
Cache::forever('notification.' . $notification->id, $notification);
|
Cache::forever('notification.'.$notification->id, $notification);
|
||||||
|
|
||||||
$redis = Redis::connection();
|
$redis = Redis::connection();
|
||||||
|
|
||||||
$nkey = config('cache.prefix').':user.' . $target->id . '.notifications';
|
$nkey = config('cache.prefix').':user.'.$target->id.'.notifications';
|
||||||
$redis->lpush($nkey, $notification->id);
|
$redis->lpush($nkey, $notification->id);
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error($e);
|
Log::error($e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,12 @@
|
||||||
|
|
||||||
namespace App\Jobs\ImageOptimizePipeline;
|
namespace App\Jobs\ImageOptimizePipeline;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use App\Media;
|
||||||
use ImageOptimizer;
|
|
||||||
use App\{Media, Status};
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ImageOptimize implements ShouldQueue
|
class ImageOptimize implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -35,8 +33,8 @@ class ImageOptimize implements ShouldQueue
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$media = $this->media;
|
$media = $this->media;
|
||||||
$path = storage_path('app/'. $media->media_path);
|
$path = storage_path('app/'.$media->media_path);
|
||||||
if(!is_file($path)) {
|
if (!is_file($path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,13 @@
|
||||||
|
|
||||||
namespace App\Jobs\ImageOptimizePipeline;
|
namespace App\Jobs\ImageOptimizePipeline;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use App\Media;
|
||||||
use ImageOptimizer;
|
|
||||||
use App\{Media, Status};
|
|
||||||
use App\Util\Media\Image;
|
use App\Util\Media\Image;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ImageResize implements ShouldQueue
|
class ImageResize implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -36,16 +34,15 @@ class ImageResize implements ShouldQueue
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$media = $this->media;
|
$media = $this->media;
|
||||||
$path = storage_path('app/'. $media->media_path);
|
$path = storage_path('app/'.$media->media_path);
|
||||||
if(!is_file($path)) {
|
if (!is_file($path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$img = new Image;
|
$img = new Image();
|
||||||
$img->resizeImage($media);
|
$img->resizeImage($media);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageThumbnail::dispatch($media);
|
ImageThumbnail::dispatch($media);
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace App\Jobs\ImageOptimizePipeline;
|
namespace App\Jobs\ImageOptimizePipeline;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use App\Media;
|
||||||
use App\{Media, Status};
|
|
||||||
use App\Util\Media\Image;
|
use App\Util\Media\Image;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ImageThumbnail implements ShouldQueue
|
class ImageThumbnail implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -35,16 +35,15 @@ class ImageThumbnail implements ShouldQueue
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$media = $this->media;
|
$media = $this->media;
|
||||||
$path = storage_path('app/'. $media->media_path);
|
$path = storage_path('app/'.$media->media_path);
|
||||||
if(!is_file($path)) {
|
if (!is_file($path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$img = new Image;
|
$img = new Image();
|
||||||
$img->resizeThumbnail($media);
|
$img->resizeThumbnail($media);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$media->processed_at = Carbon::now();
|
$media->processed_at = Carbon::now();
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
namespace App\Jobs\ImageOptimizePipeline;
|
namespace App\Jobs\ImageOptimizePipeline;
|
||||||
|
|
||||||
use ImageOptimizer;
|
use App\Media;
|
||||||
use App\{Media, Status};
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use ImageOptimizer;
|
||||||
|
|
||||||
class ImageUpdate implements ShouldQueue
|
class ImageUpdate implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@ class ImageUpdate implements ShouldQueue
|
||||||
protected $protectedMimes = [
|
protected $protectedMimes = [
|
||||||
'image/gif',
|
'image/gif',
|
||||||
'image/bmp',
|
'image/bmp',
|
||||||
'video/mp4'
|
'video/mp4',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,18 +40,18 @@ class ImageUpdate implements ShouldQueue
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$media = $this->media;
|
$media = $this->media;
|
||||||
$path = storage_path('app/'. $media->media_path);
|
$path = storage_path('app/'.$media->media_path);
|
||||||
$thumb = storage_path('app/'. $media->thumbnail_path);
|
$thumb = storage_path('app/'.$media->thumbnail_path);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!in_array($media->mime, $this->protectedMimes))
|
if (!in_array($media->mime, $this->protectedMimes)) {
|
||||||
{
|
|
||||||
ImageOptimizer::optimize($thumb);
|
ImageOptimizer::optimize($thumb);
|
||||||
ImageOptimizer::optimize($path);
|
ImageOptimizer::optimize($path);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!is_file($path) || !is_file($thumb)) {
|
if (!is_file($path) || !is_file($thumb)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$photo_size = filesize($path);
|
$photo_size = filesize($path);
|
||||||
|
|
|
@ -5,10 +5,10 @@ namespace App\Jobs\InboxPipeline;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use App\Util\ActivityPub\Inbox;
|
use App\Util\ActivityPub\Inbox;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class InboxWorker implements ShouldQueue
|
class InboxWorker implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -39,5 +39,4 @@ class InboxWorker implements ShouldQueue
|
||||||
{
|
{
|
||||||
(new Inbox($this->request, $this->profile, $this->payload))->handle();
|
(new Inbox($this->request, $this->profile, $this->payload))->handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
namespace App\Jobs\InboxPipeline;
|
namespace App\Jobs\InboxPipeline;
|
||||||
|
|
||||||
use App\Profile;
|
|
||||||
use App\Util\ActivityPub\Inbox;
|
use App\Util\ActivityPub\Inbox;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class SharedInboxWorker implements ShouldQueue
|
class SharedInboxWorker implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,19 +2,23 @@
|
||||||
|
|
||||||
namespace App\Jobs\LikePipeline;
|
namespace App\Jobs\LikePipeline;
|
||||||
|
|
||||||
use Cache, Log, Redis;
|
use App\Like;
|
||||||
use App\{Like, Notification};
|
use App\Notification;
|
||||||
|
use Cache;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Log;
|
||||||
|
use Redis;
|
||||||
|
|
||||||
class LikePipeline implements ShouldQueue
|
class LikePipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $like;
|
protected $like;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
@ -37,7 +41,7 @@ class LikePipeline implements ShouldQueue
|
||||||
$status = $this->like->status;
|
$status = $this->like->status;
|
||||||
$actor = $this->like->actor;
|
$actor = $this->like->actor;
|
||||||
|
|
||||||
if($status->url !== null) {
|
if ($status->url !== null) {
|
||||||
// Ignore notifications to remote statuses
|
// Ignore notifications to remote statuses
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -49,13 +53,12 @@ class LikePipeline implements ShouldQueue
|
||||||
->whereItemType('App\Status')
|
->whereItemType('App\Status')
|
||||||
->count();
|
->count();
|
||||||
|
|
||||||
if($actor->id === $status->profile_id || $exists !== 0) {
|
if ($actor->id === $status->profile_id || $exists !== 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$notification = new Notification();
|
||||||
$notification = new Notification;
|
|
||||||
$notification->profile_id = $status->profile_id;
|
$notification->profile_id = $status->profile_id;
|
||||||
$notification->actor_id = $actor->id;
|
$notification->actor_id = $actor->id;
|
||||||
$notification->action = 'like';
|
$notification->action = 'like';
|
||||||
|
@ -65,12 +68,11 @@ class LikePipeline implements ShouldQueue
|
||||||
$notification->item_type = "App\Status";
|
$notification->item_type = "App\Status";
|
||||||
$notification->save();
|
$notification->save();
|
||||||
|
|
||||||
Cache::forever('notification.' . $notification->id, $notification);
|
Cache::forever('notification.'.$notification->id, $notification);
|
||||||
|
|
||||||
$redis = Redis::connection();
|
$redis = Redis::connection();
|
||||||
$key = config('cache.prefix').':user.' . $status->profile_id . '.notifications';
|
$key = config('cache.prefix').':user.'.$status->profile_id.'.notifications';
|
||||||
$redis->lpush($key, $notification->id);
|
$redis->lpush($key, $notification->id);
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error($e);
|
Log::error($e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
namespace App\Jobs\MentionPipeline;
|
namespace App\Jobs\MentionPipeline;
|
||||||
|
|
||||||
use Cache, Log, Redis;
|
use App\Mention;
|
||||||
use App\{Mention, Notification, Profile, Status};
|
use App\Notification;
|
||||||
|
use App\Status;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class MentionPipeline implements ShouldQueue
|
class MentionPipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -35,7 +36,6 @@ class MentionPipeline implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|
||||||
$status = $this->status;
|
$status = $this->status;
|
||||||
$mention = $this->mention;
|
$mention = $this->mention;
|
||||||
$actor = $this->status->profile;
|
$actor = $this->status->profile;
|
||||||
|
@ -48,13 +48,12 @@ class MentionPipeline implements ShouldQueue
|
||||||
->whereItemType('App\Status')
|
->whereItemType('App\Status')
|
||||||
->count();
|
->count();
|
||||||
|
|
||||||
if($actor->id === $target || $exists !== 0) {
|
if ($actor->id === $target || $exists !== 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$notification = new Notification();
|
||||||
$notification = new Notification;
|
|
||||||
$notification->profile_id = $target;
|
$notification->profile_id = $target;
|
||||||
$notification->actor_id = $actor->id;
|
$notification->actor_id = $actor->id;
|
||||||
$notification->action = 'mention';
|
$notification->action = 'mention';
|
||||||
|
@ -63,10 +62,7 @@ class MentionPipeline implements ShouldQueue
|
||||||
$notification->item_id = $status->id;
|
$notification->item_id = $status->id;
|
||||||
$notification->item_type = "App\Status";
|
$notification->item_type = "App\Status";
|
||||||
$notification->save();
|
$notification->save();
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,20 @@
|
||||||
|
|
||||||
namespace App\Jobs\RemoteFollowPipeline;
|
namespace App\Jobs\RemoteFollowPipeline;
|
||||||
|
|
||||||
use Zttp\Zttp;
|
use App\Jobs\ImageOptimizePipeline\ImageThumbnail;
|
||||||
use Log, Storage;
|
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
||||||
|
use App\Media;
|
||||||
|
use App\Status;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\File;
|
|
||||||
use App\{Media, Profile, Status};
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
use Illuminate\Http\File;
|
||||||
use App\Jobs\ImageOptimizePipeline\ImageThumbnail;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Log;
|
||||||
|
use Storage;
|
||||||
|
use Zttp\Zttp;
|
||||||
|
|
||||||
class RemoteFollowImportRecent implements ShouldQueue
|
class RemoteFollowImportRecent implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -42,7 +44,7 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
'image/jpg',
|
'image/jpg',
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/png',
|
'image/png',
|
||||||
'image/gif'
|
'image/gif',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
$url = ($url == false) ? $this->actor['outbox'] : $url;
|
$url = ($url == false) ? $this->actor['outbox'] : $url;
|
||||||
|
|
||||||
$response = Zttp::withHeaders([
|
$response = Zttp::withHeaders([
|
||||||
'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org'
|
'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org',
|
||||||
])->get($url);
|
])->get($url);
|
||||||
|
|
||||||
$this->outbox = $response->json();
|
$this->outbox = $response->json();
|
||||||
|
@ -73,13 +75,14 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
{
|
{
|
||||||
$types = ['OrderedCollection', 'OrderedCollectionPage'];
|
$types = ['OrderedCollection', 'OrderedCollectionPage'];
|
||||||
|
|
||||||
if(isset($outbox['totalItems']) && $outbox['totalItems'] < 1) {
|
if (isset($outbox['totalItems']) && $outbox['totalItems'] < 1) {
|
||||||
// Skip remote fetch, not enough posts
|
// Skip remote fetch, not enough posts
|
||||||
Log::info('not enough items');
|
Log::info('not enough items');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($outbox['type']) && in_array($outbox['type'], $types)) {
|
if (isset($outbox['type']) && in_array($outbox['type'], $types)) {
|
||||||
Log::info('handle ordered collection');
|
Log::info('handle ordered collection');
|
||||||
$this->handleOrderedCollection();
|
$this->handleOrderedCollection();
|
||||||
}
|
}
|
||||||
|
@ -89,19 +92,20 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
{
|
{
|
||||||
$outbox = $this->outbox;
|
$outbox = $this->outbox;
|
||||||
|
|
||||||
if(!isset($outbox['next']) && !isset($outbox['first']['next']) && $this->cursor !== 1) {
|
if (!isset($outbox['next']) && !isset($outbox['first']['next']) && $this->cursor !== 1) {
|
||||||
$this->cursor = 40;
|
$this->cursor = 40;
|
||||||
$outbox['next'] = false;
|
$outbox['next'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($outbox['type'] == 'OrderedCollectionPage') {
|
if ($outbox['type'] == 'OrderedCollectionPage') {
|
||||||
$this->nextUrl = $outbox['next'];
|
$this->nextUrl = $outbox['next'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($outbox['first']) && !is_array($outbox['first'])) {
|
if (isset($outbox['first']) && !is_array($outbox['first'])) {
|
||||||
// Mastodon detected
|
// Mastodon detected
|
||||||
Log::info('Mastodon detected...');
|
Log::info('Mastodon detected...');
|
||||||
$this->nextUrl = $outbox['first'];
|
$this->nextUrl = $outbox['first'];
|
||||||
|
|
||||||
return $this->fetchOutbox($this->nextUrl);
|
return $this->fetchOutbox($this->nextUrl);
|
||||||
} else {
|
} else {
|
||||||
// Pleroma detected.
|
// Pleroma detected.
|
||||||
|
@ -110,33 +114,31 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
$orderedItems = isset($outbox['orderedItems']) ? $outbox['orderedItems'] : $outbox['first']['orderedItems'];
|
$orderedItems = isset($outbox['orderedItems']) ? $outbox['orderedItems'] : $outbox['first']['orderedItems'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($orderedItems as $item) {
|
||||||
foreach($orderedItems as $item) {
|
|
||||||
Log::info('Parsing items...');
|
Log::info('Parsing items...');
|
||||||
$parsed = $this->parseObject($item);
|
$parsed = $this->parseObject($item);
|
||||||
if($parsed !== 0) {
|
if ($parsed !== 0) {
|
||||||
Log::info('Found media!');
|
Log::info('Found media!');
|
||||||
$this->importActivity($item);
|
$this->importActivity($item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->cursor < 40 && $this->mediaCount < 9) {
|
if ($this->cursor < 40 && $this->mediaCount < 9) {
|
||||||
$this->cursor++;
|
$this->cursor++;
|
||||||
$this->mediaCount++;
|
$this->mediaCount++;
|
||||||
$this->fetchOutbox($this->nextUrl);
|
$this->fetchOutbox($this->nextUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parseObject($parsed)
|
public function parseObject($parsed)
|
||||||
{
|
{
|
||||||
if($parsed['type'] !== 'Create') {
|
if ($parsed['type'] !== 'Create') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$activity = $parsed['object'];
|
$activity = $parsed['object'];
|
||||||
|
|
||||||
if(isset($activity['attachment']) && !empty($activity['attachment'])) {
|
if (isset($activity['attachment']) && !empty($activity['attachment'])) {
|
||||||
return $this->detectSupportedMedia($activity['attachment']);
|
return $this->detectSupportedMedia($activity['attachment']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +148,7 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
$supported = $this->supported;
|
$supported = $this->supported;
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
||||||
foreach($attachments as $media) {
|
foreach ($attachments as $media) {
|
||||||
$mime = $media['mediaType'];
|
$mime = $media['mediaType'];
|
||||||
$count = in_array($mime, $supported) ? ($count + 1) : $count;
|
$count = in_array($mime, $supported) ? ($count + 1) : $count;
|
||||||
}
|
}
|
||||||
|
@ -161,11 +163,11 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
$attachments = $activity['object']['attachment'];
|
$attachments = $activity['object']['attachment'];
|
||||||
$caption = str_limit($activity['object']['content'], 125);
|
$caption = str_limit($activity['object']['content'], 125);
|
||||||
|
|
||||||
if(Status::whereUrl($activity['id'])->count() !== 0) {
|
if (Status::whereUrl($activity['id'])->count() !== 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$status = new Status;
|
$status = new Status();
|
||||||
$status->profile_id = $profile->id;
|
$status->profile_id = $profile->id;
|
||||||
$status->url = $activity['id'];
|
$status->url = $activity['id'];
|
||||||
$status->local = false;
|
$status->local = false;
|
||||||
|
@ -174,56 +176,56 @@ class RemoteFollowImportRecent implements ShouldQueue
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
||||||
foreach($attachments as $media) {
|
foreach ($attachments as $media) {
|
||||||
Log::info($media['mediaType'] . ' - ' . $media['url']);
|
Log::info($media['mediaType'].' - '.$media['url']);
|
||||||
$url = $media['url'];
|
$url = $media['url'];
|
||||||
$mime = $media['mediaType'];
|
$mime = $media['mediaType'];
|
||||||
if(!in_array($mime, $supported)) {
|
if (!in_array($mime, $supported)) {
|
||||||
Log::info('Invalid media, skipping. ' . $mime);
|
Log::info('Invalid media, skipping. '.$mime);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$count++;
|
$count++;
|
||||||
|
|
||||||
if($count === 1) {
|
if ($count === 1) {
|
||||||
$status->save();
|
$status->save();
|
||||||
}
|
}
|
||||||
$this->importMedia($url, $mime, $status);
|
$this->importMedia($url, $mime, $status);
|
||||||
}
|
}
|
||||||
Log::info(count($attachments) . ' media found...');
|
Log::info(count($attachments).' media found...');
|
||||||
|
|
||||||
if($count !== 0) {
|
if ($count !== 0) {
|
||||||
NewStatusPipeline::dispatch($status, $status->media->first());
|
NewStatusPipeline::dispatch($status, $status->media->first());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function importMedia($url, $mime, $status)
|
public function importMedia($url, $mime, $status)
|
||||||
{
|
{
|
||||||
$user = $this->profile;
|
$user = $this->profile;
|
||||||
$monthHash = hash('sha1', date('Y') . date('m'));
|
$monthHash = hash('sha1', date('Y').date('m'));
|
||||||
$userHash = hash('sha1', $user->id . (string) $user->created_at);
|
$userHash = hash('sha1', $user->id.(string) $user->created_at);
|
||||||
$storagePath = "public/m/{$monthHash}/{$userHash}";
|
$storagePath = "public/m/{$monthHash}/{$userHash}";
|
||||||
try {
|
|
||||||
$info = pathinfo($url);
|
|
||||||
$img = file_get_contents($url);
|
|
||||||
$file = '/tmp/' . str_random(12) . $info['basename'];
|
|
||||||
file_put_contents($file, $img);
|
|
||||||
$path = Storage::putFile($storagePath, new File($file), 'public');
|
|
||||||
|
|
||||||
$media = new Media;
|
try {
|
||||||
$media->status_id = $status->id;
|
$info = pathinfo($url);
|
||||||
$media->profile_id = $status->profile_id;
|
$img = file_get_contents($url);
|
||||||
$media->user_id = null;
|
$file = '/tmp/'.str_random(12).$info['basename'];
|
||||||
$media->media_path = $path;
|
file_put_contents($file, $img);
|
||||||
$media->size = 0;
|
$path = Storage::putFile($storagePath, new File($file), 'public');
|
||||||
$media->mime = $mime;
|
|
||||||
$media->save();
|
|
||||||
|
|
||||||
ImageThumbnail::dispatch($media);
|
$media = new Media();
|
||||||
|
$media->status_id = $status->id;
|
||||||
|
$media->profile_id = $status->profile_id;
|
||||||
|
$media->user_id = null;
|
||||||
|
$media->media_path = $path;
|
||||||
|
$media->size = 0;
|
||||||
|
$media->mime = $mime;
|
||||||
|
$media->save();
|
||||||
|
|
||||||
return true;
|
ImageThumbnail::dispatch($media);
|
||||||
} catch (Exception $e) {
|
|
||||||
return false;
|
return true;
|
||||||
}
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,17 @@
|
||||||
|
|
||||||
namespace App\Jobs\RemoteFollowPipeline;
|
namespace App\Jobs\RemoteFollowPipeline;
|
||||||
|
|
||||||
use Zttp\Zttp;
|
use App\Jobs\AvatarPipeline\CreateAvatar;
|
||||||
use App\{Profile};
|
use App\{Profile};
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use HttpSignatures\Context;
|
use HttpSignatures\Context;
|
||||||
use HttpSignatures\GuzzleHttpSignatures;
|
use HttpSignatures\GuzzleHttpSignatures;
|
||||||
use App\Jobs\RemoteFollowPipeline\RemoteFollowImportRecent;
|
|
||||||
use App\Jobs\AvatarPipeline\CreateAvatar;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Zttp\Zttp;
|
||||||
|
|
||||||
class RemoteFollowPipeline implements ShouldQueue
|
class RemoteFollowPipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -44,27 +43,28 @@ class RemoteFollowPipeline implements ShouldQueue
|
||||||
$follower = $this->follower;
|
$follower = $this->follower;
|
||||||
$url = $this->url;
|
$url = $this->url;
|
||||||
|
|
||||||
if(Profile::whereRemoteUrl($url)->count() !== 0) {
|
if (Profile::whereRemoteUrl($url)->count() !== 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->discover($url);
|
$this->discover($url);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function discover($url)
|
public function discover($url)
|
||||||
{
|
{
|
||||||
$context = new Context([
|
$context = new Context([
|
||||||
'keys' => ['examplekey' => 'secret-key-here'],
|
'keys' => ['examplekey' => 'secret-key-here'],
|
||||||
'algorithm' => 'hmac-sha256',
|
'algorithm' => 'hmac-sha256',
|
||||||
'headers' => ['(request-target)', 'date'],
|
'headers' => ['(request-target)', 'date'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$handlerStack = GuzzleHttpSignatures::defaultHandlerFromContext($context);
|
$handlerStack = GuzzleHttpSignatures::defaultHandlerFromContext($context);
|
||||||
$client = new Client(['handler' => $handlerStack]);
|
$client = new Client(['handler' => $handlerStack]);
|
||||||
$response = Zttp::withHeaders([
|
$response = Zttp::withHeaders([
|
||||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||||
'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org'
|
'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org',
|
||||||
])->get($url);
|
])->get($url);
|
||||||
$this->response = $response->json();
|
$this->response = $response->json();
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ class RemoteFollowPipeline implements ShouldQueue
|
||||||
$username = $res['preferredUsername'];
|
$username = $res['preferredUsername'];
|
||||||
$remoteUsername = "@{$username}@{$domain}";
|
$remoteUsername = "@{$username}@{$domain}";
|
||||||
|
|
||||||
$profile = new Profile;
|
$profile = new Profile();
|
||||||
$profile->user_id = null;
|
$profile->user_id = null;
|
||||||
$profile->domain = $domain;
|
$profile->domain = $domain;
|
||||||
$profile->username = $remoteUsername;
|
$profile->username = $remoteUsername;
|
||||||
|
@ -98,8 +98,8 @@ class RemoteFollowPipeline implements ShouldQueue
|
||||||
$url = $res['inbox'];
|
$url = $res['inbox'];
|
||||||
|
|
||||||
$activity = Zttp::withHeaders(['Content-Type' => 'application/activity+json'])->post($url, [
|
$activity = Zttp::withHeaders(['Content-Type' => 'application/activity+json'])->post($url, [
|
||||||
'type' => 'Follow',
|
'type' => 'Follow',
|
||||||
'object' => $this->follower->url()
|
'object' => $this->follower->url(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace App\Jobs\StatusPipeline;
|
namespace App\Jobs\StatusPipeline;
|
||||||
|
|
||||||
use Cache, Redis;
|
use App\Status;
|
||||||
use App\{Media, Status};
|
use Cache;
|
||||||
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Redis;
|
||||||
|
|
||||||
class NewStatusPipeline implements ShouldQueue
|
class NewStatusPipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -39,9 +39,9 @@ class NewStatusPipeline implements ShouldQueue
|
||||||
StatusEntityLexer::dispatch($status);
|
StatusEntityLexer::dispatch($status);
|
||||||
//StatusActivityPubDeliver::dispatch($status);
|
//StatusActivityPubDeliver::dispatch($status);
|
||||||
|
|
||||||
Cache::forever('post.' . $status->id, $status);
|
Cache::forever('post.'.$status->id, $status);
|
||||||
|
|
||||||
$redis = Redis::connection();
|
$redis = Redis::connection();
|
||||||
$redis->lpush(config('cache.prefix').':user.' . $status->profile_id . '.posts', $status->id);
|
$redis->lpush(config('cache.prefix').':user.'.$status->profile_id.'.posts', $status->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,19 @@
|
||||||
|
|
||||||
namespace App\Jobs\StatusPipeline;
|
namespace App\Jobs\StatusPipeline;
|
||||||
|
|
||||||
use App\{Media, Status};
|
use App\Status;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class StatusActivityPubDeliver implements ShouldQueue
|
class StatusActivityPubDeliver implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $status;
|
protected $status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
namespace App\Jobs\StatusPipeline;
|
namespace App\Jobs\StatusPipeline;
|
||||||
|
|
||||||
use App\{Media, Notification, StatusHashtag, Status};
|
use App\Notification;
|
||||||
|
use App\Status;
|
||||||
|
use App\StatusHashtag;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class StatusDelete implements ShouldQueue
|
class StatusDelete implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -38,28 +40,27 @@ class StatusDelete implements ShouldQueue
|
||||||
|
|
||||||
public function unlinkRemoveMedia($status)
|
public function unlinkRemoveMedia($status)
|
||||||
{
|
{
|
||||||
if($status->media()->count() == 0) {
|
if ($status->media()->count() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($status->media as $media) {
|
foreach ($status->media as $media) {
|
||||||
$thumbnail = storage_path("app/{$media->thumbnail_path}");
|
$thumbnail = storage_path("app/{$media->thumbnail_path}");
|
||||||
$photo = storage_path("app/{$media->media_path}");
|
$photo = storage_path("app/{$media->media_path}");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(is_file($thumbnail)) {
|
if (is_file($thumbnail)) {
|
||||||
unlink($thumbnail);
|
unlink($thumbnail);
|
||||||
}
|
}
|
||||||
if(is_file($photo)) {
|
if (is_file($photo)) {
|
||||||
unlink($photo);
|
unlink($photo);
|
||||||
}
|
}
|
||||||
$media->delete();
|
$media->delete();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$comments = Status::where('in_reply_to_id', $status->id)->get();
|
$comments = Status::where('in_reply_to_id', $status->id)->get();
|
||||||
foreach($comments as $comment) {
|
foreach ($comments as $comment) {
|
||||||
$comment->in_reply_to_id = null;
|
$comment->in_reply_to_id = null;
|
||||||
$comment->save();
|
$comment->save();
|
||||||
Notification::whereItemType('App\Status')
|
Notification::whereItemType('App\Status')
|
||||||
|
|
|
@ -2,23 +2,20 @@
|
||||||
|
|
||||||
namespace App\Jobs\StatusPipeline;
|
namespace App\Jobs\StatusPipeline;
|
||||||
|
|
||||||
use DB, Cache;
|
use App\Hashtag;
|
||||||
use App\{
|
use App\Jobs\MentionPipeline\MentionPipeline;
|
||||||
Hashtag,
|
use App\Mention;
|
||||||
Media,
|
use App\Profile;
|
||||||
Mention,
|
use App\Status;
|
||||||
Profile,
|
use App\StatusHashtag;
|
||||||
Status,
|
use App\Util\Lexer\Autolink;
|
||||||
StatusHashtag
|
use App\Util\Lexer\Extractor;
|
||||||
};
|
use DB;
|
||||||
use App\Util\Lexer\Hashtag as HashtagLexer;
|
|
||||||
use App\Util\Lexer\{Autolink, Extractor};
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use App\Jobs\MentionPipeline\MentionPipeline;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class StatusEntityLexer implements ShouldQueue
|
class StatusEntityLexer implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -83,7 +80,7 @@ class StatusEntityLexer implements ShouldQueue
|
||||||
$tags = array_unique($this->entities['hashtags']);
|
$tags = array_unique($this->entities['hashtags']);
|
||||||
$status = $this->status;
|
$status = $this->status;
|
||||||
|
|
||||||
foreach($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
DB::transaction(function () use ($status, $tag) {
|
DB::transaction(function () use ($status, $tag) {
|
||||||
$slug = str_slug($tag);
|
$slug = str_slug($tag);
|
||||||
$hashtag = Hashtag::firstOrCreate(
|
$hashtag = Hashtag::firstOrCreate(
|
||||||
|
@ -101,15 +98,15 @@ class StatusEntityLexer implements ShouldQueue
|
||||||
$mentions = array_unique($this->entities['mentions']);
|
$mentions = array_unique($this->entities['mentions']);
|
||||||
$status = $this->status;
|
$status = $this->status;
|
||||||
|
|
||||||
foreach($mentions as $mention) {
|
foreach ($mentions as $mention) {
|
||||||
$mentioned = Profile::whereUsername($mention)->firstOrFail();
|
$mentioned = Profile::whereUsername($mention)->firstOrFail();
|
||||||
|
|
||||||
if(empty($mentioned) || !isset($mentioned->id)) {
|
if (empty($mentioned) || !isset($mentioned->id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::transaction(function () use ($status, $mentioned) {
|
DB::transaction(function () use ($status, $mentioned) {
|
||||||
$m = new Mention;
|
$m = new Mention();
|
||||||
$m->status_id = $status->id;
|
$m->status_id = $status->id;
|
||||||
$m->profile_id = $mentioned->id;
|
$m->profile_id = $mentioned->id;
|
||||||
$m->save();
|
$m->save();
|
||||||
|
@ -118,5 +115,4 @@ class StatusEntityLexer implements ShouldQueue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
16
app/Like.php
16
app/Like.php
|
@ -18,25 +18,27 @@ class Like extends Model
|
||||||
|
|
||||||
public function actor()
|
public function actor()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function status()
|
public function status()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Status::class);
|
return $this->belongsTo(Status::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toText()
|
public function toText()
|
||||||
{
|
{
|
||||||
$actorName = $this->actor->username;
|
$actorName = $this->actor->username;
|
||||||
return "{$actorName} " . __('notification.likedPhoto');
|
|
||||||
|
return "{$actorName} ".__('notification.likedPhoto');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toHtml()
|
public function toHtml()
|
||||||
{
|
{
|
||||||
$actorName = $this->actor->username;
|
$actorName = $this->actor->username;
|
||||||
$actorUrl = $this->actor->url();
|
$actorUrl = $this->actor->url();
|
||||||
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> " .
|
|
||||||
|
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
|
||||||
__('notification.likedPhoto');
|
__('notification.likedPhoto');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use App\EmailVerification;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
|
|
||||||
class ConfirmEmail extends Mailable
|
class ConfirmEmail extends Mailable
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Storage;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Storage;
|
||||||
|
|
||||||
class Media extends Model
|
class Media extends Model
|
||||||
{
|
{
|
||||||
|
@ -19,15 +19,17 @@ class Media extends Model
|
||||||
|
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
$path = $this->media_path;
|
$path = $this->media_path;
|
||||||
$url = Storage::url($path);
|
$url = Storage::url($path);
|
||||||
return url($url);
|
|
||||||
|
return url($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function thumbnailUrl()
|
public function thumbnailUrl()
|
||||||
{
|
{
|
||||||
$path = $this->thumbnail_path;
|
$path = $this->thumbnail_path;
|
||||||
$url = Storage::url($path);
|
$url = Storage::url($path);
|
||||||
return url($url);
|
|
||||||
|
return url($url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,25 +18,27 @@ class Mention extends Model
|
||||||
|
|
||||||
public function profile()
|
public function profile()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function status()
|
public function status()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Status::class, 'status_id', 'id');
|
return $this->belongsTo(Status::class, 'status_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toText()
|
public function toText()
|
||||||
{
|
{
|
||||||
$actorName = $this->status->profile->username;
|
$actorName = $this->status->profile->username;
|
||||||
return "{$actorName} " . __('notification.mentionedYou');
|
|
||||||
|
return "{$actorName} ".__('notification.mentionedYou');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toHtml()
|
public function toHtml()
|
||||||
{
|
{
|
||||||
$actorName = $this->status->profile->username;
|
$actorName = $this->status->profile->username;
|
||||||
$actorUrl = $this->status->profile->url();
|
$actorUrl = $this->status->profile->url();
|
||||||
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> " .
|
|
||||||
|
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
|
||||||
__('notification.mentionedYou');
|
__('notification.mentionedYou');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,22 +18,21 @@ class Notification extends Model
|
||||||
|
|
||||||
public function actor()
|
public function actor()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'actor_id', 'id');
|
return $this->belongsTo(Profile::class, 'actor_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function profile()
|
public function profile()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function item()
|
public function item()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function status()
|
public function status()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Status::class, 'item_id', 'id');
|
return $this->belongsTo(Status::class, 'item_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,31 @@
|
||||||
|
|
||||||
namespace App\Observers;
|
namespace App\Observers;
|
||||||
|
|
||||||
use App\{Profile, User, UserSetting};
|
|
||||||
use App\Jobs\AvatarPipeline\CreateAvatar;
|
use App\Jobs\AvatarPipeline\CreateAvatar;
|
||||||
|
use App\Profile;
|
||||||
|
use App\User;
|
||||||
|
use App\UserSetting;
|
||||||
|
|
||||||
class UserObserver
|
class UserObserver
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Listen to the User created event.
|
* Listen to the User created event.
|
||||||
*
|
*
|
||||||
* @param \App\User $user
|
* @param \App\User $user
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function saved(User $user)
|
public function saved(User $user)
|
||||||
{
|
{
|
||||||
if(empty($user->profile)) {
|
if (empty($user->profile)) {
|
||||||
$profile = new Profile;
|
$profile = new Profile();
|
||||||
$profile->user_id = $user->id;
|
$profile->user_id = $user->id;
|
||||||
$profile->username = $user->username;
|
$profile->username = $user->username;
|
||||||
$profile->name = $user->name;
|
$profile->name = $user->name;
|
||||||
$pkiConfig = [
|
$pkiConfig = [
|
||||||
"digest_alg" => "sha512",
|
'digest_alg' => 'sha512',
|
||||||
"private_key_bits" => 2048,
|
'private_key_bits' => 2048,
|
||||||
"private_key_type" => OPENSSL_KEYTYPE_RSA,
|
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||||||
];
|
];
|
||||||
$pki = openssl_pkey_new($pkiConfig);
|
$pki = openssl_pkey_new($pkiConfig);
|
||||||
openssl_pkey_export($pki, $pki_private);
|
openssl_pkey_export($pki, $pki_private);
|
||||||
|
@ -37,11 +40,10 @@ class UserObserver
|
||||||
CreateAvatar::dispatch($profile);
|
CreateAvatar::dispatch($profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(empty($user->settings)) {
|
if (empty($user->settings)) {
|
||||||
$settings = new UserSetting;
|
$settings = new UserSetting();
|
||||||
$settings->user_id = $user->id;
|
$settings->user_id = $user->id;
|
||||||
$settings->save();
|
$settings->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Auth, Cache, Storage;
|
|
||||||
use App\Util\Lexer\PrettyNumber;
|
use App\Util\Lexer\PrettyNumber;
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Storage;
|
||||||
|
|
||||||
class Profile extends Model
|
class Profile extends Model
|
||||||
{
|
{
|
||||||
|
@ -30,27 +32,28 @@ class Profile extends Model
|
||||||
|
|
||||||
public function url($suffix = '')
|
public function url($suffix = '')
|
||||||
{
|
{
|
||||||
if($this->remote_url) {
|
if ($this->remote_url) {
|
||||||
return $this->remote_url;
|
return $this->remote_url;
|
||||||
} else {
|
} else {
|
||||||
return url($this->username . $suffix);
|
return url($this->username.$suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function localUrl($suffix = '')
|
public function localUrl($suffix = '')
|
||||||
{
|
{
|
||||||
return url($this->username . $suffix);
|
return url($this->username.$suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function permalink($suffix = '')
|
public function permalink($suffix = '')
|
||||||
{
|
{
|
||||||
return url('users/' . $this->username . $suffix);
|
return url('users/'.$this->username.$suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function emailUrl()
|
public function emailUrl()
|
||||||
{
|
{
|
||||||
$domain = parse_url(config('app.url'), PHP_URL_HOST);
|
$domain = parse_url(config('app.url'), PHP_URL_HOST);
|
||||||
return $this->username . '@' . $domain;
|
|
||||||
|
return $this->username.'@'.$domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function statuses()
|
public function statuses()
|
||||||
|
@ -61,7 +64,7 @@ class Profile extends Model
|
||||||
public function followingCount($short = false)
|
public function followingCount($short = false)
|
||||||
{
|
{
|
||||||
$count = $this->following()->count();
|
$count = $this->following()->count();
|
||||||
if($short) {
|
if ($short) {
|
||||||
return PrettyNumber::convert($count);
|
return PrettyNumber::convert($count);
|
||||||
} else {
|
} else {
|
||||||
return $count;
|
return $count;
|
||||||
|
@ -71,7 +74,7 @@ class Profile extends Model
|
||||||
public function followerCount($short = false)
|
public function followerCount($short = false)
|
||||||
{
|
{
|
||||||
$count = $this->followers()->count();
|
$count = $this->followers()->count();
|
||||||
if($short) {
|
if ($short) {
|
||||||
return PrettyNumber::convert($count);
|
return PrettyNumber::convert($count);
|
||||||
} else {
|
} else {
|
||||||
return $count;
|
return $count;
|
||||||
|
@ -81,7 +84,7 @@ class Profile extends Model
|
||||||
public function following()
|
public function following()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(
|
return $this->belongsToMany(
|
||||||
Profile::class,
|
self::class,
|
||||||
'followers',
|
'followers',
|
||||||
'profile_id',
|
'profile_id',
|
||||||
'following_id'
|
'following_id'
|
||||||
|
@ -91,7 +94,7 @@ class Profile extends Model
|
||||||
public function followers()
|
public function followers()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(
|
return $this->belongsToMany(
|
||||||
Profile::class,
|
self::class,
|
||||||
'followers',
|
'followers',
|
||||||
'following_id',
|
'following_id',
|
||||||
'profile_id'
|
'profile_id'
|
||||||
|
@ -126,18 +129,20 @@ class Profile extends Model
|
||||||
public function avatar()
|
public function avatar()
|
||||||
{
|
{
|
||||||
return $this->hasOne(Avatar::class)->withDefault([
|
return $this->hasOne(Avatar::class)->withDefault([
|
||||||
'media_path' => 'public/avatars/default.png'
|
'media_path' => 'public/avatars/default.png',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function avatarUrl()
|
public function avatarUrl()
|
||||||
{
|
{
|
||||||
$url = Cache::remember("avatar:{$this->id}", 1440, function() {
|
$url = Cache::remember("avatar:{$this->id}", 1440, function () {
|
||||||
$path = optional($this->avatar)->media_path;
|
$path = optional($this->avatar)->media_path;
|
||||||
$version = hash('sha1', $this->avatar->created_at);
|
$version = hash('sha1', $this->avatar->created_at);
|
||||||
$path = "{$path}?v={$version}";
|
$path = "{$path}?v={$version}";
|
||||||
|
|
||||||
return url(Storage::url($path));
|
return url(Storage::url($path));
|
||||||
});
|
});
|
||||||
|
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,8 +172,8 @@ class Profile extends Model
|
||||||
->limit(3)
|
->limit(3)
|
||||||
->pluck('following_id');
|
->pluck('following_id');
|
||||||
$recommended = [];
|
$recommended = [];
|
||||||
foreach($following as $follow) {
|
foreach ($following as $follow) {
|
||||||
$recommended[] = Profile::findOrFail($follow);
|
$recommended[] = self::findOrFail($follow);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $recommended;
|
return $recommended;
|
||||||
|
@ -176,9 +181,10 @@ class Profile extends Model
|
||||||
|
|
||||||
public function keyId()
|
public function keyId()
|
||||||
{
|
{
|
||||||
if($this->remote_url) {
|
if ($this->remote_url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->permalink('#main-key');
|
return $this->permalink('#main-key');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\User;
|
|
||||||
use Auth, Horizon;
|
|
||||||
use App\Observers\UserObserver;
|
use App\Observers\UserObserver;
|
||||||
|
use App\User;
|
||||||
|
use Auth;
|
||||||
|
use Horizon;
|
||||||
use Illuminate\Support\Facades\Blade;
|
use Illuminate\Support\Facades\Blade;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
@ -26,34 +27,39 @@ class AppServiceProvider extends ServiceProvider
|
||||||
return Auth::check() && $request->user()->is_admin;
|
return Auth::check() && $request->user()->is_admin;
|
||||||
});
|
});
|
||||||
|
|
||||||
Blade::directive('prettyNumber', function($expression) {
|
Blade::directive('prettyNumber', function ($expression) {
|
||||||
$num = $expression;
|
$num = $expression;
|
||||||
$abbrevs = array(12 => "T", 9 => "B", 6 => "M", 3 => "K", 0 => "");
|
$abbrevs = [12 => 'T', 9 => 'B', 6 => 'M', 3 => 'K', 0 => ''];
|
||||||
foreach($abbrevs as $exponent => $abbrev) {
|
foreach ($abbrevs as $exponent => $abbrev) {
|
||||||
if($expression >= pow(10, $exponent)) {
|
if ($expression >= pow(10, $exponent)) {
|
||||||
$display_num = $expression / pow(10, $exponent);
|
$display_num = $expression / pow(10, $exponent);
|
||||||
$num = number_format($display_num,0) . $abbrev;
|
$num = number_format($display_num, 0).$abbrev;
|
||||||
return "<?php echo '$num'; ?>";
|
|
||||||
|
return "<?php echo '$num'; ?>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "<?php echo $num; ?>";
|
return "<?php echo $num; ?>";
|
||||||
});
|
});
|
||||||
|
|
||||||
Blade::directive('prettySize', function($expression) {
|
Blade::directive('prettySize', function ($expression) {
|
||||||
$size = intval($expression);
|
$size = intval($expression);
|
||||||
$precision = 0;
|
$precision = 0;
|
||||||
$short = true;
|
$short = true;
|
||||||
$units = $short ?
|
$units = $short ?
|
||||||
['B','k','M','G','T','P','E','Z','Y'] :
|
['B', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] :
|
||||||
['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
for($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {}
|
for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {
|
||||||
|
}
|
||||||
$res = round($size, $precision).$units[$i];
|
$res = round($size, $precision).$units[$i];
|
||||||
|
|
||||||
return "<?php echo '$res'; ?>";
|
return "<?php echo '$res'; ?>";
|
||||||
});
|
});
|
||||||
|
|
||||||
Blade::directive('maxFileSize', function() {
|
Blade::directive('maxFileSize', function () {
|
||||||
$value = config('pixelfed.max_photo_size');
|
$value = config('pixelfed.max_photo_size');
|
||||||
return \App\Util\Lexer\PrettyNumber::size($value, true);
|
|
||||||
|
return \App\Util\Lexer\PrettyNumber::size($value, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
|
|
||||||
class AuthServiceProvider extends ServiceProvider
|
class AuthServiceProvider extends ServiceProvider
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
|
||||||
use Illuminate\Support\Facades\Broadcast;
|
use Illuminate\Support\Facades\Broadcast;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class BroadcastServiceProvider extends ServiceProvider
|
class BroadcastServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Event;
|
|
||||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
class EventServiceProvider extends ServiceProvider
|
class EventServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
|
||||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
class RouteServiceProvider extends ServiceProvider
|
class RouteServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,18 +10,18 @@ class Report extends Model
|
||||||
|
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
return url('/i/admin/reports/show/' . $this->id);
|
return url('/i/admin/reports/show/'.$this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reporter()
|
public function reporter()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'profile_id');
|
return $this->belongsTo(Profile::class, 'profile_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reported()
|
public function reported()
|
||||||
{
|
{
|
||||||
$class = $this->object_type;
|
$class = $this->object_type;
|
||||||
switch ($class) {
|
switch ($class) {
|
||||||
case 'App\Status':
|
case 'App\Status':
|
||||||
$column = 'id';
|
$column = 'id';
|
||||||
break;
|
break;
|
||||||
|
@ -30,11 +30,12 @@ class Report extends Model
|
||||||
$column = 'id';
|
$column = 'id';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return (new $class())->where($column, $this->object_id)->firstOrFail();
|
|
||||||
|
return (new $class())->where($column, $this->object_id)->firstOrFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reportedUser()
|
public function reportedUser()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class, 'reported_profile_id', 'id');
|
return $this->belongsTo(Profile::class, 'reported_profile_id', 'id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
154
app/Status.php
154
app/Status.php
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Auth, Storage;
|
use Auth;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Storage;
|
||||||
|
|
||||||
class Status extends Model
|
class Status extends Model
|
||||||
{
|
{
|
||||||
|
@ -19,122 +20,130 @@ class Status extends Model
|
||||||
|
|
||||||
public function profile()
|
public function profile()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Profile::class);
|
return $this->belongsTo(Profile::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function media()
|
public function media()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Media::class);
|
return $this->hasMany(Media::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function firstMedia()
|
public function firstMedia()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Media::class)->orderBy('order', 'asc')->first();
|
return $this->hasMany(Media::class)->orderBy('order', 'asc')->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function viewType()
|
public function viewType()
|
||||||
{
|
{
|
||||||
$media = $this->firstMedia();
|
$media = $this->firstMedia();
|
||||||
$type = explode('/', $media->mime);
|
$type = explode('/', $media->mime);
|
||||||
return $type[0];
|
|
||||||
|
return $type[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function thumb($showNsfw = false)
|
public function thumb($showNsfw = false)
|
||||||
{
|
{
|
||||||
$type = $this->viewType();
|
$type = $this->viewType();
|
||||||
$is_nsfw = !$showNsfw ? $this->is_nsfw : false;
|
$is_nsfw = !$showNsfw ? $this->is_nsfw : false;
|
||||||
if($this->media->count() == 0 || $is_nsfw || $type != 'image') {
|
if ($this->media->count() == 0 || $is_nsfw || $type != 'image') {
|
||||||
return "data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
|
return 'data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==';
|
||||||
}
|
}
|
||||||
return url(Storage::url($this->firstMedia()->thumbnail_path));
|
|
||||||
|
return url(Storage::url($this->firstMedia()->thumbnail_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
$id = $this->id;
|
$id = $this->id;
|
||||||
$username = $this->profile->username;
|
$username = $this->profile->username;
|
||||||
$path = config('app.url') . "/p/{$username}/{$id}";
|
$path = config('app.url')."/p/{$username}/{$id}";
|
||||||
if(!is_null($this->in_reply_to_id)) {
|
if (!is_null($this->in_reply_to_id)) {
|
||||||
$pid = $this->in_reply_to_id;
|
$pid = $this->in_reply_to_id;
|
||||||
$path = config('app.url') . "/p/{$username}/{$pid}/c/{$id}";
|
$path = config('app.url')."/p/{$username}/{$pid}/c/{$id}";
|
||||||
}
|
}
|
||||||
return url($path);
|
|
||||||
|
return url($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function permalink($suffix = '/activity')
|
public function permalink($suffix = '/activity')
|
||||||
{
|
{
|
||||||
$id = $this->id;
|
$id = $this->id;
|
||||||
$username = $this->profile->username;
|
$username = $this->profile->username;
|
||||||
$path = config('app.url') . "/p/{$username}/{$id}{$suffix}";
|
$path = config('app.url')."/p/{$username}/{$id}{$suffix}";
|
||||||
return url($path);
|
|
||||||
|
return url($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function editUrl()
|
public function editUrl()
|
||||||
{
|
{
|
||||||
return $this->url() . '/edit';
|
return $this->url().'/edit';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mediaUrl()
|
public function mediaUrl()
|
||||||
{
|
{
|
||||||
$media = $this->firstMedia();
|
$media = $this->firstMedia();
|
||||||
$path = $media->media_path;
|
$path = $media->media_path;
|
||||||
$hash = is_null($media->processed_at) ? md5('unprocessed') : md5($media->created_at);
|
$hash = is_null($media->processed_at) ? md5('unprocessed') : md5($media->created_at);
|
||||||
$url = Storage::url($path) . "?v={$hash}";
|
$url = Storage::url($path)."?v={$hash}";
|
||||||
return url($url);
|
|
||||||
|
return url($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function likes()
|
public function likes()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Like::class);
|
return $this->hasMany(Like::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function liked() : bool
|
public function liked() : bool
|
||||||
{
|
{
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
return Like::whereProfileId($profile->id)->whereStatusId($this->id)->count();
|
|
||||||
|
return Like::whereProfileId($profile->id)->whereStatusId($this->id)->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function comments()
|
public function comments()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Status::class, 'in_reply_to_id');
|
return $this->hasMany(self::class, 'in_reply_to_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bookmarked()
|
public function bookmarked()
|
||||||
{
|
{
|
||||||
if(!Auth::check()) {
|
if (!Auth::check()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
return Bookmark::whereProfileId($profile->id)->whereStatusId($this->id)->count();
|
|
||||||
|
return Bookmark::whereProfileId($profile->id)->whereStatusId($this->id)->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shares()
|
public function shares()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Status::class, 'reblog_of_id');
|
return $this->hasMany(self::class, 'reblog_of_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shared() : bool
|
public function shared() : bool
|
||||||
{
|
{
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
return Status::whereProfileId($profile->id)->whereReblogOfId($this->id)->count();
|
|
||||||
|
return self::whereProfileId($profile->id)->whereReblogOfId($this->id)->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parent()
|
public function parent()
|
||||||
{
|
{
|
||||||
$parent = $this->in_reply_to_id ?? $this->reblog_of_id;
|
$parent = $this->in_reply_to_id ?? $this->reblog_of_id;
|
||||||
if(!empty($parent)) {
|
if (!empty($parent)) {
|
||||||
return Status::findOrFail($parent);
|
return self::findOrFail($parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function conversation()
|
public function conversation()
|
||||||
{
|
{
|
||||||
return $this->hasOne(Conversation::class);
|
return $this->hasOne(Conversation::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hashtags()
|
public function hashtags()
|
||||||
{
|
{
|
||||||
return $this->hasManyThrough(
|
return $this->hasManyThrough(
|
||||||
Hashtag::class,
|
Hashtag::class,
|
||||||
StatusHashtag::class,
|
StatusHashtag::class,
|
||||||
'status_id',
|
'status_id',
|
||||||
|
@ -146,7 +155,7 @@ class Status extends Model
|
||||||
|
|
||||||
public function mentions()
|
public function mentions()
|
||||||
{
|
{
|
||||||
return $this->hasManyThrough(
|
return $this->hasManyThrough(
|
||||||
Profile::class,
|
Profile::class,
|
||||||
Mention::class,
|
Mention::class,
|
||||||
'status_id',
|
'status_id',
|
||||||
|
@ -158,45 +167,48 @@ class Status extends Model
|
||||||
|
|
||||||
public function reportUrl()
|
public function reportUrl()
|
||||||
{
|
{
|
||||||
return route('report.form') . "?type=post&id={$this->id}";
|
return route('report.form')."?type=post&id={$this->id}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toActivityStream()
|
public function toActivityStream()
|
||||||
{
|
{
|
||||||
$media = $this->media;
|
$media = $this->media;
|
||||||
$mediaCollection = [];
|
$mediaCollection = [];
|
||||||
foreach($media as $image) {
|
foreach ($media as $image) {
|
||||||
$mediaCollection[] = [
|
$mediaCollection[] = [
|
||||||
"type" => "Link",
|
'type' => 'Link',
|
||||||
"href" => $image->url(),
|
'href' => $image->url(),
|
||||||
"mediaType" => $image->mime
|
'mediaType' => $image->mime,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$obj = [
|
$obj = [
|
||||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||||
"type" => "Image",
|
'type' => 'Image',
|
||||||
"name" => null,
|
'name' => null,
|
||||||
"url" => $mediaCollection
|
'url' => $mediaCollection,
|
||||||
];
|
];
|
||||||
return $obj;
|
|
||||||
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function replyToText()
|
public function replyToText()
|
||||||
{
|
{
|
||||||
$actorName = $this->profile->username;
|
$actorName = $this->profile->username;
|
||||||
return "{$actorName} " . __('notification.commented');
|
|
||||||
|
return "{$actorName} ".__('notification.commented');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function replyToHtml()
|
public function replyToHtml()
|
||||||
{
|
{
|
||||||
$actorName = $this->profile->username;
|
$actorName = $this->profile->username;
|
||||||
$actorUrl = $this->profile->url();
|
$actorUrl = $this->profile->url();
|
||||||
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> " .
|
|
||||||
|
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
|
||||||
__('notification.commented');
|
__('notification.commented');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function recentComments()
|
public function recentComments()
|
||||||
{
|
{
|
||||||
return $this->comments()->orderBy('created_at','desc')->take(3);
|
return $this->comments()->orderBy('created_at', 'desc')->take(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,18 @@ use League\Fractal;
|
||||||
|
|
||||||
class ProfileOutbox extends Fractal\TransformerAbstract
|
class ProfileOutbox extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
|
public function transform(Profile $profile)
|
||||||
public function transform(Profile $profile)
|
{
|
||||||
{
|
$count = $profile->statuses()->count();
|
||||||
$count = $profile->statuses()->count();
|
$statuses = $profile->statuses()->has('media')->orderBy('id', 'desc')->take(20)->get()->map(function ($i, $k) {
|
||||||
$statuses = $profile->statuses()->has('media')->orderBy('id','desc')->take(20)->get()->map(function($i, $k) {
|
$item = [
|
||||||
$item = [
|
|
||||||
'id' => $i->permalink(),
|
'id' => $i->permalink(),
|
||||||
// TODO: handle other types
|
// TODO: handle other types
|
||||||
'type' => 'Create',
|
'type' => 'Create',
|
||||||
'actor' => $i->profile->url(),
|
'actor' => $i->profile->url(),
|
||||||
'published' => $i->created_at->toISO8601String(),
|
'published' => $i->created_at->toISO8601String(),
|
||||||
'to' => [
|
'to' => [
|
||||||
'https://www.w3.org/ns/activitystreams#Public'
|
'https://www.w3.org/ns/activitystreams#Public',
|
||||||
],
|
],
|
||||||
'cc' => [
|
'cc' => [
|
||||||
$i->profile->permalink('/followers'),
|
$i->profile->permalink('/followers'),
|
||||||
|
@ -31,48 +30,48 @@ class ProfileOutbox extends Fractal\TransformerAbstract
|
||||||
'type' => 'Note',
|
'type' => 'Note',
|
||||||
|
|
||||||
// XXX: CW Title
|
// XXX: CW Title
|
||||||
'summary' => null,
|
'summary' => null,
|
||||||
'content' => $i->rendered ?? $i->caption,
|
'content' => $i->rendered ?? $i->caption,
|
||||||
'inReplyTo' => null,
|
'inReplyTo' => null,
|
||||||
|
|
||||||
// TODO: fix date format
|
// TODO: fix date format
|
||||||
'published' => $i->created_at->toAtomString(),
|
'published' => $i->created_at->toAtomString(),
|
||||||
'url' => $i->url(),
|
'url' => $i->url(),
|
||||||
'attributedTo' => $i->profile->permalink(),
|
'attributedTo' => $i->profile->permalink(),
|
||||||
'to' => [
|
'to' => [
|
||||||
// TODO: handle proper scope
|
// TODO: handle proper scope
|
||||||
'https://www.w3.org/ns/activitystreams#Public'
|
'https://www.w3.org/ns/activitystreams#Public',
|
||||||
],
|
],
|
||||||
'cc' => [
|
'cc' => [
|
||||||
// TODO: add cc's
|
// TODO: add cc's
|
||||||
//"{$notice->getProfile()->getUrl()}/subscribers",
|
//"{$notice->getProfile()->getUrl()}/subscribers",
|
||||||
],
|
],
|
||||||
'sensitive' => (bool) $i->is_nsfw,
|
'sensitive' => (bool) $i->is_nsfw,
|
||||||
'atomUri' => $i->url(),
|
'atomUri' => $i->url(),
|
||||||
'inReplyToAtomUri' => null,
|
'inReplyToAtomUri' => null,
|
||||||
'attachment' => [
|
'attachment' => [
|
||||||
|
|
||||||
// TODO: support more than 1 attachment
|
// TODO: support more than 1 attachment
|
||||||
[
|
[
|
||||||
'type' => 'Document',
|
'type' => 'Document',
|
||||||
'mediaType' => $i->firstMedia()->mime,
|
'mediaType' => $i->firstMedia()->mime,
|
||||||
'url' => $i->firstMedia()->url(),
|
'url' => $i->firstMedia()->url(),
|
||||||
'name' => null
|
'name' => null,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'tag' => []
|
'tag' => [],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
return $item;
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
return $item;
|
||||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
});
|
||||||
'id' => $profile->permalink('/outbox'),
|
|
||||||
'type' => 'OrderedCollection',
|
return [
|
||||||
'totalItems' => $count,
|
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||||
'orderedItems' => $statuses
|
'id' => $profile->permalink('/outbox'),
|
||||||
|
'type' => 'OrderedCollection',
|
||||||
|
'totalItems' => $count,
|
||||||
|
'orderedItems' => $statuses,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,48 +7,46 @@ use League\Fractal;
|
||||||
|
|
||||||
class ProfileTransformer extends Fractal\TransformerAbstract
|
class ProfileTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
|
public function transform(Profile $profile)
|
||||||
public function transform(Profile $profile)
|
{
|
||||||
{
|
return [
|
||||||
return [
|
|
||||||
'@context' => [
|
'@context' => [
|
||||||
'https://www.w3.org/ns/activitystreams',
|
'https://www.w3.org/ns/activitystreams',
|
||||||
'https://w3id.org/security/v1',
|
'https://w3id.org/security/v1',
|
||||||
[
|
[
|
||||||
"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
|
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
||||||
"featured" => [
|
'featured' => [
|
||||||
"https://pixelfed.org/ns#featured" => ["@type" => "@id"],
|
'https://pixelfed.org/ns#featured' => ['@type' => '@id'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'id' => $profile->permalink(),
|
'id' => $profile->permalink(),
|
||||||
'type' => 'Person',
|
'type' => 'Person',
|
||||||
'following' => $profile->permalink('/following'),
|
'following' => $profile->permalink('/following'),
|
||||||
'followers' => $profile->permalink('/followers'),
|
'followers' => $profile->permalink('/followers'),
|
||||||
'inbox' => $profile->permalink('/inbox'),
|
'inbox' => $profile->permalink('/inbox'),
|
||||||
'outbox' => $profile->permalink('/outbox'),
|
'outbox' => $profile->permalink('/outbox'),
|
||||||
'featured' => $profile->permalink('/collections/featured'),
|
'featured' => $profile->permalink('/collections/featured'),
|
||||||
'preferredUsername' => $profile->username,
|
'preferredUsername' => $profile->username,
|
||||||
'name' => $profile->name,
|
'name' => $profile->name,
|
||||||
'summary' => $profile->bio,
|
'summary' => $profile->bio,
|
||||||
'url' => $profile->url(),
|
'url' => $profile->url(),
|
||||||
'manuallyApprovesFollowers' => (bool) $profile->is_private,
|
'manuallyApprovesFollowers' => (bool) $profile->is_private,
|
||||||
// 'follower_count' => $profile->followers()->count(),
|
// 'follower_count' => $profile->followers()->count(),
|
||||||
// 'following_count' => $profile->following()->count(),
|
// 'following_count' => $profile->following()->count(),
|
||||||
'publicKey' => [
|
'publicKey' => [
|
||||||
'id' => $profile->permalink() . '#main-key',
|
'id' => $profile->permalink().'#main-key',
|
||||||
'owner' => $profile->permalink(),
|
'owner' => $profile->permalink(),
|
||||||
'publicKeyPem' => $profile->public_key
|
'publicKeyPem' => $profile->public_key,
|
||||||
],
|
],
|
||||||
'endpoints' => [
|
'endpoints' => [
|
||||||
'sharedInbox' => config('routes.api.sharedInbox')
|
'sharedInbox' => config('routes.api.sharedInbox'),
|
||||||
],
|
],
|
||||||
'icon' => [
|
'icon' => [
|
||||||
'type' => 'Image',
|
'type' => 'Image',
|
||||||
'mediaType' => 'image/jpeg',
|
'mediaType' => 'image/jpeg',
|
||||||
'url' => $profile->avatarUrl()
|
'url' => $profile->avatarUrl(),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,24 +2,23 @@
|
||||||
|
|
||||||
namespace App\Transformer\ActivityPub;
|
namespace App\Transformer\ActivityPub;
|
||||||
|
|
||||||
use App\{Profile, Status};
|
use App\Status;
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
|
|
||||||
class StatusTransformer extends Fractal\TransformerAbstract
|
class StatusTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
|
public function transform(Status $status)
|
||||||
public function transform(Status $status)
|
{
|
||||||
{
|
return [
|
||||||
return [
|
|
||||||
'@context' => [
|
'@context' => [
|
||||||
'https://www.w3.org/ns/activitystreams',
|
'https://www.w3.org/ns/activitystreams',
|
||||||
'https://w3id.org/security/v1',
|
'https://w3id.org/security/v1',
|
||||||
[
|
[
|
||||||
"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
|
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
|
||||||
"featured" => [
|
'featured' => [
|
||||||
"https://pixelfed.org/ns#featured" => ["@type" => "@id"],
|
'https://pixelfed.org/ns#featured' => ['@type' => '@id'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'id' => $status->url(),
|
'id' => $status->url(),
|
||||||
|
|
||||||
|
@ -27,35 +26,34 @@ class StatusTransformer extends Fractal\TransformerAbstract
|
||||||
'type' => 'Note',
|
'type' => 'Note',
|
||||||
|
|
||||||
// XXX: CW Title
|
// XXX: CW Title
|
||||||
'summary' => null,
|
'summary' => null,
|
||||||
'content' => $status->rendered ?? $status->caption,
|
'content' => $status->rendered ?? $status->caption,
|
||||||
'inReplyTo' => null,
|
'inReplyTo' => null,
|
||||||
|
|
||||||
// TODO: fix date format
|
// TODO: fix date format
|
||||||
'published' => $status->created_at->toAtomString(),
|
'published' => $status->created_at->toAtomString(),
|
||||||
'url' => $status->url(),
|
'url' => $status->url(),
|
||||||
'attributedTo' => $status->profile->permalink(),
|
'attributedTo' => $status->profile->permalink(),
|
||||||
'to' => [
|
'to' => [
|
||||||
// TODO: handle proper scope
|
// TODO: handle proper scope
|
||||||
'https://www.w3.org/ns/activitystreams#Public'
|
'https://www.w3.org/ns/activitystreams#Public',
|
||||||
],
|
],
|
||||||
'cc' => [
|
'cc' => [
|
||||||
// TODO: add cc's
|
// TODO: add cc's
|
||||||
$status->profile->permalink('/followers'),
|
$status->profile->permalink('/followers'),
|
||||||
],
|
],
|
||||||
'sensitive' => (bool) $status->is_nsfw,
|
'sensitive' => (bool) $status->is_nsfw,
|
||||||
'atomUri' => $status->url(),
|
'atomUri' => $status->url(),
|
||||||
'inReplyToAtomUri' => null,
|
'inReplyToAtomUri' => null,
|
||||||
'attachment' => $status->media->map(function($media) {
|
'attachment' => $status->media->map(function ($media) {
|
||||||
return [
|
return [
|
||||||
'type' => 'Document',
|
'type' => 'Document',
|
||||||
'mediaType' => $media->mime,
|
'mediaType' => $media->mime,
|
||||||
'url' => $media->url(),
|
'url' => $media->url(),
|
||||||
'name' => null
|
'name' => null,
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
'tag' => []
|
'tag' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,27 +7,27 @@ use League\Fractal;
|
||||||
|
|
||||||
class AccountTransformer extends Fractal\TransformerAbstract
|
class AccountTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
public function transform(Profile $profile)
|
public function transform(Profile $profile)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $profile->id,
|
'id' => $profile->id,
|
||||||
'username' => $profile->username,
|
'username' => $profile->username,
|
||||||
'acct' => $profile->username,
|
'acct' => $profile->username,
|
||||||
'display_name' => $profile->name,
|
'display_name' => $profile->name,
|
||||||
'locked' => (bool) $profile->is_private,
|
'locked' => (bool) $profile->is_private,
|
||||||
'created_at' => $profile->created_at->format('c'),
|
'created_at' => $profile->created_at->format('c'),
|
||||||
'followers_count' => $profile->followerCount(),
|
'followers_count' => $profile->followerCount(),
|
||||||
'following_count' => $profile->followingCount(),
|
'following_count' => $profile->followingCount(),
|
||||||
'statuses_count' => $profile->statusCount(),
|
'statuses_count' => $profile->statusCount(),
|
||||||
'note' => $profile->bio,
|
'note' => $profile->bio,
|
||||||
'url' => $profile->url(),
|
'url' => $profile->url(),
|
||||||
'avatar' => $profile->avatarUrl(),
|
'avatar' => $profile->avatarUrl(),
|
||||||
'avatar_static' => $profile->avatarUrl(),
|
'avatar_static' => $profile->avatarUrl(),
|
||||||
'header' => '',
|
'header' => '',
|
||||||
'header_static' => '',
|
'header_static' => '',
|
||||||
'moved' => null,
|
'moved' => null,
|
||||||
'fields' => null,
|
'fields' => null,
|
||||||
'bot' => null
|
'bot' => null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,11 +6,11 @@ use League\Fractal;
|
||||||
|
|
||||||
class ApplicationTransformer extends Fractal\TransformerAbstract
|
class ApplicationTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
public function transform()
|
public function transform()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'website' => null
|
'website' => null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,6 @@ namespace App\Transformer\Api;
|
||||||
|
|
||||||
use App\Hashtag;
|
use App\Hashtag;
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
use League\Fractal\Serializer\ArraySerializer;
|
|
||||||
|
|
||||||
class HashtagTransformer extends Fractal\TransformerAbstract
|
class HashtagTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
|
@ -12,7 +11,7 @@ class HashtagTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => $hashtag->name,
|
'name' => $hashtag->name,
|
||||||
'url' => $hashtag->url(),
|
'url' => $hashtag->url(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,21 +4,20 @@ namespace App\Transformer\Api;
|
||||||
|
|
||||||
use App\Media;
|
use App\Media;
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
use League\Fractal\Serializer\ArraySerializer;
|
|
||||||
|
|
||||||
class MediaTransformer extends Fractal\TransformerAbstract
|
class MediaTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
public function transform(Media $media)
|
public function transform(Media $media)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $media->id,
|
'id' => $media->id,
|
||||||
'type' => 'image',
|
'type' => 'image',
|
||||||
'url' => $media->url(),
|
'url' => $media->url(),
|
||||||
'remote_url' => null,
|
'remote_url' => null,
|
||||||
'preview_url' => $media->thumbnailUrl(),
|
'preview_url' => $media->thumbnailUrl(),
|
||||||
'text_url' => null,
|
'text_url' => null,
|
||||||
'meta' => null,
|
'meta' => null,
|
||||||
'description' => null
|
'description' => null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,10 +10,10 @@ class MentionTransformer extends Fractal\TransformerAbstract
|
||||||
public function transform(Profile $profile)
|
public function transform(Profile $profile)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $profile->id,
|
'id' => $profile->id,
|
||||||
'url' => $profile->url(),
|
'url' => $profile->url(),
|
||||||
'username' => $profile->username,
|
'username' => $profile->username,
|
||||||
'acct' => $profile->username,
|
'acct' => $profile->username,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,59 +11,63 @@ class StatusTransformer extends Fractal\TransformerAbstract
|
||||||
'account',
|
'account',
|
||||||
'mentions',
|
'mentions',
|
||||||
'media_attachments',
|
'media_attachments',
|
||||||
'tags'
|
'tags',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function transform(Status $status)
|
public function transform(Status $status)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $status->id,
|
'id' => $status->id,
|
||||||
'uri' => $status->url(),
|
'uri' => $status->url(),
|
||||||
'url' => $status->url(),
|
'url' => $status->url(),
|
||||||
'in_reply_to_id' => $status->in_reply_to_id,
|
'in_reply_to_id' => $status->in_reply_to_id,
|
||||||
'in_reply_to_account_id' => $status->in_reply_to_profile_id,
|
'in_reply_to_account_id' => $status->in_reply_to_profile_id,
|
||||||
|
|
||||||
// TODO: fixme
|
// TODO: fixme
|
||||||
'reblog' => null,
|
'reblog' => null,
|
||||||
|
|
||||||
'content' => "<p>$status->rendered</p>",
|
'content' => "<p>$status->rendered</p>",
|
||||||
'created_at' => $status->created_at->format('c'),
|
'created_at' => $status->created_at->format('c'),
|
||||||
'emojis' => [],
|
'emojis' => [],
|
||||||
'reblogs_count' => $status->shares()->count(),
|
'reblogs_count' => $status->shares()->count(),
|
||||||
'favourites_count' => $status->likes()->count(),
|
'favourites_count' => $status->likes()->count(),
|
||||||
'reblogged' => $status->shared(),
|
'reblogged' => $status->shared(),
|
||||||
'favourited' => $status->liked(),
|
'favourited' => $status->liked(),
|
||||||
'muted' => null,
|
'muted' => null,
|
||||||
'sensitive' => (bool) $status->is_nsfw,
|
'sensitive' => (bool) $status->is_nsfw,
|
||||||
'spoiler_text' => '',
|
'spoiler_text' => '',
|
||||||
'visibility' => $status->visibility,
|
'visibility' => $status->visibility,
|
||||||
'application' => null,
|
'application' => null,
|
||||||
'language' => null,
|
'language' => null,
|
||||||
'pinned' => null
|
'pinned' => null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeAccount(Status $status)
|
public function includeAccount(Status $status)
|
||||||
{
|
{
|
||||||
$account = $status->profile;
|
$account = $status->profile;
|
||||||
return $this->item($account, new AccountTransformer);
|
|
||||||
|
return $this->item($account, new AccountTransformer());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeMentions(Status $status)
|
public function includeMentions(Status $status)
|
||||||
{
|
{
|
||||||
$mentions = $status->mentions;
|
$mentions = $status->mentions;
|
||||||
return $this->collection($mentions, new MentionTransformer);
|
|
||||||
|
return $this->collection($mentions, new MentionTransformer());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeMediaAttachments(Status $status)
|
public function includeMediaAttachments(Status $status)
|
||||||
{
|
{
|
||||||
$media = $status->media;
|
$media = $status->media;
|
||||||
return $this->collection($media, new MediaTransformer);
|
|
||||||
|
return $this->collection($media, new MediaTransformer());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeTags(Status $status)
|
public function includeTags(Status $status)
|
||||||
{
|
{
|
||||||
$tags = $status->hashtags;
|
$tags = $status->hashtags;
|
||||||
return $this->collection($tags, new HashtagTransformer);
|
|
||||||
|
return $this->collection($tags, new HashtagTransformer());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Illuminate\Notifications\Notifiable;
|
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ class User extends Authenticatable
|
||||||
|
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
return url(config('app.url') . '/' . $this->username);
|
return url(config('app.url').'/'.$this->username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function settings()
|
public function settings()
|
||||||
|
|
|
@ -7,9 +7,9 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
class UserFilter extends Model
|
class UserFilter extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'user_id',
|
'user_id',
|
||||||
'filterable_id',
|
'filterable_id',
|
||||||
'filterable_type',
|
'filterable_type',
|
||||||
'filter_type'
|
'filter_type',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ namespace App\Util\ActivityPub\Concern;
|
||||||
|
|
||||||
use Zttp\Zttp;
|
use Zttp\Zttp;
|
||||||
|
|
||||||
class HTTPSignature {
|
class HTTPSignature
|
||||||
|
{
|
||||||
protected $localhosts = [
|
protected $localhosts = [
|
||||||
'127.0.0.1', 'localhost', '::1'
|
'127.0.0.1', 'localhost', '::1',
|
||||||
];
|
];
|
||||||
public $profile;
|
public $profile;
|
||||||
public $is_url;
|
public $is_url;
|
||||||
|
@ -15,20 +15,22 @@ class HTTPSignature {
|
||||||
public function validateUrl()
|
public function validateUrl()
|
||||||
{
|
{
|
||||||
// If the profile exists, assume its valid
|
// If the profile exists, assume its valid
|
||||||
if($this->is_url === false) {
|
if ($this->is_url === false) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = $this->profile;
|
$url = $this->profile;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$url = filter_var($url, FILTER_VALIDATE_URL);
|
$url = filter_var($url, FILTER_VALIDATE_URL);
|
||||||
$parsed = parse_url($url, PHP_URL_HOST);
|
$parsed = parse_url($url, PHP_URL_HOST);
|
||||||
if(!$parsed || in_array($parsed, $this->localhosts)) {
|
if (!$parsed || in_array($parsed, $this->localhosts)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,22 +39,22 @@ class HTTPSignature {
|
||||||
$this->profile = $profile;
|
$this->profile = $profile;
|
||||||
$this->is_url = $is_url;
|
$this->is_url = $is_url;
|
||||||
$valid = $this->validateUrl();
|
$valid = $this->validateUrl();
|
||||||
if(!$valid) {
|
if (!$valid) {
|
||||||
throw new \Exception('Invalid URL provided');
|
throw new \Exception('Invalid URL provided');
|
||||||
}
|
}
|
||||||
if($is_url && isset($profile->public_key) && $profile->public_key) {
|
if ($is_url && isset($profile->public_key) && $profile->public_key) {
|
||||||
return $profile->public_key;
|
return $profile->public_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$url = $this->profile;
|
$url = $this->profile;
|
||||||
$res = Zttp::timeout(30)->withHeaders([
|
$res = Zttp::timeout(30)->withHeaders([
|
||||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||||
'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org'
|
'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org',
|
||||||
])->get($url);
|
])->get($url);
|
||||||
$actor = json_decode($res->getBody(), true);
|
$actor = json_decode($res->getBody(), true);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new Exception('Unable to fetch public key');
|
throw new Exception('Unable to fetch public key');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $actor['publicKey']['publicKeyPem'];
|
return $actor['publicKey']['publicKeyPem'];
|
||||||
|
@ -62,33 +64,31 @@ class HTTPSignature {
|
||||||
{
|
{
|
||||||
$profile = $senderProfile;
|
$profile = $senderProfile;
|
||||||
$context = new Context([
|
$context = new Context([
|
||||||
'keys' => [$profile->keyId() => $profile->private_key],
|
'keys' => [$profile->keyId() => $profile->private_key],
|
||||||
'algorithm' => 'rsa-sha256',
|
'algorithm' => 'rsa-sha256',
|
||||||
'headers' => ['(request-target)', 'Date'],
|
'headers' => ['(request-target)', 'Date'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$handlerStack = GuzzleHttpSignatures::defaultHandlerFromContext($context);
|
$handlerStack = GuzzleHttpSignatures::defaultHandlerFromContext($context);
|
||||||
$client = new Client(['handler' => $handlerStack]);
|
$client = new Client(['handler' => $handlerStack]);
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||||
'Date' => date('D, d M Y h:i:s') . ' GMT',
|
'Date' => date('D, d M Y h:i:s').' GMT',
|
||||||
'Content-Type' => 'application/activity+json',
|
'Content-Type' => 'application/activity+json',
|
||||||
'User-Agent' => 'PixelFedBot - https://pixelfed.org'
|
'User-Agent' => 'PixelFedBot - https://pixelfed.org',
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = $client->post($url, [
|
$response = $client->post($url, [
|
||||||
'options' => [
|
'options' => [
|
||||||
'allow_redirects' => false,
|
'allow_redirects' => false,
|
||||||
'verify' => true,
|
'verify' => true,
|
||||||
'timeout' => 30
|
'timeout' => 30,
|
||||||
],
|
],
|
||||||
'headers' => $headers,
|
'headers' => $headers,
|
||||||
'body' => $body
|
'body' => $body,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $response->getBody();
|
return $response->getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,48 +2,48 @@
|
||||||
|
|
||||||
namespace App\Util\ActivityPub;
|
namespace App\Util\ActivityPub;
|
||||||
|
|
||||||
use \Zttp\Zttp;
|
use Zttp\Zttp;
|
||||||
|
|
||||||
class DiscoverActor {
|
class DiscoverActor
|
||||||
|
{
|
||||||
|
protected $url;
|
||||||
|
protected $response;
|
||||||
|
|
||||||
protected $url;
|
public function __construct($url)
|
||||||
protected $response;
|
{
|
||||||
|
$this->url = $url;
|
||||||
public function __construct($url)
|
|
||||||
{
|
|
||||||
$this->url = $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fetch()
|
|
||||||
{
|
|
||||||
$res = Zttp::withHeaders([
|
|
||||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
|
||||||
'User-Agent' => 'PixelFedBot - https://pixelfed.org'
|
|
||||||
])->get($this->url);
|
|
||||||
$this->response = $res->body();
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResponse()
|
|
||||||
{
|
|
||||||
return json_decode($this->response, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJsonResponse()
|
|
||||||
{
|
|
||||||
return $this->response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function discover()
|
|
||||||
{
|
|
||||||
$this->fetch();
|
|
||||||
$res = $this->getResponse();
|
|
||||||
|
|
||||||
if(empty($res) || !in_array('type', $res) || $res['type'] !== 'Person') {
|
|
||||||
throw new \Exception('Invalid Actor Object');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $res;
|
public function fetch()
|
||||||
}
|
{
|
||||||
|
$res = Zttp::withHeaders([
|
||||||
|
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
||||||
|
'User-Agent' => 'PixelFedBot - https://pixelfed.org',
|
||||||
|
])->get($this->url);
|
||||||
|
$this->response = $res->body();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResponse()
|
||||||
|
{
|
||||||
|
return json_decode($this->response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJsonResponse()
|
||||||
|
{
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function discover()
|
||||||
|
{
|
||||||
|
$this->fetch();
|
||||||
|
$res = $this->getResponse();
|
||||||
|
|
||||||
|
if (empty($res) || !in_array('type', $res) || $res['type'] !== 'Person') {
|
||||||
|
throw new \Exception('Invalid Actor Object');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
namespace App\Util\ActivityPub;
|
namespace App\Util\ActivityPub;
|
||||||
|
|
||||||
use App\Jobs\AvatarPipeline\CreateAvatar;
|
use App\Like;
|
||||||
use App\{Follower, Like, Profile, Like, Status, User};
|
use App\Profile;
|
||||||
|
|
||||||
class Inbox {
|
|
||||||
|
|
||||||
|
class Inbox
|
||||||
|
{
|
||||||
protected $request;
|
protected $request;
|
||||||
protected $profile;
|
protected $profile;
|
||||||
protected $payload;
|
protected $payload;
|
||||||
|
@ -58,12 +58,11 @@ class Inbox {
|
||||||
{
|
{
|
||||||
$actor = $this->payload['object'];
|
$actor = $this->payload['object'];
|
||||||
$target = $this->profile;
|
$target = $this->profile;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function actorFirstOrCreate($actorUrl)
|
public function actorFirstOrCreate($actorUrl)
|
||||||
{
|
{
|
||||||
if(Profile::whereRemoteUrl($actorUrl)->count() !== 0) {
|
if (Profile::whereRemoteUrl($actorUrl)->count() !== 0) {
|
||||||
return Profile::whereRemoteUrl($actorUrl)->firstOrFail();
|
return Profile::whereRemoteUrl($actorUrl)->firstOrFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@ class Inbox {
|
||||||
$username = $res['preferredUsername'];
|
$username = $res['preferredUsername'];
|
||||||
$remoteUsername = "@{$username}@{$domain}";
|
$remoteUsername = "@{$username}@{$domain}";
|
||||||
|
|
||||||
$profile = new Profile;
|
$profile = new Profile();
|
||||||
$profile->user_id = null;
|
$profile->user_id = null;
|
||||||
$profile->domain = $domain;
|
$profile->domain = $domain;
|
||||||
$profile->username = $remoteUsername;
|
$profile->username = $remoteUsername;
|
||||||
|
@ -82,7 +81,5 @@ class Inbox {
|
||||||
$profile->sharedInbox = $res['endpoints']['sharedInbox'];
|
$profile->sharedInbox = $res['endpoints']['sharedInbox'];
|
||||||
$profile->remote_url = $res['url'];
|
$profile->remote_url = $res['url'];
|
||||||
$profile->save();
|
$profile->save();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -8,11 +8,11 @@ class HashPreprocessor implements \Bitverse\Identicon\Preprocessor\PreprocessorI
|
||||||
|
|
||||||
public function __construct($algo = 'sha256')
|
public function __construct($algo = 'sha256')
|
||||||
{
|
{
|
||||||
$this->algo = $algo;
|
$this->algo = $algo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function process($string)
|
public function process($string)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,17 +5,12 @@
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
use App\Util\Lexer\Regex;
|
|
||||||
use App\Util\Lexer\Extractor;
|
|
||||||
use App\Util\Lexer\StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter Autolink Class
|
* Twitter Autolink Class.
|
||||||
*
|
*
|
||||||
* Parses tweets and generates HTML anchor tags around URLs, usernames,
|
* Parses tweets and generates HTML anchor tags around URLs, usernames,
|
||||||
* username/list pairs and hashtags.
|
* username/list pairs and hashtags.
|
||||||
|
@ -28,85 +23,83 @@ use App\Util\Lexer\StringUtils;
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
class Autolink extends Regex
|
class Autolink extends Regex
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked URLs.
|
* CSS class for auto-linked URLs.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $class_url = '';
|
protected $class_url = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked username URLs.
|
* CSS class for auto-linked username URLs.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $class_user = 'u-url mention';
|
protected $class_user = 'u-url mention';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked list URLs.
|
* CSS class for auto-linked list URLs.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $class_list = 'u-url list-slug';
|
protected $class_list = 'u-url list-slug';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked hashtag URLs.
|
* CSS class for auto-linked hashtag URLs.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $class_hash = 'u-url hashtag';
|
protected $class_hash = 'u-url hashtag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked cashtag URLs.
|
* CSS class for auto-linked cashtag URLs.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $class_cash = 'u-url cashtag';
|
protected $class_cash = 'u-url cashtag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL base for username links (the username without the @ will be appended).
|
* URL base for username links (the username without the @ will be appended).
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $url_base_user = null;
|
protected $url_base_user = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL base for list links (the username/list without the @ will be appended).
|
* URL base for list links (the username/list without the @ will be appended).
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $url_base_list = null;
|
protected $url_base_list = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL base for hashtag links (the hashtag without the # will be appended).
|
* URL base for hashtag links (the hashtag without the # will be appended).
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $url_base_hash = null;
|
protected $url_base_hash = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL base for cashtag links (the hashtag without the $ will be appended).
|
* URL base for cashtag links (the hashtag without the $ will be appended).
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $url_base_cash = null;
|
protected $url_base_cash = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected $nofollow = true;
|
protected $nofollow = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to include the value 'noopener' in the 'rel' attribute.
|
* Whether to include the value 'noopener' in the 'rel' attribute.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected $noopener = true;
|
protected $noopener = true;
|
||||||
|
|
||||||
|
@ -118,7 +111,7 @@ class Autolink extends Regex
|
||||||
* been undeprecated and thus the 'target' attribute can be used. If this is
|
* been undeprecated and thus the 'target' attribute can be used. If this is
|
||||||
* set to false then the 'target' attribute will be output.
|
* set to false then the 'target' attribute will be output.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected $external = true;
|
protected $external = true;
|
||||||
|
|
||||||
|
@ -129,19 +122,18 @@ class Autolink extends Regex
|
||||||
* since been reinstated in HTML 5. To output the 'target' attribute you
|
* since been reinstated in HTML 5. To output the 'target' attribute you
|
||||||
* must disable the adding of the string 'external' to the 'rel' attribute.
|
* must disable the adding of the string 'external' to the 'rel' attribute.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $target = '_blank';
|
protected $target = '_blank';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* attribute for invisible span tag
|
* attribute for invisible span tag.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $invisibleTagAttrs = "style='position:absolute;left:-9999px;'";
|
protected $invisibleTagAttrs = "style='position:absolute;left:-9999px;'";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @var Extractor
|
* @var Extractor
|
||||||
*/
|
*/
|
||||||
protected $extractor = null;
|
protected $extractor = null;
|
||||||
|
@ -149,12 +141,12 @@ class Autolink extends Regex
|
||||||
/**
|
/**
|
||||||
* Provides fluent method chaining.
|
* Provides fluent method chaining.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to be converted.
|
* @param string $tweet The tweet to be converted.
|
||||||
* @param bool $full_encode Whether to encode all special characters.
|
* @param bool $full_encode Whether to encode all special characters.
|
||||||
*
|
*
|
||||||
* @see __construct()
|
* @see __construct()
|
||||||
*
|
*
|
||||||
* @return Autolink
|
* @return Autolink
|
||||||
*/
|
*/
|
||||||
public static function create($tweet = null, $full_encode = false)
|
public static function create($tweet = null, $full_encode = false)
|
||||||
{
|
{
|
||||||
|
@ -169,9 +161,9 @@ class Autolink extends Regex
|
||||||
*
|
*
|
||||||
* @see htmlspecialchars()
|
* @see htmlspecialchars()
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to be converted.
|
* @param string $tweet The tweet to be converted.
|
||||||
* @param bool $escape Whether to escape the tweet (default: true).
|
* @param bool $escape Whether to escape the tweet (default: true).
|
||||||
* @param bool $full_encode Whether to encode all special characters.
|
* @param bool $full_encode Whether to encode all special characters.
|
||||||
*/
|
*/
|
||||||
public function __construct($tweet = null, $escape = true, $full_encode = false)
|
public function __construct($tweet = null, $escape = true, $full_encode = false)
|
||||||
{
|
{
|
||||||
|
@ -185,16 +177,16 @@ class Autolink extends Regex
|
||||||
parent::__construct($tweet);
|
parent::__construct($tweet);
|
||||||
}
|
}
|
||||||
$this->extractor = Extractor::create();
|
$this->extractor = Extractor::create();
|
||||||
$this->url_base_user = config('app.url') . '/';
|
$this->url_base_user = config('app.url').'/';
|
||||||
$this->url_base_list = config('app.url') . '/';
|
$this->url_base_list = config('app.url').'/';
|
||||||
$this->url_base_hash = config('app.url') . "/discover/tags/";
|
$this->url_base_hash = config('app.url').'/discover/tags/';
|
||||||
$this->url_base_cash = config('app.url') . '/search?q=%24';
|
$this->url_base_cash = config('app.url').'/search?q=%24';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked URLs.
|
* CSS class for auto-linked URLs.
|
||||||
*
|
*
|
||||||
* @return string CSS class for URL links.
|
* @return string CSS class for URL links.
|
||||||
*/
|
*/
|
||||||
public function getURLClass()
|
public function getURLClass()
|
||||||
{
|
{
|
||||||
|
@ -204,20 +196,21 @@ class Autolink extends Regex
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked URLs.
|
* CSS class for auto-linked URLs.
|
||||||
*
|
*
|
||||||
* @param string $v CSS class for URL links.
|
* @param string $v CSS class for URL links.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setURLClass($v)
|
public function setURLClass($v)
|
||||||
{
|
{
|
||||||
$this->class_url = trim($v);
|
$this->class_url = trim($v);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked username URLs.
|
* CSS class for auto-linked username URLs.
|
||||||
*
|
*
|
||||||
* @return string CSS class for username links.
|
* @return string CSS class for username links.
|
||||||
*/
|
*/
|
||||||
public function getUsernameClass()
|
public function getUsernameClass()
|
||||||
{
|
{
|
||||||
|
@ -227,20 +220,21 @@ class Autolink extends Regex
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked username URLs.
|
* CSS class for auto-linked username URLs.
|
||||||
*
|
*
|
||||||
* @param string $v CSS class for username links.
|
* @param string $v CSS class for username links.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setUsernameClass($v)
|
public function setUsernameClass($v)
|
||||||
{
|
{
|
||||||
$this->class_user = trim($v);
|
$this->class_user = trim($v);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked username/list URLs.
|
* CSS class for auto-linked username/list URLs.
|
||||||
*
|
*
|
||||||
* @return string CSS class for username/list links.
|
* @return string CSS class for username/list links.
|
||||||
*/
|
*/
|
||||||
public function getListClass()
|
public function getListClass()
|
||||||
{
|
{
|
||||||
|
@ -250,20 +244,21 @@ class Autolink extends Regex
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked username/list URLs.
|
* CSS class for auto-linked username/list URLs.
|
||||||
*
|
*
|
||||||
* @param string $v CSS class for username/list links.
|
* @param string $v CSS class for username/list links.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setListClass($v)
|
public function setListClass($v)
|
||||||
{
|
{
|
||||||
$this->class_list = trim($v);
|
$this->class_list = trim($v);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked hashtag URLs.
|
* CSS class for auto-linked hashtag URLs.
|
||||||
*
|
*
|
||||||
* @return string CSS class for hashtag links.
|
* @return string CSS class for hashtag links.
|
||||||
*/
|
*/
|
||||||
public function getHashtagClass()
|
public function getHashtagClass()
|
||||||
{
|
{
|
||||||
|
@ -273,20 +268,21 @@ class Autolink extends Regex
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked hashtag URLs.
|
* CSS class for auto-linked hashtag URLs.
|
||||||
*
|
*
|
||||||
* @param string $v CSS class for hashtag links.
|
* @param string $v CSS class for hashtag links.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setHashtagClass($v)
|
public function setHashtagClass($v)
|
||||||
{
|
{
|
||||||
$this->class_hash = trim($v);
|
$this->class_hash = trim($v);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked cashtag URLs.
|
* CSS class for auto-linked cashtag URLs.
|
||||||
*
|
*
|
||||||
* @return string CSS class for cashtag links.
|
* @return string CSS class for cashtag links.
|
||||||
*/
|
*/
|
||||||
public function getCashtagClass()
|
public function getCashtagClass()
|
||||||
{
|
{
|
||||||
|
@ -296,20 +292,21 @@ class Autolink extends Regex
|
||||||
/**
|
/**
|
||||||
* CSS class for auto-linked cashtag URLs.
|
* CSS class for auto-linked cashtag URLs.
|
||||||
*
|
*
|
||||||
* @param string $v CSS class for cashtag links.
|
* @param string $v CSS class for cashtag links.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setCashtagClass($v)
|
public function setCashtagClass($v)
|
||||||
{
|
{
|
||||||
$this->class_cash = trim($v);
|
$this->class_cash = trim($v);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
||||||
*
|
*
|
||||||
* @return bool Whether to add 'nofollow' to the 'rel' attribute.
|
* @return bool Whether to add 'nofollow' to the 'rel' attribute.
|
||||||
*/
|
*/
|
||||||
public function getNoFollow()
|
public function getNoFollow()
|
||||||
{
|
{
|
||||||
|
@ -319,13 +316,14 @@ class Autolink extends Regex
|
||||||
/**
|
/**
|
||||||
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
||||||
*
|
*
|
||||||
* @param bool $v The value to add to the 'target' attribute.
|
* @param bool $v The value to add to the 'target' attribute.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setNoFollow($v)
|
public function setNoFollow($v)
|
||||||
{
|
{
|
||||||
$this->nofollow = $v;
|
$this->nofollow = $v;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +335,7 @@ class Autolink extends Regex
|
||||||
* been undeprecated and thus the 'target' attribute can be used. If this is
|
* been undeprecated and thus the 'target' attribute can be used. If this is
|
||||||
* set to false then the 'target' attribute will be output.
|
* set to false then the 'target' attribute will be output.
|
||||||
*
|
*
|
||||||
* @return bool Whether to add 'external' to the 'rel' attribute.
|
* @return bool Whether to add 'external' to the 'rel' attribute.
|
||||||
*/
|
*/
|
||||||
public function getExternal()
|
public function getExternal()
|
||||||
{
|
{
|
||||||
|
@ -352,13 +350,14 @@ class Autolink extends Regex
|
||||||
* been undeprecated and thus the 'target' attribute can be used. If this is
|
* been undeprecated and thus the 'target' attribute can be used. If this is
|
||||||
* set to false then the 'target' attribute will be output.
|
* set to false then the 'target' attribute will be output.
|
||||||
*
|
*
|
||||||
* @param bool $v The value to add to the 'target' attribute.
|
* @param bool $v The value to add to the 'target' attribute.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setExternal($v)
|
public function setExternal($v)
|
||||||
{
|
{
|
||||||
$this->external = $v;
|
$this->external = $v;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +368,7 @@ class Autolink extends Regex
|
||||||
* since been reinstated in HTML 5. To output the 'target' attribute you
|
* since been reinstated in HTML 5. To output the 'target' attribute you
|
||||||
* must disable the adding of the string 'external' to the 'rel' attribute.
|
* must disable the adding of the string 'external' to the 'rel' attribute.
|
||||||
*
|
*
|
||||||
* @return string The value to add to the 'target' attribute.
|
* @return string The value to add to the 'target' attribute.
|
||||||
*/
|
*/
|
||||||
public function getTarget()
|
public function getTarget()
|
||||||
{
|
{
|
||||||
|
@ -383,22 +382,25 @@ class Autolink extends Regex
|
||||||
* since been reinstated in HTML 5. To output the 'target' attribute you
|
* since been reinstated in HTML 5. To output the 'target' attribute you
|
||||||
* must disable the adding of the string 'external' to the 'rel' attribute.
|
* must disable the adding of the string 'external' to the 'rel' attribute.
|
||||||
*
|
*
|
||||||
* @param string $v The value to add to the 'target' attribute.
|
* @param string $v The value to add to the 'target' attribute.
|
||||||
*
|
*
|
||||||
* @return Autolink Fluid method chaining.
|
* @return Autolink Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setTarget($v)
|
public function setTarget($v)
|
||||||
{
|
{
|
||||||
$this->target = trim($v);
|
$this->target = trim($v);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Autolink with entities
|
* Autolink with entities.
|
||||||
*
|
*
|
||||||
* @param string $tweet
|
* @param string $tweet
|
||||||
* @param array $entities
|
* @param array $entities
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function autoLinkEntities($tweet = null, $entities = null)
|
public function autoLinkEntities($tweet = null, $entities = null)
|
||||||
|
@ -428,6 +430,7 @@ class Autolink extends Regex
|
||||||
$beginIndex = $entity['indices'][1];
|
$beginIndex = $entity['indices'][1];
|
||||||
}
|
}
|
||||||
$text .= StringUtils::substr($tweet, $beginIndex, StringUtils::strlen($tweet));
|
$text .= StringUtils::substr($tweet, $beginIndex, StringUtils::strlen($tweet));
|
||||||
|
|
||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,13 +439,15 @@ class Autolink extends Regex
|
||||||
*
|
*
|
||||||
* @param string The tweet to be converted
|
* @param string The tweet to be converted
|
||||||
* @param mixed The entities info
|
* @param mixed The entities info
|
||||||
|
*
|
||||||
* @return string that auto-link HTML added
|
* @return string that auto-link HTML added
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function autoLinkWithJson($tweet = null, $json = null)
|
public function autoLinkWithJson($tweet = null, $json = null)
|
||||||
{
|
{
|
||||||
// concatenate entities
|
// concatenate entities
|
||||||
$entities = array();
|
$entities = [];
|
||||||
if (is_object($json)) {
|
if (is_object($json)) {
|
||||||
$json = $this->object2array($json);
|
$json = $this->object2array($json);
|
||||||
}
|
}
|
||||||
|
@ -460,13 +465,15 @@ class Autolink extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
$entities = $this->extractor->removeOverlappingEntities($entities);
|
$entities = $this->extractor->removeOverlappingEntities($entities);
|
||||||
|
|
||||||
return $this->autoLinkEntities($tweet, $entities);
|
return $this->autoLinkEntities($tweet, $entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert Object to Array
|
* convert Object to Array.
|
||||||
*
|
*
|
||||||
* @param mixed $obj
|
* @param mixed $obj
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function object2array($obj)
|
protected function object2array($obj)
|
||||||
|
@ -477,6 +484,7 @@ class Autolink extends Regex
|
||||||
$array[$key] = $this->object2array($var);
|
$array[$key] = $this->object2array($var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +492,9 @@ class Autolink extends Regex
|
||||||
* Auto-link hashtags, URLs, usernames and lists.
|
* Auto-link hashtags, URLs, usernames and lists.
|
||||||
*
|
*
|
||||||
* @param string The tweet to be converted
|
* @param string The tweet to be converted
|
||||||
|
*
|
||||||
* @return string that auto-link HTML added
|
* @return string that auto-link HTML added
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function autoLink($tweet = null)
|
public function autoLink($tweet = null)
|
||||||
|
@ -493,6 +503,7 @@ class Autolink extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$entities = $this->extractor->extractURLWithoutProtocol(false)->extractEntitiesWithIndices($tweet);
|
$entities = $this->extractor->extractURLWithoutProtocol(false)->extractEntitiesWithIndices($tweet);
|
||||||
|
|
||||||
return $this->autoLinkEntities($tweet, $entities);
|
return $this->autoLinkEntities($tweet, $entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,6 +513,7 @@ class Autolink extends Regex
|
||||||
* added.
|
* added.
|
||||||
*
|
*
|
||||||
* @return string that auto-link HTML added
|
* @return string that auto-link HTML added
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function autoLinkUsernamesAndLists($tweet = null)
|
public function autoLinkUsernamesAndLists($tweet = null)
|
||||||
|
@ -510,6 +522,7 @@ class Autolink extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$entities = $this->extractor->extractMentionsOrListsWithIndices($tweet);
|
$entities = $this->extractor->extractMentionsOrListsWithIndices($tweet);
|
||||||
|
|
||||||
return $this->autoLinkEntities($tweet, $entities);
|
return $this->autoLinkEntities($tweet, $entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,6 +531,7 @@ class Autolink extends Regex
|
||||||
* added.
|
* added.
|
||||||
*
|
*
|
||||||
* @return string that auto-link HTML added
|
* @return string that auto-link HTML added
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function autoLinkHashtags($tweet = null)
|
public function autoLinkHashtags($tweet = null)
|
||||||
|
@ -526,6 +540,7 @@ class Autolink extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$entities = $this->extractor->extractHashtagsWithIndices($tweet);
|
$entities = $this->extractor->extractHashtagsWithIndices($tweet);
|
||||||
|
|
||||||
return $this->autoLinkEntities($tweet, $entities);
|
return $this->autoLinkEntities($tweet, $entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,6 +550,7 @@ class Autolink extends Regex
|
||||||
* This only auto-links URLs with protocol.
|
* This only auto-links URLs with protocol.
|
||||||
*
|
*
|
||||||
* @return string that auto-link HTML added
|
* @return string that auto-link HTML added
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function autoLinkURLs($tweet = null)
|
public function autoLinkURLs($tweet = null)
|
||||||
|
@ -543,6 +559,7 @@ class Autolink extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$entities = $this->extractor->extractURLWithoutProtocol(false)->extractURLsWithIndices($tweet);
|
$entities = $this->extractor->extractURLWithoutProtocol(false)->extractURLsWithIndices($tweet);
|
||||||
|
|
||||||
return $this->autoLinkEntities($tweet, $entities);
|
return $this->autoLinkEntities($tweet, $entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,6 +568,7 @@ class Autolink extends Regex
|
||||||
* added.
|
* added.
|
||||||
*
|
*
|
||||||
* @return string that auto-link HTML added
|
* @return string that auto-link HTML added
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function autoLinkCashtags($tweet = null)
|
public function autoLinkCashtags($tweet = null)
|
||||||
|
@ -559,6 +577,7 @@ class Autolink extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$entities = $this->extractor->extractCashtagsWithIndices($tweet);
|
$entities = $this->extractor->extractCashtagsWithIndices($tweet);
|
||||||
|
|
||||||
return $this->autoLinkEntities($tweet, $entities);
|
return $this->autoLinkEntities($tweet, $entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,10 +658,11 @@ class Autolink extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param array $entity
|
* @param array $entity
|
||||||
* @param string $tweet
|
* @param string $tweet
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function linkToHashtag($entity, $tweet = null)
|
public function linkToHashtag($entity, $tweet = null)
|
||||||
|
@ -651,13 +671,13 @@ class Autolink extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$this->target = false;
|
$this->target = false;
|
||||||
$attributes = array();
|
$attributes = [];
|
||||||
$class = array();
|
$class = [];
|
||||||
$hash = StringUtils::substr($tweet, $entity['indices'][0], 1);
|
$hash = StringUtils::substr($tweet, $entity['indices'][0], 1);
|
||||||
$linkText = $hash . $entity['hashtag'];
|
$linkText = $hash.$entity['hashtag'];
|
||||||
|
|
||||||
$attributes['href'] = $this->url_base_hash . $entity['hashtag'] . '?src=hash';
|
$attributes['href'] = $this->url_base_hash.$entity['hashtag'].'?src=hash';
|
||||||
$attributes['title'] = '#' . $entity['hashtag'];
|
$attributes['title'] = '#'.$entity['hashtag'];
|
||||||
if (!empty($this->class_hash)) {
|
if (!empty($this->class_hash)) {
|
||||||
$class[] = $this->class_hash;
|
$class[] = $this->class_hash;
|
||||||
}
|
}
|
||||||
|
@ -665,32 +685,33 @@ class Autolink extends Regex
|
||||||
$class[] = 'rtl';
|
$class[] = 'rtl';
|
||||||
}
|
}
|
||||||
if (!empty($class)) {
|
if (!empty($class)) {
|
||||||
$attributes['class'] = join(' ', $class);
|
$attributes['class'] = implode(' ', $class);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->linkToText($entity, $linkText, $attributes);
|
return $this->linkToText($entity, $linkText, $attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param array $entity
|
||||||
*
|
*
|
||||||
* @param array $entity
|
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function linkToMentionAndList($entity)
|
public function linkToMentionAndList($entity)
|
||||||
{
|
{
|
||||||
$attributes = array();
|
$attributes = [];
|
||||||
|
|
||||||
if (!empty($entity['list_slug'])) {
|
if (!empty($entity['list_slug'])) {
|
||||||
# Replace the list and username
|
// Replace the list and username
|
||||||
$linkText = $entity['screen_name'] . $entity['list_slug'];
|
$linkText = $entity['screen_name'].$entity['list_slug'];
|
||||||
$class = $this->class_list;
|
$class = $this->class_list;
|
||||||
$url = $this->url_base_list . $linkText;
|
$url = $this->url_base_list.$linkText;
|
||||||
} else {
|
} else {
|
||||||
# Replace the username
|
// Replace the username
|
||||||
$linkText = $entity['screen_name'];
|
$linkText = $entity['screen_name'];
|
||||||
$class = $this->class_user;
|
$class = $this->class_user;
|
||||||
$url = $this->url_base_user . $linkText;
|
$url = $this->url_base_user.$linkText;
|
||||||
}
|
}
|
||||||
if (!empty($class)) {
|
if (!empty($class)) {
|
||||||
$attributes['class'] = $class;
|
$attributes['class'] = $class;
|
||||||
|
@ -701,10 +722,11 @@ class Autolink extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param array $entity
|
* @param array $entity
|
||||||
* @param string $tweet
|
* @param string $tweet
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function linkToCashtag($entity, $tweet = null)
|
public function linkToCashtag($entity, $tweet = null)
|
||||||
|
@ -712,10 +734,10 @@ class Autolink extends Regex
|
||||||
if (is_null($tweet)) {
|
if (is_null($tweet)) {
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$attributes = array();
|
$attributes = [];
|
||||||
$doller = StringUtils::substr($tweet, $entity['indices'][0], 1);
|
$doller = StringUtils::substr($tweet, $entity['indices'][0], 1);
|
||||||
$linkText = $doller . $entity['cashtag'];
|
$linkText = $doller.$entity['cashtag'];
|
||||||
$attributes['href'] = $this->url_base_cash . $entity['cashtag'];
|
$attributes['href'] = $this->url_base_cash.$entity['cashtag'];
|
||||||
$attributes['title'] = $linkText;
|
$attributes['title'] = $linkText;
|
||||||
if (!empty($this->class_cash)) {
|
if (!empty($this->class_cash)) {
|
||||||
$attributes['class'] = $this->class_cash;
|
$attributes['class'] = $this->class_cash;
|
||||||
|
@ -725,16 +747,17 @@ class Autolink extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @param array $entity
|
||||||
* @param array $entity
|
|
||||||
* @param string $text
|
* @param string $text
|
||||||
* @param array $attributes
|
* @param array $attributes
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public function linkToText(array $entity, $text, $attributes = array())
|
public function linkToText(array $entity, $text, $attributes = [])
|
||||||
{
|
{
|
||||||
$rel = array();
|
$rel = [];
|
||||||
if ($this->external) {
|
if ($this->external) {
|
||||||
$rel[] = 'external';
|
$rel[] = 'external';
|
||||||
}
|
}
|
||||||
|
@ -745,23 +768,25 @@ class Autolink extends Regex
|
||||||
$rel[] = 'noopener';
|
$rel[] = 'noopener';
|
||||||
}
|
}
|
||||||
if (!empty($rel)) {
|
if (!empty($rel)) {
|
||||||
$attributes['rel'] = join(' ', $rel);
|
$attributes['rel'] = implode(' ', $rel);
|
||||||
}
|
}
|
||||||
if ($this->target) {
|
if ($this->target) {
|
||||||
$attributes['target'] = $this->target;
|
$attributes['target'] = $this->target;
|
||||||
}
|
}
|
||||||
$link = '<a';
|
$link = '<a';
|
||||||
foreach ($attributes as $key => $val) {
|
foreach ($attributes as $key => $val) {
|
||||||
$link .= ' ' . $key . '="' . $this->escapeHTML($val) . '"';
|
$link .= ' '.$key.'="'.$this->escapeHTML($val).'"';
|
||||||
}
|
}
|
||||||
$link .= '>' . $text . '</a>';
|
$link .= '>'.$text.'</a>';
|
||||||
|
|
||||||
return $link;
|
return $link;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* html escape
|
* html escape.
|
||||||
*
|
*
|
||||||
* @param string $text
|
* @param string $text
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function escapeHTML($text)
|
protected function escapeHTML($text)
|
||||||
|
|
|
@ -5,16 +5,12 @@
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
use App\Util\Lexer\Regex;
|
|
||||||
use App\Util\Lexer\StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter Extractor Class
|
* Twitter Extractor Class.
|
||||||
*
|
*
|
||||||
* Parses tweets and extracts URLs, usernames, username/list pairs and
|
* Parses tweets and extracts URLs, usernames, username/list pairs and
|
||||||
* hashtags.
|
* hashtags.
|
||||||
|
@ -27,24 +23,22 @@ use App\Util\Lexer\StringUtils;
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
class Extractor extends Regex
|
class Extractor extends Regex
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected $extractURLWithoutProtocol = true;
|
protected $extractURLWithoutProtocol = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides fluent method chaining.
|
* Provides fluent method chaining.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to be converted.
|
* @param string $tweet The tweet to be converted.
|
||||||
*
|
*
|
||||||
* @see __construct()
|
* @see __construct()
|
||||||
*
|
*
|
||||||
* @return Extractor
|
* @return Extractor
|
||||||
*/
|
*/
|
||||||
public static function create($tweet = null)
|
public static function create($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -56,7 +50,7 @@ class Extractor extends Regex
|
||||||
*
|
*
|
||||||
* Extracts various parts of a tweet including URLs, usernames, hashtags...
|
* Extracts various parts of a tweet including URLs, usernames, hashtags...
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
*/
|
*/
|
||||||
public function __construct($tweet = null)
|
public function __construct($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -67,29 +61,32 @@ class Extractor extends Regex
|
||||||
* Extracts all parts of a tweet and returns an associative array containing
|
* Extracts all parts of a tweet and returns an associative array containing
|
||||||
* the extracted elements.
|
* the extracted elements.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The elements in the tweet.
|
*
|
||||||
|
* @return array The elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extract($tweet = null)
|
public function extract($tweet = null)
|
||||||
{
|
{
|
||||||
if (is_null($tweet)) {
|
if (is_null($tweet)) {
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
return array(
|
|
||||||
'hashtags' => $this->extractHashtags($tweet),
|
return [
|
||||||
'urls' => $this->extractURLs($tweet),
|
'hashtags' => $this->extractHashtags($tweet),
|
||||||
'mentions' => $this->extractMentionedUsernames($tweet),
|
'urls' => $this->extractURLs($tweet),
|
||||||
'replyto' => $this->extractRepliedUsernames($tweet),
|
'mentions' => $this->extractMentionedUsernames($tweet),
|
||||||
|
'replyto' => $this->extractRepliedUsernames($tweet),
|
||||||
'hashtags_with_indices' => $this->extractHashtagsWithIndices($tweet),
|
'hashtags_with_indices' => $this->extractHashtagsWithIndices($tweet),
|
||||||
'urls_with_indices' => $this->extractURLsWithIndices($tweet),
|
'urls_with_indices' => $this->extractURLsWithIndices($tweet),
|
||||||
'mentions_with_indices' => $this->extractMentionedUsernamesWithIndices($tweet),
|
'mentions_with_indices' => $this->extractMentionedUsernamesWithIndices($tweet),
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract URLs, @mentions, lists and #hashtag from a given text/tweet.
|
* Extract URLs, @mentions, lists and #hashtag from a given text/tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
|
*
|
||||||
* @return array list of extracted entities
|
* @return array list of extracted entities
|
||||||
*/
|
*/
|
||||||
public function extractEntitiesWithIndices($tweet = null)
|
public function extractEntitiesWithIndices($tweet = null)
|
||||||
|
@ -97,63 +94,70 @@ class Extractor extends Regex
|
||||||
if (is_null($tweet)) {
|
if (is_null($tweet)) {
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$entities = array();
|
$entities = [];
|
||||||
$entities = array_merge($entities, $this->extractURLsWithIndices($tweet));
|
$entities = array_merge($entities, $this->extractURLsWithIndices($tweet));
|
||||||
$entities = array_merge($entities, $this->extractHashtagsWithIndices($tweet, false));
|
$entities = array_merge($entities, $this->extractHashtagsWithIndices($tweet, false));
|
||||||
$entities = array_merge($entities, $this->extractMentionsOrListsWithIndices($tweet));
|
$entities = array_merge($entities, $this->extractMentionsOrListsWithIndices($tweet));
|
||||||
$entities = array_merge($entities, $this->extractCashtagsWithIndices($tweet));
|
$entities = array_merge($entities, $this->extractCashtagsWithIndices($tweet));
|
||||||
$entities = $this->removeOverlappingEntities($entities);
|
$entities = $this->removeOverlappingEntities($entities);
|
||||||
|
|
||||||
return $entities;
|
return $entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts all the hashtags from the tweet.
|
* Extracts all the hashtags from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The hashtag elements in the tweet.
|
*
|
||||||
|
* @return array The hashtag elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractHashtags($tweet = null)
|
public function extractHashtags($tweet = null)
|
||||||
{
|
{
|
||||||
$hashtagsOnly = array();
|
$hashtagsOnly = [];
|
||||||
$hashtagsWithIndices = $this->extractHashtagsWithIndices($tweet);
|
$hashtagsWithIndices = $this->extractHashtagsWithIndices($tweet);
|
||||||
|
|
||||||
foreach ($hashtagsWithIndices as $hashtagWithIndex) {
|
foreach ($hashtagsWithIndices as $hashtagWithIndex) {
|
||||||
$hashtagsOnly[] = $hashtagWithIndex['hashtag'];
|
$hashtagsOnly[] = $hashtagWithIndex['hashtag'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $hashtagsOnly;
|
return $hashtagsOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts all the cashtags from the tweet.
|
* Extracts all the cashtags from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The cashtag elements in the tweet.
|
*
|
||||||
|
* @return array The cashtag elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractCashtags($tweet = null)
|
public function extractCashtags($tweet = null)
|
||||||
{
|
{
|
||||||
$cashtagsOnly = array();
|
$cashtagsOnly = [];
|
||||||
$cashtagsWithIndices = $this->extractCashtagsWithIndices($tweet);
|
$cashtagsWithIndices = $this->extractCashtagsWithIndices($tweet);
|
||||||
|
|
||||||
foreach ($cashtagsWithIndices as $cashtagWithIndex) {
|
foreach ($cashtagsWithIndices as $cashtagWithIndex) {
|
||||||
$cashtagsOnly[] = $cashtagWithIndex['cashtag'];
|
$cashtagsOnly[] = $cashtagWithIndex['cashtag'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cashtagsOnly;
|
return $cashtagsOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts all the URLs from the tweet.
|
* Extracts all the URLs from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The URL elements in the tweet.
|
*
|
||||||
|
* @return array The URL elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractURLs($tweet = null)
|
public function extractURLs($tweet = null)
|
||||||
{
|
{
|
||||||
$urlsOnly = array();
|
$urlsOnly = [];
|
||||||
$urlsWithIndices = $this->extractURLsWithIndices($tweet);
|
$urlsWithIndices = $this->extractURLsWithIndices($tweet);
|
||||||
|
|
||||||
foreach ($urlsWithIndices as $urlWithIndex) {
|
foreach ($urlsWithIndices as $urlWithIndex) {
|
||||||
$urlsOnly[] = $urlWithIndex['url'];
|
$urlsOnly[] = $urlWithIndex['url'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $urlsOnly;
|
return $urlsOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,21 +166,23 @@ class Extractor extends Regex
|
||||||
*
|
*
|
||||||
* A mention is an occurrence of a username anywhere in a tweet.
|
* A mention is an occurrence of a username anywhere in a tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The usernames elements in the tweet.
|
*
|
||||||
|
* @return array The usernames elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractMentionedScreennames($tweet = null)
|
public function extractMentionedScreennames($tweet = null)
|
||||||
{
|
{
|
||||||
$usernamesOnly = array();
|
$usernamesOnly = [];
|
||||||
$mentionsWithIndices = $this->extractMentionsOrListsWithIndices($tweet);
|
$mentionsWithIndices = $this->extractMentionsOrListsWithIndices($tweet);
|
||||||
|
|
||||||
foreach ($mentionsWithIndices as $mentionWithIndex) {
|
foreach ($mentionsWithIndices as $mentionWithIndex) {
|
||||||
$screen_name = mb_strtolower($mentionWithIndex['screen_name']);
|
$screen_name = mb_strtolower($mentionWithIndex['screen_name']);
|
||||||
if (empty($screen_name) OR in_array($screen_name, $usernamesOnly)) {
|
if (empty($screen_name) or in_array($screen_name, $usernamesOnly)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$usernamesOnly[] = $screen_name;
|
$usernamesOnly[] = $screen_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $usernamesOnly;
|
return $usernamesOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,12 +191,14 @@ class Extractor extends Regex
|
||||||
*
|
*
|
||||||
* A mention is an occurrence of a username anywhere in a tweet.
|
* A mention is an occurrence of a username anywhere in a tweet.
|
||||||
*
|
*
|
||||||
* @return array The usernames elements in the tweet.
|
* @return array The usernames elements in the tweet.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function extractMentionedUsernames($tweet)
|
public function extractMentionedUsernames($tweet)
|
||||||
{
|
{
|
||||||
$this->tweet = $tweet;
|
$this->tweet = $tweet;
|
||||||
|
|
||||||
return $this->extractMentionedScreennames($tweet);
|
return $this->extractMentionedScreennames($tweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +207,9 @@ class Extractor extends Regex
|
||||||
*
|
*
|
||||||
* A reply is an occurrence of a username at the beginning of a tweet.
|
* A reply is an occurrence of a username at the beginning of a tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The usernames replied to in a tweet.
|
*
|
||||||
|
* @return array The usernames replied to in a tweet.
|
||||||
*/
|
*/
|
||||||
public function extractReplyScreenname($tweet = null)
|
public function extractReplyScreenname($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -208,10 +217,11 @@ class Extractor extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
$matched = preg_match(self::$patterns['valid_reply'], $tweet, $matches);
|
$matched = preg_match(self::$patterns['valid_reply'], $tweet, $matches);
|
||||||
# Check username ending in
|
// Check username ending in
|
||||||
if ($matched && preg_match(self::$patterns['end_mention_match'], $matches[2])) {
|
if ($matched && preg_match(self::$patterns['end_mention_match'], $matches[2])) {
|
||||||
$matched = false;
|
$matched = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $matched ? $matches[1] : null;
|
return $matched ? $matches[1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +230,8 @@ class Extractor extends Regex
|
||||||
*
|
*
|
||||||
* A reply is an occurrence of a username at the beginning of a tweet.
|
* A reply is an occurrence of a username at the beginning of a tweet.
|
||||||
*
|
*
|
||||||
* @return array The usernames replied to in a tweet.
|
* @return array The usernames replied to in a tweet.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function extractRepliedUsernames()
|
public function extractRepliedUsernames()
|
||||||
|
@ -231,9 +242,10 @@ class Extractor extends Regex
|
||||||
/**
|
/**
|
||||||
* Extracts all the hashtags and the indices they occur at from the tweet.
|
* Extracts all the hashtags and the indices they occur at from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @param boolean $checkUrlOverlap if true, check if extracted hashtags overlap URLs and remove overlapping ones
|
* @param bool $checkUrlOverlap if true, check if extracted hashtags overlap URLs and remove overlapping ones
|
||||||
* @return array The hashtag elements in the tweet.
|
*
|
||||||
|
* @return array The hashtag elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractHashtagsWithIndices($tweet = null, $checkUrlOverlap = true)
|
public function extractHashtagsWithIndices($tweet = null, $checkUrlOverlap = true)
|
||||||
{
|
{
|
||||||
|
@ -242,36 +254,36 @@ class Extractor extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/[##]/iu', $tweet)) {
|
if (!preg_match('/[##]/iu', $tweet)) {
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
preg_match_all(self::$patterns['valid_hashtag'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
preg_match_all(self::$patterns['valid_hashtag'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
||||||
$tags = array();
|
$tags = [];
|
||||||
|
|
||||||
foreach ($matches as $match) {
|
foreach ($matches as $match) {
|
||||||
list($all, $before, $hash, $hashtag, $outer) = array_pad($match, 3, array('', 0));
|
list($all, $before, $hash, $hashtag, $outer) = array_pad($match, 3, ['', 0]);
|
||||||
$start_position = $hash[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $hash[1])) : $hash[1];
|
$start_position = $hash[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $hash[1])) : $hash[1];
|
||||||
$end_position = $start_position + StringUtils::strlen($hash[0] . $hashtag[0]);
|
$end_position = $start_position + StringUtils::strlen($hash[0].$hashtag[0]);
|
||||||
|
|
||||||
if (preg_match(self::$patterns['end_hashtag_match'], $outer[0])) {
|
if (preg_match(self::$patterns['end_hashtag_match'], $outer[0])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tags[] = array(
|
$tags[] = [
|
||||||
'hashtag' => $hashtag[0],
|
'hashtag' => $hashtag[0],
|
||||||
'indices' => array($start_position, $end_position)
|
'indices' => [$start_position, $end_position],
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$checkUrlOverlap) {
|
if (!$checkUrlOverlap) {
|
||||||
return $tags;
|
return $tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
# check url overlap
|
// check url overlap
|
||||||
$urls = $this->extractURLsWithIndices($tweet);
|
$urls = $this->extractURLsWithIndices($tweet);
|
||||||
$entities = $this->removeOverlappingEntities(array_merge($tags, $urls));
|
$entities = $this->removeOverlappingEntities(array_merge($tags, $urls));
|
||||||
|
|
||||||
$validTags = array();
|
$validTags = [];
|
||||||
foreach ($entities as $entity) {
|
foreach ($entities as $entity) {
|
||||||
if (empty($entity['hashtag'])) {
|
if (empty($entity['hashtag'])) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -285,8 +297,9 @@ class Extractor extends Regex
|
||||||
/**
|
/**
|
||||||
* Extracts all the cashtags and the indices they occur at from the tweet.
|
* Extracts all the cashtags and the indices they occur at from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The cashtag elements in the tweet.
|
*
|
||||||
|
* @return array The cashtag elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractCashtagsWithIndices($tweet = null)
|
public function extractCashtagsWithIndices($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -295,25 +308,25 @@ class Extractor extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/\$/iu', $tweet)) {
|
if (!preg_match('/\$/iu', $tweet)) {
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
preg_match_all(self::$patterns['valid_cashtag'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
preg_match_all(self::$patterns['valid_cashtag'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
||||||
$tags = array();
|
$tags = [];
|
||||||
|
|
||||||
foreach ($matches as $match) {
|
foreach ($matches as $match) {
|
||||||
list($all, $before, $dollar, $cash_text, $outer) = array_pad($match, 3, array('', 0));
|
list($all, $before, $dollar, $cash_text, $outer) = array_pad($match, 3, ['', 0]);
|
||||||
$start_position = $dollar[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $dollar[1])) : $dollar[1];
|
$start_position = $dollar[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $dollar[1])) : $dollar[1];
|
||||||
$end_position = $start_position + StringUtils::strlen($dollar[0] . $cash_text[0]);
|
$end_position = $start_position + StringUtils::strlen($dollar[0].$cash_text[0]);
|
||||||
|
|
||||||
if (preg_match(self::$patterns['end_hashtag_match'], $outer[0])) {
|
if (preg_match(self::$patterns['end_hashtag_match'], $outer[0])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tags[] = array(
|
$tags[] = [
|
||||||
'cashtag' => $cash_text[0],
|
'cashtag' => $cash_text[0],
|
||||||
'indices' => array($start_position, $end_position)
|
'indices' => [$start_position, $end_position],
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tags;
|
return $tags;
|
||||||
|
@ -322,8 +335,9 @@ class Extractor extends Regex
|
||||||
/**
|
/**
|
||||||
* Extracts all the URLs and the indices they occur at from the tweet.
|
* Extracts all the URLs and the indices they occur at from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The URLs elements in the tweet.
|
*
|
||||||
|
* @return array The URLs elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractURLsWithIndices($tweet = null)
|
public function extractURLsWithIndices($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -333,14 +347,14 @@ class Extractor extends Regex
|
||||||
|
|
||||||
$needle = $this->extractURLWithoutProtocol() ? '.' : ':';
|
$needle = $this->extractURLWithoutProtocol() ? '.' : ':';
|
||||||
if (strpos($tweet, $needle) === false) {
|
if (strpos($tweet, $needle) === false) {
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$urls = array();
|
$urls = [];
|
||||||
preg_match_all(self::$patterns['valid_url'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
preg_match_all(self::$patterns['valid_url'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
||||||
|
|
||||||
foreach ($matches as $match) {
|
foreach ($matches as $match) {
|
||||||
list($all, $before, $url, $protocol, $domain, $port, $path, $query) = array_pad($match, 8, array(''));
|
list($all, $before, $url, $protocol, $domain, $port, $path, $query) = array_pad($match, 8, ['']);
|
||||||
$start_position = $url[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $url[1])) : $url[1];
|
$start_position = $url[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $url[1])) : $url[1];
|
||||||
$end_position = $start_position + StringUtils::strlen($url[0]);
|
$end_position = $start_position + StringUtils::strlen($url[0]);
|
||||||
|
|
||||||
|
@ -364,13 +378,13 @@ class Extractor extends Regex
|
||||||
$ascii_end_position = 0;
|
$ascii_end_position = 0;
|
||||||
|
|
||||||
if (preg_match(self::$patterns['valid_ascii_domain'], $domain, $asciiDomain)) {
|
if (preg_match(self::$patterns['valid_ascii_domain'], $domain, $asciiDomain)) {
|
||||||
$asciiDomain[0] = preg_replace('/' . preg_quote($domain, '/') . '/u', $asciiDomain[0], $url);
|
$asciiDomain[0] = preg_replace('/'.preg_quote($domain, '/').'/u', $asciiDomain[0], $url);
|
||||||
$ascii_start_position = StringUtils::strpos($domain, $asciiDomain[0], $ascii_end_position);
|
$ascii_start_position = StringUtils::strpos($domain, $asciiDomain[0], $ascii_end_position);
|
||||||
$ascii_end_position = $ascii_start_position + StringUtils::strlen($asciiDomain[0]);
|
$ascii_end_position = $ascii_start_position + StringUtils::strlen($asciiDomain[0]);
|
||||||
$last_url = array(
|
$last_url = [
|
||||||
'url' => $asciiDomain[0],
|
'url' => $asciiDomain[0],
|
||||||
'indices' => array($start_position + $ascii_start_position, $start_position + $ascii_end_position),
|
'indices' => [$start_position + $ascii_start_position, $start_position + $ascii_end_position],
|
||||||
);
|
];
|
||||||
if (!empty($path)
|
if (!empty($path)
|
||||||
|| preg_match(self::$patterns['valid_special_short_domain'], $asciiDomain[0])
|
|| preg_match(self::$patterns['valid_special_short_domain'], $asciiDomain[0])
|
||||||
|| !preg_match(self::$patterns['invalid_short_domain'], $asciiDomain[0])) {
|
|| !preg_match(self::$patterns['invalid_short_domain'], $asciiDomain[0])) {
|
||||||
|
@ -386,7 +400,7 @@ class Extractor extends Regex
|
||||||
// $last_url only contains domain. Need to add path and query if they exist.
|
// $last_url only contains domain. Need to add path and query if they exist.
|
||||||
if (!empty($path)) {
|
if (!empty($path)) {
|
||||||
// last_url was not added. Add it to urls here.
|
// last_url was not added. Add it to urls here.
|
||||||
$last_url['url'] = preg_replace('/' . preg_quote($domain, '/') . '/u', $last_url['url'], $url);
|
$last_url['url'] = preg_replace('/'.preg_quote($domain, '/').'/u', $last_url['url'], $url);
|
||||||
$last_url['indices'][1] = $end_position;
|
$last_url['indices'][1] = $end_position;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -395,10 +409,10 @@ class Extractor extends Regex
|
||||||
$url = $tcoUrlMatches[0];
|
$url = $tcoUrlMatches[0];
|
||||||
$end_position = $start_position + StringUtils::strlen($url);
|
$end_position = $start_position + StringUtils::strlen($url);
|
||||||
}
|
}
|
||||||
$urls[] = array(
|
$urls[] = [
|
||||||
'url' => $url,
|
'url' => $url,
|
||||||
'indices' => array($start_position, $end_position),
|
'indices' => [$start_position, $end_position],
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,8 +422,9 @@ class Extractor extends Regex
|
||||||
/**
|
/**
|
||||||
* Extracts all the usernames and the indices they occur at from the tweet.
|
* Extracts all the usernames and the indices they occur at from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The username elements in the tweet.
|
*
|
||||||
|
* @return array The username elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractMentionedScreennamesWithIndices($tweet = null)
|
public function extractMentionedScreennamesWithIndices($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -417,7 +432,7 @@ class Extractor extends Regex
|
||||||
$tweet = $this->tweet;
|
$tweet = $this->tweet;
|
||||||
}
|
}
|
||||||
|
|
||||||
$usernamesOnly = array();
|
$usernamesOnly = [];
|
||||||
$mentions = $this->extractMentionsOrListsWithIndices($tweet);
|
$mentions = $this->extractMentionsOrListsWithIndices($tweet);
|
||||||
foreach ($mentions as $mention) {
|
foreach ($mentions as $mention) {
|
||||||
if (isset($mention['list_slug'])) {
|
if (isset($mention['list_slug'])) {
|
||||||
|
@ -425,13 +440,15 @@ class Extractor extends Regex
|
||||||
}
|
}
|
||||||
$usernamesOnly[] = $mention;
|
$usernamesOnly[] = $mention;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $usernamesOnly;
|
return $usernamesOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts all the usernames and the indices they occur at from the tweet.
|
* Extracts all the usernames and the indices they occur at from the tweet.
|
||||||
*
|
*
|
||||||
* @return array The username elements in the tweet.
|
* @return array The username elements in the tweet.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function extractMentionedUsernamesWithIndices()
|
public function extractMentionedUsernamesWithIndices()
|
||||||
|
@ -442,8 +459,9 @@ class Extractor extends Regex
|
||||||
/**
|
/**
|
||||||
* Extracts all the usernames and the indices they occur at from the tweet.
|
* Extracts all the usernames and the indices they occur at from the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to extract.
|
* @param string $tweet The tweet to extract.
|
||||||
* @return array The username elements in the tweet.
|
*
|
||||||
|
* @return array The username elements in the tweet.
|
||||||
*/
|
*/
|
||||||
public function extractMentionsOrListsWithIndices($tweet = null)
|
public function extractMentionsOrListsWithIndices($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -452,21 +470,21 @@ class Extractor extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/[@@]/iu', $tweet)) {
|
if (!preg_match('/[@@]/iu', $tweet)) {
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
preg_match_all(self::$patterns['valid_mentions_or_lists'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
preg_match_all(self::$patterns['valid_mentions_or_lists'], $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
|
||||||
$results = array();
|
$results = [];
|
||||||
|
|
||||||
foreach ($matches as $match) {
|
foreach ($matches as $match) {
|
||||||
list($all, $before, $at, $username, $list_slug, $outer) = array_pad($match, 6, array('', 0));
|
list($all, $before, $at, $username, $list_slug, $outer) = array_pad($match, 6, ['', 0]);
|
||||||
$start_position = $at[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $at[1])) : $at[1];
|
$start_position = $at[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $at[1])) : $at[1];
|
||||||
$end_position = $start_position + StringUtils::strlen($at[0]) + StringUtils::strlen($username[0]);
|
$end_position = $start_position + StringUtils::strlen($at[0]) + StringUtils::strlen($username[0]);
|
||||||
$entity = array(
|
$entity = [
|
||||||
'screen_name' => $username[0],
|
'screen_name' => $username[0],
|
||||||
'list_slug' => $list_slug[0],
|
'list_slug' => $list_slug[0],
|
||||||
'indices' => array($start_position, $end_position),
|
'indices' => [$start_position, $end_position],
|
||||||
);
|
];
|
||||||
|
|
||||||
if (preg_match(self::$patterns['end_mention_match'], $outer[0])) {
|
if (preg_match(self::$patterns['end_mention_match'], $outer[0])) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -485,7 +503,8 @@ class Extractor extends Regex
|
||||||
/**
|
/**
|
||||||
* Extracts all the usernames and the indices they occur at from the tweet.
|
* Extracts all the usernames and the indices they occur at from the tweet.
|
||||||
*
|
*
|
||||||
* @return array The username elements in the tweet.
|
* @return array The username elements in the tweet.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function extractMentionedUsernamesOrListsWithIndices()
|
public function extractMentionedUsernamesOrListsWithIndices()
|
||||||
|
@ -494,9 +513,10 @@ class Extractor extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setter/getter for extractURLWithoutProtocol
|
* setter/getter for extractURLWithoutProtocol.
|
||||||
|
*
|
||||||
|
* @param bool $flag
|
||||||
*
|
*
|
||||||
* @param boolean $flag
|
|
||||||
* @return Extractor
|
* @return Extractor
|
||||||
*/
|
*/
|
||||||
public function extractURLWithoutProtocol($flag = null)
|
public function extractURLWithoutProtocol($flag = null)
|
||||||
|
@ -505,6 +525,7 @@ class Extractor extends Regex
|
||||||
return $this->extractURLWithoutProtocol;
|
return $this->extractURLWithoutProtocol;
|
||||||
}
|
}
|
||||||
$this->extractURLWithoutProtocol = (bool) $flag;
|
$this->extractURLWithoutProtocol = (bool) $flag;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,12 +534,13 @@ class Extractor extends Regex
|
||||||
* This returns a new array with no overlapping entities.
|
* This returns a new array with no overlapping entities.
|
||||||
*
|
*
|
||||||
* @param array $entities
|
* @param array $entities
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function removeOverlappingEntities($entities)
|
public function removeOverlappingEntities($entities)
|
||||||
{
|
{
|
||||||
$result = array();
|
$result = [];
|
||||||
usort($entities, array($this, 'sortEntites'));
|
usort($entities, [$this, 'sortEntites']);
|
||||||
|
|
||||||
$prev = null;
|
$prev = null;
|
||||||
foreach ($entities as $entity) {
|
foreach ($entities as $entity) {
|
||||||
|
@ -528,14 +550,16 @@ class Extractor extends Regex
|
||||||
$prev = $entity;
|
$prev = $entity;
|
||||||
$result[] = $entity;
|
$result[] = $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sort by entity start index
|
* sort by entity start index.
|
||||||
*
|
*
|
||||||
* @param array $a
|
* @param array $a
|
||||||
* @param array $b
|
* @param array $b
|
||||||
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
protected function sortEntites($a, $b)
|
protected function sortEntites($a, $b)
|
||||||
|
@ -543,6 +567,7 @@ class Extractor extends Regex
|
||||||
if ($a['indices'][0] == $b['indices'][0]) {
|
if ($a['indices'][0] == $b['indices'][0]) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($a['indices'][0] < $b['indices'][0]) ? -1 : 1;
|
return ($a['indices'][0] < $b['indices'][0]) ? -1 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,35 +2,36 @@
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
class Hashtag {
|
class Hashtag
|
||||||
|
{
|
||||||
|
public static function getHashtags($status)
|
||||||
|
{
|
||||||
|
$hashtags = false;
|
||||||
|
preg_match_all("/(?<!&)(#\w+)/u", $status, $matches);
|
||||||
|
if ($matches) {
|
||||||
|
$res = array_count_values($matches[0]);
|
||||||
|
$hashtags = array_keys($res);
|
||||||
|
}
|
||||||
|
|
||||||
public static function getHashtags($status)
|
return $hashtags;
|
||||||
{
|
|
||||||
$hashtags = false;
|
|
||||||
preg_match_all("/(?<!&)(#\w+)/u", $status, $matches);
|
|
||||||
if ($matches) {
|
|
||||||
$res = array_count_values($matches[0]);
|
|
||||||
$hashtags = array_keys($res);
|
|
||||||
}
|
|
||||||
return $hashtags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function replaceHashtagsWithLinks($status)
|
|
||||||
{
|
|
||||||
$hashtags = self::getHashtags($status);
|
|
||||||
if(!$hashtags) { return false; }
|
|
||||||
|
|
||||||
$rendered = $status;
|
|
||||||
|
|
||||||
foreach($hashtags as $hashtag) {
|
|
||||||
$tag = substr($hashtag, 1);
|
|
||||||
$link = config('routes.hashtag.search') . $tag;
|
|
||||||
$href = "<a href='{$link}' class='mention hashtag status-link' rel='noopener'>{$hashtag}</a>";
|
|
||||||
$rendered = str_replace($hashtag, $href, $rendered);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rendered;
|
public static function replaceHashtagsWithLinks($status)
|
||||||
|
{
|
||||||
|
$hashtags = self::getHashtags($status);
|
||||||
|
if (!$hashtags) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
$rendered = $status;
|
||||||
|
|
||||||
|
foreach ($hashtags as $hashtag) {
|
||||||
|
$tag = substr($hashtag, 1);
|
||||||
|
$link = config('routes.hashtag.search').$tag;
|
||||||
|
$href = "<a href='{$link}' class='mention hashtag status-link' rel='noopener'>{$hashtag}</a>";
|
||||||
|
$rendered = str_replace($hashtag, $href, $rendered);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rendered;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,16 +4,12 @@
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Nick Pope
|
* @copyright Copyright © 2010, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
use App\Util\Lexer\Regex;
|
|
||||||
use App\Util\Lexer\StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter HitHighlighter Class
|
* Twitter HitHighlighter Class.
|
||||||
*
|
*
|
||||||
* Performs "hit highlighting" on tweets that have been auto-linked already.
|
* Performs "hit highlighting" on tweets that have been auto-linked already.
|
||||||
* Useful with the results returned from the search API.
|
* Useful with the results returned from the search API.
|
||||||
|
@ -25,27 +21,25 @@ use App\Util\Lexer\StringUtils;
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Nick Pope
|
* @copyright Copyright © 2010, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
class HitHighlighter extends Regex
|
class HitHighlighter extends Regex
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tag to surround hits with.
|
* The tag to surround hits with.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $tag = 'em';
|
protected $tag = 'em';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides fluent method chaining.
|
* Provides fluent method chaining.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to be hit highlighted.
|
* @param string $tweet The tweet to be hit highlighted.
|
||||||
* @param bool $full_encode Whether to encode all special characters.
|
* @param bool $full_encode Whether to encode all special characters.
|
||||||
*
|
*
|
||||||
* @see __construct()
|
* @see __construct()
|
||||||
*
|
*
|
||||||
* @return HitHighlighter
|
* @return HitHighlighter
|
||||||
*/
|
*/
|
||||||
public static function create($tweet = null, $full_encode = false)
|
public static function create($tweet = null, $full_encode = false)
|
||||||
{
|
{
|
||||||
|
@ -59,9 +53,9 @@ class HitHighlighter extends Regex
|
||||||
*
|
*
|
||||||
* @see htmlspecialchars()
|
* @see htmlspecialchars()
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to be hit highlighted.
|
* @param string $tweet The tweet to be hit highlighted.
|
||||||
* @param bool $escape Whether to escape the tweet (default: true).
|
* @param bool $escape Whether to escape the tweet (default: true).
|
||||||
* @param bool $full_encode Whether to encode all special characters.
|
* @param bool $full_encode Whether to encode all special characters.
|
||||||
*/
|
*/
|
||||||
public function __construct($tweet = null, $escape = true, $full_encode = false)
|
public function __construct($tweet = null, $escape = true, $full_encode = false)
|
||||||
{
|
{
|
||||||
|
@ -79,7 +73,7 @@ class HitHighlighter extends Regex
|
||||||
/**
|
/**
|
||||||
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
||||||
*
|
*
|
||||||
* @return string The tag name.
|
* @return string The tag name.
|
||||||
*/
|
*/
|
||||||
public function getTag()
|
public function getTag()
|
||||||
{
|
{
|
||||||
|
@ -89,26 +83,27 @@ class HitHighlighter extends Regex
|
||||||
/**
|
/**
|
||||||
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
||||||
*
|
*
|
||||||
* @param string $v The tag name.
|
* @param string $v The tag name.
|
||||||
*
|
*
|
||||||
* @return HitHighlighter Fluid method chaining.
|
* @return HitHighlighter Fluid method chaining.
|
||||||
*/
|
*/
|
||||||
public function setTag($v)
|
public function setTag($v)
|
||||||
{
|
{
|
||||||
$this->tag = $v;
|
$this->tag = $v;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hit highlights the tweet.
|
* Hit highlights the tweet.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to be hit highlighted.
|
* @param string $tweet The tweet to be hit highlighted.
|
||||||
* @param array $hits An array containing the start and end index pairs
|
* @param array $hits An array containing the start and end index pairs
|
||||||
* for the highlighting.
|
* for the highlighting.
|
||||||
* @param bool $escape Whether to escape the tweet (default: true).
|
* @param bool $escape Whether to escape the tweet (default: true).
|
||||||
* @param bool $full_encode Whether to encode all special characters.
|
* @param bool $full_encode Whether to encode all special characters.
|
||||||
*
|
*
|
||||||
* @return string The hit highlighted tweet.
|
* @return string The hit highlighted tweet.
|
||||||
*/
|
*/
|
||||||
public function highlight($tweet = null, array $hits = null)
|
public function highlight($tweet = null, array $hits = null)
|
||||||
{
|
{
|
||||||
|
@ -119,8 +114,8 @@ class HitHighlighter extends Regex
|
||||||
return $tweet;
|
return $tweet;
|
||||||
}
|
}
|
||||||
$highlightTweet = '';
|
$highlightTweet = '';
|
||||||
$tags = array('<' . $this->tag . '>', '</' . $this->tag . '>');
|
$tags = ['<'.$this->tag.'>', '</'.$this->tag.'>'];
|
||||||
# Check whether we can simply replace or whether we need to chunk...
|
// Check whether we can simply replace or whether we need to chunk...
|
||||||
if (strpos($tweet, '<') === false) {
|
if (strpos($tweet, '<') === false) {
|
||||||
$ti = 0; // tag increment (for added tags)
|
$ti = 0; // tag increment (for added tags)
|
||||||
$highlightTweet = $tweet;
|
$highlightTweet = $tweet;
|
||||||
|
@ -137,12 +132,12 @@ class HitHighlighter extends Regex
|
||||||
$chunk_cursor = 0;
|
$chunk_cursor = 0;
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
$start_in_chunk = false;
|
$start_in_chunk = false;
|
||||||
# Flatten the multidimensional hits array:
|
// Flatten the multidimensional hits array:
|
||||||
$hits_flat = array();
|
$hits_flat = [];
|
||||||
foreach ($hits as $hit) {
|
foreach ($hits as $hit) {
|
||||||
$hits_flat = array_merge($hits_flat, $hit);
|
$hits_flat = array_merge($hits_flat, $hit);
|
||||||
}
|
}
|
||||||
# Loop over the hit indices:
|
// Loop over the hit indices:
|
||||||
for ($index = 0; $index < count($hits_flat); $index++) {
|
for ($index = 0; $index < count($hits_flat); $index++) {
|
||||||
$hit = $hits_flat[$index];
|
$hit = $hits_flat[$index];
|
||||||
$tag = $tags[$index % 2];
|
$tag = $tags[$index % 2];
|
||||||
|
@ -154,7 +149,7 @@ class HitHighlighter extends Regex
|
||||||
$placed = true;
|
$placed = true;
|
||||||
}
|
}
|
||||||
if (isset($chunks[$chunk_index + 1])) {
|
if (isset($chunks[$chunk_index + 1])) {
|
||||||
$highlightTweet .= '<' . $chunks[$chunk_index + 1] . '>';
|
$highlightTweet .= '<'.$chunks[$chunk_index + 1].'>';
|
||||||
}
|
}
|
||||||
$offset += StringUtils::strlen($chunk);
|
$offset += StringUtils::strlen($chunk);
|
||||||
$chunk_cursor = 0;
|
$chunk_cursor = 0;
|
||||||
|
@ -164,12 +159,12 @@ class HitHighlighter extends Regex
|
||||||
}
|
}
|
||||||
if (!$placed && $chunk !== null) {
|
if (!$placed && $chunk !== null) {
|
||||||
$hit_spot = $hit - $offset;
|
$hit_spot = $hit - $offset;
|
||||||
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor, $hit_spot - $chunk_cursor) . $tag;
|
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor, $hit_spot - $chunk_cursor).$tag;
|
||||||
$chunk_cursor = $hit_spot;
|
$chunk_cursor = $hit_spot;
|
||||||
$start_in_chunk = ($index % 2 === 0);
|
$start_in_chunk = ($index % 2 === 0);
|
||||||
$placed = true;
|
$placed = true;
|
||||||
}
|
}
|
||||||
# Ultimate fallback - hits that run off the end get a closing tag:
|
// Ultimate fallback - hits that run off the end get a closing tag:
|
||||||
if (!$placed) {
|
if (!$placed) {
|
||||||
$highlightTweet .= $tag;
|
$highlightTweet .= $tag;
|
||||||
}
|
}
|
||||||
|
@ -179,20 +174,22 @@ class HitHighlighter extends Regex
|
||||||
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
|
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
|
||||||
}
|
}
|
||||||
for ($index = $chunk_index + 1; $index < count($chunks); $index++) {
|
for ($index = $chunk_index + 1; $index < count($chunks); $index++) {
|
||||||
$highlightTweet .= ($index % 2 === 0 ? $chunks[$index] : '<' . $chunks[$index] . '>');
|
$highlightTweet .= ($index % 2 === 0 ? $chunks[$index] : '<'.$chunks[$index].'>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $highlightTweet;
|
return $highlightTweet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hit highlights the tweet.
|
* Hit highlights the tweet.
|
||||||
*
|
*
|
||||||
* @param array $hits An array containing the start and end index pairs
|
* @param array $hits An array containing the start and end index pairs
|
||||||
* for the highlighting.
|
* for the highlighting.
|
||||||
|
*
|
||||||
|
* @return string The hit highlighted tweet.
|
||||||
*
|
*
|
||||||
* @return string The hit highlighted tweet.
|
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function addHitHighlighting(array $hits)
|
public function addHitHighlighting(array $hits)
|
||||||
|
|
|
@ -6,15 +6,12 @@
|
||||||
* @author Takashi Nojima
|
* @author Takashi Nojima
|
||||||
* @copyright Copyright 2014 Mike Cochrane, Nick Pope, Takashi Nojima
|
* @copyright Copyright 2014 Mike Cochrane, Nick Pope, Takashi Nojima
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
use App\Util\Lexer\Autolink;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter LooseAutolink Class
|
* Twitter LooseAutolink Class.
|
||||||
*
|
*
|
||||||
* Parses tweets and generates HTML anchor tags around URLs, usernames,
|
* Parses tweets and generates HTML anchor tags around URLs, usernames,
|
||||||
* username/list pairs and hashtags.
|
* username/list pairs and hashtags.
|
||||||
|
@ -28,18 +25,19 @@ use App\Util\Lexer\Autolink;
|
||||||
* @author Takashi Nojima
|
* @author Takashi Nojima
|
||||||
* @copyright Copyright 2014 Mike Cochrane, Nick Pope, Takashi Nojima
|
* @copyright Copyright 2014 Mike Cochrane, Nick Pope, Takashi Nojima
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
*
|
||||||
* @since 1.8.0
|
* @since 1.8.0
|
||||||
* @deprecated since version 1.9.0
|
* @deprecated since version 1.9.0
|
||||||
*/
|
*/
|
||||||
class LooseAutolink extends Autolink
|
class LooseAutolink extends Autolink
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auto-link hashtags, URLs, usernames and lists.
|
* Auto-link hashtags, URLs, usernames and lists.
|
||||||
*
|
*
|
||||||
* @param string The tweet to be converted
|
* @param string The tweet to be converted
|
||||||
|
*
|
||||||
* @return string that auto-link HTML added
|
* @return string that auto-link HTML added
|
||||||
|
*
|
||||||
* @deprecated since version 1.9.0
|
* @deprecated since version 1.9.0
|
||||||
*/
|
*/
|
||||||
public function autoLink($tweet = null)
|
public function autoLink($tweet = null)
|
||||||
|
@ -47,6 +45,7 @@ class LooseAutolink extends Autolink
|
||||||
if (!is_null($tweet)) {
|
if (!is_null($tweet)) {
|
||||||
$this->tweet = $tweet;
|
$this->tweet = $tweet;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->addLinks();
|
return $this->addLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +61,7 @@ class LooseAutolink extends Autolink
|
||||||
if (!is_null($tweet)) {
|
if (!is_null($tweet)) {
|
||||||
$this->tweet = $tweet;
|
$this->tweet = $tweet;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->addLinksToUsernamesAndLists();
|
return $this->addLinksToUsernamesAndLists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ class LooseAutolink extends Autolink
|
||||||
if (!is_null($tweet)) {
|
if (!is_null($tweet)) {
|
||||||
$this->tweet = $tweet;
|
$this->tweet = $tweet;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->addLinksToHashtags();
|
return $this->addLinksToHashtags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +92,7 @@ class LooseAutolink extends Autolink
|
||||||
if (!is_null($tweet)) {
|
if (!is_null($tweet)) {
|
||||||
$this->tweet = $tweet;
|
$this->tweet = $tweet;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->addLinksToURLs();
|
return $this->addLinksToURLs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,13 +107,15 @@ class LooseAutolink extends Autolink
|
||||||
if (!is_null($tweet)) {
|
if (!is_null($tweet)) {
|
||||||
$this->tweet = $tweet;
|
$this->tweet = $tweet;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->addLinksToCashtags();
|
return $this->addLinksToCashtags();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds links to all elements in the tweet.
|
* Adds links to all elements in the tweet.
|
||||||
*
|
*
|
||||||
* @return string The modified tweet.
|
* @return string The modified tweet.
|
||||||
|
*
|
||||||
* @deprecated since version 1.9.0
|
* @deprecated since version 1.9.0
|
||||||
*/
|
*/
|
||||||
public function addLinks()
|
public function addLinks()
|
||||||
|
@ -123,19 +127,20 @@ class LooseAutolink extends Autolink
|
||||||
$this->tweet = $this->addLinksToUsernamesAndLists();
|
$this->tweet = $this->addLinksToUsernamesAndLists();
|
||||||
$modified = $this->tweet;
|
$modified = $this->tweet;
|
||||||
$this->tweet = $original;
|
$this->tweet = $original;
|
||||||
|
|
||||||
return $modified;
|
return $modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds links to hashtag elements in the tweet.
|
* Adds links to hashtag elements in the tweet.
|
||||||
*
|
*
|
||||||
* @return string The modified tweet.
|
* @return string The modified tweet.
|
||||||
*/
|
*/
|
||||||
public function addLinksToHashtags()
|
public function addLinksToHashtags()
|
||||||
{
|
{
|
||||||
return preg_replace_callback(
|
return preg_replace_callback(
|
||||||
self::$patterns['valid_hashtag'],
|
self::$patterns['valid_hashtag'],
|
||||||
array($this, '_addLinksToHashtags'),
|
[$this, '_addLinksToHashtags'],
|
||||||
$this->tweet
|
$this->tweet
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -143,13 +148,13 @@ class LooseAutolink extends Autolink
|
||||||
/**
|
/**
|
||||||
* Adds links to cashtag elements in the tweet.
|
* Adds links to cashtag elements in the tweet.
|
||||||
*
|
*
|
||||||
* @return string The modified tweet.
|
* @return string The modified tweet.
|
||||||
*/
|
*/
|
||||||
public function addLinksToCashtags()
|
public function addLinksToCashtags()
|
||||||
{
|
{
|
||||||
return preg_replace_callback(
|
return preg_replace_callback(
|
||||||
self::$patterns['valid_cashtag'],
|
self::$patterns['valid_cashtag'],
|
||||||
array($this, '_addLinksToCashtags'),
|
[$this, '_addLinksToCashtags'],
|
||||||
$this->tweet
|
$this->tweet
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -157,23 +162,23 @@ class LooseAutolink extends Autolink
|
||||||
/**
|
/**
|
||||||
* Adds links to URL elements in the tweet.
|
* Adds links to URL elements in the tweet.
|
||||||
*
|
*
|
||||||
* @return string The modified tweet
|
* @return string The modified tweet
|
||||||
*/
|
*/
|
||||||
public function addLinksToURLs()
|
public function addLinksToURLs()
|
||||||
{
|
{
|
||||||
return preg_replace_callback(self::$patterns['valid_url'], array($this, '_addLinksToURLs'), $this->tweet);
|
return preg_replace_callback(self::$patterns['valid_url'], [$this, '_addLinksToURLs'], $this->tweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds links to username/list elements in the tweet.
|
* Adds links to username/list elements in the tweet.
|
||||||
*
|
*
|
||||||
* @return string The modified tweet.
|
* @return string The modified tweet.
|
||||||
*/
|
*/
|
||||||
public function addLinksToUsernamesAndLists()
|
public function addLinksToUsernamesAndLists()
|
||||||
{
|
{
|
||||||
return preg_replace_callback(
|
return preg_replace_callback(
|
||||||
self::$patterns['valid_mentions_or_lists'],
|
self::$patterns['valid_mentions_or_lists'],
|
||||||
array($this, '_addLinksToUsernamesAndLists'),
|
[$this, '_addLinksToUsernamesAndLists'],
|
||||||
$this->tweet
|
$this->tweet
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -183,21 +188,22 @@ class LooseAutolink extends Autolink
|
||||||
*
|
*
|
||||||
* This is a helper function to perform the generation of the link.
|
* This is a helper function to perform the generation of the link.
|
||||||
*
|
*
|
||||||
* @param string $url The URL to use as the href.
|
* @param string $url The URL to use as the href.
|
||||||
* @param string $class The CSS class(es) to apply (space separated).
|
* @param string $class The CSS class(es) to apply (space separated).
|
||||||
* @param string $element The tweet element to wrap.
|
* @param string $element The tweet element to wrap.
|
||||||
|
*
|
||||||
|
* @return string The tweet element with a link applied.
|
||||||
*
|
*
|
||||||
* @return string The tweet element with a link applied.
|
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
protected function wrap($url, $class, $element)
|
protected function wrap($url, $class, $element)
|
||||||
{
|
{
|
||||||
$link = '<a';
|
$link = '<a';
|
||||||
if ($class) {
|
if ($class) {
|
||||||
$link .= ' class="' . $class . '"';
|
$link .= ' class="'.$class.'"';
|
||||||
}
|
}
|
||||||
$link .= ' href="' . $url . '"';
|
$link .= ' href="'.$url.'"';
|
||||||
$rel = array();
|
$rel = [];
|
||||||
if ($this->external) {
|
if ($this->external) {
|
||||||
$rel[] = 'external';
|
$rel[] = 'external';
|
||||||
}
|
}
|
||||||
|
@ -205,12 +211,13 @@ class LooseAutolink extends Autolink
|
||||||
$rel[] = 'nofollow';
|
$rel[] = 'nofollow';
|
||||||
}
|
}
|
||||||
if (!empty($rel)) {
|
if (!empty($rel)) {
|
||||||
$link .= ' rel="' . implode(' ', $rel) . '"';
|
$link .= ' rel="'.implode(' ', $rel).'"';
|
||||||
}
|
}
|
||||||
if ($this->target) {
|
if ($this->target) {
|
||||||
$link .= ' target="' . $this->target . '"';
|
$link .= ' target="'.$this->target.'"';
|
||||||
}
|
}
|
||||||
$link .= '>' . $element . '</a>';
|
$link .= '>'.$element.'</a>';
|
||||||
|
|
||||||
return $link;
|
return $link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,22 +226,22 @@ class LooseAutolink extends Autolink
|
||||||
*
|
*
|
||||||
* This is a helper function to perform the generation of the hashtag link.
|
* This is a helper function to perform the generation of the hashtag link.
|
||||||
*
|
*
|
||||||
* @param string $url The URL to use as the href.
|
* @param string $url The URL to use as the href.
|
||||||
* @param string $class The CSS class(es) to apply (space separated).
|
* @param string $class The CSS class(es) to apply (space separated).
|
||||||
* @param string $element The tweet element to wrap.
|
* @param string $element The tweet element to wrap.
|
||||||
*
|
*
|
||||||
* @return string The tweet element with a link applied.
|
* @return string The tweet element with a link applied.
|
||||||
*/
|
*/
|
||||||
protected function wrapHash($url, $class, $element)
|
protected function wrapHash($url, $class, $element)
|
||||||
{
|
{
|
||||||
$title = preg_replace('/#/u', '#', $element);
|
$title = preg_replace('/#/u', '#', $element);
|
||||||
$link = '<a';
|
$link = '<a';
|
||||||
$link .= ' href="' . $url . '"';
|
$link .= ' href="'.$url.'"';
|
||||||
$link .= ' title="' . $title . '"';
|
$link .= ' title="'.$title.'"';
|
||||||
if ($class) {
|
if ($class) {
|
||||||
$link .= ' class="' . $class . '"';
|
$link .= ' class="'.$class.'"';
|
||||||
}
|
}
|
||||||
$rel = array();
|
$rel = [];
|
||||||
if ($this->external) {
|
if ($this->external) {
|
||||||
$rel[] = 'external';
|
$rel[] = 'external';
|
||||||
}
|
}
|
||||||
|
@ -242,12 +249,13 @@ class LooseAutolink extends Autolink
|
||||||
$rel[] = 'nofollow';
|
$rel[] = 'nofollow';
|
||||||
}
|
}
|
||||||
if (!empty($rel)) {
|
if (!empty($rel)) {
|
||||||
$link .= ' rel="' . implode(' ', $rel) . '"';
|
$link .= ' rel="'.implode(' ', $rel).'"';
|
||||||
}
|
}
|
||||||
if ($this->target) {
|
if ($this->target) {
|
||||||
$link .= ' target="' . $this->target . '"';
|
$link .= ' target="'.$this->target.'"';
|
||||||
}
|
}
|
||||||
$link .= '>' . $element . '</a>';
|
$link .= '>'.$element.'</a>';
|
||||||
|
|
||||||
return $link;
|
return $link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,8 +263,10 @@ class LooseAutolink extends Autolink
|
||||||
* Callback used by the method that adds links to hashtags.
|
* Callback used by the method that adds links to hashtags.
|
||||||
*
|
*
|
||||||
* @see addLinksToHashtags()
|
* @see addLinksToHashtags()
|
||||||
* @param array $matches The regular expression matches.
|
*
|
||||||
* @return string The link-wrapped hashtag.
|
* @param array $matches The regular expression matches.
|
||||||
|
*
|
||||||
|
* @return string The link-wrapped hashtag.
|
||||||
*/
|
*/
|
||||||
protected function _addLinksToHashtags($matches)
|
protected function _addLinksToHashtags($matches)
|
||||||
{
|
{
|
||||||
|
@ -266,13 +276,14 @@ class LooseAutolink extends Autolink
|
||||||
return $all;
|
return $all;
|
||||||
}
|
}
|
||||||
$replacement = $before;
|
$replacement = $before;
|
||||||
$element = $hash . $tag;
|
$element = $hash.$tag;
|
||||||
$url = $this->url_base_hash . $tag;
|
$url = $this->url_base_hash.$tag;
|
||||||
$class_hash = $this->class_hash;
|
$class_hash = $this->class_hash;
|
||||||
if (preg_match(self::$patterns['rtl_chars'], $element)) {
|
if (preg_match(self::$patterns['rtl_chars'], $element)) {
|
||||||
$class_hash .= ' rtl';
|
$class_hash .= ' rtl';
|
||||||
}
|
}
|
||||||
$replacement .= $this->wrapHash($url, $class_hash, $element);
|
$replacement .= $this->wrapHash($url, $class_hash, $element);
|
||||||
|
|
||||||
return $replacement;
|
return $replacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,8 +291,10 @@ class LooseAutolink extends Autolink
|
||||||
* Callback used by the method that adds links to cashtags.
|
* Callback used by the method that adds links to cashtags.
|
||||||
*
|
*
|
||||||
* @see addLinksToCashtags()
|
* @see addLinksToCashtags()
|
||||||
* @param array $matches The regular expression matches.
|
*
|
||||||
* @return string The link-wrapped cashtag.
|
* @param array $matches The regular expression matches.
|
||||||
|
*
|
||||||
|
* @return string The link-wrapped cashtag.
|
||||||
*/
|
*/
|
||||||
protected function _addLinksToCashtags($matches)
|
protected function _addLinksToCashtags($matches)
|
||||||
{
|
{
|
||||||
|
@ -291,9 +304,10 @@ class LooseAutolink extends Autolink
|
||||||
return $all;
|
return $all;
|
||||||
}
|
}
|
||||||
$replacement = $before;
|
$replacement = $before;
|
||||||
$element = $cash . $tag;
|
$element = $cash.$tag;
|
||||||
$url = $this->url_base_cash . $tag;
|
$url = $this->url_base_cash.$tag;
|
||||||
$replacement .= $this->wrapHash($url, $this->class_cash, $element);
|
$replacement .= $this->wrapHash($url, $this->class_cash, $element);
|
||||||
|
|
||||||
return $replacement;
|
return $replacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,8 +315,10 @@ class LooseAutolink extends Autolink
|
||||||
* Callback used by the method that adds links to URLs.
|
* Callback used by the method that adds links to URLs.
|
||||||
*
|
*
|
||||||
* @see addLinksToURLs()
|
* @see addLinksToURLs()
|
||||||
* @param array $matches The regular expression matches.
|
*
|
||||||
* @return string The link-wrapped URL.
|
* @param array $matches The regular expression matches.
|
||||||
|
*
|
||||||
|
* @return string The link-wrapped URL.
|
||||||
*/
|
*/
|
||||||
protected function _addLinksToURLs($matches)
|
protected function _addLinksToURLs($matches)
|
||||||
{
|
{
|
||||||
|
@ -311,38 +327,41 @@ class LooseAutolink extends Autolink
|
||||||
if (!$protocol) {
|
if (!$protocol) {
|
||||||
return $all;
|
return $all;
|
||||||
}
|
}
|
||||||
return $before . $this->wrap($url, $this->class_url, $url);
|
|
||||||
|
return $before.$this->wrap($url, $this->class_url, $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback used by the method that adds links to username/list pairs.
|
* Callback used by the method that adds links to username/list pairs.
|
||||||
*
|
*
|
||||||
* @see addLinksToUsernamesAndLists()
|
* @see addLinksToUsernamesAndLists()
|
||||||
* @param array $matches The regular expression matches.
|
*
|
||||||
* @return string The link-wrapped username/list pair.
|
* @param array $matches The regular expression matches.
|
||||||
|
*
|
||||||
|
* @return string The link-wrapped username/list pair.
|
||||||
*/
|
*/
|
||||||
protected function _addLinksToUsernamesAndLists($matches)
|
protected function _addLinksToUsernamesAndLists($matches)
|
||||||
{
|
{
|
||||||
list($all, $before, $at, $username, $slash_listname, $after) = array_pad($matches, 6, '');
|
list($all, $before, $at, $username, $slash_listname, $after) = array_pad($matches, 6, '');
|
||||||
# If $after is not empty, there is an invalid character.
|
// If $after is not empty, there is an invalid character.
|
||||||
if (!empty($slash_listname)) {
|
if (!empty($slash_listname)) {
|
||||||
# Replace the list and username
|
// Replace the list and username
|
||||||
$element = $username . $slash_listname;
|
$element = $username.$slash_listname;
|
||||||
$class = $this->class_list;
|
$class = $this->class_list;
|
||||||
$url = $this->url_base_list . $element;
|
$url = $this->url_base_list.$element;
|
||||||
} else {
|
} else {
|
||||||
if (preg_match(self::$patterns['end_mention_match'], $after)) {
|
if (preg_match(self::$patterns['end_mention_match'], $after)) {
|
||||||
return $all;
|
return $all;
|
||||||
}
|
}
|
||||||
# Replace the username
|
// Replace the username
|
||||||
$element = $username;
|
$element = $username;
|
||||||
$class = $this->class_user;
|
$class = $this->class_user;
|
||||||
$url = $this->url_base_user . $element;
|
$url = $this->url_base_user.$element;
|
||||||
}
|
}
|
||||||
# XXX: Due to use of preg_replace_callback() for multiple replacements in a
|
// XXX: Due to use of preg_replace_callback() for multiple replacements in a
|
||||||
# single tweet and also as only the match is replaced and we have to
|
// single tweet and also as only the match is replaced and we have to
|
||||||
# use a look-ahead for $after because there is no equivalent for the
|
// use a look-ahead for $after because there is no equivalent for the
|
||||||
# $' (dollar apostrophe) global from Ruby, we MUST NOT append $after.
|
// $' (dollar apostrophe) global from Ruby, we MUST NOT append $after.
|
||||||
return $before . $at . $this->wrap($url, $class, $element);
|
return $before.$at.$this->wrap($url, $class, $element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,41 +2,41 @@
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
class Nickname {
|
class Nickname
|
||||||
|
{
|
||||||
public static function normalizeProfileUrl($url)
|
public static function normalizeProfileUrl($url)
|
||||||
{
|
{
|
||||||
if(starts_with($url, 'acct:')) {
|
if (starts_with($url, 'acct:')) {
|
||||||
$url = str_replace('acct:', '', $url);
|
$url = str_replace('acct:', '', $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!str_contains($url, '@') && filter_var($url, FILTER_VALIDATE_URL)) {
|
if (!str_contains($url, '@') && filter_var($url, FILTER_VALIDATE_URL)) {
|
||||||
$parsed = parse_url($url);
|
$parsed = parse_url($url);
|
||||||
$username = str_replace(['/','\\','@'], '', $parsed['path']);
|
$username = str_replace(['/', '\\', '@'], '', $parsed['path']);
|
||||||
return ['domain' => $parsed['host'], 'username' => $username];
|
|
||||||
}
|
|
||||||
$parts = explode('@', $url);
|
|
||||||
$username = null;
|
|
||||||
$domain = null;
|
|
||||||
|
|
||||||
foreach ($parts as $part) {
|
return ['domain' => $parsed['host'], 'username' => $username];
|
||||||
|
}
|
||||||
|
$parts = explode('@', $url);
|
||||||
|
$username = null;
|
||||||
|
$domain = null;
|
||||||
|
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
|
||||||
// skip empty array slices
|
// skip empty array slices
|
||||||
if(empty($part)) {
|
if (empty($part)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if slice contains . assume its a domain
|
// if slice contains . assume its a domain
|
||||||
if(str_contains($part, '.')) {
|
if (str_contains($part, '.')) {
|
||||||
$domain = filter_var($part, FILTER_VALIDATE_URL) ?
|
$domain = filter_var($part, FILTER_VALIDATE_URL) ?
|
||||||
parse_url($part, PHP_URL_HOST) :
|
parse_url($part, PHP_URL_HOST) :
|
||||||
$part;
|
$part;
|
||||||
} else {
|
} else {
|
||||||
$username = $part;
|
$username = $part;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return ['domain' => $domain, 'username' => $username];
|
||||||
return ['domain' => $domain, 'username' => $username];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,35 +2,38 @@
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
class PrettyNumber {
|
class PrettyNumber
|
||||||
|
{
|
||||||
|
public static function convert($expression)
|
||||||
|
{
|
||||||
|
$abbrevs = [12 => 'T', 9 => 'B', 6 => 'M', 3 => 'K', 0 => ''];
|
||||||
|
foreach ($abbrevs as $exponent => $abbrev) {
|
||||||
|
if ($expression >= pow(10, $exponent)) {
|
||||||
|
$display_num = $expression / pow(10, $exponent);
|
||||||
|
$num = number_format($display_num, 0).$abbrev;
|
||||||
|
|
||||||
public static function convert($expression)
|
return $num;
|
||||||
{
|
}
|
||||||
$abbrevs = array(12 => "T", 9 => "B", 6 => "M", 3 => "K", 0 => "");
|
}
|
||||||
foreach($abbrevs as $exponent => $abbrev) {
|
|
||||||
if($expression >= pow(10, $exponent)) {
|
|
||||||
$display_num = $expression / pow(10, $exponent);
|
|
||||||
$num = number_format($display_num,0) . $abbrev;
|
|
||||||
return $num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function size($expression, $kb = false)
|
return $expression;
|
||||||
{
|
}
|
||||||
if($kb) {
|
|
||||||
$expression = $expression * 1024;
|
|
||||||
}
|
|
||||||
$size = intval($expression);
|
|
||||||
$precision = 0;
|
|
||||||
$short = true;
|
|
||||||
$units = $short ?
|
|
||||||
['B','k','M','G','T','P','E','Z','Y'] :
|
|
||||||
['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
|
||||||
for($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {}
|
|
||||||
$res = round($size, $precision).$units[$i];
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public static function size($expression, $kb = false)
|
||||||
|
{
|
||||||
|
if ($kb) {
|
||||||
|
$expression = $expression * 1024;
|
||||||
|
}
|
||||||
|
$size = intval($expression);
|
||||||
|
$precision = 0;
|
||||||
|
$short = true;
|
||||||
|
$units = $short ?
|
||||||
|
['B', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] :
|
||||||
|
['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {
|
||||||
|
}
|
||||||
|
$res = round($size, $precision).$units[$i];
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
|
@ -2,180 +2,179 @@
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
class RestrictedNames {
|
class RestrictedNames
|
||||||
|
{
|
||||||
static $blacklist = [
|
public static $blacklist = [
|
||||||
"about",
|
'about',
|
||||||
"abuse",
|
'abuse',
|
||||||
"administrator",
|
'administrator',
|
||||||
"app",
|
'app',
|
||||||
"autoconfig",
|
'autoconfig',
|
||||||
"blog",
|
'blog',
|
||||||
"broadcasthost",
|
'broadcasthost',
|
||||||
"community",
|
'community',
|
||||||
"contact",
|
'contact',
|
||||||
"contact-us",
|
'contact-us',
|
||||||
"contact_us",
|
'contact_us',
|
||||||
"copyright",
|
'copyright',
|
||||||
"d",
|
'd',
|
||||||
"dashboard",
|
'dashboard',
|
||||||
"dev",
|
'dev',
|
||||||
"developer",
|
'developer',
|
||||||
"developers",
|
'developers',
|
||||||
"discover",
|
'discover',
|
||||||
"discovers",
|
'discovers',
|
||||||
"doc",
|
'doc',
|
||||||
"docs",
|
'docs',
|
||||||
"download",
|
'download',
|
||||||
"domainadmin",
|
'domainadmin',
|
||||||
"domainadministrator",
|
'domainadministrator',
|
||||||
"email",
|
'email',
|
||||||
"errors",
|
'errors',
|
||||||
"events",
|
'events',
|
||||||
"example",
|
'example',
|
||||||
"faq",
|
'faq',
|
||||||
"faqs",
|
'faqs',
|
||||||
"features",
|
'features',
|
||||||
"ftp",
|
'ftp',
|
||||||
"guest",
|
'guest',
|
||||||
"guests",
|
'guests',
|
||||||
"help",
|
'help',
|
||||||
"hostmaster",
|
'hostmaster',
|
||||||
"hostmaster",
|
'hostmaster',
|
||||||
"image",
|
'image',
|
||||||
"images",
|
'images',
|
||||||
"imap",
|
'imap',
|
||||||
"img",
|
'img',
|
||||||
"info",
|
'info',
|
||||||
"info",
|
'info',
|
||||||
"is",
|
'is',
|
||||||
"isatap",
|
'isatap',
|
||||||
"it",
|
'it',
|
||||||
"localdomain",
|
'localdomain',
|
||||||
"localhost",
|
'localhost',
|
||||||
"mail",
|
'mail',
|
||||||
"mailer-daemon",
|
'mailer-daemon',
|
||||||
"mailerdaemon",
|
'mailerdaemon',
|
||||||
"marketing",
|
'marketing',
|
||||||
"me",
|
'me',
|
||||||
"media",
|
'media',
|
||||||
"mis",
|
'mis',
|
||||||
"mx",
|
'mx',
|
||||||
"new",
|
'new',
|
||||||
"news",
|
'news',
|
||||||
"news",
|
'news',
|
||||||
"no-reply",
|
'no-reply',
|
||||||
"nobody",
|
'nobody',
|
||||||
"noc",
|
'noc',
|
||||||
"noreply",
|
'noreply',
|
||||||
"ns0",
|
'ns0',
|
||||||
"ns1",
|
'ns1',
|
||||||
"ns2",
|
'ns2',
|
||||||
"ns3",
|
'ns3',
|
||||||
"ns4",
|
'ns4',
|
||||||
"ns5",
|
'ns5',
|
||||||
"ns6",
|
'ns6',
|
||||||
"ns7",
|
'ns7',
|
||||||
"ns8",
|
'ns8',
|
||||||
"ns9",
|
'ns9',
|
||||||
"owner",
|
'owner',
|
||||||
"pop",
|
'pop',
|
||||||
"pop3",
|
'pop3',
|
||||||
"postmaster",
|
'postmaster',
|
||||||
"pricing",
|
'pricing',
|
||||||
"privacy",
|
'privacy',
|
||||||
"root",
|
'root',
|
||||||
"sales",
|
'sales',
|
||||||
"security",
|
'security',
|
||||||
"signin",
|
'signin',
|
||||||
"signout",
|
'signout',
|
||||||
"smtp",
|
'smtp',
|
||||||
"src",
|
'src',
|
||||||
"ssladmin",
|
'ssladmin',
|
||||||
"ssladministrator",
|
'ssladministrator',
|
||||||
"sslwebmaster",
|
'sslwebmaster',
|
||||||
"status",
|
'status',
|
||||||
"support",
|
'support',
|
||||||
"support",
|
'support',
|
||||||
"sys",
|
'sys',
|
||||||
"sysadmin",
|
'sysadmin',
|
||||||
"system",
|
'system',
|
||||||
"terms",
|
'terms',
|
||||||
"tutorial",
|
'tutorial',
|
||||||
"tutorials",
|
'tutorials',
|
||||||
"usenet",
|
'usenet',
|
||||||
"uucp",
|
'uucp',
|
||||||
"webmaster",
|
'webmaster',
|
||||||
"wpad",
|
'wpad',
|
||||||
"www"
|
'www',
|
||||||
];
|
];
|
||||||
|
|
||||||
static $reserved = [
|
public static $reserved = [
|
||||||
// Reserved for instance admin
|
// Reserved for instance admin
|
||||||
"admin",
|
'admin',
|
||||||
|
|
||||||
// Static Assets
|
// Static Assets
|
||||||
"assets",
|
'assets',
|
||||||
"storage",
|
'storage',
|
||||||
|
|
||||||
// Laravel Horizon
|
// Laravel Horizon
|
||||||
"horizon",
|
'horizon',
|
||||||
|
|
||||||
// Reserved routes
|
// Reserved routes
|
||||||
"account",
|
'account',
|
||||||
"api",
|
'api',
|
||||||
"auth",
|
'auth',
|
||||||
"css",
|
'css',
|
||||||
"c",
|
'c',
|
||||||
"i",
|
'i',
|
||||||
"dashboard",
|
'dashboard',
|
||||||
"deck",
|
'deck',
|
||||||
"discover",
|
'discover',
|
||||||
"docs",
|
'docs',
|
||||||
"fonts",
|
'fonts',
|
||||||
"home",
|
'home',
|
||||||
"img",
|
'img',
|
||||||
"js",
|
'js',
|
||||||
"login",
|
'login',
|
||||||
"logout",
|
'logout',
|
||||||
"media",
|
'media',
|
||||||
"p",
|
'p',
|
||||||
"password",
|
'password',
|
||||||
"report",
|
'report',
|
||||||
"reports",
|
'reports',
|
||||||
"search",
|
'search',
|
||||||
"settings",
|
'settings',
|
||||||
"statuses",
|
'statuses',
|
||||||
"site",
|
'site',
|
||||||
"sites",
|
'sites',
|
||||||
"timeline",
|
'timeline',
|
||||||
"timelines",
|
'timelines',
|
||||||
"tour",
|
'tour',
|
||||||
"user",
|
'user',
|
||||||
"users",
|
'users',
|
||||||
"vendor",
|
'vendor',
|
||||||
"400",
|
'400',
|
||||||
"401",
|
'401',
|
||||||
"403",
|
'403',
|
||||||
"404",
|
'404',
|
||||||
"500",
|
'500',
|
||||||
"503",
|
'503',
|
||||||
"504",
|
'504',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function get()
|
public static function get()
|
||||||
{
|
{
|
||||||
|
$reserved = $blacklist = [];
|
||||||
|
|
||||||
$reserved = $blacklist = [];
|
if (true == config('pixelfed.restricted_names.use_blacklist')) {
|
||||||
|
$blacklist = self::$blacklist;
|
||||||
|
}
|
||||||
|
|
||||||
if(true == config('pixelfed.restricted_names.use_blacklist')) {
|
if (true == config('pixelfed.restricted_names.reserved_routes')) {
|
||||||
$blacklist = self::$blacklist;
|
$reserved = self::$reserved;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(true == config('pixelfed.restricted_names.reserved_routes')) {
|
|
||||||
$reserved = self::$reserved;
|
|
||||||
}
|
|
||||||
return array_merge($blacklist, $reserved);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return array_merge($blacklist, $reserved);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,29 +4,27 @@
|
||||||
* @author Takashi Nojima
|
* @author Takashi Nojima
|
||||||
* @copyright Copyright 2014, Takashi Nojima
|
* @copyright Copyright 2014, Takashi Nojima
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String utility
|
* String utility.
|
||||||
*
|
*
|
||||||
* @author Takashi Nojima
|
* @author Takashi Nojima
|
||||||
* @copyright Copyright 2014, Takashi Nojima
|
* @copyright Copyright 2014, Takashi Nojima
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter
|
|
||||||
*/
|
*/
|
||||||
class StringUtils
|
class StringUtils
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* alias of mb_substr
|
* alias of mb_substr.
|
||||||
*
|
*
|
||||||
* @param string $str
|
* @param string $str
|
||||||
* @param integer $start
|
* @param int $start
|
||||||
* @param integer $length
|
* @param int $length
|
||||||
* @param string $encoding
|
* @param string $encoding
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function substr($str, $start, $length = null, $encoding = 'UTF-8')
|
public static function substr($str, $start, $length = null, $encoding = 'UTF-8')
|
||||||
|
@ -35,15 +33,17 @@ class StringUtils
|
||||||
// for PHP <= 5.4.7
|
// for PHP <= 5.4.7
|
||||||
$length = mb_strlen($str, $encoding);
|
$length = mb_strlen($str, $encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mb_substr($str, $start, $length, $encoding);
|
return mb_substr($str, $start, $length, $encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* alias of mb_strlen
|
* alias of mb_strlen.
|
||||||
*
|
*
|
||||||
* @param string $str
|
* @param string $str
|
||||||
* @param string $encoding
|
* @param string $encoding
|
||||||
* @return integer
|
*
|
||||||
|
* @return int
|
||||||
*/
|
*/
|
||||||
public static function strlen($str, $encoding = 'UTF-8')
|
public static function strlen($str, $encoding = 'UTF-8')
|
||||||
{
|
{
|
||||||
|
@ -51,13 +51,14 @@ class StringUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* alias of mb_strpos
|
* alias of mb_strpos.
|
||||||
*
|
*
|
||||||
* @param string $haystack
|
* @param string $haystack
|
||||||
* @param string $needle
|
* @param string $needle
|
||||||
* @param integer $offset
|
* @param int $offset
|
||||||
* @param string $encoding
|
* @param string $encoding
|
||||||
* @return integer
|
*
|
||||||
|
* @return int
|
||||||
*/
|
*/
|
||||||
public static function strpos($haystack, $needle, $offset = 0, $encoding = 'UTF-8')
|
public static function strpos($haystack, $needle, $offset = 0, $encoding = 'UTF-8')
|
||||||
{
|
{
|
||||||
|
@ -67,13 +68,13 @@ class StringUtils
|
||||||
/**
|
/**
|
||||||
* A multibyte-aware substring replacement function.
|
* A multibyte-aware substring replacement function.
|
||||||
*
|
*
|
||||||
* @param string $string The string to modify.
|
* @param string $string The string to modify.
|
||||||
* @param string $replacement The replacement string.
|
* @param string $replacement The replacement string.
|
||||||
* @param int $start The start of the replacement.
|
* @param int $start The start of the replacement.
|
||||||
* @param int $length The number of characters to replace.
|
* @param int $length The number of characters to replace.
|
||||||
* @param string $encoding The encoding of the string.
|
* @param string $encoding The encoding of the string.
|
||||||
*
|
*
|
||||||
* @return string The modified string.
|
* @return string The modified string.
|
||||||
*
|
*
|
||||||
* @see http://www.php.net/manual/en/function.substr-replace.php#90146
|
* @see http://www.php.net/manual/en/function.substr-replace.php#90146
|
||||||
*/
|
*/
|
||||||
|
@ -97,8 +98,10 @@ class StringUtils
|
||||||
|
|
||||||
$suffixOffset = $start + $length;
|
$suffixOffset = $start + $length;
|
||||||
$suffixLength = $string_length - $start - $length;
|
$suffixLength = $string_length - $start - $length;
|
||||||
return static::substr($string, 0, $start, $encoding) . $replacement . static::substr($string, $suffixOffset, $suffixLength, $encoding);
|
|
||||||
|
return static::substr($string, 0, $start, $encoding).$replacement.static::substr($string, $suffixOffset, $suffixLength, $encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (is_null($length) === true) ? substr_replace($string, $replacement, $start) : substr_replace($string, $replacement, $start, $length);
|
return (is_null($length) === true) ? substr_replace($string, $replacement, $start) : substr_replace($string, $replacement, $start, $length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,12 @@
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Nick Pope
|
* @copyright Copyright © 2010, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Util\Lexer;
|
namespace App\Util\Lexer;
|
||||||
|
|
||||||
use App\Util\Lexer\Regex;
|
|
||||||
use App\Util\Lexer\Extractor;
|
|
||||||
use App\Util\Lexer\StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter Validator Class
|
* Twitter Validator Class.
|
||||||
*
|
*
|
||||||
* Performs "validation" on tweets.
|
* Performs "validation" on tweets.
|
||||||
*
|
*
|
||||||
|
@ -25,34 +20,31 @@ use App\Util\Lexer\StringUtils;
|
||||||
* @author Nick Pope <nick@nickpope.me.uk>
|
* @author Nick Pope <nick@nickpope.me.uk>
|
||||||
* @copyright Copyright © 2010, Nick Pope
|
* @copyright Copyright © 2010, Nick Pope
|
||||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||||
* @package Twitter.Text
|
|
||||||
*/
|
*/
|
||||||
class Validator extends Regex
|
class Validator extends Regex
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum length of a tweet.
|
* The maximum length of a tweet.
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
const MAX_LENGTH = 140;
|
const MAX_LENGTH = 140;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of a short URL beginning with http:
|
* The length of a short URL beginning with http:.
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected $short_url_length = 23;
|
protected $short_url_length = 23;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of a short URL beginning with http:
|
* The length of a short URL beginning with http:.
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected $short_url_length_https = 23;
|
protected $short_url_length_https = 23;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @var Extractor
|
* @var Extractor
|
||||||
*/
|
*/
|
||||||
protected $extractor = null;
|
protected $extractor = null;
|
||||||
|
@ -60,12 +52,12 @@ class Validator extends Regex
|
||||||
/**
|
/**
|
||||||
* Provides fluent method chaining.
|
* Provides fluent method chaining.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to be validated.
|
* @param string $tweet The tweet to be validated.
|
||||||
* @param mixed $config Setup short URL length from Twitter API /help/configuration response.
|
* @param mixed $config Setup short URL length from Twitter API /help/configuration response.
|
||||||
*
|
*
|
||||||
* @see __construct()
|
* @see __construct()
|
||||||
*
|
*
|
||||||
* @return Validator
|
* @return Validator
|
||||||
*/
|
*/
|
||||||
public static function create($tweet = null, $config = null)
|
public static function create($tweet = null, $config = null)
|
||||||
{
|
{
|
||||||
|
@ -75,7 +67,7 @@ class Validator extends Regex
|
||||||
/**
|
/**
|
||||||
* Reads in a tweet to be parsed and validates it.
|
* Reads in a tweet to be parsed and validates it.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to validate.
|
* @param string $tweet The tweet to validate.
|
||||||
*/
|
*/
|
||||||
public function __construct($tweet = null, $config = null)
|
public function __construct($tweet = null, $config = null)
|
||||||
{
|
{
|
||||||
|
@ -87,10 +79,12 @@ class Validator extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup short URL length from Twitter API /help/configuration response
|
* Setup short URL length from Twitter API /help/configuration response.
|
||||||
*
|
*
|
||||||
* @param mixed $config
|
* @param mixed $config
|
||||||
|
*
|
||||||
* @return Validator
|
* @return Validator
|
||||||
|
*
|
||||||
* @link https://dev.twitter.com/docs/api/1/get/help/configuration
|
* @link https://dev.twitter.com/docs/api/1/get/help/configuration
|
||||||
*/
|
*/
|
||||||
public function setConfiguration($config)
|
public function setConfiguration($config)
|
||||||
|
@ -117,19 +111,21 @@ class Validator extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the length of a short URL beginning with http:
|
* Set the length of a short URL beginning with http:.
|
||||||
*
|
*
|
||||||
* @param mixed $length
|
* @param mixed $length
|
||||||
|
*
|
||||||
* @return Validator
|
* @return Validator
|
||||||
*/
|
*/
|
||||||
public function setShortUrlLength($length)
|
public function setShortUrlLength($length)
|
||||||
{
|
{
|
||||||
$this->short_url_length = intval($length);
|
$this->short_url_length = intval($length);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the length of a short URL beginning with http:
|
* Get the length of a short URL beginning with http:.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
|
@ -139,19 +135,21 @@ class Validator extends Regex
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the length of a short URL beginning with https:
|
* Set the length of a short URL beginning with https:.
|
||||||
*
|
*
|
||||||
* @param mixed $length
|
* @param mixed $length
|
||||||
|
*
|
||||||
* @return Validator
|
* @return Validator
|
||||||
*/
|
*/
|
||||||
public function setShortUrlLengthHttps($length)
|
public function setShortUrlLengthHttps($length)
|
||||||
{
|
{
|
||||||
$this->short_url_length_https = intval($length);
|
$this->short_url_length_https = intval($length);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the length of a short URL beginning with https:
|
* Get the length of a short URL beginning with https:.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
|
@ -164,7 +162,8 @@ class Validator extends Regex
|
||||||
* Check whether a tweet is valid.
|
* Check whether a tweet is valid.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to validate.
|
* @param string $tweet The tweet to validate.
|
||||||
* @return boolean Whether the tweet is valid.
|
*
|
||||||
|
* @return bool Whether the tweet is valid.
|
||||||
*/
|
*/
|
||||||
public function isValidTweetText($tweet = null)
|
public function isValidTweetText($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -181,13 +180,15 @@ class Validator extends Regex
|
||||||
if (preg_match(self::$patterns['invalid_characters'], $tweet)) {
|
if (preg_match(self::$patterns['invalid_characters'], $tweet)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a tweet is valid.
|
* Check whether a tweet is valid.
|
||||||
*
|
*
|
||||||
* @return boolean Whether the tweet is valid.
|
* @return bool Whether the tweet is valid.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function validateTweet()
|
public function validateTweet()
|
||||||
|
@ -199,7 +200,8 @@ class Validator extends Regex
|
||||||
* Check whether a username is valid.
|
* Check whether a username is valid.
|
||||||
*
|
*
|
||||||
* @param string $username The username to validate.
|
* @param string $username The username to validate.
|
||||||
* @return boolean Whether the username is valid.
|
*
|
||||||
|
* @return bool Whether the username is valid.
|
||||||
*/
|
*/
|
||||||
public function isValidUsername($username = null)
|
public function isValidUsername($username = null)
|
||||||
{
|
{
|
||||||
|
@ -211,13 +213,15 @@ class Validator extends Regex
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$extracted = $this->extractor->extractMentionedScreennames($username);
|
$extracted = $this->extractor->extractMentionedScreennames($username);
|
||||||
|
|
||||||
return count($extracted) === 1 && $extracted[0] === substr($username, 1);
|
return count($extracted) === 1 && $extracted[0] === substr($username, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a username is valid.
|
* Check whether a username is valid.
|
||||||
*
|
*
|
||||||
* @return boolean Whether the username is valid.
|
* @return bool Whether the username is valid.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function validateUsername()
|
public function validateUsername()
|
||||||
|
@ -229,7 +233,8 @@ class Validator extends Regex
|
||||||
* Check whether a list is valid.
|
* Check whether a list is valid.
|
||||||
*
|
*
|
||||||
* @param string $list The list name to validate.
|
* @param string $list The list name to validate.
|
||||||
* @return boolean Whether the list is valid.
|
*
|
||||||
|
* @return bool Whether the list is valid.
|
||||||
*/
|
*/
|
||||||
public function isValidList($list = null)
|
public function isValidList($list = null)
|
||||||
{
|
{
|
||||||
|
@ -242,13 +247,15 @@ class Validator extends Regex
|
||||||
}
|
}
|
||||||
preg_match(self::$patterns['valid_mentions_or_lists'], $list, $matches);
|
preg_match(self::$patterns['valid_mentions_or_lists'], $list, $matches);
|
||||||
$matches = array_pad($matches, 5, '');
|
$matches = array_pad($matches, 5, '');
|
||||||
|
|
||||||
return isset($matches) && $matches[1] === '' && $matches[4] && !empty($matches[4]) && $matches[5] === '';
|
return isset($matches) && $matches[1] === '' && $matches[4] && !empty($matches[4]) && $matches[5] === '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a list is valid.
|
* Check whether a list is valid.
|
||||||
*
|
*
|
||||||
* @return boolean Whether the list is valid.
|
* @return bool Whether the list is valid.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function validateList()
|
public function validateList()
|
||||||
|
@ -260,7 +267,8 @@ class Validator extends Regex
|
||||||
* Check whether a hashtag is valid.
|
* Check whether a hashtag is valid.
|
||||||
*
|
*
|
||||||
* @param string $hashtag The hashtag to validate.
|
* @param string $hashtag The hashtag to validate.
|
||||||
* @return boolean Whether the hashtag is valid.
|
*
|
||||||
|
* @return bool Whether the hashtag is valid.
|
||||||
*/
|
*/
|
||||||
public function isValidHashtag($hashtag = null)
|
public function isValidHashtag($hashtag = null)
|
||||||
{
|
{
|
||||||
|
@ -272,13 +280,15 @@ class Validator extends Regex
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$extracted = $this->extractor->extractHashtags($hashtag);
|
$extracted = $this->extractor->extractHashtags($hashtag);
|
||||||
|
|
||||||
return count($extracted) === 1 && $extracted[0] === substr($hashtag, 1);
|
return count($extracted) === 1 && $extracted[0] === substr($hashtag, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a hashtag is valid.
|
* Check whether a hashtag is valid.
|
||||||
*
|
*
|
||||||
* @return boolean Whether the hashtag is valid.
|
* @return bool Whether the hashtag is valid.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function validateHashtag()
|
public function validateHashtag()
|
||||||
|
@ -289,11 +299,11 @@ class Validator extends Regex
|
||||||
/**
|
/**
|
||||||
* Check whether a URL is valid.
|
* Check whether a URL is valid.
|
||||||
*
|
*
|
||||||
* @param string $url The url to validate.
|
* @param string $url The url to validate.
|
||||||
* @param boolean $unicode_domains Consider the domain to be unicode.
|
* @param bool $unicode_domains Consider the domain to be unicode.
|
||||||
* @param boolean $require_protocol Require a protocol for valid domain?
|
* @param bool $require_protocol Require a protocol for valid domain?
|
||||||
*
|
*
|
||||||
* @return boolean Whether the URL is valid.
|
* @return bool Whether the URL is valid.
|
||||||
*/
|
*/
|
||||||
public function isValidURL($url = null, $unicode_domains = true, $require_protocol = true)
|
public function isValidURL($url = null, $unicode_domains = true, $require_protocol = true)
|
||||||
{
|
{
|
||||||
|
@ -310,25 +320,27 @@ class Validator extends Regex
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
list($scheme, $authority, $path, $query, $fragment) = array_pad($matches, 5, '');
|
list($scheme, $authority, $path, $query, $fragment) = array_pad($matches, 5, '');
|
||||||
# Check scheme, path, query, fragment:
|
// Check scheme, path, query, fragment:
|
||||||
if (($require_protocol && !(
|
if (($require_protocol && !(
|
||||||
self::isValidMatch($scheme, self::$patterns['validate_url_scheme']) && preg_match('/^https?$/i', $scheme))
|
self::isValidMatch($scheme, self::$patterns['validate_url_scheme']) && preg_match('/^https?$/i', $scheme))
|
||||||
) || !self::isValidMatch($path, self::$patterns['validate_url_path']) || !self::isValidMatch($query, self::$patterns['validate_url_query'], true)
|
) || !self::isValidMatch($path, self::$patterns['validate_url_path']) || !self::isValidMatch($query, self::$patterns['validate_url_query'], true)
|
||||||
|| !self::isValidMatch($fragment, self::$patterns['validate_url_fragment'], true)) {
|
|| !self::isValidMatch($fragment, self::$patterns['validate_url_fragment'], true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
# Check authority:
|
// Check authority:
|
||||||
$authority_pattern = $unicode_domains ? 'validate_url_unicode_authority' : 'validate_url_authority';
|
$authority_pattern = $unicode_domains ? 'validate_url_unicode_authority' : 'validate_url_authority';
|
||||||
|
|
||||||
return self::isValidMatch($authority, self::$patterns[$authority_pattern]);
|
return self::isValidMatch($authority, self::$patterns[$authority_pattern]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a URL is valid.
|
* Check whether a URL is valid.
|
||||||
*
|
*
|
||||||
* @param boolean $unicode_domains Consider the domain to be unicode.
|
* @param bool $unicode_domains Consider the domain to be unicode.
|
||||||
* @param boolean $require_protocol Require a protocol for valid domain?
|
* @param bool $require_protocol Require a protocol for valid domain?
|
||||||
|
*
|
||||||
|
* @return bool Whether the URL is valid.
|
||||||
*
|
*
|
||||||
* @return boolean Whether the URL is valid.
|
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function validateURL($unicode_domains = true, $require_protocol = true)
|
public function validateURL($unicode_domains = true, $require_protocol = true)
|
||||||
|
@ -340,7 +352,8 @@ class Validator extends Regex
|
||||||
* Determines the length of a tweet. Takes shortening of URLs into account.
|
* Determines the length of a tweet. Takes shortening of URLs into account.
|
||||||
*
|
*
|
||||||
* @param string $tweet The tweet to validate.
|
* @param string $tweet The tweet to validate.
|
||||||
* @return int the length of a tweet.
|
*
|
||||||
|
* @return int the length of a tweet.
|
||||||
*/
|
*/
|
||||||
public function getTweetLength($tweet = null)
|
public function getTweetLength($tweet = null)
|
||||||
{
|
{
|
||||||
|
@ -353,13 +366,15 @@ class Validator extends Regex
|
||||||
$length += $x['indices'][0] - $x['indices'][1];
|
$length += $x['indices'][0] - $x['indices'][1];
|
||||||
$length += stripos($x['url'], 'https://') === 0 ? $this->short_url_length_https : $this->short_url_length;
|
$length += stripos($x['url'], 'https://') === 0 ? $this->short_url_length_https : $this->short_url_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $length;
|
return $length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines the length of a tweet. Takes shortening of URLs into account.
|
* Determines the length of a tweet. Takes shortening of URLs into account.
|
||||||
*
|
*
|
||||||
* @return int the length of a tweet.
|
* @return int the length of a tweet.
|
||||||
|
*
|
||||||
* @deprecated since version 1.1.0
|
* @deprecated since version 1.1.0
|
||||||
*/
|
*/
|
||||||
public function getLength()
|
public function getLength()
|
||||||
|
@ -370,17 +385,17 @@ class Validator extends Regex
|
||||||
/**
|
/**
|
||||||
* A helper function to check for a valid match. Used in URL validation.
|
* A helper function to check for a valid match. Used in URL validation.
|
||||||
*
|
*
|
||||||
* @param string $string The subject string to test.
|
* @param string $string The subject string to test.
|
||||||
* @param string $pattern The pattern to match against.
|
* @param string $pattern The pattern to match against.
|
||||||
* @param boolean $optional Whether a match is compulsory or not.
|
* @param bool $optional Whether a match is compulsory or not.
|
||||||
*
|
*
|
||||||
* @return boolean Whether an exact match was found.
|
* @return bool Whether an exact match was found.
|
||||||
*/
|
*/
|
||||||
protected static function isValidMatch($string, $pattern, $optional = false)
|
protected static function isValidMatch($string, $pattern, $optional = false)
|
||||||
{
|
{
|
||||||
$found = preg_match($pattern, $string, $matches);
|
$found = preg_match($pattern, $string, $matches);
|
||||||
if (!$optional) {
|
if (!$optional) {
|
||||||
return (($string || $string === '') && $found && $matches[0] === $string);
|
return ($string || $string === '') && $found && $matches[0] === $string;
|
||||||
} else {
|
} else {
|
||||||
return !(($string || $string === '') && (!$found || $matches[0] !== $string));
|
return !(($string || $string === '') && (!$found || $matches[0] !== $string));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,151 +2,145 @@
|
||||||
|
|
||||||
namespace App\Util\Media;
|
namespace App\Util\Media;
|
||||||
|
|
||||||
use Storage;
|
|
||||||
use App\Media;
|
use App\Media;
|
||||||
use Image as Intervention;
|
use Image as Intervention;
|
||||||
|
use Storage;
|
||||||
|
|
||||||
class Image {
|
class Image
|
||||||
|
{
|
||||||
|
public $square;
|
||||||
|
public $landscape;
|
||||||
|
public $portrait;
|
||||||
|
public $thumbnail;
|
||||||
|
public $orientation;
|
||||||
|
|
||||||
public $square;
|
public function __construct()
|
||||||
public $landscape;
|
{
|
||||||
public $portrait;
|
ini_set('memory_limit', config('pixelfed.memory_limit', '1024M'));
|
||||||
public $thumbnail;
|
|
||||||
public $orientation;
|
|
||||||
|
|
||||||
|
$this->square = $this->orientations()['square'];
|
||||||
public function __construct()
|
$this->landscape = $this->orientations()['landscape'];
|
||||||
{
|
$this->portrait = $this->orientations()['portrait'];
|
||||||
ini_set('memory_limit', config('pixelfed.memory_limit', '1024M'));
|
$this->thumbnail = [
|
||||||
|
'width' => 293,
|
||||||
$this->square = $this->orientations()['square'];
|
'height' => 293,
|
||||||
$this->landscape = $this->orientations()['landscape'];
|
|
||||||
$this->portrait = $this->orientations()['portrait'];
|
|
||||||
$this->thumbnail = [
|
|
||||||
'width' => 293,
|
|
||||||
'height' => 293
|
|
||||||
];
|
];
|
||||||
$this->orientation = null;
|
$this->orientation = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function orientations()
|
public function orientations()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'square' => [
|
'square' => [
|
||||||
'width' => 1080,
|
'width' => 1080,
|
||||||
'height' => 1080
|
'height' => 1080,
|
||||||
],
|
],
|
||||||
'landscape' => [
|
'landscape' => [
|
||||||
'width' => 1920,
|
'width' => 1920,
|
||||||
'height' => 1080
|
'height' => 1080,
|
||||||
],
|
],
|
||||||
'portrait' => [
|
'portrait' => [
|
||||||
'width' => 1080,
|
'width' => 1080,
|
||||||
'height' => 1350
|
'height' => 1350,
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
|
||||||
|
|
||||||
public function getAspectRatio($mediaPath, $thumbnail = false)
|
|
||||||
{
|
|
||||||
if(!is_file($mediaPath)) {
|
|
||||||
throw new \Exception('Invalid Media Path');
|
|
||||||
}
|
|
||||||
if($thumbnail) {
|
|
||||||
return [
|
|
||||||
'dimensions' => $this->thumbnail,
|
|
||||||
'orientation' => 'thumbnail'
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list($width, $height) = getimagesize($mediaPath);
|
public function getAspectRatio($mediaPath, $thumbnail = false)
|
||||||
$aspect = $width / $height;
|
|
||||||
$orientation = $aspect === 1 ? 'square' :
|
|
||||||
($aspect > 1 ? 'landscape' : 'portrait');
|
|
||||||
$this->orientation = $orientation;
|
|
||||||
return [
|
|
||||||
'dimensions' => $this->orientations()[$orientation],
|
|
||||||
'orientation' => $orientation
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function resizeImage(Media $media)
|
|
||||||
{
|
|
||||||
$basePath = storage_path('app/' . $media->media_path);
|
|
||||||
|
|
||||||
$this->handleResizeImage($media);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function resizeThumbnail(Media $media)
|
|
||||||
{
|
|
||||||
$basePath = storage_path('app/' . $media->media_path);
|
|
||||||
|
|
||||||
$this->handleThumbnailImage($media);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleResizeImage(Media $media)
|
|
||||||
{
|
|
||||||
$this->handleImageTransform($media, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleThumbnailImage(Media $media)
|
|
||||||
{
|
|
||||||
$this->handleImageTransform($media, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleImageTransform(Media $media, $thumbnail = false)
|
|
||||||
{
|
|
||||||
$path = $media->media_path;
|
|
||||||
$file = storage_path('app/'.$path);
|
|
||||||
$ratio = $this->getAspectRatio($file, $thumbnail);
|
|
||||||
$aspect = $ratio['dimensions'];
|
|
||||||
$orientation = $ratio['orientation'];
|
|
||||||
if($media->mime === 'image/gif' && !$thumbnail)
|
|
||||||
{
|
{
|
||||||
return;
|
if (!is_file($mediaPath)) {
|
||||||
|
throw new \Exception('Invalid Media Path');
|
||||||
|
}
|
||||||
|
if ($thumbnail) {
|
||||||
|
return [
|
||||||
|
'dimensions' => $this->thumbnail,
|
||||||
|
'orientation' => 'thumbnail',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
list($width, $height) = getimagesize($mediaPath);
|
||||||
|
$aspect = $width / $height;
|
||||||
|
$orientation = $aspect === 1 ? 'square' :
|
||||||
|
($aspect > 1 ? 'landscape' : 'portrait');
|
||||||
|
$this->orientation = $orientation;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dimensions' => $this->orientations()[$orientation],
|
||||||
|
'orientation' => $orientation,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
public function resizeImage(Media $media)
|
||||||
$img = Intervention::make($file)->orientate();
|
{
|
||||||
$img->resize($aspect['width'], $aspect['height'], function ($constraint) {
|
$basePath = storage_path('app/'.$media->media_path);
|
||||||
$constraint->aspectRatio();
|
|
||||||
});
|
|
||||||
$converted = $this->setBaseName($path, $thumbnail, $img->extension);
|
|
||||||
$newPath = storage_path('app/'.$converted['path']);
|
|
||||||
|
|
||||||
$quality = config('pixelfed.image_quality');
|
|
||||||
$img->save($newPath, $quality);
|
|
||||||
|
|
||||||
if(!$thumbnail) {
|
|
||||||
$media->orientation = $orientation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($thumbnail == true) {
|
|
||||||
$media->thumbnail_path = $converted['path'];
|
|
||||||
$media->thumbnail_url = url(Storage::url($converted['path']));
|
|
||||||
} else {
|
|
||||||
$media->media_path = $converted['path'];
|
|
||||||
$media->mime = $img->mime;
|
|
||||||
}
|
|
||||||
|
|
||||||
$media->save();
|
|
||||||
|
|
||||||
} catch (Exception $e) {
|
|
||||||
|
|
||||||
|
$this->handleResizeImage($media);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function setBaseName($basePath, $thumbnail = false, $extension)
|
public function resizeThumbnail(Media $media)
|
||||||
{
|
{
|
||||||
$png = false;
|
$basePath = storage_path('app/'.$media->media_path);
|
||||||
$path = explode('.', $basePath);
|
|
||||||
$name = ($thumbnail == true) ? $path[0] . '_thumb' : $path[0];
|
|
||||||
$ext = last($path);
|
|
||||||
$basePath = "{$name}.{$ext}";
|
|
||||||
|
|
||||||
return ['path' => $basePath, 'png' => $png];
|
$this->handleThumbnailImage($media);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function handleResizeImage(Media $media)
|
||||||
|
{
|
||||||
|
$this->handleImageTransform($media, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleThumbnailImage(Media $media)
|
||||||
|
{
|
||||||
|
$this->handleImageTransform($media, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleImageTransform(Media $media, $thumbnail = false)
|
||||||
|
{
|
||||||
|
$path = $media->media_path;
|
||||||
|
$file = storage_path('app/'.$path);
|
||||||
|
$ratio = $this->getAspectRatio($file, $thumbnail);
|
||||||
|
$aspect = $ratio['dimensions'];
|
||||||
|
$orientation = $ratio['orientation'];
|
||||||
|
if ($media->mime === 'image/gif' && !$thumbnail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$img = Intervention::make($file)->orientate();
|
||||||
|
$img->resize($aspect['width'], $aspect['height'], function ($constraint) {
|
||||||
|
$constraint->aspectRatio();
|
||||||
|
});
|
||||||
|
$converted = $this->setBaseName($path, $thumbnail, $img->extension);
|
||||||
|
$newPath = storage_path('app/'.$converted['path']);
|
||||||
|
|
||||||
|
$quality = config('pixelfed.image_quality');
|
||||||
|
$img->save($newPath, $quality);
|
||||||
|
|
||||||
|
if (!$thumbnail) {
|
||||||
|
$media->orientation = $orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($thumbnail == true) {
|
||||||
|
$media->thumbnail_path = $converted['path'];
|
||||||
|
$media->thumbnail_url = url(Storage::url($converted['path']));
|
||||||
|
} else {
|
||||||
|
$media->media_path = $converted['path'];
|
||||||
|
$media->mime = $img->mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
$media->save();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBaseName($basePath, $thumbnail, $extension)
|
||||||
|
{
|
||||||
|
$png = false;
|
||||||
|
$path = explode('.', $basePath);
|
||||||
|
$name = ($thumbnail == true) ? $path[0].'_thumb' : $path[0];
|
||||||
|
$ext = last($path);
|
||||||
|
$basePath = "{$name}.{$ext}";
|
||||||
|
|
||||||
|
return ['path' => $basePath, 'png' => $png];
|
||||||
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue