mirror of https://github.com/pixelfed/pixelfed
commit
afc758764c
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Circle extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'bcc',
|
||||
'scope',
|
||||
'active'
|
||||
];
|
||||
|
||||
public function members()
|
||||
{
|
||||
return $this->hasManyThrough(
|
||||
Profile::class,
|
||||
CircleProfile::class,
|
||||
'circle_id',
|
||||
'id',
|
||||
'id',
|
||||
'profile_id'
|
||||
);
|
||||
}
|
||||
|
||||
public function owner()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'profile_id');
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
return url("/i/circle/show/{$this->id}");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CircleProfile extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'circle_id',
|
||||
'profile_id'
|
||||
];
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\{Status, StatusHashtag};
|
||||
|
||||
class DiscoverCategory extends Model
|
||||
{
|
||||
protected $fillable = ['slug'];
|
||||
|
||||
public function media()
|
||||
{
|
||||
return $this->belongsTo(Media::class);
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
return url('/discover/c/'.$this->slug);
|
||||
}
|
||||
|
||||
public function editUrl()
|
||||
{
|
||||
return url('/i/admin/discover/category/edit/' . $this->id);
|
||||
}
|
||||
|
||||
public function thumb()
|
||||
{
|
||||
return $this->media->thumb();
|
||||
}
|
||||
|
||||
public function mediaUrl()
|
||||
{
|
||||
return $this->media->url();
|
||||
}
|
||||
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany(DiscoverCategoryHashtag::class, 'discover_category_id');
|
||||
}
|
||||
|
||||
public function hashtags()
|
||||
{
|
||||
return $this->hasManyThrough(
|
||||
Hashtag::class,
|
||||
DiscoverCategoryHashtag::class,
|
||||
'discover_category_id',
|
||||
'id',
|
||||
'id',
|
||||
'hashtag_id'
|
||||
);
|
||||
}
|
||||
|
||||
public function posts()
|
||||
{
|
||||
return Status::select('*')
|
||||
->join('status_hashtags', 'statuses.id', '=', 'status_hashtags.status_id')
|
||||
->join('hashtags', 'status_hashtags.hashtag_id', '=', 'hashtags.id')
|
||||
->join('discover_category_hashtags', 'hashtags.id', '=', 'discover_category_hashtags.hashtag_id')
|
||||
->join('discover_categories', 'discover_category_hashtags.discover_category_id', '=', 'discover_categories.id')
|
||||
->where('discover_categories.id', $this->id);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DiscoverCategoryHashtag extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'discover_category_id',
|
||||
'hashtag_id'
|
||||
];
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class FailedJob extends Model
|
||||
{
|
||||
const CREATED_AT = 'failed_at';
|
||||
const UPDATED_AT = 'failed_at';
|
||||
|
||||
public $timestamps = 'failed_at';
|
||||
|
||||
public function getFailedAtAttribute($val)
|
||||
{
|
||||
return Carbon::parse($val);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use DB, Cache;
|
||||
use App\{
|
||||
DiscoverCategory,
|
||||
DiscoverCategoryHashtag,
|
||||
Hashtag,
|
||||
Media,
|
||||
Profile,
|
||||
StatusHashtag
|
||||
};
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
trait AdminDiscoverController
|
||||
{
|
||||
public function discoverHome()
|
||||
{
|
||||
$categories = DiscoverCategory::orderByDesc('id')->paginate(10);
|
||||
return view('admin.discover.home', compact('categories'));
|
||||
}
|
||||
|
||||
public function discoverCreateCategory()
|
||||
{
|
||||
return view('admin.discover.create-category');
|
||||
}
|
||||
|
||||
public function discoverCreateCategoryStore(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|min:1',
|
||||
'active' => 'required|boolean',
|
||||
'media' => 'nullable|integer|min:1'
|
||||
]);
|
||||
|
||||
$name = $request->input('name');
|
||||
$slug = str_slug($name);
|
||||
$active = $request->input('active');
|
||||
$media = (int) $request->input('media');
|
||||
|
||||
$media = Media::findOrFail($media);
|
||||
|
||||
$category = DiscoverCategory::firstOrNew(['slug' => $slug]);
|
||||
$category->name = $name;
|
||||
$category->active = $active;
|
||||
$category->media_id = $media->id;
|
||||
$category->save();
|
||||
return $category;
|
||||
}
|
||||
|
||||
public function discoverCategoryEdit(Request $request, $id)
|
||||
{
|
||||
$category = DiscoverCategory::findOrFail($id);
|
||||
return view('admin.discover.show', compact('category'));
|
||||
}
|
||||
|
||||
public function discoverCategoryUpdate(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|min:1',
|
||||
'active' => 'required|boolean',
|
||||
'media' => 'nullable|integer|min:1',
|
||||
'hashtags' => 'nullable|string'
|
||||
]);
|
||||
$name = $request->input('name');
|
||||
$slug = str_slug($name);
|
||||
$active = $request->input('active');
|
||||
$media = (int) $request->input('media');
|
||||
$media = Media::findOrFail($media);
|
||||
|
||||
$category = DiscoverCategory::findOrFail($id);
|
||||
$category->name = $name;
|
||||
$category->active = $active;
|
||||
$category->media_id = $media->id;
|
||||
$category->save();
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
public function discoveryCategoryTagStore(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'category_id' => 'required|integer|min:1',
|
||||
'hashtag' => 'required|string',
|
||||
'action' => 'required|string|min:1|max:6'
|
||||
]);
|
||||
$category_id = $request->input('category_id');
|
||||
$category = DiscoverCategory::findOrFail($category_id);
|
||||
$hashtag = Hashtag::whereName($request->input('hashtag'))->firstOrFail();
|
||||
|
||||
$tag = DiscoverCategoryHashtag::firstOrCreate([
|
||||
'hashtag_id' => $hashtag->id,
|
||||
'discover_category_id' => $category->id
|
||||
]);
|
||||
|
||||
if($request->input('action') == 'delete') {
|
||||
$tag->delete();
|
||||
return [];
|
||||
}
|
||||
return $tag;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use DB, Cache;
|
||||
use App\{Instance, Profile};
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
trait AdminInstanceController
|
||||
{
|
||||
|
||||
public function instances(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'filter' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:20',
|
||||
Rule::in(['autocw', 'unlisted', 'banned'])
|
||||
],
|
||||
]);
|
||||
if($request->has('filter') && $request->filled('filter')) {
|
||||
switch ($request->filter) {
|
||||
case 'autocw':
|
||||
$instances = Instance::whereAutoCw(true)->orderByDesc('id')->paginate(5);
|
||||
break;
|
||||
case 'unlisted':
|
||||
$instances = Instance::whereUnlisted(true)->orderByDesc('id')->paginate(5);
|
||||
break;
|
||||
case 'banned':
|
||||
$instances = Instance::whereBanned(true)->orderByDesc('id')->paginate(5);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$instances = Instance::orderByDesc('id')->paginate(5);
|
||||
}
|
||||
return view('admin.instances.home', compact('instances'));
|
||||
}
|
||||
|
||||
public function instanceScan(Request $request)
|
||||
{
|
||||
DB::transaction(function() {
|
||||
Profile::whereNotNull('domain')
|
||||
->groupBy('domain')
|
||||
->chunk(50, function($domains) {
|
||||
foreach($domains as $domain) {
|
||||
Instance::firstOrCreate([
|
||||
'domain' => $domain->domain
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function instanceShow(Request $request, $id)
|
||||
{
|
||||
$instance = Instance::findOrFail($id);
|
||||
return view('admin.instances.show', compact('instance'));
|
||||
}
|
||||
|
||||
public function instanceEdit(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:20',
|
||||
Rule::in(['autocw', 'unlist', 'ban'])
|
||||
],
|
||||
]);
|
||||
|
||||
$instance = Instance::findOrFail($id);
|
||||
$unlisted = $instance->unlisted;
|
||||
$autocw = $instance->auto_cw;
|
||||
$banned = $instance->banned;
|
||||
|
||||
switch ($request->action) {
|
||||
case 'autocw':
|
||||
$instance->auto_cw = $autocw == true ? false : true;
|
||||
$instance->save();
|
||||
break;
|
||||
|
||||
case 'unlist':
|
||||
$instance->unlisted = $unlisted == true ? false : true;
|
||||
$instance->save();
|
||||
break;
|
||||
|
||||
case 'ban':
|
||||
$instance->banned = $banned == true ? false : true;
|
||||
$instance->save();
|
||||
break;
|
||||
}
|
||||
|
||||
return response()->json([]);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use DB, Cache;
|
||||
use App\{
|
||||
Media,
|
||||
Profile,
|
||||
Status
|
||||
};
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
trait AdminMediaController
|
||||
{
|
||||
public function media(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'layout' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:4',
|
||||
Rule::in(['grid','list'])
|
||||
],
|
||||
'search' => 'nullable|string|min:1|max:20'
|
||||
]);
|
||||
if($request->filled('search')) {
|
||||
$profiles = Profile::where('username', 'like', '%'.$request->input('search').'%')->pluck('id')->toArray();
|
||||
$media = Media::whereHas('status')
|
||||
->with('status')
|
||||
->orderby('id', 'desc')
|
||||
->whereIn('profile_id', $profiles)
|
||||
->orWhere('mime', $request->input('search'))
|
||||
->paginate(12);
|
||||
} else {
|
||||
$media = Media::whereHas('status')->with('status')->orderby('id', 'desc')->paginate(12);
|
||||
}
|
||||
return view('admin.media.home', compact('media'));
|
||||
}
|
||||
|
||||
public function mediaShow(Request $request, $id)
|
||||
{
|
||||
$media = Media::findOrFail($id);
|
||||
return view('admin.media.show', compact('media'));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Artisan, Cache, DB;
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
use App\{Comment, Like, Media, Page, Profile, Report, Status, User};
|
||||
use App\Http\Controllers\Controller;
|
||||
use Jackiedo\DotenvEditor\Facades\DotenvEditor;
|
||||
use App\Util\Lexer\PrettyNumber;
|
||||
|
||||
trait AdminSettingsController
|
||||
{
|
||||
public function settings(Request $request)
|
||||
{
|
||||
return view('admin.settings.home');
|
||||
}
|
||||
|
||||
public function settingsBackups(Request $request)
|
||||
{
|
||||
$path = storage_path('app/PixelFed');
|
||||
$files = new \DirectoryIterator($path);
|
||||
return view('admin.settings.backups', compact('files'));
|
||||
}
|
||||
|
||||
public function settingsConfig(Request $request, DotenvEditor $editor)
|
||||
{
|
||||
return view('admin.settings.config', compact('editor'));
|
||||
}
|
||||
|
||||
public function settingsMaintenance(Request $request)
|
||||
{
|
||||
return view('admin.settings.maintenance');
|
||||
}
|
||||
|
||||
public function settingsStorage(Request $request)
|
||||
{
|
||||
$databaseSum = Cache::remember('admin:settings:storage:db:storageUsed', 360, function() {
|
||||
$q = 'SELECT sum(ROUND(((data_length + index_length)), 0)) AS size FROM information_schema.TABLES WHERE table_schema = ?';
|
||||
$db = config('database.default');
|
||||
$db = config("database.connections.{$db}.database");
|
||||
return DB::select($q, [$db])[0]->size;
|
||||
});
|
||||
$mediaSum = Cache::remember('admin:settings:storage:media:storageUsed', 360, function() {
|
||||
return Media::sum('size');
|
||||
});
|
||||
$backupSum = Cache::remember('admin:settings:storage:backups:storageUsed', 360, function() {
|
||||
$dir = storage_path('app/'.config('app.name'));
|
||||
$size = 0;
|
||||
foreach (glob(rtrim($dir, '/').'/*', GLOB_NOSORT) as $each) {
|
||||
$size += is_file($each) ? filesize($each) : folderSize($each);
|
||||
}
|
||||
return $size;
|
||||
});
|
||||
$storage = new \StdClass;
|
||||
$storage->total = disk_total_space(base_path());
|
||||
$storage->free = disk_free_space(base_path());
|
||||
$storage->prettyTotal = PrettyNumber::size($storage->total, false, false);
|
||||
$storage->prettyFree = PrettyNumber::size($storage->free, false, false);
|
||||
$storage->percentFree = ceil($storage->free / $storage->total * 100);
|
||||
$storage->percentUsed = ceil(100 - $storage->percentFree);
|
||||
$storage->media = [
|
||||
'used' => $mediaSum,
|
||||
'prettyUsed' => PrettyNumber::size($mediaSum),
|
||||
'percentUsed' => ceil($mediaSum / $storage->total * 100)
|
||||
];
|
||||
$storage->backups = [
|
||||
'used' => $backupSum
|
||||
];
|
||||
$storage->database = [
|
||||
'used' => $databaseSum
|
||||
];
|
||||
return view('admin.settings.storage', compact('storage'));
|
||||
}
|
||||
|
||||
public function settingsFeatures(Request $request)
|
||||
{
|
||||
return view('admin.settings.features');
|
||||
}
|
||||
|
||||
public function settingsHomeStore(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'APP_NAME' => 'required|string',
|
||||
]);
|
||||
Artisan::call('config:clear');
|
||||
DotenvEditor::setKey('APP_NAME', $request->input('APP_NAME'));
|
||||
DotenvEditor::save();
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function settingsPages(Request $request)
|
||||
{
|
||||
$pages = Page::orderByDesc('updated_at')->paginate(10);
|
||||
return view('admin.pages.home', compact('pages'));
|
||||
}
|
||||
|
||||
public function settingsPageEdit(Request $request)
|
||||
{
|
||||
return view('admin.pages.edit');
|
||||
}
|
||||
|
||||
public function settingsSystem(Request $request)
|
||||
{
|
||||
$sys = [
|
||||
'pixelfed' => config('pixelfed.version'),
|
||||
'mysql' => DB::select( DB::raw("select version()") )[0]->{'version()'},
|
||||
'php' => phpversion(),
|
||||
'redis' => explode(' ',exec('redis-cli -v'))[1],
|
||||
];
|
||||
return view('admin.settings.system', compact('sys'));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Auth;
|
||||
use App\{
|
||||
Circle,
|
||||
CircleProfile,
|
||||
Profile,
|
||||
Status,
|
||||
};
|
||||
|
||||
class CircleController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function home(Request $request)
|
||||
{
|
||||
$circles = Circle::whereProfileId(Auth::user()->profile->id)
|
||||
->orderByDesc('created_at')
|
||||
->paginate(10);
|
||||
return view('account.circles.home', compact('circles'));
|
||||
}
|
||||
|
||||
public function create(Request $request)
|
||||
{
|
||||
return view('account.circles.create');
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|min:1',
|
||||
'description' => 'nullable|string|max:255',
|
||||
'scope' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::in([
|
||||
'public',
|
||||
'private',
|
||||
'unlisted',
|
||||
'exclusive'
|
||||
])
|
||||
],
|
||||
]);
|
||||
|
||||
$circle = Circle::firstOrCreate([
|
||||
'profile_id' => Auth::user()->profile->id,
|
||||
'name' => $request->input('name')
|
||||
], [
|
||||
'description' => $request->input('description'),
|
||||
'scope' => $request->input('scope'),
|
||||
'active' => false
|
||||
]);
|
||||
|
||||
return redirect(route('account.circles'));
|
||||
}
|
||||
|
||||
public function show(Request $request, $id)
|
||||
{
|
||||
$circle = Circle::findOrFail($id);
|
||||
return view('account.circles.show', compact('circle'));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CircleProfileController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DeckController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function home()
|
||||
{
|
||||
return view('deck.index');
|
||||
}
|
||||
|
||||
|
||||
public function insights()
|
||||
{
|
||||
return view('deck.insights.index');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DiscoverCategoryController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DiscoverCategoryHashtagController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\{
|
||||
Profile,
|
||||
Status,
|
||||
};
|
||||
use Auth, DB, Purify;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class MicroController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function composeText(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'type' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::in(['text'])
|
||||
],
|
||||
'title' => 'nullable|string|max:140',
|
||||
'content' => 'required|string|max:500',
|
||||
'visibility' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::in([
|
||||
'public',
|
||||
'unlisted',
|
||||
'private',
|
||||
'draft'
|
||||
])
|
||||
]
|
||||
]);
|
||||
$profile = Auth::user()->profile;
|
||||
$title = $request->input('title');
|
||||
$content = $request->input('content');
|
||||
$visibility = $request->input('visibility');
|
||||
|
||||
$status = DB::transaction(function() use($profile, $content, $visibility, $title) {
|
||||
$status = new Status;
|
||||
$status->type = 'text';
|
||||
$status->profile_id = $profile->id;
|
||||
$status->caption = strip_tags($content);
|
||||
$status->rendered = Purify::clean($content);
|
||||
$status->is_nsfw = false;
|
||||
|
||||
// TODO: remove deprecated visibility in favor of scope
|
||||
$status->visibility = $visibility;
|
||||
$status->scope = $visibility;
|
||||
$status->entities = json_encode(['title'=>$title]);
|
||||
$status->save();
|
||||
return $status;
|
||||
});
|
||||
|
||||
$fractal = new \League\Fractal\Manager();
|
||||
$fractal->setSerializer(new \League\Fractal\Serializer\ArraySerializer());
|
||||
$s = new \League\Fractal\Resource\Item($status, new \App\Transformer\Api\StatusTransformer());
|
||||
return $fractal->createData($s)->toArray();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Auth;
|
||||
use App\Page;
|
||||
|
||||
class PageController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(['auth', 'admin']);
|
||||
}
|
||||
|
||||
protected function authCheck($admin_only = false)
|
||||
{
|
||||
$auth = $admin_only ?
|
||||
Auth::check() && Auth::user()->is_admin == true :
|
||||
Auth::check();
|
||||
if($auth == false) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
public function edit(Request $request)
|
||||
{
|
||||
$this->authCheck(true);
|
||||
$this->validate($request, [
|
||||
'page' => 'required|string'
|
||||
]);
|
||||
$slug = urldecode($request->page);
|
||||
$page = Page::firstOrCreate(['slug' => $slug]);
|
||||
return view('admin.pages.edit', compact('page'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'slug' => 'required|string',
|
||||
'content' => 'required|string',
|
||||
'title' => 'nullable|string',
|
||||
'active' => 'required|boolean'
|
||||
]);
|
||||
$slug = urldecode($request->input('slug'));
|
||||
$page = Page::firstOrCreate(['slug' => $slug]);
|
||||
$page->content = $request->input('content');
|
||||
$page->title = $request->input('title');
|
||||
$page->active = (bool) $request->input('active');
|
||||
$page->save();
|
||||
return response()->json(['msg' => 200]);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Avatar;
|
||||
use App\Profile;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class ImportAvatar implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $url;
|
||||
protected $profile;
|
||||
|
||||
/**
|
||||
* Delete the job if its models no longer exist.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($url, Profile $profile)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->profile = $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$url = $this->url;
|
||||
$profile = $this->profile;
|
||||
|
||||
$basePath = $this->buildPath();
|
||||
}
|
||||
|
||||
public function buildPath()
|
||||
{
|
||||
$baseDir = storage_path('app/public/avatars');
|
||||
if (!is_dir($baseDir)) {
|
||||
mkdir($baseDir);
|
||||
}
|
||||
|
||||
$prefix = $this->profile->id;
|
||||
$padded = str_pad($prefix, 12, 0, STR_PAD_LEFT);
|
||||
$parts = str_split($padded, 3);
|
||||
foreach ($parts as $k => $part) {
|
||||
if ($k == 0) {
|
||||
$prefix = storage_path('app/public/avatars/'.$parts[0]);
|
||||
if (!is_dir($prefix)) {
|
||||
mkdir($prefix);
|
||||
}
|
||||
}
|
||||
if ($k == 1) {
|
||||
$prefix = storage_path('app/public/avatars/'.$parts[0].'/'.$parts[1]);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
$dir = storage_path('app/'.$avatarpath);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir);
|
||||
}
|
||||
$path = $avatarpath.'/avatar.svg';
|
||||
return storage_path('app/'.$path);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class OauthClient extends Model
|
||||
{
|
||||
|
||||
protected $table = 'oauth_clients';
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Page extends Model
|
||||
{
|
||||
const SLUG_ROOT = [
|
||||
'site',
|
||||
'page'
|
||||
];
|
||||
|
||||
protected $fillable = ['slug'];
|
||||
|
||||
public function url()
|
||||
{
|
||||
return url($this->slug);
|
||||
}
|
||||
|
||||
public function editUrl()
|
||||
{
|
||||
return url("/i/admin/settings/pages/edit?page=".urlencode($this->slug));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Storage;
|
||||
|
||||
class StoryItem extends Model
|
||||
{
|
||||
public function story()
|
||||
{
|
||||
return $this->belongsTo(Story::class);
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
return Storage::url($this->media_path);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class StoryView extends Model
|
||||
{
|
||||
public function story()
|
||||
{
|
||||
return $this->belongsTo(Story::class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\StoryItem;
|
||||
use League\Fractal;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class StoryItemTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
|
||||
public function transform(StoryItem $item)
|
||||
{
|
||||
return [
|
||||
'id' => (string) Str::uuid(),
|
||||
'type' => $item->type,
|
||||
'length' => $item->duration,
|
||||
'src' => $item->url(),
|
||||
'preview' => null,
|
||||
'link' => null,
|
||||
'linkText' => null,
|
||||
'time' => $item->updated_at->format('U'),
|
||||
'seen' => $item->story->seen(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Transformer\Api;
|
||||
|
||||
use App\Story;
|
||||
use League\Fractal;
|
||||
|
||||
class StoryTransformer extends Fractal\TransformerAbstract
|
||||
{
|
||||
protected $defaultIncludes = [
|
||||
'items',
|
||||
];
|
||||
|
||||
public function transform(Story $story)
|
||||
{
|
||||
return [
|
||||
'id' => (string) $story->id,
|
||||
'photo' => $story->profile->avatarUrl(),
|
||||
'name' => '',
|
||||
'link' => '',
|
||||
'lastUpdated' => $story->updated_at->format('U'),
|
||||
'seen' => $story->seen(),
|
||||
'items' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function includeItems(Story $story)
|
||||
{
|
||||
$items = $story->items;
|
||||
|
||||
return $this->collection($items, new StoryItemTransformer());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Util\ActivityPub\Validator;
|
||||
|
||||
use Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class Follow {
|
||||
|
||||
public static function validate($payload)
|
||||
{
|
||||
$valid = Validator::make($payload, [
|
||||
'@context' => 'required',
|
||||
'id' => 'required|string',
|
||||
'type' => [
|
||||
'required',
|
||||
Rule::in(['Follow'])
|
||||
],
|
||||
'actor' => 'required|url|active_url',
|
||||
'object' => 'required|url|active_url'
|
||||
])->passes();
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Util\ActivityPub\Validator;
|
||||
|
||||
use Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class Like {
|
||||
|
||||
public static function validate($payload)
|
||||
{
|
||||
$valid = Validator::make($payload, [
|
||||
'@context' => 'required',
|
||||
'id' => 'required|string',
|
||||
'type' => [
|
||||
'required',
|
||||
Rule::in(['Like'])
|
||||
],
|
||||
'actor' => 'required|url|active_url',
|
||||
'object' => 'required|url|active_url'
|
||||
])->passes();
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class Stories extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('story_items', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('story_id')->unsigned()->index();
|
||||
$table->string('media_path')->nullable();
|
||||
$table->string('media_url')->nullable();
|
||||
$table->tinyInteger('duration')->unsigned();
|
||||
$table->string('filter')->nullable();
|
||||
$table->string('link_url')->nullable()->index();
|
||||
$table->string('link_text')->nullable();
|
||||
$table->tinyInteger('order')->unsigned()->nullable();
|
||||
$table->string('type')->default('photo');
|
||||
$table->json('layers')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('story_views', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('story_id')->unsigned()->index();
|
||||
$table->bigInteger('profile_id')->unsigned()->index();
|
||||
$table->unique(['story_id', 'profile_id']);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('stories', function (Blueprint $table) {
|
||||
$table->string('title')->nullable()->after('profile_id');
|
||||
$table->boolean('preview_photo')->default(false)->after('title');
|
||||
$table->boolean('local_only')->default(false)->after('preview_photo');
|
||||
$table->boolean('is_live')->default(false)->after('local_only');
|
||||
$table->string('broadcast_url')->nullable()->after('is_live');
|
||||
$table->string('broadcast_key')->nullable()->after('broadcast_url');
|
||||
});
|
||||
|
||||
Schema::table('story_reactions', function (Blueprint $table) {
|
||||
$table->bigInteger('story_id')->unsigned()->index()->after('profile_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('story_items');
|
||||
Schema::dropIfExists('story_views');
|
||||
|
||||
Schema::table('stories', function (Blueprint $table) {
|
||||
$table->dropColumn('title');
|
||||
$table->dropColumn('preview_photo');
|
||||
$table->dropColumn('local_only');
|
||||
$table->dropColumn('is_live');
|
||||
$table->dropColumn('broadcast_url');
|
||||
$table->dropColumn('broadcast_key');
|
||||
});
|
||||
|
||||
Schema::table('story_reactions', function (Blueprint $table) {
|
||||
$table->dropColumn('story_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('pages', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('root')->nullable()->index();
|
||||
$table->string('slug')->nullable()->unique()->index();
|
||||
$table->string('title')->nullable();
|
||||
$table->unsignedInteger('category_id')->nullable()->index();
|
||||
$table->longText('content')->nullable();
|
||||
$table->string('template')->default('layouts.app')->index();
|
||||
$table->boolean('active')->default(false)->index();
|
||||
$table->boolean('cached')->default(true)->index();
|
||||
$table->timestamp('active_until')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('pages');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddRemoteToAvatarsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('avatars', function (Blueprint $table) {
|
||||
$table->string('remote_url')->nullable()->index()->after('thumb_path');
|
||||
$table->timestamp('last_fetched_at')->nullable()->after('change_count');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('avatars', function (Blueprint $table) {
|
||||
$table->dropColumn('remote_url');
|
||||
$table->dropColumn('last_fetched_at');
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue