mirror of https://github.com/pixelfed/pixelfed
Merge branch 'dev' into maxlength
commit
a1588a82c1
@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
@ -1,4 +1,76 @@
|
||||
# PixelFed
|
||||
Federated Image Sharing
|
||||
# PixelFed: Federated Image Sharing
|
||||
|
||||
> This project is still in active development and not yet ready for use.
|
||||
PixelFed is a federated social image sharing platform, similar to Instagram.
|
||||
Federation is done using the [ActivityPub](https://activitypub.rocks/) protocol,
|
||||
which is used by [Mastodon](http://joinmastodon.org/), [PeerTube](https://joinpeertube.org/en/),
|
||||
[Pleroma](https://pleroma.social/), and more. Through ActivityPub PixelFed can share
|
||||
and interact with these platforms, as well as other instances of PixelFed.
|
||||
|
||||
**_Please note this is alpha software, not recommended for production use,
|
||||
and federation is not supported yet._**
|
||||
|
||||
PixelFed is very early into the development stage. If you would like to have a
|
||||
permanent instance with minimal breakage, **do not use this software until
|
||||
there is a stable release**. The following setup instructions are intended for
|
||||
testing and development.
|
||||
|
||||
## Requirements
|
||||
- PHP >= 7.1.3 (7.2+ recommended for stable version)
|
||||
- MySQL >= 5.7, Postgres (MariaDB and sqlite are not supported yet)
|
||||
- Redis
|
||||
- Composer
|
||||
- GD or ImageMagick
|
||||
- OpenSSL PHP Extension
|
||||
- PDO PHP Extension
|
||||
- Mbstring PHP Extension
|
||||
- Tokenizer PHP Extension
|
||||
- XML PHP Extension
|
||||
- Ctype PHP Extension
|
||||
- JSON PHP Extension
|
||||
- JpegOptim
|
||||
- Optipng
|
||||
- Pngquant 2
|
||||
- SVGO
|
||||
- Gifsicle
|
||||
|
||||
## Installation
|
||||
|
||||
This guide assumes you have NGINX/Apache installed, along with the dependencies.
|
||||
Those will not be covered in these early docs.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/dansup/pixelfed.git
|
||||
cd pixelfed
|
||||
composer install
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
**Edit .env file with proper values**
|
||||
|
||||
```bash
|
||||
php artisan key:generate
|
||||
```
|
||||
|
||||
```bash
|
||||
php artisan storage:link
|
||||
php artisan migrate
|
||||
php artisan horizon
|
||||
php artisan serve --host=localhost --port=80
|
||||
```
|
||||
|
||||
Check your browser at http://localhost
|
||||
|
||||
## Communication
|
||||
|
||||
The ways you can communicate on the project are below. Before interacting, please
|
||||
read through the [Code Of Conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
* IRC: #pixelfed on irc.freenode.net ([#freenode_#pixelfed:matrix.org through
|
||||
Matrix](https://matrix.to/#/#freenode_#pixelfed:matrix.org)
|
||||
* Project on Mastodon: [@pixelfed@mastodon.social](https://mastodon.social/@pixelfed)
|
||||
* E-mail: [hello@pixelfed.org](mailto:hello@pixelfed.org)
|
||||
|
||||
## Support
|
||||
|
||||
The lead maintainer is on Patreon! You can become a Patron at
|
||||
https://www.patreon.com/dansup
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AccountLog extends Model
|
||||
{
|
||||
//
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class EmailVerification extends Model
|
||||
{
|
||||
public function url()
|
||||
{
|
||||
$base = config('app.url');
|
||||
$path = '/i/confirm-email/' . $this->user_token . '/' . $this->random_token;
|
||||
return "{$base}{$path}";
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Broadcasting\Channel;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Broadcasting\PresenceChannel;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use App\{User, UserSetting};
|
||||
|
||||
class AuthLoginEvent
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function handle(User $user)
|
||||
{
|
||||
if(empty($user->settings)) {
|
||||
$settings = new UserSetting;
|
||||
$settings->user_id = $user->id;
|
||||
$settings->save();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
use App\{Comment, Like, Media, Profile, Report, Status, User};
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
trait AdminReportController
|
||||
{
|
||||
public function updateReport(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => 'required|string'
|
||||
]);
|
||||
|
||||
$action = $request->input('action');
|
||||
|
||||
$actions = [
|
||||
'ignore',
|
||||
'cw',
|
||||
'unlist',
|
||||
'delete',
|
||||
'shadowban',
|
||||
'ban'
|
||||
];
|
||||
|
||||
if(!in_array($action, $actions)) {
|
||||
return abort(403);
|
||||
}
|
||||
|
||||
$report = Report::findOrFail($id);
|
||||
|
||||
$this->handleReportAction($report, $action);
|
||||
|
||||
return response()->json(['msg'=> 'Success']);
|
||||
}
|
||||
|
||||
public function handleReportAction(Report $report, $action)
|
||||
{
|
||||
$item = $report->reported();
|
||||
$report->admin_seen = Carbon::now();
|
||||
|
||||
switch ($action) {
|
||||
case 'ignore':
|
||||
$report->not_interested = true;
|
||||
break;
|
||||
|
||||
case 'cw':
|
||||
$item->is_nsfw = true;
|
||||
$item->save();
|
||||
$report->nsfw = true;
|
||||
break;
|
||||
|
||||
case 'unlist':
|
||||
$item->visibility = 'unlisted';
|
||||
$item->save();
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
// Todo: fire delete job
|
||||
$report->admin_seen = null;
|
||||
break;
|
||||
|
||||
case 'shadowban':
|
||||
// Todo: fire delete job
|
||||
$report->admin_seen = null;
|
||||
break;
|
||||
|
||||
case 'ban':
|
||||
// Todo: fire delete job
|
||||
$report->admin_seen = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
$report->admin_seen = null;
|
||||
break;
|
||||
}
|
||||
|
||||
$report->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Auth, Cache;
|
||||
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\Util\Webfinger\Webfinger;
|
||||
use App\Transformer\Api\{
|
||||
AccountTransformer,
|
||||
StatusTransformer
|
||||
};
|
||||
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
|
||||
class BaseApiController extends Controller
|
||||
{
|
||||
protected $fractal;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->fractal = new Fractal\Manager();
|
||||
$this->fractal->setSerializer(new ArraySerializer());
|
||||
}
|
||||
|
||||
public function accounts(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$resource = new Fractal\Resource\Item($profile, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function accountFollowers(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$followers = $profile->followers;
|
||||
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function accountFollowing(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$following = $profile->following;
|
||||
$resource = new Fractal\Resource\Collection($following, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function accountStatuses(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$statuses = $profile->statuses()->orderBy('id', 'desc')->paginate(20);
|
||||
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
|
||||
public function followSuggestions(Request $request)
|
||||
{
|
||||
$followers = Auth::user()->profile->recommendFollowers();
|
||||
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
public function avatarUpdate(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'upload' => 'required|mimes:jpeg,png,gif|max:2000',
|
||||
]);
|
||||
try {
|
||||
$user = Auth::user();
|
||||
$profile = $user->profile;
|
||||
$file = $request->file('upload');
|
||||
$path = (new AvatarController())->getPath($user, $file);
|
||||
$dir = $path['root'];
|
||||
$name = $path['name'];
|
||||
$public = $path['storage'];
|
||||
$currentAvatar = storage_path('app/'.$profile->avatar->media_path);
|
||||
$loc = $request->file('upload')->storeAs($public, $name);
|
||||
|
||||
$avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
|
||||
$opath = $avatar->media_path;
|
||||
$avatar->media_path = "$public/$name";
|
||||
$avatar->thumb_path = null;
|
||||
$avatar->change_count = ++$avatar->change_count;
|
||||
$avatar->last_processed_at = null;
|
||||
$avatar->save();
|
||||
|
||||
Cache::forget("avatar:{$profile->id}");
|
||||
AvatarOptimize::dispatch($user->profile, $currentAvatar);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'code' => 200,
|
||||
'msg' => 'Avatar successfully updated'
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ImportDataController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Auth, Closure;
|
||||
|
||||
class EmailVerificationCheck
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if($request->user() &&
|
||||
config('pixelfed.enforce_email_verification') &&
|
||||
is_null($request->user()->email_verified_at) &&
|
||||
!$request->is('i/verify-email', 'log*', 'i/confirm-email/*', 'settings/home')
|
||||
) {
|
||||
return redirect('/i/verify-email');
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ImportJob extends Model
|
||||
{
|
||||
//
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\AvatarPipeline;
|
||||
|
||||
use \Carbon\Carbon;
|
||||
use Image as Intervention;
|
||||
use App\{Avatar, Profile};
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class AvatarOptimize implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $profile;
|
||||
protected $current;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Profile $profile, $current)
|
||||
{
|
||||
$this->profile = $profile;
|
||||
$this->current = $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$avatar = $this->profile->avatar;
|
||||
$file = storage_path("app/$avatar->media_path");
|
||||
|
||||
try {
|
||||
$img = Intervention::make($file)->orientate();
|
||||
$img->fit(200, 200, function ($constraint) {
|
||||
$constraint->upsize();
|
||||
});
|
||||
$quality = config('pixelfed.image_quality');
|
||||
$img->save($file, $quality);
|
||||
|
||||
$avatar = Avatar::whereProfileId($this->profile->id)->firstOrFail();
|
||||
$avatar->thumb_path = $avatar->media_path;
|
||||
$avatar->change_count = ++$avatar->change_count;
|
||||
$avatar->last_processed_at = Carbon::now();
|
||||
$avatar->save();
|
||||
$this->deleteOldAvatar($avatar->media_path, $this->current);
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteOldAvatar($new, $current)
|
||||
{
|
||||
if(storage_path('app/' . $new) == $current) {
|
||||
return;
|
||||
}
|
||||
if(is_file($current)) {
|
||||
@unlink($current);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\InboxPipeline;
|
||||
|
||||
use App\Profile;
|
||||
use App\Util\ActivityPub\Inbox;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class InboxWorker implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $request;
|
||||
protected $profile;
|
||||
protected $payload;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($request, Profile $profile, $payload)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->profile = $profile;
|
||||
$this->payload = $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
(new Inbox($this->request, $this->profile, $this->payload))->handle();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\InboxPipeline;
|
||||
|
||||
use App\Profile;
|
||||
use App\Util\ActivityPub\Inbox;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class SharedInboxWorker implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $request;
|
||||
protected $profile;
|
||||
protected $payload;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($request, $payload)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->payload = $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
(new Inbox($this->request, null, $this->payload))->handleSharedInbox();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\MentionPipeline;
|
||||
|
||||
use Cache, Log, Redis;
|
||||
use App\{Mention, Notification, Profile, Status};
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class MentionPipeline implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $status;
|
||||
protected $mention;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Status $status, Mention $mention)
|
||||
{
|
||||
$this->status = $status;
|
||||
$this->mention = $mention;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$status = $this->status;
|
||||
$mention = $this->mention;
|
||||
$actor = $this->status->profile;
|
||||
$target = $this->mention->profile_id;
|
||||
|
||||
$exists = Notification::whereProfileId($target)
|
||||
->whereActorId($actor->id)
|
||||
->whereAction('mention')
|
||||
->whereItemId($status->id)
|
||||
->whereItemType('App\Status')
|
||||
->count();
|
||||
|
||||
if($actor->id === $target || $exists !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
$notification = new Notification;
|
||||
$notification->profile_id = $target;
|
||||
$notification->actor_id = $actor->id;
|
||||
$notification->action = 'mention';
|
||||
$notification->message = $mention->toText();
|
||||
$notification->rendered = $mention->toHtml();
|
||||
$notification->item_id = $status->id;
|
||||
$notification->item_type = "App\Status";
|
||||
$notification->save();
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\EmailVerification;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class ConfirmEmail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(EmailVerification $verify)
|
||||
{
|
||||
$this->verify = $verify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->markdown('emails.confirm_email')->with(['verify'=>$this->verify]);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Mention extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
public function profile()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'profile_id', 'id');
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
return $this->belongsTo(Status::class, 'status_id', 'id');
|
||||
}
|
||||
|
||||
public function toText()
|
||||
{
|
||||
$actorName = $this->status->profile->username;
|
||||
return "{$actorName} " . __('notification.mentionedYou');
|
||||
}
|
||||
|
||||
public function toHtml()
|
||||
{
|
||||
$actorName = $this->status->profile->username;
|
||||
$actorUrl = $this->status->profile->url();
|
||||
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> " .
|
||||
__('notification.mentionedYou');
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ReportComment extends Model
|
||||
{
|
||||
//
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ReportLog extends Model
|
||||
{
|
||||
//
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\ActivityPub;
|
||||
|
||||
use App\{Profile, Status};
|
||||
use League\Fractal;
|
||||
|
||||
class StatusTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
|
||||
public function transform(Status $status)
|
||||
{
|
||||
return [
|
||||
'@context' => [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1',
|
||||
[
|
||||
"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
|
||||
"featured" => [
|
||||
"https://pixelfed.org/ns#featured" => ["@type" => "@id"],
|
||||
]
|
||||
]
|
||||
],
|
||||
'id' => $status->url(),
|
||||
|
||||
// TODO: handle other types
|
||||
'type' => 'Note',
|
||||
|
||||
// XXX: CW Title
|
||||
'summary' => null,
|
||||
'content' => $status->rendered ?? $status->caption,
|
||||
'inReplyTo' => null,
|
||||
|
||||
// TODO: fix date format
|
||||
'published' => $status->created_at->toAtomString(),
|
||||
'url' => $status->url(),
|
||||
'attributedTo' => $status->profile->permalink(),
|
||||
'to' => [
|
||||
// TODO: handle proper scope
|
||||
'https://www.w3.org/ns/activitystreams#Public'
|
||||
],
|
||||
'cc' => [
|
||||
// TODO: add cc's
|
||||
$status->profile->permalink('/followers'),
|
||||
],
|
||||
'sensitive' => (bool) $status->is_nsfw,
|
||||
'atomUri' => $status->url(),
|
||||
'inReplyToAtomUri' => null,
|
||||
'attachment' => $status->media->map(function($media) {
|
||||
return [
|
||||
'type' => 'Document',
|
||||
'mediaType' => $media->mime,
|
||||
'url' => $media->url(),
|
||||
'name' => null
|
||||
];
|
||||
}),
|
||||
'tag' => []
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\Profile;
|
||||
use League\Fractal;
|
||||
|
||||
class AccountTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(Profile $profile)
|
||||
{
|
||||
return [
|
||||
'id' => $profile->id,
|
||||
'username' => $profile->username,
|
||||
'acct' => $profile->username,
|
||||
'display_name' => $profile->name,
|
||||
'locked' => (bool) $profile->is_private,
|
||||
'created_at' => $profile->created_at->format('c'),
|
||||
'followers_count' => $profile->followerCount(),
|
||||
'following_count' => $profile->followingCount(),
|
||||
'statuses_count' => $profile->statusCount(),
|
||||
'note' => $profile->bio,
|
||||
'url' => $profile->url(),
|
||||
'avatar' => $profile->avatarUrl(),
|
||||
'avatar_static' => $profile->avatarUrl(),
|
||||
'header' => '',
|
||||
'header_static' => '',
|
||||
'moved' => null,
|
||||
'fields' => null,
|
||||
'bot' => null
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use League\Fractal;
|
||||
|
||||
class ApplicationTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform()
|
||||
{
|
||||
return [
|
||||
'name' => '',
|
||||
'website' => null
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\Hashtag;
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
|
||||
class HashtagTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(Hashtag $hashtag)
|
||||
{
|
||||
return [
|
||||
'name' => $hashtag->name,
|
||||
'url' => $hashtag->url(),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\Media;
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
|
||||
class MediaTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(Media $media)
|
||||
{
|
||||
return [
|
||||
'id' => $media->id,
|
||||
'type' => 'image',
|
||||
'url' => $media->url(),
|
||||
'remote_url' => null,
|
||||
'preview_url' => $media->thumbnailUrl(),
|
||||
'text_url' => null,
|
||||
'meta' => null,
|
||||
'description' => null
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\Profile;
|
||||
use League\Fractal;
|
||||
|
||||
class MentionTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
public function transform(Profile $profile)
|
||||
{
|
||||
return [
|
||||
'id' => $profile->id,
|
||||
'url' => $profile->url(),
|
||||
'username' => $profile->username,
|
||||
'acct' => $profile->username,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\Status;
|
||||
use League\Fractal;
|
||||
|
||||
class StatusTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
protected $defaultIncludes = [
|
||||
'account',
|
||||
'mentions',
|
||||
'media_attachments',
|
||||
'tags'
|
||||
];
|
||||
|
||||
public function transform(Status $status)
|
||||
{
|
||||
return [
|
||||
'id' => $status->id,
|
||||
'uri' => $status->url(),
|
||||
'url' => $status->url(),
|
||||
'in_reply_to_id' => $status->in_reply_to_id,
|
||||
'in_reply_to_account_id' => $status->in_reply_to_profile_id,
|
||||
|
||||
// TODO: fixme
|
||||
'reblog' => null,
|
||||
|
||||
'content' => "<p>$status->rendered</p>",
|
||||
'created_at' => $status->created_at->format('c'),
|
||||
'emojis' => [],
|
||||
'reblogs_count' => $status->shares()->count(),
|
||||
'favourites_count' => $status->likes()->count(),
|
||||
'reblogged' => $status->shared(),
|
||||
'favourited' => $status->liked(),
|
||||
'muted' => null,
|
||||
'sensitive' => (bool) $status->is_nsfw,
|
||||
'spoiler_text' => '',
|
||||
'visibility' => $status->visibility,
|
||||
'application' => null,
|
||||
'language' => null,
|
||||
'pinned' => null
|
||||
];
|
||||
}
|
||||
|
||||
public function includeAccount(Status $status)
|
||||
{
|
||||
$account = $status->profile;
|
||||
return $this->item($account, new AccountTransformer);
|
||||
}
|
||||
|
||||
public function includeMentions(Status $status)
|
||||
{
|
||||
$mentions = $status->mentions;
|
||||
return $this->collection($mentions, new MentionTransformer);
|
||||
}
|
||||
|
||||
public function includeMediaAttachments(Status $status)
|
||||
{
|
||||
$media = $status->media;
|
||||
return $this->collection($media, new MediaTransformer);
|
||||
}
|
||||
|
||||
public function includeTags(Status $status)
|
||||
{
|
||||
$tags = $status->hashtags;
|
||||
return $this->collection($tags, new HashtagTransformer);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserFilter extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'filterable_id',
|
||||
'filterable_type',
|
||||
'filter_type'
|
||||
];
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserSetting extends Model
|
||||
{
|
||||
//
|
||||
}
|
@ -0,0 +1,771 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Mike Cochrane <mikec@mikenz.geek.nz>
|
||||
* @author Nick Pope <nick@nickpope.me.uk>
|
||||
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter.Text
|
||||
*/
|
||||
|
||||
namespace App\Util\Lexer;
|
||||
|
||||
use App\Util\Lexer\Regex;
|
||||
use App\Util\Lexer\Extractor;
|
||||
use App\Util\Lexer\StringUtils;
|
||||
|
||||
/**
|
||||
* Twitter Autolink Class
|
||||
*
|
||||
* Parses tweets and generates HTML anchor tags around URLs, usernames,
|
||||
* username/list pairs and hashtags.
|
||||
*
|
||||
* Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
|
||||
* is based on code by {@link http://github.com/mzsanford Matt Sanford} and
|
||||
* heavily modified by {@link http://github.com/ngnpope Nick Pope}.
|
||||
*
|
||||
* @author Mike Cochrane <mikec@mikenz.geek.nz>
|
||||
* @author Nick Pope <nick@nickpope.me.uk>
|
||||
* @copyright Copyright © 2010, Mike Cochrane, Nick Pope
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter.Text
|
||||
*/
|
||||
class Autolink extends Regex
|
||||
{
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked URLs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $class_url = '';
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked username URLs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $class_user = 'u-url mention';
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked list URLs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $class_list = 'u-url list-slug';
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked hashtag URLs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $class_hash = 'u-url hashtag';
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked cashtag URLs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $class_cash = 'u-url cashtag';
|
||||
|
||||
/**
|
||||
* URL base for username links (the username without the @ will be appended).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url_base_user = null;
|
||||
|
||||
/**
|
||||
* URL base for list links (the username/list without the @ will be appended).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url_base_list = null;
|
||||
|
||||
/**
|
||||
* URL base for hashtag links (the hashtag without the # will be appended).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url_base_hash = null;
|
||||
|
||||
/**
|
||||
* URL base for cashtag links (the hashtag without the $ will be appended).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url_base_cash = null;
|
||||
|
||||
/**
|
||||
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $nofollow = true;
|
||||
|
||||
/**
|
||||
* Whether to include the value 'noopener' in the 'rel' attribute.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $noopener = true;
|
||||
|
||||
/**
|
||||
* Whether to include the value 'external' in the 'rel' attribute.
|
||||
*
|
||||
* Often this is used to be matched on in JavaScript for dynamically adding
|
||||
* the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
|
||||
* been undeprecated and thus the 'target' attribute can be used. If this is
|
||||
* set to false then the 'target' attribute will be output.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $external = true;
|
||||
|
||||
/**
|
||||
* The scope to open the link in.
|
||||
*
|
||||
* Support for the 'target' attribute was deprecated in HTML 4.01 but has
|
||||
* since been reinstated in HTML 5. To output the 'target' attribute you
|
||||
* must disable the adding of the string 'external' to the 'rel' attribute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target = '_blank';
|
||||
|
||||
/**
|
||||
* attribute for invisible span tag
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $invisibleTagAttrs = "style='position:absolute;left:-9999px;'";
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Extractor
|
||||
*/
|
||||
protected $extractor = null;
|
||||
|
||||
/**
|
||||
* Provides fluent method chaining.
|
||||
*
|
||||
* @param string $tweet The tweet to be converted.
|
||||
* @param bool $full_encode Whether to encode all special characters.
|
||||
*
|
||||
* @see __construct()
|
||||
*
|
||||
* @return Autolink
|
||||
*/
|
||||
public static function create($tweet = null, $full_encode = false)
|
||||
{
|
||||
return new static($tweet, $full_encode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a tweet to be parsed and converted to contain links.
|
||||
*
|
||||
* As the intent is to produce links and output the modified tweet to the
|
||||
* user, we take this opportunity to ensure that we escape user input.
|
||||
*
|
||||
* @see htmlspecialchars()
|
||||
*
|
||||
* @param string $tweet The tweet to be converted.
|
||||
* @param bool $escape Whether to escape the tweet (default: true).
|
||||
* @param bool $full_encode Whether to encode all special characters.
|
||||
*/
|
||||
public function __construct($tweet = null, $escape = true, $full_encode = false)
|
||||
{
|
||||
if ($escape && !empty($tweet)) {
|
||||
if ($full_encode) {
|
||||
parent::__construct(htmlentities($tweet, ENT_QUOTES, 'UTF-8', false));
|
||||
} else {
|
||||
parent::__construct(htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false));
|
||||
}
|
||||
} else {
|
||||
parent::__construct($tweet);
|
||||
}
|
||||
$this->extractor = Extractor::create();
|
||||
$this->url_base_user = config('app.url') . '/';
|
||||
$this->url_base_list = config('app.url') . '/';
|
||||
$this->url_base_hash = config('app.url') . "/discover/tags/";
|
||||
$this->url_base_cash = config('app.url') . '/search?q=%24';
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked URLs.
|
||||
*
|
||||
* @return string CSS class for URL links.
|
||||
*/
|
||||
public function getURLClass()
|
||||
{
|
||||
return $this->class_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked URLs.
|
||||
*
|
||||
* @param string $v CSS class for URL links.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setURLClass($v)
|
||||
{
|
||||
$this->class_url = trim($v);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked username URLs.
|
||||
*
|
||||
* @return string CSS class for username links.
|
||||
*/
|
||||
public function getUsernameClass()
|
||||
{
|
||||
return $this->class_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked username URLs.
|
||||
*
|
||||
* @param string $v CSS class for username links.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setUsernameClass($v)
|
||||
{
|
||||
$this->class_user = trim($v);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked username/list URLs.
|
||||
*
|
||||
* @return string CSS class for username/list links.
|
||||
*/
|
||||
public function getListClass()
|
||||
{
|
||||
return $this->class_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked username/list URLs.
|
||||
*
|
||||
* @param string $v CSS class for username/list links.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setListClass($v)
|
||||
{
|
||||
$this->class_list = trim($v);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked hashtag URLs.
|
||||
*
|
||||
* @return string CSS class for hashtag links.
|
||||
*/
|
||||
public function getHashtagClass()
|
||||
{
|
||||
return $this->class_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked hashtag URLs.
|
||||
*
|
||||
* @param string $v CSS class for hashtag links.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setHashtagClass($v)
|
||||
{
|
||||
$this->class_hash = trim($v);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked cashtag URLs.
|
||||
*
|
||||
* @return string CSS class for cashtag links.
|
||||
*/
|
||||
public function getCashtagClass()
|
||||
{
|
||||
return $this->class_cash;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class for auto-linked cashtag URLs.
|
||||
*
|
||||
* @param string $v CSS class for cashtag links.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setCashtagClass($v)
|
||||
{
|
||||
$this->class_cash = trim($v);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
||||
*
|
||||
* @return bool Whether to add 'nofollow' to the 'rel' attribute.
|
||||
*/
|
||||
public function getNoFollow()
|
||||
{
|
||||
return $this->nofollow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to include the value 'nofollow' in the 'rel' attribute.
|
||||
*
|
||||
* @param bool $v The value to add to the 'target' attribute.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setNoFollow($v)
|
||||
{
|
||||
$this->nofollow = $v;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to include the value 'external' in the 'rel' attribute.
|
||||
*
|
||||
* Often this is used to be matched on in JavaScript for dynamically adding
|
||||
* the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
|
||||
* been undeprecated and thus the 'target' attribute can be used. If this is
|
||||
* set to false then the 'target' attribute will be output.
|
||||
*
|
||||
* @return bool Whether to add 'external' to the 'rel' attribute.
|
||||
*/
|
||||
public function getExternal()
|
||||
{
|
||||
return $this->external;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to include the value 'external' in the 'rel' attribute.
|
||||
*
|
||||
* Often this is used to be matched on in JavaScript for dynamically adding
|
||||
* the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
|
||||
* been undeprecated and thus the 'target' attribute can be used. If this is
|
||||
* set to false then the 'target' attribute will be output.
|
||||
*
|
||||
* @param bool $v The value to add to the 'target' attribute.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setExternal($v)
|
||||
{
|
||||
$this->external = $v;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope to open the link in.
|
||||
*
|
||||
* Support for the 'target' attribute was deprecated in HTML 4.01 but has
|
||||
* since been reinstated in HTML 5. To output the 'target' attribute you
|
||||
* must disable the adding of the string 'external' to the 'rel' attribute.
|
||||
*
|
||||
* @return string The value to add to the 'target' attribute.
|
||||
*/
|
||||
public function getTarget()
|
||||
{
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope to open the link in.
|
||||
*
|
||||
* Support for the 'target' attribute was deprecated in HTML 4.01 but has
|
||||
* since been reinstated in HTML 5. To output the 'target' attribute you
|
||||
* must disable the adding of the string 'external' to the 'rel' attribute.
|
||||
*
|
||||
* @param string $v The value to add to the 'target' attribute.
|
||||
*
|
||||
* @return Autolink Fluid method chaining.
|
||||
*/
|
||||
public function setTarget($v)
|
||||
{
|
||||
$this->target = trim($v);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Autolink with entities
|
||||
*
|
||||
* @param string $tweet
|
||||
* @param array $entities
|
||||
* @return string
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function autoLinkEntities($tweet = null, $entities = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
|
||||
$text = '';
|
||||
$beginIndex = 0;
|
||||
foreach ($entities as $entity) {
|
||||
if (isset($entity['screen_name'])) {
|
||||
$text .= StringUtils::substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex + 1);
|
||||
} else {
|
||||
$text .= StringUtils::substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex);
|
||||
}
|
||||
|
||||
if (isset($entity['url'])) {
|
||||
$text .= $this->linkToUrl($entity);
|
||||
} elseif (isset($entity['hashtag'])) {
|
||||
$text .= $this->linkToHashtag($entity, $tweet);
|
||||
} elseif (isset($entity['screen_name'])) {
|
||||
$text .= $this->linkToMentionAndList($entity);
|
||||
} elseif (isset($entity['cashtag'])) {
|
||||
$text .= $this->linkToCashtag($entity, $tweet);
|
||||
}
|
||||
$beginIndex = $entity['indices'][1];
|
||||
}
|
||||
$text .= StringUtils::substr($tweet, $beginIndex, StringUtils::strlen($tweet));
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-link hashtags, URLs, usernames and lists, with JSON entities.
|
||||
*
|
||||
* @param string The tweet to be converted
|
||||
* @param mixed The entities info
|
||||
* @return string that auto-link HTML added
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function autoLinkWithJson($tweet = null, $json = null)
|
||||
{
|
||||
// concatenate entities
|
||||
$entities = array();
|
||||
if (is_object($json)) {
|
||||
$json = $this->object2array($json);
|
||||
}
|
||||
if (is_array($json)) {
|
||||
foreach ($json as $key => $vals) {
|
||||
$entities = array_merge($entities, $json[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// map JSON entity to twitter-text entity
|
||||
foreach ($entities as $idx => $entity) {
|
||||
if (!empty($entity['text'])) {
|
||||
$entities[$idx]['hashtag'] = $entity['text'];
|
||||
}
|
||||
}
|
||||
|
||||
$entities = $this->extractor->removeOverlappingEntities($entities);
|
||||
return $this->autoLinkEntities($tweet, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert Object to Array
|
||||
*
|
||||
* @param mixed $obj
|
||||
* @return array
|
||||
*/
|
||||
protected function object2array($obj)
|
||||
{
|
||||
$array = (array) $obj;
|
||||
foreach ($array as $key => $var) {
|
||||
if (is_object($var) || is_array($var)) {
|
||||
$array[$key] = $this->object2array($var);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-link hashtags, URLs, usernames and lists.
|
||||
*
|
||||
* @param string The tweet to be converted
|
||||
* @return string that auto-link HTML added
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function autoLink($tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$entities = $this->extractor->extractURLWithoutProtocol(false)->extractEntitiesWithIndices($tweet);
|
||||
return $this->autoLinkEntities($tweet, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-link the @username and @username/list references in the provided text. Links to @username references will
|
||||
* have the usernameClass CSS classes added. Links to @username/list references will have the listClass CSS class
|
||||
* added.
|
||||
*
|
||||
* @return string that auto-link HTML added
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function autoLinkUsernamesAndLists($tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$entities = $this->extractor->extractMentionsOrListsWithIndices($tweet);
|
||||
return $this->autoLinkEntities($tweet, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-link #hashtag references in the provided Tweet text. The #hashtag links will have the hashtagClass CSS class
|
||||
* added.
|
||||
*
|
||||
* @return string that auto-link HTML added
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function autoLinkHashtags($tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$entities = $this->extractor->extractHashtagsWithIndices($tweet);
|
||||
return $this->autoLinkEntities($tweet, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-link URLs in the Tweet text provided.
|
||||
* <p/>
|
||||
* This only auto-links URLs with protocol.
|
||||
*
|
||||
* @return string that auto-link HTML added
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function autoLinkURLs($tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$entities = $this->extractor->extractURLWithoutProtocol(false)->extractURLsWithIndices($tweet);
|
||||
return $this->autoLinkEntities($tweet, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-link $cashtag references in the provided Tweet text. The $cashtag links will have the cashtagClass CSS class
|
||||
* added.
|
||||
*
|
||||
* @return string that auto-link HTML added
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function autoLinkCashtags($tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$entities = $this->extractor->extractCashtagsWithIndices($tweet);
|
||||
return $this->autoLinkEntities($tweet, $entities);
|
||||
}
|
||||
|
||||
public function linkToUrl($entity)
|
||||
{
|
||||
if (!empty($this->class_url)) {
|
||||
$attributes['class'] = $this->class_url;
|
||||
}
|
||||
$attributes['href'] = $entity['url'];
|
||||
$linkText = $this->escapeHTML($entity['url']);
|
||||
|
||||
if (!empty($entity['display_url']) && !empty($entity['expanded_url'])) {
|
||||
// Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste
|
||||
// should contain the full original URL (expanded_url), not the display URL.
|
||||
//
|
||||
// Method: Whenever possible, we actually emit HTML that contains expanded_url, and use
|
||||
// font-size:0 to hide those parts that should not be displayed (because they are not part of display_url).
|
||||
// Elements with font-size:0 get copied even though they are not visible.
|
||||
// Note that display:none doesn't work here. Elements with display:none don't get copied.
|
||||
//
|
||||
// Additionally, we want to *display* ellipses, but we don't want them copied. To make this happen we
|
||||
// wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on
|
||||
// everything with the tco-ellipsis class.
|
||||
//
|
||||
// As an example: The user tweets "hi http://longdomainname.com/foo"
|
||||
// This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo"
|
||||
// This will get rendered as:
|
||||
// <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
|
||||
// …
|
||||
// <!-- There's a chance the onCopy event handler might not fire. In case that happens,
|
||||
// we include an here so that the … doesn't bump up against the URL and ruin it.
|
||||
// The is inside the tco-ellipsis span so that when the onCopy handler *does*
|
||||
// fire, it doesn't get copied. Otherwise the copied text would have two spaces in a row,
|
||||
// e.g. "hi http://longdomainname.com/foo".
|
||||
// <span style='font-size:0'> </span>
|
||||
// </span>
|
||||
// <span style='font-size:0'> <!-- This stuff should get copied but not displayed -->
|
||||
// http://longdomai
|
||||
// </span>
|
||||
// <span class='js-display-url'> <!-- This stuff should get displayed *and* copied -->
|
||||
// nname.com/foo
|
||||
// </span>
|
||||
// <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
|
||||
// <span style='font-size:0'> </span>
|
||||
// …
|
||||
// </span>
|
||||
//
|
||||
// Exception: pic.socialhub.dev images, for which expandedUrl = "https://socialhub.dev/#!/username/status/1234/photo/1
|
||||
// For those URLs, display_url is not a substring of expanded_url, so we don't do anything special to render the elided parts.
|
||||
// For a pic.socialhub.dev URL, the only elided part will be the "https://", so this is fine.
|
||||
$displayURL = $entity['display_url'];
|
||||
$expandedURL = $entity['expanded_url'];
|
||||
$displayURLSansEllipses = preg_replace('/…/u', '', $displayURL);
|
||||
$diplayURLIndexInExpandedURL = mb_strpos($expandedURL, $displayURLSansEllipses);
|
||||
|
||||
if ($diplayURLIndexInExpandedURL !== false) {
|
||||
$beforeDisplayURL = mb_substr($expandedURL, 0, $diplayURLIndexInExpandedURL);
|
||||
$afterDisplayURL = mb_substr($expandedURL, $diplayURLIndexInExpandedURL + mb_strlen($displayURLSansEllipses));
|
||||
$precedingEllipsis = (preg_match('/\A…/u', $displayURL)) ? '…' : '';
|
||||
$followingEllipsis = (preg_match('/…\z/u', $displayURL)) ? '…' : '';
|
||||
|
||||
$invisibleSpan = "<span {$this->invisibleTagAttrs}>";
|
||||
|
||||
$linkText = "<span class='tco-ellipsis'>{$precedingEllipsis}{$invisibleSpan} </span></span>";
|
||||
$linkText .= "{$invisibleSpan}{$this->escapeHTML($beforeDisplayURL)}</span>";
|
||||
$linkText .= "<span class='js-display-url'>{$this->escapeHTML($displayURLSansEllipses)}</span>";
|
||||
$linkText .= "{$invisibleSpan}{$this->escapeHTML($afterDisplayURL)}</span>";
|
||||
$linkText .= "<span class='tco-ellipsis'>{$invisibleSpan} </span>{$followingEllipsis}</span>";
|
||||
} else {
|
||||
$linkText = $entity['display_url'];
|
||||
}
|
||||
$attributes['title'] = $entity['expanded_url'];
|
||||
} elseif (!empty($entity['display_url'])) {
|
||||
$linkText = $entity['display_url'];
|
||||
}
|
||||
|
||||
return $this->linkToText($entity, $linkText, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $entity
|
||||
* @param string $tweet
|
||||
* @return string
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function linkToHashtag($entity, $tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$this->target = false;
|
||||
$attributes = array();
|
||||
$class = array();
|
||||
$hash = StringUtils::substr($tweet, $entity['indices'][0], 1);
|
||||
$linkText = $hash . $entity['hashtag'];
|
||||
|
||||
$attributes['href'] = $this->url_base_hash . $entity['hashtag'] . '?src=hash';
|
||||
$attributes['title'] = '#' . $entity['hashtag'];
|
||||
if (!empty($this->class_hash)) {
|
||||
$class[] = $this->class_hash;
|
||||
}
|
||||
if (preg_match(self::$patterns['rtl_chars'], $linkText)) {
|
||||
$class[] = 'rtl';
|
||||
}
|
||||
if (!empty($class)) {
|
||||
$attributes['class'] = join(' ', $class);
|
||||
}
|
||||
|
||||
return $this->linkToText($entity, $linkText, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $entity
|
||||
* @return string
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function linkToMentionAndList($entity)
|
||||
{
|
||||
$attributes = array();
|
||||
|
||||
if (!empty($entity['list_slug'])) {
|
||||
# Replace the list and username
|
||||
$linkText = $entity['screen_name'] . $entity['list_slug'];
|
||||
$class = $this->class_list;
|
||||
$url = $this->url_base_list . $linkText;
|
||||
} else {
|
||||
# Replace the username
|
||||
$linkText = $entity['screen_name'];
|
||||
$class = $this->class_user;
|
||||
$url = $this->url_base_user . $linkText;
|
||||
}
|
||||
if (!empty($class)) {
|
||||
$attributes['class'] = $class;
|
||||
}
|
||||
$attributes['href'] = $url;
|
||||
|
||||
return $this->linkToText($entity, $linkText, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $entity
|
||||
* @param string $tweet
|
||||
* @return string
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function linkToCashtag($entity, $tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$attributes = array();
|
||||
$doller = StringUtils::substr($tweet, $entity['indices'][0], 1);
|
||||
$linkText = $doller . $entity['cashtag'];
|
||||
$attributes['href'] = $this->url_base_cash . $entity['cashtag'];
|
||||
$attributes['title'] = $linkText;
|
||||
if (!empty($this->class_cash)) {
|
||||
$attributes['class'] = $this->class_cash;
|
||||
}
|
||||
|
||||
return $this->linkToText($entity, $linkText, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $entity
|
||||
* @param string $text
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function linkToText(array $entity, $text, $attributes = array())
|
||||
{
|
||||
$rel = array();
|
||||
if ($this->external) {
|
||||
$rel[] = 'external';
|
||||
}
|
||||
if ($this->nofollow) {
|
||||
$rel[] = 'nofollow';
|
||||
}
|
||||
if ($this->noopener) {
|
||||
$rel[] = 'noopener';
|
||||
}
|
||||
if (!empty($rel)) {
|
||||
$attributes['rel'] = join(' ', $rel);
|
||||
}
|
||||
if ($this->target) {
|
||||
$attributes['target'] = $this->target;
|
||||
}
|
||||
$link = '<a';
|
||||
foreach ($attributes as $key => $val) {
|
||||
$link .= ' ' . $key . '="' . $this->escapeHTML($val) . '"';
|
||||
}
|
||||
$link .= '>' . $text . '</a>';
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* html escape
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function escapeHTML($text)
|
||||
{
|
||||
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Nick Pope <nick@nickpope.me.uk>
|
||||
* @copyright Copyright © 2010, Nick Pope
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter.Text
|
||||
*/
|
||||
|
||||
namespace App\Util\Lexer;
|
||||
|
||||
use App\Util\Lexer\Regex;
|
||||
use App\Util\Lexer\StringUtils;
|
||||
|
||||
/**
|
||||
* Twitter HitHighlighter Class
|
||||
*
|
||||
* Performs "hit highlighting" on tweets that have been auto-linked already.
|
||||
* Useful with the results returned from the search API.
|
||||
*
|
||||
* Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
|
||||
* is based on code by {@link http://github.com/mzsanford Matt Sanford} and
|
||||
* heavily modified by {@link http://github.com/ngnpope Nick Pope}.
|
||||
*
|
||||
* @author Nick Pope <nick@nickpope.me.uk>
|
||||
* @copyright Copyright © 2010, Nick Pope
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter.Text
|
||||
*/
|
||||
class HitHighlighter extends Regex
|
||||
{
|
||||
|
||||
/**
|
||||
* The tag to surround hits with.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tag = 'em';
|
||||
|
||||
/**
|
||||
* Provides fluent method chaining.
|
||||
*
|
||||
* @param string $tweet The tweet to be hit highlighted.
|
||||
* @param bool $full_encode Whether to encode all special characters.
|
||||
*
|
||||
* @see __construct()
|
||||
*
|
||||
* @return HitHighlighter
|
||||
*/
|
||||
public static function create($tweet = null, $full_encode = false)
|
||||
{
|
||||
return new self($tweet, $full_encode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a tweet to be parsed and hit highlighted.
|
||||
*
|
||||
* We take this opportunity to ensure that we escape user input.
|
||||
*
|
||||
* @see htmlspecialchars()
|
||||
*
|
||||
* @param string $tweet The tweet to be hit highlighted.
|
||||
* @param bool $escape Whether to escape the tweet (default: true).
|
||||
* @param bool $full_encode Whether to encode all special characters.
|
||||
*/
|
||||
public function __construct($tweet = null, $escape = true, $full_encode = false)
|
||||
{
|
||||
if (!empty($tweet) && $escape) {
|
||||
if ($full_encode) {
|
||||
parent::__construct(htmlentities($tweet, ENT_QUOTES, 'UTF-8', false));
|
||||
} else {
|
||||
parent::__construct(htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false));
|
||||
}
|
||||
} else {
|
||||
parent::__construct($tweet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
||||
*
|
||||
* @return string The tag name.
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the highlighting tag to surround hits with. The default tag is 'em'.
|
||||
*
|
||||
* @param string $v The tag name.
|
||||
*
|
||||
* @return HitHighlighter Fluid method chaining.
|
||||
*/
|
||||
public function setTag($v)
|
||||
{
|
||||
$this->tag = $v;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hit highlights the tweet.
|
||||
*
|
||||
* @param string $tweet The tweet to be hit highlighted.
|
||||
* @param array $hits An array containing the start and end index pairs
|
||||
* for the highlighting.
|
||||
* @param bool $escape Whether to escape the tweet (default: true).
|
||||
* @param bool $full_encode Whether to encode all special characters.
|
||||
*
|
||||
* @return string The hit highlighted tweet.
|
||||
*/
|
||||
public function highlight($tweet = null, array $hits = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
if (empty($hits)) {
|
||||
return $tweet;
|
||||
}
|
||||
$highlightTweet = '';
|
||||
$tags = array('<' . $this->tag . '>', '</' . $this->tag . '>');
|
||||
# Check whether we can simply replace or whether we need to chunk...
|
||||
if (strpos($tweet, '<') === false) {
|
||||
$ti = 0; // tag increment (for added tags)
|
||||
$highlightTweet = $tweet;
|
||||
foreach ($hits as $hit) {
|
||||
$highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[0], $hit[0] + $ti, 0);
|
||||
$ti += StringUtils::strlen($tags[0]);
|
||||
$highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[1], $hit[1] + $ti, 0);
|
||||
$ti += StringUtils::strlen($tags[1]);
|
||||
}
|
||||
} else {
|
||||
$chunks = preg_split('/[<>]/iu', $tweet);
|
||||
$chunk = $chunks[0];
|
||||
$chunk_index = 0;
|
||||
$chunk_cursor = 0;
|
||||
$offset = 0;
|
||||
$start_in_chunk = false;
|
||||
# Flatten the multidimensional hits array:
|
||||
$hits_flat = array();
|
||||
foreach ($hits as $hit) {
|
||||
$hits_flat = array_merge($hits_flat, $hit);
|
||||
}
|
||||
# Loop over the hit indices:
|
||||
for ($index = 0; $index < count($hits_flat); $index++) {
|
||||
$hit = $hits_flat[$index];
|
||||
$tag = $tags[$index % 2];
|
||||
$placed = false;
|
||||
while ($chunk !== null && $hit >= ($i = $offset + StringUtils::strlen($chunk))) {
|
||||
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
|
||||
if ($start_in_chunk && $hit === $i) {
|
||||
$highlightTweet .= $tag;
|
||||
$placed = true;
|
||||
}
|
||||
if (isset($chunks[$chunk_index + 1])) {
|
||||
$highlightTweet .= '<' . $chunks[$chunk_index + 1] . '>';
|
||||
}
|
||||
$offset += StringUtils::strlen($chunk);
|
||||
$chunk_cursor = 0;
|
||||
$chunk_index += 2;
|
||||
$chunk = (isset($chunks[$chunk_index]) ? $chunks[$chunk_index] : null);
|
||||
$start_in_chunk = false;
|
||||
}
|
||||
if (!$placed && $chunk !== null) {
|
||||
$hit_spot = $hit - $offset;
|
||||
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor, $hit_spot - $chunk_cursor) . $tag;
|
||||
$chunk_cursor = $hit_spot;
|
||||
$start_in_chunk = ($index % 2 === 0);
|
||||
$placed = true;
|
||||
}
|
||||
# Ultimate fallback - hits that run off the end get a closing tag:
|
||||
if (!$placed) {
|
||||
$highlightTweet .= $tag;
|
||||
}
|
||||
}
|
||||
if ($chunk !== null) {
|
||||
if ($chunk_cursor < StringUtils::strlen($chunk)) {
|
||||
$highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
|
||||
}
|
||||
for ($index = $chunk_index + 1; $index < count($chunks); $index++) {
|
||||
$highlightTweet .= ($index % 2 === 0 ? $chunks[$index] : '<' . $chunks[$index] . '>');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $highlightTweet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hit highlights the tweet.
|
||||
*
|
||||
* @param array $hits An array containing the start and end index pairs
|
||||
* for the highlighting.
|
||||
*
|
||||
* @return string The hit highlighted tweet.
|
||||
* @deprecated since version 1.1.0
|
||||
*/
|
||||
public function addHitHighlighting(array $hits)
|
||||
{
|
||||
return $this->highlight($this->tweet, $hits);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Takashi Nojima
|
||||
* @copyright Copyright 2014, Takashi Nojima
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter.Text
|
||||
*/
|
||||
|
||||
namespace App\Util\Lexer;
|
||||
|
||||
/**
|
||||
* String utility
|
||||
*
|
||||
* @author Takashi Nojima
|
||||
* @copyright Copyright 2014, Takashi Nojima
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter
|
||||
*/
|
||||
class StringUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* alias of mb_substr
|
||||
*
|
||||
* @param string $str
|
||||
* @param integer $start
|
||||
* @param integer $length
|
||||
* @param string $encoding
|
||||
* @return string
|
||||
*/
|
||||
public static function substr($str, $start, $length = null, $encoding = 'UTF-8')
|
||||
{
|
||||
if (is_null($length)) {
|
||||
// for PHP <= 5.4.7
|
||||
$length = mb_strlen($str, $encoding);
|
||||
}
|
||||
return mb_substr($str, $start, $length, $encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* alias of mb_strlen
|
||||
*
|
||||
* @param string $str
|
||||
* @param string $encoding
|
||||
* @return integer
|
||||
*/
|
||||
public static function strlen($str, $encoding = 'UTF-8')
|
||||
{
|
||||
return mb_strlen($str, $encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* alias of mb_strpos
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @param integer $offset
|
||||
* @param string $encoding
|
||||
* @return integer
|
||||
*/
|
||||
public static function strpos($haystack, $needle, $offset = 0, $encoding = 'UTF-8')
|
||||
{
|
||||
return mb_strpos($haystack, $needle, $offset, $encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* A multibyte-aware substring replacement function.
|
||||
*
|
||||
* @param string $string The string to modify.
|
||||
* @param string $replacement The replacement string.
|
||||
* @param int $start The start of the replacement.
|
||||
* @param int $length The number of characters to replace.
|
||||
* @param string $encoding The encoding of the string.
|
||||
*
|
||||
* @return string The modified string.
|
||||
*
|
||||
* @see http://www.php.net/manual/en/function.substr-replace.php#90146
|
||||
*/
|
||||
public static function substrReplace($string, $replacement, $start, $length = null, $encoding = 'UTF-8')
|
||||
{
|
||||
if (extension_loaded('mbstring') === true) {
|
||||
$string_length = static::strlen($string, $encoding);
|
||||
if ($start < 0) {
|
||||
$start = max(0, $string_length + $start);
|
||||
} elseif ($start > $string_length) {
|
||||
$start = $string_length;
|
||||
}
|
||||
if ($length < 0) {
|
||||
$length = max(0, $string_length - $start + $length);
|
||||
} elseif ((is_null($length) === true) || ($length > $string_length)) {
|
||||
$length = $string_length;
|
||||
}
|
||||
if (($start + $length) > $string_length) {
|
||||
$length = $string_length - $start;
|
||||
}
|
||||
|
||||
$suffixOffset = $start + $length;
|
||||
$suffixLength = $string_length - $start - $length;
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Nick Pope <nick@nickpope.me.uk>
|
||||
* @copyright Copyright © 2010, Nick Pope
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter.Text
|
||||
*/
|
||||
|
||||
namespace App\Util\Lexer;
|
||||
|
||||
use App\Util\Lexer\Regex;
|
||||
use App\Util\Lexer\Extractor;
|
||||
use App\Util\Lexer\StringUtils;
|
||||
|
||||
/**
|
||||
* Twitter Validator Class
|
||||
*
|
||||
* Performs "validation" on tweets.
|
||||
*
|
||||
* Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
|
||||
* is based on code by {@link http://github.com/mzsanford Matt Sanford} and
|
||||
* heavily modified by {@link http://github.com/ngnpope Nick Pope}.
|
||||
*
|
||||
* @author Nick Pope <nick@nickpope.me.uk>
|
||||
* @copyright Copyright © 2010, Nick Pope
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
|
||||
* @package Twitter.Text
|
||||
*/
|
||||
class Validator extends Regex
|
||||
{
|
||||
|
||||
/**
|
||||
* The maximum length of a tweet.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAX_LENGTH = 140;
|
||||
|
||||
/**
|
||||
* The length of a short URL beginning with http:
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $short_url_length = 23;
|
||||
|
||||
/**
|
||||
* The length of a short URL beginning with http:
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $short_url_length_https = 23;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Extractor
|
||||
*/
|
||||
protected $extractor = null;
|
||||
|
||||
/**
|
||||
* Provides fluent method chaining.
|
||||
*
|
||||
* @param string $tweet The tweet to be validated.
|
||||
* @param mixed $config Setup short URL length from Twitter API /help/configuration response.
|
||||
*
|
||||
* @see __construct()
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
public static function create($tweet = null, $config = null)
|
||||
{
|
||||
return new self($tweet, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a tweet to be parsed and validates it.
|
||||
*
|
||||
* @param string $tweet The tweet to validate.
|
||||
*/
|
||||
public function __construct($tweet = null, $config = null)
|
||||
{
|
||||
parent::__construct($tweet);
|
||||
if (!empty($config)) {
|
||||
$this->setConfiguration($config);
|
||||
}
|
||||
$this->extractor = Extractor::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup short URL length from Twitter API /help/configuration response
|
||||
*
|
||||
* @param mixed $config
|
||||
* @return Validator
|
||||
* @link https://dev.twitter.com/docs/api/1/get/help/configuration
|
||||
*/
|
||||
public function setConfiguration($config)
|
||||
{
|
||||
if (is_array($config)) {
|
||||
// setup from array
|
||||
if (isset($config['short_url_length'])) {
|
||||
$this->setShortUrlLength($config['short_url_length']);
|
||||
}
|
||||
if (isset($config['short_url_length_https'])) {
|
||||
$this->setShortUrlLengthHttps($config['short_url_length_https']);
|
||||
}
|
||||
} elseif (is_object($config)) {
|
||||
// setup from object
|
||||
if (isset($config->short_url_length)) {
|
||||
$this->setShortUrlLength($config->short_url_length);
|
||||
}
|
||||
if (isset($config->short_url_length_https)) {
|
||||
$this->setShortUrlLengthHttps($config->short_url_length_https);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the length of a short URL beginning with http:
|
||||
*
|
||||
* @param mixed $length
|
||||
* @return Validator
|
||||
*/
|
||||
public function setShortUrlLength($length)
|
||||
{
|
||||
$this->short_url_length = intval($length);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of a short URL beginning with http:
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShortUrlLength()
|
||||
{
|
||||
return $this->short_url_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the length of a short URL beginning with https:
|
||||
*
|
||||
* @param mixed $length
|
||||
* @return Validator
|
||||
*/
|
||||
public function setShortUrlLengthHttps($length)
|
||||
{
|
||||
$this->short_url_length_https = intval($length);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of a short URL beginning with https:
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShortUrlLengthHttps()
|
||||
{
|
||||
return $this->short_url_length_https;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a tweet is valid.
|
||||
*
|
||||
* @param string $tweet The tweet to validate.
|
||||
* @return boolean Whether the tweet is valid.
|
||||
*/
|
||||
public function isValidTweetText($tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$length = $this->getTweetLength($tweet);
|
||||
if (!$tweet || !$length) {
|
||||
return false;
|
||||
}
|
||||
if ($length > self::MAX_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
if (preg_match(self::$patterns['invalid_characters'], $tweet)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a tweet is valid.
|
||||
*
|
||||
* @return boolean Whether the tweet is valid.
|
||||
* @deprecated since version 1.1.0
|
||||
*/
|
||||
public function validateTweet()
|
||||
{
|
||||
return $this->isValidTweetText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a username is valid.
|
||||
*
|
||||
* @param string $username The username to validate.
|
||||
* @return boolean Whether the username is valid.
|
||||
*/
|
||||
public function isValidUsername($username = null)
|
||||
{
|
||||
if (is_null($username)) {
|
||||
$username = $this->tweet;
|
||||
}
|
||||
$length = StringUtils::strlen($username);
|
||||
if (empty($username) || !$length) {
|
||||
return false;
|
||||
}
|
||||
$extracted = $this->extractor->extractMentionedScreennames($username);
|
||||
return count($extracted) === 1 && $extracted[0] === substr($username, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a username is valid.
|
||||
*
|
||||
* @return boolean Whether the username is valid.
|
||||
* @deprecated since version 1.1.0
|
||||
*/
|
||||
public function validateUsername()
|
||||
{
|
||||
return $this->isValidUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a list is valid.
|
||||
*
|
||||
* @param string $list The list name to validate.
|
||||
* @return boolean Whether the list is valid.
|
||||
*/
|
||||
public function isValidList($list = null)
|
||||
{
|
||||
if (is_null($list)) {
|
||||
$list = $this->tweet;
|
||||
}
|
||||
$length = StringUtils::strlen($list);
|
||||
if (empty($list) || !$length) {
|
||||
return false;
|
||||
}
|
||||
preg_match(self::$patterns['valid_mentions_or_lists'], $list, $matches);
|
||||
$matches = array_pad($matches, 5, '');
|
||||
return isset($matches) && $matches[1] === '' && $matches[4] && !empty($matches[4]) && $matches[5] === '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a list is valid.
|
||||
*
|
||||
* @return boolean Whether the list is valid.
|
||||
* @deprecated since version 1.1.0
|
||||
*/
|
||||
public function validateList()
|
||||
{
|
||||
return $this->isValidList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a hashtag is valid.
|
||||
*
|
||||
* @param string $hashtag The hashtag to validate.
|
||||
* @return boolean Whether the hashtag is valid.
|
||||
*/
|
||||
public function isValidHashtag($hashtag = null)
|
||||
{
|
||||
if (is_null($hashtag)) {
|
||||
$hashtag = $this->tweet;
|
||||
}
|
||||
$length = StringUtils::strlen($hashtag);
|
||||
if (empty($hashtag) || !$length) {
|
||||
return false;
|
||||
}
|
||||
$extracted = $this->extractor->extractHashtags($hashtag);
|
||||
return count($extracted) === 1 && $extracted[0] === substr($hashtag, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a hashtag is valid.
|
||||
*
|
||||
* @return boolean Whether the hashtag is valid.
|
||||
* @deprecated since version 1.1.0
|
||||
*/
|
||||
public function validateHashtag()
|
||||
{
|
||||
return $this->isValidHashtag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a URL is valid.
|
||||
*
|
||||
* @param string $url The url to validate.
|
||||
* @param boolean $unicode_domains Consider the domain to be unicode.
|
||||
* @param boolean $require_protocol Require a protocol for valid domain?
|
||||
*
|
||||
* @return boolean Whether the URL is valid.
|
||||
*/
|
||||
public function isValidURL($url = null, $unicode_domains = true, $require_protocol = true)
|
||||
{
|
||||
if (is_null($url)) {
|
||||
$url = $this->tweet;
|
||||
}
|
||||
$length = StringUtils::strlen($url);
|
||||
if (empty($url) || !$length) {
|
||||
return false;
|
||||
}
|
||||
preg_match(self::$patterns['validate_url_unencoded'], $url, $matches);
|
||||
$match = array_shift($matches);
|
||||
if (!$matches || $match !== $url) {
|
||||
return false;
|
||||
}
|
||||
list($scheme, $authority, $path, $query, $fragment) = array_pad($matches, 5, '');
|
||||
# Check scheme, path, query, fragment:
|
||||
if (($require_protocol && !(
|
||||
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($fragment, self::$patterns['validate_url_fragment'], true)) {
|
||||
return false;
|
||||
}
|
||||
# Check authority:
|
||||
$authority_pattern = $unicode_domains ? 'validate_url_unicode_authority' : 'validate_url_authority';
|
||||
return self::isValidMatch($authority, self::$patterns[$authority_pattern]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a URL is valid.
|
||||
*
|
||||
* @param boolean $unicode_domains Consider the domain to be unicode.
|
||||
* @param boolean $require_protocol Require a protocol for valid domain?
|
||||
*
|
||||
* @return boolean Whether the URL is valid.
|
||||
* @deprecated since version 1.1.0
|
||||
*/
|
||||
public function validateURL($unicode_domains = true, $require_protocol = true)
|
||||
{
|
||||
return $this->isValidURL(null, $unicode_domains, $require_protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the length of a tweet. Takes shortening of URLs into account.
|
||||
*
|
||||
* @param string $tweet The tweet to validate.
|
||||
* @return int the length of a tweet.
|
||||
*/
|
||||
public function getTweetLength($tweet = null)
|
||||
{
|
||||
if (is_null($tweet)) {
|
||||
$tweet = $this->tweet;
|
||||
}
|
||||
$length = StringUtils::strlen($tweet);
|
||||
$urls_with_indices = $this->extractor->extractURLsWithIndices($tweet);
|
||||
foreach ($urls_with_indices as $x) {
|
||||
$length += $x['indices'][0] - $x['indices'][1];
|
||||
$length += stripos($x['url'], 'https://') === 0 ? $this->short_url_length_https : $this->short_url_length;
|
||||
}
|
||||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the length of a tweet. Takes shortening of URLs into account.
|
||||
*
|
||||
* @return int the length of a tweet.
|
||||
* @deprecated since version 1.1.0
|
||||
*/
|
||||
public function getLength()
|
||||
{
|
||||
return $this->getTweetLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to check for a valid match. Used in URL validation.
|
||||
*
|
||||
* @param string $string The subject string to test.
|
||||
* @param string $pattern The pattern to match against.
|
||||
* @param boolean $optional Whether a match is compulsory or not.
|
||||
*
|
||||
* @return boolean Whether an exact match was found.
|
||||
*/
|
||||
protected static function isValidMatch($string, $pattern, $optional = false)
|
||||
{
|
||||
$found = preg_match($pattern, $string, $matches);
|
||||
if (!$optional) {
|
||||
return (($string || $string === '') && $found && $matches[0] === $string);
|
||||
} else {
|
||||
return !(($string || $string === '') && (!$found || $matches[0] !== $string));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
|
||||
/*
|
||||
|----------------------------------------------------------------------
|
||||
| Auto backup mode
|
||||
|----------------------------------------------------------------------
|
||||
|
|
||||
| This value is used when you save your file content. If value is true,
|
||||
| the original file will be backed up before save.
|
||||
*/
|
||||
|
||||
'autoBackup' => true,
|
||||
|
||||
/*
|
||||
|----------------------------------------------------------------------
|
||||
| Backup location
|
||||
|----------------------------------------------------------------------
|
||||
|
|
||||
| This value is used when you backup your file. This value is the sub
|
||||
| path from root folder of project application.
|
||||
*/
|
||||
|
||||
'backupPath' => base_path('storage/dotenv-editor/backups/')
|
||||
|
||||
);
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateWebSubsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('web_subs', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('follower_id')->unsigned()->index();
|
||||
$table->bigInteger('following_id')->unsigned()->index();
|
||||
$table->string('profile_url')->index();
|
||||
$table->timestamp('approved_at')->nullable();
|
||||
$table->unique(['follower_id', 'following_id', 'profile_url']);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('web_subs');
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateImportJobsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('import_jobs', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->bigInteger('profile_id')->unsigned();
|
||||
$table->string('service')->default('instagram');
|
||||
$table->string('uuid')->nullable();
|
||||
$table->string('storage_path')->nullable();
|
||||
$table->tinyInteger('stage')->unsigned()->default(0);
|
||||
$table->text('media_json')->nullable();
|
||||
$table->timestamp('completed_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('import_jobs');
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateMentionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('mentions', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('status_id')->unsigned();
|
||||
$table->bigInteger('profile_id')->unsigned();
|
||||
$table->boolean('local')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('mentions');
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddFiltersToMediaTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('media', function (Blueprint $table) {
|
||||
$table->string('filter_name')->nullable()->after('orientation');
|
||||
$table->string('filter_class')->nullable()->after('filter_name');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('media', function (Blueprint $table) {
|
||||
$table->dropColumn('filter_name');
|
||||
$table->dropColumn('filter_class');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddSoftDeletesToModels extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('avatars', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::table('likes', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::table('media', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::table('mentions', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::table('notifications', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::table('profiles', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::table('statuses', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::table('users', function ($table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateEmailVerificationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('email_verifications', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('user_id')->unsigned();
|
||||
$table->string('email')->nullable();
|
||||
$table->string('user_token')->index();
|
||||
$table->string('random_token')->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('email_verifications');
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateReportCommentsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('report_comments', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->bigInteger('report_id')->unsigned()->index();
|
||||
$table->bigInteger('profile_id')->unsigned();
|
||||
$table->bigInteger('user_id')->unsigned();
|
||||
$table->text('comment');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('report_comments');
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateReportLogsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('report_logs', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->bigInteger('profile_id')->unsigned();
|
||||
$table->bigInteger('item_id')->unsigned()->nullable();
|
||||
$table->string('item_type')->nullable();
|
||||
$table->string('action')->nullable();
|
||||
$table->boolean('system_message')->default(false);
|
||||
$table->json('metadata')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('report_logs');
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateAccountLogsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('account_logs', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('user_id')->unsigned()->index();
|
||||
$table->bigInteger('item_id')->unsigned()->nullable();
|
||||
$table->string('item_type')->nullable();
|
||||
$table->string('action')->nullable();
|
||||
$table->string('message')->nullable();
|
||||
$table->string('link')->nullable();
|
||||
$table->string('ip_address')->nullable();
|
||||
$table->string('user_agent')->nullable();
|
||||
$table->json('metadata')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('account_logs');
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateUserSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('user_settings', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('user_id')->unsigned()->unique();
|
||||
$table->string('role')->default('user');
|
||||
$table->boolean('crawlable')->default(true);
|
||||
$table->boolean('show_guests')->default(true);
|
||||
$table->boolean('show_discover')->default(true);
|
||||
$table->boolean('public_dm')->default(false);
|
||||
$table->boolean('hide_cw_search')->default(true);
|
||||
$table->boolean('hide_blocked_search')->default(true);
|
||||
$table->boolean('always_show_cw')->default(false);
|
||||
$table->boolean('compose_media_descriptions')->default(false);
|
||||
$table->boolean('reduce_motion')->default(false);
|
||||
$table->boolean('optimize_screen_reader')->default(false);
|
||||
$table->boolean('high_contrast_mode')->default(false);
|
||||
$table->boolean('video_autoplay')->default(false);
|
||||
$table->boolean('send_email_new_follower')->default(false);
|
||||
$table->boolean('send_email_new_follower_request')->default(true);
|
||||
$table->boolean('send_email_on_share')->default(false);
|
||||
$table->boolean('send_email_on_like')->default(false);
|
||||
$table->boolean('send_email_on_mention')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_settings');
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class Add2faToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('2fa_enabled')->default(false);
|
||||
$table->string('2fa_secret')->nullable();
|
||||
$table->json('2fa_backup_codes')->nullable();
|
||||
$table->timestamp('2fa_setup_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('2fa_enabled');
|
||||
$table->dropColumn('2fa_secret');
|
||||
$table->dropColumn('2fa_backup_codes');
|
||||
$table->dropColumn('2fa_setup_at');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateUserFiltersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('user_filters', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('user_id')->unsigned()->index();
|
||||
$table->bigInteger('filterable_id')->unsigned();
|
||||
$table->string('filterable_type');
|
||||
$table->string('filter_type')->default('block')->index();
|
||||
$table->unique([
|
||||
'user_id',
|
||||
'filterable_id',
|
||||
'filterable_type',
|
||||
'filter_type'
|
||||
], 'filter_unique');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_filters');
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateStatusTableChangeCaptionToText extends Migration
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
DB::getDoctrineSchemaManager()
|
||||
->getDatabasePlatform()
|
||||
->registerDoctrineTypeMapping('enum', 'string');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('statuses', function ($table) {
|
||||
$table->text('caption')->change();
|
||||
$table->text('rendered')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('user_settings', function (Blueprint $table) {
|
||||
$table->boolean('show_profile_followers')->default(true);
|
||||
$table->boolean('show_profile_follower_count')->default(true);
|
||||
$table->boolean('show_profile_following')->default(true);
|
||||
$table->boolean('show_profile_following_count')->default(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$table->dropColumn('show_profile_followers');
|
||||
$table->dropColumn('show_profile_follower_count');
|
||||
$table->dropColumn('show_profile_following');
|
||||
$table->dropColumn('show_profile_following_count');
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue