mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-13 01:54:30 +00:00
commit
afc758764c
198 changed files with 7395 additions and 8839 deletions
11
.env.example
11
.env.example
|
@ -53,3 +53,14 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
MIX_APP_URL="${APP_URL}"
|
MIX_APP_URL="${APP_URL}"
|
||||||
MIX_API_BASE="${API_BASE}"
|
MIX_API_BASE="${API_BASE}"
|
||||||
MIX_API_SEARCH="${API_SEARCH}"
|
MIX_API_SEARCH="${API_SEARCH}"
|
||||||
|
|
||||||
|
ACTIVITYPUB_INBOX=false
|
||||||
|
ACTIVITYPUB_SHAREDINBOX=false
|
||||||
|
|
||||||
|
# Set these both "true" to enable federation.
|
||||||
|
# You might need to also run:
|
||||||
|
# php artisan cache:clear
|
||||||
|
# php artisan optimize:clear
|
||||||
|
# php artisan optimize
|
||||||
|
ACTIVITY_PUB=false
|
||||||
|
REMOTE_FOLLOW=false
|
||||||
|
|
|
@ -40,7 +40,7 @@ SESSION_SECURE_COOKIE=true
|
||||||
API_BASE="/api/1/"
|
API_BASE="/api/1/"
|
||||||
API_SEARCH="/api/search"
|
API_SEARCH="/api/search"
|
||||||
|
|
||||||
OPEN_REGISTRATION=true
|
OPEN_REGISTRATION=false
|
||||||
RECAPTCHA_ENABLED=false
|
RECAPTCHA_ENABLED=false
|
||||||
ENFORCE_EMAIL_VERIFICATION=true
|
ENFORCE_EMAIL_VERIFICATION=true
|
||||||
|
|
||||||
|
@ -55,3 +55,4 @@ MIX_API_BASE="${API_BASE}"
|
||||||
MIX_API_SEARCH="${API_SEARCH}"
|
MIX_API_SEARCH="${API_SEARCH}"
|
||||||
|
|
||||||
TELESCOPE_ENABLED=false
|
TELESCOPE_ENABLED=false
|
||||||
|
PF_MAX_USERS=1000
|
||||||
|
|
103
README.md
103
README.md
|
@ -1,66 +1,27 @@
|
||||||
# PixelFed: Federated Image Sharing
|
<p align="center"><img src="https://pixelfed.nyc3.cdn.digitaloceanspaces.com/logos/pixelfed-full-color.svg" width="300px"></p>
|
||||||
[![Backers on Open Collective](https://opencollective.com/pixelfed-528/backers/badge.svg)](#backers)
|
|
||||||
[![Sponsors on Open Collective](https://opencollective.com/pixelfed-528/sponsors/badge.svg)](#sponsors)
|
|
||||||
|
|
||||||
PixelFed is a federated social image sharing platform, similar to Instagram.
|
<p align="center">
|
||||||
Federation is done using the [ActivityPub](https://activitypub.rocks/) protocol,
|
<a href="https://circleci.com/gh/pixelfed/pixelfed"><img src="https://circleci.com/gh/pixelfed/pixelfed.svg?style=svg" alt="Build Status"></a>
|
||||||
which is used by [Mastodon](http://joinmastodon.org/), [PeerTube](https://joinpeertube.org/en/),
|
<a href="https://packagist.org/packages/pixelfed/pixelfed"><img src="https://poser.pugx.org/pixelfed/pixelfed/d/total.svg" alt="Total Downloads"></a>
|
||||||
[Pleroma](https://pleroma.social/), and more. Through ActivityPub PixelFed can share
|
<a href="https://packagist.org/packages/pixelfed/pixelfed"><img src="https://poser.pugx.org/pixelfed/pixelfed/v/stable.svg" alt="Latest Stable Version"></a>
|
||||||
and interact with these platforms, as well as other instances of PixelFed.
|
<a href="https://packagist.org/packages/pixelfed/pixelfed"><img src="https://poser.pugx.org/pixelfed/pixelfed/license.svg" alt="License"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
**_Please note this is alpha software, not recommended for production use,
|
## Introduction
|
||||||
and federation is not supported yet._**
|
|
||||||
|
|
||||||
PixelFed is very early into the development stage. If you would like to have a
|
A free and ethical photo sharing platform, powered by ActivityPub federation.
|
||||||
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
|
<p align="center">
|
||||||
- PHP >= 7.1.3 < 7.3 (7.2.x recommended for stable version)
|
<img src="https://pixelfed.nyc3.cdn.digitaloceanspaces.com/media/Screen%20Shot%202019-02-05%20at%206.34.59%20PM.png">
|
||||||
- MySQL >= 5.7 (Postgres, MariaDB and sqlite are not supported)
|
</p>
|
||||||
- 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
|
|
||||||
- BCMath PHP Extension
|
|
||||||
- JpegOptim
|
|
||||||
- Optipng
|
|
||||||
- Pngquant 2
|
|
||||||
- SVGO
|
|
||||||
- Gifsicle
|
|
||||||
|
|
||||||
## Installation
|
## Official Documentation
|
||||||
|
|
||||||
This guide assumes you have NGINX/Apache installed, along with the dependencies.
|
Documentation for Pixelfed can be found on the [Pixelfed documentation website](https://pixelfed.github.io/docs/master/).
|
||||||
Those will not be covered in these early docs.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/pixelfed/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
|
|
||||||
```
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Pixelfed is open-sourced software licensed under the AGPL license.
|
||||||
|
|
||||||
## Communication
|
## Communication
|
||||||
|
|
||||||
|
@ -68,7 +29,7 @@ The ways you can communicate on the project are below. Before interacting, pleas
|
||||||
read through the [Code Of Conduct](CODE_OF_CONDUCT.md).
|
read through the [Code Of Conduct](CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
* IRC: #pixelfed on irc.freenode.net ([#freenode_#pixelfed:matrix.org through
|
* IRC: #pixelfed on irc.freenode.net ([#freenode_#pixelfed:matrix.org through
|
||||||
Matrix](https://matrix.to/#/#freenode_#pixelfed:matrix.org)
|
Matrix](https://matrix.to/#/#freenode_#pixelfed:matrix.org))
|
||||||
* Project on Mastodon: [@pixelfed@mastodon.social](https://mastodon.social/@pixelfed)
|
* Project on Mastodon: [@pixelfed@mastodon.social](https://mastodon.social/@pixelfed)
|
||||||
* E-mail: [hello@pixelfed.org](mailto:hello@pixelfed.org)
|
* E-mail: [hello@pixelfed.org](mailto:hello@pixelfed.org)
|
||||||
|
|
||||||
|
@ -80,29 +41,27 @@ https://www.patreon.com/dansup
|
||||||
### Contributors
|
### Contributors
|
||||||
|
|
||||||
This project exists thanks to all the people who contribute.
|
This project exists thanks to all the people who contribute.
|
||||||
<a href="https://github.com/pixelfed/pixelfed/graphs/contributors"><img src="https://opencollective.com/pixelfed-528/contributors.svg?width=890&button=false" /></a>
|
<a href="https://github.com/pixelfed/pixelfed/graphs/contributors"><img src="https://opencollective.com/pixelfed/contributors.svg?width=890&button=false" /></a>
|
||||||
|
|
||||||
|
|
||||||
### Backers
|
### Backers
|
||||||
|
|
||||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/pixelfed-528#backer)]
|
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/pixelfed#backer)]
|
||||||
|
|
||||||
<a href="https://opencollective.com/pixelfed-528#backers" target="_blank"><img src="https://opencollective.com/pixelfed-528/backers.svg?width=890"></a>
|
<a href="https://opencollective.com/pixelfed#backers" target="_blank"><img src="https://opencollective.com/pixelfed/backers.svg?width=890"></a>
|
||||||
|
|
||||||
|
|
||||||
### Sponsors
|
### Sponsors
|
||||||
|
|
||||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/pixelfed-528#sponsor)]
|
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/pixelfed#sponsor)]
|
||||||
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/0/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/1/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/2/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/3/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/4/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/5/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/6/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/7/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/8/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/pixelfed-528/sponsor/9/website" target="_blank"><img src="https://opencollective.com/pixelfed-528/sponsor/9/avatar.svg"></a>
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/0/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/0/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/1/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/1/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/2/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/2/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/3/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/3/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/4/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/4/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/5/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/5/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/6/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/6/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/7/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/7/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/8/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/8/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/pixelfed/sponsor/9/website" target="_blank"><img src="https://opencollective.com/pixelfed/sponsor/9/avatar.svg"></a>
|
|
@ -6,5 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class AccountLog extends Model
|
class AccountLog extends Model
|
||||||
{
|
{
|
||||||
//
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,4 +7,14 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
class Activity extends Model
|
class Activity extends Model
|
||||||
{
|
{
|
||||||
protected $dates = ['processed_at'];
|
protected $dates = ['processed_at'];
|
||||||
|
|
||||||
|
public function toProfile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class, 'to_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fromProfile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class, 'from_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,16 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Bookmark extends Model
|
class Bookmark extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = ['profile_id', 'status_id'];
|
protected $fillable = ['profile_id', 'status_id'];
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Status::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function profile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
38
app/Circle.php
Normal file
38
app/Circle.php
Normal file
|
@ -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}");
|
||||||
|
}
|
||||||
|
}
|
13
app/CircleProfile.php
Normal file
13
app/CircleProfile.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class CircleProfile extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'circle_id',
|
||||||
|
'profile_id'
|
||||||
|
];
|
||||||
|
}
|
|
@ -6,5 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Collection extends Model
|
class Collection extends Model
|
||||||
{
|
{
|
||||||
//
|
public function profile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class CollectionItem extends Model
|
class CollectionItem extends Model
|
||||||
{
|
{
|
||||||
//
|
public function collection()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Collection::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
64
app/DiscoverCategory.php
Normal file
64
app/DiscoverCategory.php
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
13
app/DiscoverCategoryHashtag.php
Normal file
13
app/DiscoverCategoryHashtag.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class DiscoverCategoryHashtag extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'discover_category_id',
|
||||||
|
'hashtag_id'
|
||||||
|
];
|
||||||
|
}
|
|
@ -13,4 +13,9 @@ class EmailVerification extends Model
|
||||||
|
|
||||||
return "{$base}{$path}";
|
return "{$base}{$path}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
19
app/FailedJob.php
Normal file
19
app/FailedJob.php
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
105
app/Http/Controllers/Admin/AdminDiscoverController.php
Normal file
105
app/Http/Controllers/Admin/AdminDiscoverController.php
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
101
app/Http/Controllers/Admin/AdminInstanceController.php
Normal file
101
app/Http/Controllers/Admin/AdminInstanceController.php
Normal file
|
@ -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([]);
|
||||||
|
}
|
||||||
|
}
|
48
app/Http/Controllers/Admin/AdminMediaController.php
Normal file
48
app/Http/Controllers/Admin/AdminMediaController.php
Normal file
|
@ -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'));
|
||||||
|
}
|
||||||
|
}
|
114
app/Http/Controllers/Admin/AdminSettingsController.php
Normal file
114
app/Http/Controllers/Admin/AdminSettingsController.php
Normal file
|
@ -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'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,21 +2,38 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Media;
|
use App\{
|
||||||
use App\Like;
|
FailedJob,
|
||||||
use App\Profile;
|
Hashtag,
|
||||||
use App\Report;
|
Instance,
|
||||||
use App\Status;
|
Media,
|
||||||
use App\User;
|
Like,
|
||||||
|
OauthClient,
|
||||||
|
Profile,
|
||||||
|
Report,
|
||||||
|
Status,
|
||||||
|
User
|
||||||
|
};
|
||||||
|
use DB, Cache;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Jackiedo\DotenvEditor\DotenvEditor;
|
use Jackiedo\DotenvEditor\DotenvEditor;
|
||||||
use App\Http\Controllers\Admin\AdminReportController;
|
use App\Http\Controllers\Admin\{
|
||||||
|
AdminDiscoverController,
|
||||||
|
AdminInstanceController,
|
||||||
|
AdminReportController,
|
||||||
|
AdminMediaController,
|
||||||
|
AdminSettingsController
|
||||||
|
};
|
||||||
use App\Util\Lexer\PrettyNumber;
|
use App\Util\Lexer\PrettyNumber;
|
||||||
|
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
{
|
{
|
||||||
use AdminReportController;
|
use AdminReportController,
|
||||||
|
AdminDiscoverController,
|
||||||
|
AdminMediaController,
|
||||||
|
AdminSettingsController,
|
||||||
|
AdminInstanceController;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
@ -26,7 +43,55 @@ class AdminController extends Controller
|
||||||
|
|
||||||
public function home()
|
public function home()
|
||||||
{
|
{
|
||||||
return view('admin.home');
|
$data = Cache::remember('admin:dashboard:home:data', 15, function() {
|
||||||
|
return [
|
||||||
|
'failedjobs' => [
|
||||||
|
'count' => PrettyNumber::convert(FailedJob::where('failed_at', '>=', \Carbon\Carbon::now()->subDay())->count()),
|
||||||
|
'graph' => FailedJob::selectRaw('count(*) as count, day(failed_at) as d')->groupBy('d')->whereBetween('failed_at',[now()->subDays(24), now()])->orderBy('d')->pluck('count')
|
||||||
|
],
|
||||||
|
'reports' => [
|
||||||
|
'count' => PrettyNumber::convert(Report::whereNull('admin_seen')->count()),
|
||||||
|
'graph' => Report::selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'statuses' => [
|
||||||
|
'count' => PrettyNumber::convert(Status::whereNull('in_reply_to_id')->whereNull('reblog_of_id')->count()),
|
||||||
|
'graph' => Status::selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'replies' => [
|
||||||
|
'count' => PrettyNumber::convert(Status::whereNotNull('in_reply_to_id')->count()),
|
||||||
|
'graph' => Status::whereNotNull('in_reply_to_id')->selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'shares' => [
|
||||||
|
'count' => PrettyNumber::convert(Status::whereNotNull('reblog_of_id')->count()),
|
||||||
|
'graph' => Status::whereNotNull('reblog_of_id')->selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'likes' => [
|
||||||
|
'count' => PrettyNumber::convert(Like::count()),
|
||||||
|
'graph' => Like::selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'profiles' => [
|
||||||
|
'count' => PrettyNumber::convert(Profile::count()),
|
||||||
|
'graph' => Profile::selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'users' => [
|
||||||
|
'count' => PrettyNumber::convert(User::count()),
|
||||||
|
'graph' => User::selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'instances' => [
|
||||||
|
'count' => PrettyNumber::convert(Instance::count()),
|
||||||
|
'graph' => Instance::selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(28), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'media' => [
|
||||||
|
'count' => PrettyNumber::convert(Media::count()),
|
||||||
|
'graph' => Media::selectRaw('count(*) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
],
|
||||||
|
'storage' => [
|
||||||
|
'count' => Media::sum('size'),
|
||||||
|
'graph' => Media::selectRaw('sum(size) as count, day(created_at) as day')->whereBetween('created_at',[now()->subDays(14), now()])->groupBy('day')->orderBy('day')->pluck('count')
|
||||||
|
]
|
||||||
|
];
|
||||||
|
});
|
||||||
|
return view('admin.home', compact('data'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function users(Request $request)
|
public function users(Request $request)
|
||||||
|
@ -35,6 +100,7 @@ class AdminController extends Controller
|
||||||
$dir = $request->query('dir') ?? 'desc';
|
$dir = $request->query('dir') ?? 'desc';
|
||||||
$stats = $this->collectUserStats($request);
|
$stats = $this->collectUserStats($request);
|
||||||
$users = User::withCount('statuses')->orderBy($col, $dir)->paginate(10);
|
$users = User::withCount('statuses')->orderBy($col, $dir)->paginate(10);
|
||||||
|
|
||||||
return view('admin.users.home', compact('users', 'stats'));
|
return view('admin.users.home', compact('users', 'stats'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,16 +125,23 @@ class AdminController extends Controller
|
||||||
return view('admin.statuses.show', compact('status'));
|
return view('admin.statuses.show', compact('status'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function media(Request $request)
|
|
||||||
{
|
|
||||||
$media = Status::whereHas('media')->orderby('id', 'desc')->paginate(12);
|
|
||||||
|
|
||||||
return view('admin.media.home', compact('media'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reports(Request $request)
|
public function reports(Request $request)
|
||||||
{
|
{
|
||||||
$reports = Report::orderBy('created_at','desc')->paginate(12);
|
$filter = $request->input('filter');
|
||||||
|
if(in_array($filter, ['open', 'closed'])) {
|
||||||
|
if($filter == 'open') {
|
||||||
|
$reports = Report::orderBy('created_at','desc')
|
||||||
|
->whereNotNull('admin_seen')
|
||||||
|
->paginate(10);
|
||||||
|
} else {
|
||||||
|
$reports = Report::orderBy('created_at','desc')
|
||||||
|
->whereNull('admin_seen')
|
||||||
|
->paginate(10);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$reports = Report::orderBy('created_at','desc')
|
||||||
|
->paginate(10);
|
||||||
|
}
|
||||||
return view('admin.reports.home', compact('reports'));
|
return view('admin.reports.home', compact('reports'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +151,6 @@ class AdminController extends Controller
|
||||||
return view('admin.reports.show', compact('report'));
|
return view('admin.reports.show', compact('report'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function collectUserStats($request)
|
protected function collectUserStats($request)
|
||||||
{
|
{
|
||||||
$total_duration = $request->query('total_duration') ?? '30';
|
$total_duration = $request->query('total_duration') ?? '30';
|
||||||
|
@ -106,4 +178,35 @@ class AdminController extends Controller
|
||||||
return $stats;
|
return $stats;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function profiles(Request $request)
|
||||||
|
{
|
||||||
|
$profiles = Profile::orderBy('id','desc')->paginate(10);
|
||||||
|
return view('admin.profiles.home', compact('profiles'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appsHome(Request $request)
|
||||||
|
{
|
||||||
|
$filter = $request->input('filter');
|
||||||
|
if(in_array($filter, ['revoked'])) {
|
||||||
|
$apps = OauthClient::with('user')
|
||||||
|
->whereNotNull('user_id')
|
||||||
|
->whereRevoked(true)
|
||||||
|
->orderByDesc('id')
|
||||||
|
->paginate(10);
|
||||||
|
} else {
|
||||||
|
$apps = OauthClient::with('user')
|
||||||
|
->whereNotNull('user_id')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->paginate(10);
|
||||||
|
}
|
||||||
|
return view('admin.apps.home', compact('apps'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hashtagsHome(Request $request)
|
||||||
|
{
|
||||||
|
$hashtags = Hashtag::orderByDesc('id')->paginate(10);
|
||||||
|
return view('admin.hashtags.home', compact('hashtags'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ use App\{
|
||||||
Avatar,
|
Avatar,
|
||||||
Notification,
|
Notification,
|
||||||
Media,
|
Media,
|
||||||
Profile
|
Profile,
|
||||||
|
Status
|
||||||
};
|
};
|
||||||
use App\Transformer\Api\{
|
use App\Transformer\Api\{
|
||||||
AccountTransformer,
|
AccountTransformer,
|
||||||
|
@ -23,6 +24,7 @@ use App\Transformer\Api\{
|
||||||
};
|
};
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
use League\Fractal\Serializer\ArraySerializer;
|
use League\Fractal\Serializer\ArraySerializer;
|
||||||
|
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||||
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
||||||
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
||||||
use App\Jobs\VideoPipeline\{
|
use App\Jobs\VideoPipeline\{
|
||||||
|
@ -97,13 +99,46 @@ class BaseApiController extends Controller
|
||||||
|
|
||||||
public function accountStatuses(Request $request, $id)
|
public function accountStatuses(Request $request, $id)
|
||||||
{
|
{
|
||||||
$pid = Auth::user()->profile->id;
|
$this->validate($request, [
|
||||||
$profile = Profile::findOrFail($id);
|
'only_media' => 'nullable',
|
||||||
$statuses = $profile->statuses();
|
'pinned' => 'nullable',
|
||||||
if($pid === $profile->id) {
|
'exclude_replies' => 'nullable',
|
||||||
$statuses = $statuses->orderBy('id', 'desc')->paginate(20);
|
'max_id' => 'nullable|integer|min:1',
|
||||||
|
'since_id' => 'nullable|integer|min:1',
|
||||||
|
'min_id' => 'nullable|integer|min:1',
|
||||||
|
'limit' => 'nullable|integer|min:1|max:24'
|
||||||
|
]);
|
||||||
|
$limit = $request->limit ?? 20;
|
||||||
|
$max_id = $request->max_id ?? false;
|
||||||
|
$min_id = $request->min_id ?? false;
|
||||||
|
$since_id = $request->since_id ?? false;
|
||||||
|
$only_media = $request->only_media ?? false;
|
||||||
|
$user = Auth::user();
|
||||||
|
$account = Profile::findOrFail($id);
|
||||||
|
$statuses = $account->statuses()->getQuery();
|
||||||
|
if($only_media == true) {
|
||||||
|
$statuses = $statuses
|
||||||
|
->whereHas('media')
|
||||||
|
->whereNull('in_reply_to_id')
|
||||||
|
->whereNull('reblog_of_id');
|
||||||
|
}
|
||||||
|
if($id == $account->id && !$max_id && !$min_id && !$since_id) {
|
||||||
|
$statuses = $statuses->orderBy('id', 'desc')
|
||||||
|
->paginate($limit);
|
||||||
|
} else if($since_id) {
|
||||||
|
$statuses = $statuses->where('id', '>', $since_id)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->paginate($limit);
|
||||||
|
} else if($min_id) {
|
||||||
|
$statuses = $statuses->where('id', '>', $min_id)
|
||||||
|
->orderBy('id', 'ASC')
|
||||||
|
->paginate($limit);
|
||||||
|
} else if($max_id) {
|
||||||
|
$statuses = $statuses->where('id', '<', $max_id)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->paginate($limit);
|
||||||
} else {
|
} else {
|
||||||
$statuses = $statuses->whereVisibility('public')->orderBy('id', 'desc')->paginate(20);
|
$statuses = $statuses->whereVisibility('public')->orderBy('id', 'desc')->paginate($limit);
|
||||||
}
|
}
|
||||||
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer());
|
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer());
|
||||||
$res = $this->fractal->createData($resource)->toArray();
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
@ -265,4 +300,5 @@ class BaseApiController extends Controller
|
||||||
|
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,13 @@ class RegisterController extends Controller
|
||||||
*/
|
*/
|
||||||
public function showRegistrationForm()
|
public function showRegistrationForm()
|
||||||
{
|
{
|
||||||
$view = config('pixelfed.open_registration') == true ? 'auth.register' : 'site.closed-registration';
|
$count = User::count();
|
||||||
|
$limit = config('pixelfed.max_users');
|
||||||
|
if($limit && $limit <= $count) {
|
||||||
|
$view = 'site.closed-registration';
|
||||||
|
} else {
|
||||||
|
$view = config('pixelfed.open_registration') == true ? 'auth.register' : 'site.closed-registration';
|
||||||
|
}
|
||||||
return view($view);
|
return view($view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +134,9 @@ class RegisterController extends Controller
|
||||||
*/
|
*/
|
||||||
public function register(Request $request)
|
public function register(Request $request)
|
||||||
{
|
{
|
||||||
if(false == config('pixelfed.open_registration')) {
|
$count = User::count();
|
||||||
|
$limit = config('pixelfed.max_users');
|
||||||
|
if(false == config('pixelfed.open_registration') || $limit && $limit <= $count) {
|
||||||
return abort(403);
|
return abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
69
app/Http/Controllers/CircleController.php
Normal file
69
app/Http/Controllers/CircleController.php
Normal file
|
@ -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'));
|
||||||
|
}
|
||||||
|
}
|
10
app/Http/Controllers/CircleProfileController.php
Normal file
10
app/Http/Controllers/CircleProfileController.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class CircleProfileController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
24
app/Http/Controllers/DeckController.php
Normal file
24
app/Http/Controllers/DeckController.php
Normal file
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,11 +20,12 @@ class DirectMessageController extends Controller
|
||||||
public function inbox(Request $request)
|
public function inbox(Request $request)
|
||||||
{
|
{
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$inbox = DirectMessage::whereToId($profile->id)
|
$inbox = DirectMessage::selectRaw('*, max(created_at) as createdAt')
|
||||||
|
->whereToId($profile->id)
|
||||||
->with(['author','status'])
|
->with(['author','status'])
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('createdAt', 'desc')
|
||||||
->groupBy('from_id')
|
->groupBy('from_id')
|
||||||
->paginate(10);
|
->paginate(12);
|
||||||
return view('account.messages', compact('inbox'));
|
return view('account.messages', compact('inbox'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,10 +41,12 @@ class DirectMessageController extends Controller
|
||||||
$msg = DirectMessage::whereToId($profile->id)
|
$msg = DirectMessage::whereToId($profile->id)
|
||||||
->findOrFail($mid);
|
->findOrFail($mid);
|
||||||
|
|
||||||
$thread = DirectMessage::whereToId($profile->id)
|
$thread = DirectMessage::whereIn('to_id', [$profile->id, $msg->from_id])
|
||||||
->orWhere([['from_id', $profile->id],['to_id', $msg->from_id]])
|
->whereIn('from_id', [$profile->id,$msg->from_id])
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('created_at', 'desc')
|
||||||
->paginate(10);
|
->paginate(30);
|
||||||
|
|
||||||
|
$thread = $thread->reverse();
|
||||||
|
|
||||||
return view('account.message', compact('msg', 'profile', 'thread'));
|
return view('account.message', compact('msg', 'profile', 'thread'));
|
||||||
}
|
}
|
||||||
|
|
10
app/Http/Controllers/DiscoverCategoryController.php
Normal file
10
app/Http/Controllers/DiscoverCategoryController.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class DiscoverCategoryController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
10
app/Http/Controllers/DiscoverCategoryHashtagController.php
Normal file
10
app/Http/Controllers/DiscoverCategoryHashtagController.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class DiscoverCategoryHashtagController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
|
@ -3,10 +3,12 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\{
|
use App\{
|
||||||
|
DiscoverCategory,
|
||||||
Follower,
|
Follower,
|
||||||
Hashtag,
|
Hashtag,
|
||||||
Profile,
|
Profile,
|
||||||
Status,
|
Status,
|
||||||
|
StatusHashtag,
|
||||||
UserFilter
|
UserFilter
|
||||||
};
|
};
|
||||||
use Auth, DB, Cache;
|
use Auth, DB, Cache;
|
||||||
|
@ -28,7 +30,7 @@ class DiscoverController extends Controller
|
||||||
{
|
{
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'page' => 'nullable|integer|min:1|max:10',
|
'page' => 'nullable|integer|min:1|max:10',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$tag = Hashtag::with('posts')
|
$tag = Hashtag::with('posts')
|
||||||
->withCount('posts')
|
->withCount('posts')
|
||||||
|
@ -51,4 +53,30 @@ class DiscoverController extends Controller
|
||||||
|
|
||||||
return view('discover.tags.show', compact('tag', 'posts'));
|
return view('discover.tags.show', compact('tag', 'posts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function showCategory(Request $request, $slug)
|
||||||
|
{
|
||||||
|
$tag = DiscoverCategory::whereActive(true)
|
||||||
|
->whereSlug($slug)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
// todo refactor this mess
|
||||||
|
$tagids = $tag->hashtags->pluck('id')->toArray();
|
||||||
|
$sids = StatusHashtag::whereIn('hashtag_id', $tagids)->orderByDesc('status_id')->take(500)->pluck('status_id')->toArray();
|
||||||
|
$posts = Status::whereIn('id', $sids)->whereNull('uri')->whereType('photo')->whereNull('in_reply_to_id')->whereNull('reblog_of_id')->orderByDesc('created_at')->paginate(21);
|
||||||
|
$tag->posts_count = $tag->posts()->count();
|
||||||
|
return view('discover.tags.category', compact('tag', 'posts'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showPersonal(Request $request)
|
||||||
|
{
|
||||||
|
$profile = Auth::user()->profile;
|
||||||
|
// todo refactor this mess
|
||||||
|
$tags = Hashtag::whereHas('posts')->orderByRaw('rand()')->take(5)->get();
|
||||||
|
$following = $profile->following->pluck('id');
|
||||||
|
$following = $following->push($profile->id)->toArray();
|
||||||
|
$posts = Status::withCount(['likes','comments'])->whereNotIn('profile_id', $following)->whereHas('media')->whereType('photo')->orderByDesc('created_at')->paginate(21);
|
||||||
|
$posts->post_count = Status::whereNotIn('profile_id', $following)->whereHas('media')->whereType('photo')->count();
|
||||||
|
return view('discover.personal', compact('posts', 'tags'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,37 +82,38 @@ class FederationController extends Controller
|
||||||
{
|
{
|
||||||
$res = Cache::remember('api:nodeinfo', 60, function () {
|
$res = Cache::remember('api:nodeinfo', 60, function () {
|
||||||
return [
|
return [
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'nodeName' => config('app.name'),
|
'nodeName' => config('app.name'),
|
||||||
'software' => [
|
'software' => [
|
||||||
'homepage' => 'https://pixelfed.org',
|
'homepage' => 'https://pixelfed.org',
|
||||||
'github' => 'https://github.com/pixelfed',
|
'github' => 'https://github.com/pixelfed',
|
||||||
'follow' => 'https://mastodon.social/@pixelfed',
|
'follow' => 'https://mastodon.social/@pixelfed',
|
||||||
],
|
],
|
||||||
],
|
'captcha' => (bool) config('pixelfed.recaptcha'),
|
||||||
'openRegistrations' => config('pixelfed.open_registration'),
|
],
|
||||||
'protocols' => [
|
'openRegistrations' => config('pixelfed.open_registration'),
|
||||||
'activitypub',
|
'protocols' => [
|
||||||
],
|
'activitypub',
|
||||||
'services' => [
|
],
|
||||||
'inbound' => [],
|
'services' => [
|
||||||
'outbound' => [],
|
'inbound' => [],
|
||||||
],
|
'outbound' => [],
|
||||||
'software' => [
|
],
|
||||||
'name' => 'pixelfed',
|
'software' => [
|
||||||
'version' => config('pixelfed.version'),
|
'name' => 'pixelfed',
|
||||||
],
|
'version' => config('pixelfed.version'),
|
||||||
'usage' => [
|
],
|
||||||
'localPosts' => \App\Status::whereLocal(true)->whereHas('media')->count(),
|
'usage' => [
|
||||||
'localComments' => \App\Status::whereLocal(true)->whereNotNull('in_reply_to_id')->count(),
|
'localPosts' => \App\Status::whereLocal(true)->whereHas('media')->count(),
|
||||||
'users' => [
|
'localComments' => \App\Status::whereLocal(true)->whereNotNull('in_reply_to_id')->count(),
|
||||||
'total' => \App\User::count(),
|
'users' => [
|
||||||
'activeHalfyear' => \App\AccountLog::select('user_id')->whereAction('auth.login')->where('updated_at', '>',Carbon::now()->subMonths(6)->toDateTimeString())->groupBy('user_id')->get()->count(),
|
'total' => \App\User::count(),
|
||||||
'activeMonth' => \App\AccountLog::select('user_id')->whereAction('auth.login')->where('updated_at', '>',Carbon::now()->subMonths(1)->toDateTimeString())->groupBy('user_id')->get()->count(),
|
'activeHalfyear' => \App\AccountLog::select('user_id')->whereAction('auth.login')->where('updated_at', '>',Carbon::now()->subMonths(6)->toDateTimeString())->groupBy('user_id')->get()->count(),
|
||||||
],
|
'activeMonth' => \App\AccountLog::select('user_id')->whereAction('auth.login')->where('updated_at', '>',Carbon::now()->subMonths(1)->toDateTimeString())->groupBy('user_id')->get()->count(),
|
||||||
],
|
],
|
||||||
'version' => '2.0',
|
],
|
||||||
];
|
'version' => '2.0',
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
return response()->json($res, 200, [
|
return response()->json($res, 200, [
|
||||||
|
|
|
@ -39,6 +39,7 @@ class FollowerController extends Controller
|
||||||
$user = Auth::user()->profile;
|
$user = Auth::user()->profile;
|
||||||
$target = Profile::where('id', '!=', $user->id)->whereNull('status')->findOrFail($item);
|
$target = Profile::where('id', '!=', $user->id)->whereNull('status')->findOrFail($item);
|
||||||
$private = (bool) $target->is_private;
|
$private = (bool) $target->is_private;
|
||||||
|
$remote = (bool) $target->domain;
|
||||||
$blocked = UserFilter::whereUserId($target->id)
|
$blocked = UserFilter::whereUserId($target->id)
|
||||||
->whereFilterType('block')
|
->whereFilterType('block')
|
||||||
->whereFilterableId($user->id)
|
->whereFilterableId($user->id)
|
||||||
|
@ -51,7 +52,7 @@ class FollowerController extends Controller
|
||||||
|
|
||||||
$isFollowing = Follower::whereProfileId($user->id)->whereFollowingId($target->id)->count();
|
$isFollowing = Follower::whereProfileId($user->id)->whereFollowingId($target->id)->count();
|
||||||
|
|
||||||
if($private == true && $isFollowing == 0) {
|
if($private == true && $isFollowing == 0 || $remote == true) {
|
||||||
$follow = FollowRequest::firstOrCreate([
|
$follow = FollowRequest::firstOrCreate([
|
||||||
'follower_id' => $user->id,
|
'follower_id' => $user->id,
|
||||||
'following_id' => $target->id
|
'following_id' => $target->id
|
||||||
|
|
|
@ -124,7 +124,7 @@ trait Instagram
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
$media = $request->file('media');
|
$media = $request->file('media');
|
||||||
$file = file_get_contents($media);
|
$file = file_get_contents($media);
|
||||||
$json = json_decode($file, true);
|
$json = json_decode($file, true, 5);
|
||||||
if(!$json || !isset($json['photos'])) {
|
if(!$json || !isset($json['photos'])) {
|
||||||
return abort(500);
|
return abort(500);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\{
|
use App\{
|
||||||
DirectMessage,
|
DirectMessage,
|
||||||
|
DiscoverCategory,
|
||||||
Hashtag,
|
Hashtag,
|
||||||
Follower,
|
Follower,
|
||||||
Like,
|
Like,
|
||||||
|
@ -25,6 +26,7 @@ use App\Transformer\Api\{
|
||||||
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
||||||
use League\Fractal\Serializer\ArraySerializer;
|
use League\Fractal\Serializer\ArraySerializer;
|
||||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class InternalApiController extends Controller
|
class InternalApiController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -199,14 +201,21 @@ class InternalApiController extends Controller
|
||||||
{
|
{
|
||||||
$profile = Auth::user()->profile;
|
$profile = Auth::user()->profile;
|
||||||
$pid = $profile->id;
|
$pid = $profile->id;
|
||||||
$following = Cache::remember('feature:discover:following:'.$pid, 60, function() use ($pid) {
|
$following = Cache::remember('feature:discover:following:'.$pid, 15, function() use ($pid) {
|
||||||
return Follower::whereProfileId($pid)->pluck('following_id')->toArray();
|
return Follower::whereProfileId($pid)->pluck('following_id')->toArray();
|
||||||
});
|
});
|
||||||
$filters = Cache::remember("user:filter:list:$pid", 60, function() use($pid) {
|
$filters = Cache::remember("user:filter:list:$pid", 15, function() use($pid) {
|
||||||
return UserFilter::whereUserId($pid)
|
$private = Profile::whereIsPrivate(true)
|
||||||
->whereFilterableType('App\Profile')
|
->orWhere('unlisted', true)
|
||||||
->whereIn('filter_type', ['mute', 'block'])
|
->orWhere('status', '!=', null)
|
||||||
->pluck('filterable_id')->toArray();
|
->pluck('id')
|
||||||
|
->toArray();
|
||||||
|
$filters = UserFilter::whereUserId($pid)
|
||||||
|
->whereFilterableType('App\Profile')
|
||||||
|
->whereIn('filter_type', ['mute', 'block'])
|
||||||
|
->pluck('filterable_id')
|
||||||
|
->toArray();
|
||||||
|
return array_merge($private, $filters);
|
||||||
});
|
});
|
||||||
$following = array_merge($following, $filters);
|
$following = array_merge($following, $filters);
|
||||||
|
|
||||||
|
@ -281,4 +290,94 @@ class InternalApiController extends Controller
|
||||||
|
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function stories(Request $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function discoverCategories(Request $request)
|
||||||
|
{
|
||||||
|
$categories = DiscoverCategory::whereActive(true)->orderBy('order')->take(10)->get();
|
||||||
|
$res = $categories->map(function($item) {
|
||||||
|
return [
|
||||||
|
'name' => $item->name,
|
||||||
|
'url' => $item->url(),
|
||||||
|
'thumb' => $item->thumb()
|
||||||
|
];
|
||||||
|
});
|
||||||
|
return response()->json($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modAction(Request $request)
|
||||||
|
{
|
||||||
|
abort_unless(Auth::user()->is_admin, 403);
|
||||||
|
$this->validate($request, [
|
||||||
|
'action' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
Rule::in([
|
||||||
|
'autocw',
|
||||||
|
'noautolink',
|
||||||
|
'unlisted',
|
||||||
|
'disable',
|
||||||
|
'suspend'
|
||||||
|
])
|
||||||
|
],
|
||||||
|
'item_id' => 'required|integer|min:1',
|
||||||
|
'item_type' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
Rule::in(['status'])
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$action = $request->input('action');
|
||||||
|
$item_id = $request->input('item_id');
|
||||||
|
$item_type = $request->input('item_type');
|
||||||
|
|
||||||
|
switch($action) {
|
||||||
|
case 'autocw':
|
||||||
|
$profile = $item_type == 'status' ? Status::findOrFail($item_id)->profile : null;
|
||||||
|
$profile->cw = true;
|
||||||
|
$profile->save();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'noautolink':
|
||||||
|
$profile = $item_type == 'status' ? Status::findOrFail($item_id)->profile : null;
|
||||||
|
$profile->no_autolink = true;
|
||||||
|
$profile->save();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'unlisted':
|
||||||
|
$profile = $item_type == 'status' ? Status::findOrFail($item_id)->profile : null;
|
||||||
|
$profile->unlisted = true;
|
||||||
|
$profile->save();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'disable':
|
||||||
|
$profile = $item_type == 'status' ? Status::findOrFail($item_id)->profile : null;
|
||||||
|
$user = $profile->user;
|
||||||
|
$profile->status = 'disabled';
|
||||||
|
$user->status = 'disabled';
|
||||||
|
$profile->save();
|
||||||
|
$user->save();
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case 'suspend':
|
||||||
|
$profile = $item_type == 'status' ? Status::findOrFail($item_id)->profile : null;
|
||||||
|
$user = $profile->user;
|
||||||
|
$profile->status = 'suspended';
|
||||||
|
$user->status = 'suspended';
|
||||||
|
$profile->save();
|
||||||
|
$user->save();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
# code...
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ['msg' => 200];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
67
app/Http/Controllers/MicroController.php
Normal file
67
app/Http/Controllers/MicroController.php
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
53
app/Http/Controllers/PageController.php
Normal file
53
app/Http/Controllers/PageController.php
Normal file
|
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -187,7 +187,7 @@ class ProfileController extends Controller
|
||||||
return view('profile.private', compact('user', 'is_following'));
|
return view('profile.private', compact('user', 'is_following'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$followers = $profile->followers()->whereNull('status')->orderBy('created_at', 'desc')->simplePaginate(12);
|
$followers = $profile->followers()->whereNull('status')->orderBy('followers.created_at', 'desc')->simplePaginate(12);
|
||||||
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
||||||
if ($user->remote_url) {
|
if ($user->remote_url) {
|
||||||
$settings = new \StdClass;
|
$settings = new \StdClass;
|
||||||
|
@ -217,7 +217,7 @@ class ProfileController extends Controller
|
||||||
return view('profile.private', compact('user', 'is_following'));
|
return view('profile.private', compact('user', 'is_following'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$following = $profile->following()->whereNull('status')->orderBy('created_at', 'desc')->simplePaginate(12);
|
$following = $profile->following()->whereNull('status')->orderBy('followers.created_at', 'desc')->simplePaginate(12);
|
||||||
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
|
||||||
if ($user->remote_url) {
|
if ($user->remote_url) {
|
||||||
$settings = new \StdClass;
|
$settings = new \StdClass;
|
||||||
|
|
|
@ -19,6 +19,7 @@ use Carbon\Carbon;
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
use App\Transformer\Api\{
|
use App\Transformer\Api\{
|
||||||
AccountTransformer,
|
AccountTransformer,
|
||||||
|
RelationshipTransformer,
|
||||||
StatusTransformer,
|
StatusTransformer,
|
||||||
};
|
};
|
||||||
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
||||||
|
@ -32,7 +33,6 @@ class PublicApiController extends Controller
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('throttle:3000, 30');
|
|
||||||
$this->fractal = new Fractal\Manager();
|
$this->fractal = new Fractal\Manager();
|
||||||
$this->fractal->setSerializer(new ArraySerializer());
|
$this->fractal->setSerializer(new ArraySerializer());
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,11 @@ class PublicApiController extends Controller
|
||||||
// $timeline = Timeline::build()->local();
|
// $timeline = Timeline::build()->local();
|
||||||
$pid = Auth::user()->profile->id;
|
$pid = Auth::user()->profile->id;
|
||||||
|
|
||||||
$private = Profile::whereIsPrivate(true)->orWhereNotNull('status')->where('id', '!=', $pid)->pluck('id');
|
$private = Profile::whereIsPrivate(true)
|
||||||
|
->orWhere('unlisted', true)
|
||||||
|
->orWhere('status', '!=', null)
|
||||||
|
->where('id', '!=', $pid)
|
||||||
|
->pluck('id');
|
||||||
$filters = UserFilter::whereUserId($pid)
|
$filters = UserFilter::whereUserId($pid)
|
||||||
->whereFilterableType('App\Profile')
|
->whereFilterableType('App\Profile')
|
||||||
->whereIn('filter_type', ['mute', 'block'])
|
->whereIn('filter_type', ['mute', 'block'])
|
||||||
|
@ -330,4 +334,100 @@ class PublicApiController extends Controller
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function relationships(Request $request)
|
||||||
|
{
|
||||||
|
abort_if(!Auth::check(), 403);
|
||||||
|
|
||||||
|
$this->validate($request, [
|
||||||
|
'id' => 'required|array|min:1|max:20',
|
||||||
|
'id.*' => 'required|integer'
|
||||||
|
]);
|
||||||
|
$ids = collect($request->input('id'));
|
||||||
|
$filtered = $ids->filter(function($v) {
|
||||||
|
return $v != Auth::user()->profile->id;
|
||||||
|
});
|
||||||
|
$relations = Profile::findOrFail($filtered->all());
|
||||||
|
$fractal = new Fractal\Resource\Collection($relations, new RelationshipTransformer());
|
||||||
|
$res = $this->fractal->createData($fractal)->toArray();
|
||||||
|
return response()->json($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function account(Request $request, $id)
|
||||||
|
{
|
||||||
|
$profile = Profile::whereNull('status')->findOrFail($id);
|
||||||
|
$resource = new Fractal\Resource\Item($profile, new AccountTransformer());
|
||||||
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
|
return response()->json($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function accountStatuses(Request $request, $id)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'only_media' => 'nullable',
|
||||||
|
'pinned' => 'nullable',
|
||||||
|
'exclude_replies' => 'nullable',
|
||||||
|
'max_id' => 'nullable|integer|min:1',
|
||||||
|
'since_id' => 'nullable|integer|min:1',
|
||||||
|
'min_id' => 'nullable|integer|min:1',
|
||||||
|
'limit' => 'nullable|integer|min:1|max:24'
|
||||||
|
]);
|
||||||
|
$limit = $request->limit ?? 20;
|
||||||
|
$max_id = $request->max_id ?? false;
|
||||||
|
$min_id = $request->min_id ?? false;
|
||||||
|
$since_id = $request->since_id ?? false;
|
||||||
|
$only_media = $request->only_media ?? false;
|
||||||
|
$user = Auth::user();
|
||||||
|
$account = Profile::findOrFail($id);
|
||||||
|
$statuses = $account->statuses()->getQuery();
|
||||||
|
if($only_media == true) {
|
||||||
|
$statuses = $statuses
|
||||||
|
->whereHas('media')
|
||||||
|
->whereNull('in_reply_to_id')
|
||||||
|
->whereNull('reblog_of_id');
|
||||||
|
}
|
||||||
|
if($id == $account->id && !$max_id && !$min_id && !$since_id) {
|
||||||
|
$statuses = $statuses->orderBy('id', 'desc')
|
||||||
|
->paginate($limit);
|
||||||
|
} else if($since_id) {
|
||||||
|
$statuses = $statuses->where('id', '>', $since_id)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->paginate($limit);
|
||||||
|
} else if($min_id) {
|
||||||
|
$statuses = $statuses->where('id', '>', $min_id)
|
||||||
|
->orderBy('id', 'ASC')
|
||||||
|
->paginate($limit);
|
||||||
|
} else if($max_id) {
|
||||||
|
$statuses = $statuses->where('id', '<', $max_id)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->paginate($limit);
|
||||||
|
} else {
|
||||||
|
$statuses = $statuses->whereVisibility('public')->orderBy('id', 'desc')->paginate($limit);
|
||||||
|
}
|
||||||
|
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer());
|
||||||
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
|
return response()->json($res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use App\Hashtag;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use App\Status;
|
use App\Status;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use App\Util\ActivityPub\Helpers;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
|
@ -24,6 +25,35 @@ class SearchController extends Controller
|
||||||
$hash = hash('sha256', $tag);
|
$hash = hash('sha256', $tag);
|
||||||
$tokens = Cache::remember('api:search:tag:'.$hash, 5, function () use ($tag) {
|
$tokens = Cache::remember('api:search:tag:'.$hash, 5, function () use ($tag) {
|
||||||
$tokens = collect([]);
|
$tokens = collect([]);
|
||||||
|
if(Helpers::validateUrl($tag)) {
|
||||||
|
$remote = Helpers::fetchFromUrl($tag);
|
||||||
|
if(isset($remote['type']) && in_array($remote['type'], ['Create', 'Person']) == true) {
|
||||||
|
$type = $remote['type'];
|
||||||
|
if($type == 'Person') {
|
||||||
|
$item = Helpers::profileFirstOrNew($tag);
|
||||||
|
$tokens->push([[
|
||||||
|
'count' => 1,
|
||||||
|
'url' => $item->url(),
|
||||||
|
'type' => 'profile',
|
||||||
|
'value' => $item->username,
|
||||||
|
'tokens' => [$item->username],
|
||||||
|
'name' => $item->name,
|
||||||
|
]]);
|
||||||
|
} else if ($type == 'Create') {
|
||||||
|
$item = Helpers::statusFirstOrFetch($tag, false);
|
||||||
|
$tokens->push([[
|
||||||
|
'count' => 0,
|
||||||
|
'url' => $item->url(),
|
||||||
|
'type' => 'status',
|
||||||
|
'value' => "by {$item->profile->username} <span class='float-right'>{$item->created_at->diffForHumans(null, true, true)}</span>",
|
||||||
|
'tokens' => [$item->caption],
|
||||||
|
'name' => $item->caption,
|
||||||
|
'thumb' => $item->thumb(),
|
||||||
|
]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
$hashtags = Hashtag::select('id', 'name', 'slug')->where('slug', 'like', '%'.$tag.'%')->limit(20)->get();
|
$hashtags = Hashtag::select('id', 'name', 'slug')->where('slug', 'like', '%'.$tag.'%')->limit(20)->get();
|
||||||
if($hashtags->count() > 0) {
|
if($hashtags->count() > 0) {
|
||||||
$tags = $hashtags->map(function ($item, $key) {
|
$tags = $hashtags->map(function ($item, $key) {
|
||||||
|
@ -41,6 +71,7 @@ class SearchController extends Controller
|
||||||
$users = Profile::select('username', 'name', 'id')
|
$users = Profile::select('username', 'name', 'id')
|
||||||
->whereNull('status')
|
->whereNull('status')
|
||||||
->where('username', 'like', '%'.$tag.'%')
|
->where('username', 'like', '%'.$tag.'%')
|
||||||
|
->orWhere('remote_url', $tag)
|
||||||
->limit(20)
|
->limit(20)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
@ -66,6 +97,7 @@ class SearchController extends Controller
|
||||||
->whereNull('reblog_of_id')
|
->whereNull('reblog_of_id')
|
||||||
->whereProfileId(Auth::user()->profile->id)
|
->whereProfileId(Auth::user()->profile->id)
|
||||||
->where('caption', 'like', '%'.$tag.'%')
|
->where('caption', 'like', '%'.$tag.'%')
|
||||||
|
->orWhere('uri', $tag)
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('created_at', 'desc')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Settings;
|
||||||
|
|
||||||
use App\AccountLog;
|
use App\AccountLog;
|
||||||
use App\EmailVerification;
|
use App\EmailVerification;
|
||||||
|
use App\Instance;
|
||||||
use App\Media;
|
use App\Media;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use App\User;
|
use App\User;
|
||||||
|
@ -121,7 +122,56 @@ trait PrivacySettings
|
||||||
|
|
||||||
public function blockedInstances()
|
public function blockedInstances()
|
||||||
{
|
{
|
||||||
$settings = Auth::user()->settings;
|
$pid = Auth::user()->profile->id;
|
||||||
return view('settings.privacy.blocked-instances');
|
$filters = UserFilter::whereUserId($pid)
|
||||||
|
->whereFilterableType('App\Instance')
|
||||||
|
->whereFilterType('block')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->paginate(10);
|
||||||
|
return view('settings.privacy.blocked-instances', compact('filters'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function blockedInstanceStore(Request $request)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'domain' => [
|
||||||
|
'required',
|
||||||
|
'min:3',
|
||||||
|
'max:100',
|
||||||
|
function($attribute, $value, $fail) {
|
||||||
|
if(!filter_var($value, FILTER_VALIDATE_DOMAIN)) {
|
||||||
|
$fail($attribute. 'is invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
$domain = $request->input('domain');
|
||||||
|
$instance = Instance::firstOrCreate(['domain' => $domain]);
|
||||||
|
$filter = new UserFilter;
|
||||||
|
$filter->user_id = Auth::user()->profile->id;
|
||||||
|
$filter->filterable_id = $instance->id;
|
||||||
|
$filter->filterable_type = 'App\Instance';
|
||||||
|
$filter->filter_type = 'block';
|
||||||
|
$filter->save();
|
||||||
|
return response()->json(['msg' => 200]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function blockedInstanceUnblock(Request $request)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'id' => 'required|integer|min:1'
|
||||||
|
]);
|
||||||
|
$pid = Auth::user()->profile->id;
|
||||||
|
|
||||||
|
$filter = UserFilter::whereFilterableType('App\Instance')
|
||||||
|
->whereUserId($pid)
|
||||||
|
->findOrFail($request->input('id'));
|
||||||
|
$filter->delete();
|
||||||
|
return redirect(route('settings.privacy.blocked-instances'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function blockedKeywords()
|
||||||
|
{
|
||||||
|
return view('settings.privacy.blocked-keywords');
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\AccountLog;
|
use App\AccountLog;
|
||||||
use App\Following;
|
use App\Following;
|
||||||
|
use App\Report;
|
||||||
use App\UserFilter;
|
use App\UserFilter;
|
||||||
use Auth, DB, Cache, Purify;
|
use Auth, DB, Cache, Purify;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
@ -160,6 +161,7 @@ class SettingsController extends Controller
|
||||||
if(config('pixelfed.account_deletion') == false) {
|
if(config('pixelfed.account_deletion') == false) {
|
||||||
abort(404);
|
abort(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
if($user->is_admin == true) {
|
if($user->is_admin == true) {
|
||||||
return abort(400, 'You cannot delete an admin account.');
|
return abort(400, 'You cannot delete an admin account.');
|
||||||
|
@ -175,5 +177,18 @@ class SettingsController extends Controller
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
return redirect('/');
|
return redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requestFullExport(Request $request)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
return view('settings.export.show');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reportsHome(Request $request)
|
||||||
|
{
|
||||||
|
$profile = Auth::user()->profile;
|
||||||
|
$reports = Report::whereProfileId($profile->id)->orderByDesc('created_at')->paginate(10);
|
||||||
|
return view('settings.reports', compact('reports'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,10 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App;
|
|
||||||
use App\Follower;
|
|
||||||
use App\Profile;
|
|
||||||
use App\Status;
|
|
||||||
use App\User;
|
|
||||||
use App\UserFilter;
|
|
||||||
use App\Util\Lexer\PrettyNumber;
|
|
||||||
use Auth;
|
|
||||||
use Cache;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use App, Auth, Cache, View;
|
||||||
|
use App\Util\Lexer\PrettyNumber;
|
||||||
|
use App\{Follower, Page, Profile, Status, User, UserFilter};
|
||||||
|
|
||||||
class SiteController extends Controller
|
class SiteController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -47,18 +41,42 @@ class SiteController extends Controller
|
||||||
|
|
||||||
public function about()
|
public function about()
|
||||||
{
|
{
|
||||||
$stats = Cache::remember('site:about:stats', 1440, function() {
|
$res = Cache::remember('site:about', 120, function() {
|
||||||
return [
|
$custom = Page::whereSlug('/site/about')->whereActive(true)->exists();
|
||||||
'posts' => Status::whereLocal(true)->count(),
|
if($custom) {
|
||||||
'users' => User::count(),
|
$stats = Cache::remember('site:about:stats', 60, function() {
|
||||||
'admin' => User::whereIsAdmin(true)->first()
|
return [
|
||||||
];
|
'posts' => Status::whereLocal(true)->count(),
|
||||||
|
'users' => User::count(),
|
||||||
|
'admin' => User::whereIsAdmin(true)->first()
|
||||||
|
];
|
||||||
|
});
|
||||||
|
return View::make('site.about')->with('stats', $stats)->render();
|
||||||
|
} else {
|
||||||
|
$stats = Cache::remember('site:about:stats', 60, function() {
|
||||||
|
return [
|
||||||
|
'posts' => Status::whereLocal(true)->count(),
|
||||||
|
'users' => User::count(),
|
||||||
|
'admin' => User::whereIsAdmin(true)->first()
|
||||||
|
];
|
||||||
|
});
|
||||||
|
//return view('site.about', compact('stats'));
|
||||||
|
return View::make('site.about')->with('stats', $stats)->render();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return view('site.about', compact('stats'));
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function language()
|
public function language()
|
||||||
{
|
{
|
||||||
return view('site.language');
|
return view('site.language');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function communityGuidelines(Request $request)
|
||||||
|
{
|
||||||
|
$slug = '/site/kb/community-guidelines';
|
||||||
|
$page = Page::whereSlug($slug)->whereActive(true)->first();
|
||||||
|
return view('site.help.community-guidelines', compact('page'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||||
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
||||||
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
||||||
use App\Jobs\StatusPipeline\StatusDelete;
|
use App\Jobs\StatusPipeline\StatusDelete;
|
||||||
|
use App\Jobs\SharePipeline\SharePipeline;
|
||||||
use App\Media;
|
use App\Media;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use App\Status;
|
use App\Status;
|
||||||
|
@ -234,8 +235,10 @@ class StatusController extends Controller
|
||||||
$share = new Status();
|
$share = new Status();
|
||||||
$share->profile_id = $profile->id;
|
$share->profile_id = $profile->id;
|
||||||
$share->reblog_of_id = $status->id;
|
$share->reblog_of_id = $status->id;
|
||||||
|
$share->in_reply_to_profile_id = $status->profile_id;
|
||||||
$share->save();
|
$share->save();
|
||||||
$count++;
|
$count++;
|
||||||
|
SharePipeline::dispatch($share);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
|
|
|
@ -2,6 +2,18 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class StoryController extends Controller
|
class StoryController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public function construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function home(Request $request)
|
||||||
|
{
|
||||||
|
return view('stories.home');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,63 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Instance extends Model
|
class Instance extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = ['domain'];
|
protected $fillable = ['domain'];
|
||||||
|
|
||||||
|
public function profiles()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Profile::class, 'domain', 'domain');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function statuses()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(
|
||||||
|
Status::class,
|
||||||
|
Profile::class,
|
||||||
|
'domain',
|
||||||
|
'profile_id',
|
||||||
|
'domain',
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reported()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(
|
||||||
|
Report::class,
|
||||||
|
Profile::class,
|
||||||
|
'domain',
|
||||||
|
'reported_profile_id',
|
||||||
|
'domain',
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reports()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(
|
||||||
|
Report::class,
|
||||||
|
Profile::class,
|
||||||
|
'domain',
|
||||||
|
'profile_id',
|
||||||
|
'domain',
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function media()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(
|
||||||
|
Media::class,
|
||||||
|
Profile::class,
|
||||||
|
'domain',
|
||||||
|
'profile_id',
|
||||||
|
'domain',
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUrl()
|
||||||
|
{
|
||||||
|
return url("/i/admin/instances/show/{$this->id}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,13 @@ class AvatarOptimize implements ShouldQueue
|
||||||
protected $profile;
|
protected $profile;
|
||||||
protected $current;
|
protected $current;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,6 +20,13 @@ class CreateAvatar implements ShouldQueue
|
||||||
|
|
||||||
protected $profile;
|
protected $profile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
95
app/Jobs/AvatarPipeline/ImportAvatar.php
Normal file
95
app/Jobs/AvatarPipeline/ImportAvatar.php
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,13 @@ class CommentPipeline implements ShouldQueue
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $comment;
|
protected $comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,6 +21,13 @@ class FollowActivityPubDeliver implements ShouldQueue
|
||||||
|
|
||||||
protected $followRequest;
|
protected $followRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,6 +18,13 @@ class FollowPipeline implements ShouldQueue
|
||||||
|
|
||||||
protected $follower;
|
protected $follower;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -15,6 +15,13 @@ class ImageOptimize implements ShouldQueue
|
||||||
|
|
||||||
protected $media;
|
protected $media;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -16,6 +16,13 @@ class ImageResize implements ShouldQueue
|
||||||
|
|
||||||
protected $media;
|
protected $media;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -17,6 +17,13 @@ class ImageThumbnail implements ShouldQueue
|
||||||
|
|
||||||
protected $media;
|
protected $media;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Jobs\ImageOptimizePipeline;
|
namespace App\Jobs\ImageOptimizePipeline;
|
||||||
|
|
||||||
|
use Storage;
|
||||||
use App\Media;
|
use App\Media;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
@ -9,6 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use ImageOptimizer;
|
use ImageOptimizer;
|
||||||
|
use Illuminate\Http\File;
|
||||||
|
|
||||||
class ImageUpdate implements ShouldQueue
|
class ImageUpdate implements ShouldQueue
|
||||||
{
|
{
|
||||||
|
@ -17,11 +19,17 @@ class ImageUpdate implements ShouldQueue
|
||||||
protected $media;
|
protected $media;
|
||||||
|
|
||||||
protected $protectedMimes = [
|
protected $protectedMimes = [
|
||||||
'image/gif',
|
'image/jpeg',
|
||||||
'image/bmp',
|
'image/png',
|
||||||
'video/mp4',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
@ -43,21 +51,31 @@ class ImageUpdate implements ShouldQueue
|
||||||
$path = storage_path('app/'.$media->media_path);
|
$path = storage_path('app/'.$media->media_path);
|
||||||
$thumb = storage_path('app/'.$media->thumbnail_path);
|
$thumb = storage_path('app/'.$media->thumbnail_path);
|
||||||
|
|
||||||
try {
|
if (in_array($media->mime, $this->protectedMimes) == true) {
|
||||||
if (!in_array($media->mime, $this->protectedMimes)) {
|
ImageOptimizer::optimize($thumb);
|
||||||
ImageOptimizer::optimize($thumb);
|
ImageOptimizer::optimize($path);
|
||||||
ImageOptimizer::optimize($path);
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_file($path) || !is_file($thumb)) {
|
if (!is_file($path) || !is_file($thumb)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$photo_size = filesize($path);
|
$photo_size = filesize($path);
|
||||||
$thumb_size = filesize($thumb);
|
$thumb_size = filesize($thumb);
|
||||||
$total = ($photo_size + $thumb_size);
|
$total = ($photo_size + $thumb_size);
|
||||||
$media->size = $total;
|
$media->size = $total;
|
||||||
$media->save();
|
$media->save();
|
||||||
|
|
||||||
|
if(config('pixelfed.cloud_storage') == true) {
|
||||||
|
$p = explode('/', $media->media_path);
|
||||||
|
$monthHash = $p[2];
|
||||||
|
$userHash = $p[3];
|
||||||
|
$storagePath = "public/m/{$monthHash}/{$userHash}";
|
||||||
|
$file = Storage::disk(config('filesystems.cloud'))->putFile($storagePath, new File($path), 'public');
|
||||||
|
$url = Storage::disk(config('filesystems.cloud'))->url($file);
|
||||||
|
$media->cdn_url = $url;
|
||||||
|
$media->optimized_url = $url;
|
||||||
|
$media->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,14 @@ class ImportInstagram implements ShouldQueue
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $job;
|
protected $job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,6 +19,13 @@ class LikePipeline implements ShouldQueue
|
||||||
|
|
||||||
protected $like;
|
protected $like;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,6 +18,13 @@ class MentionPipeline implements ShouldQueue
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $mention;
|
protected $mention;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -17,7 +17,14 @@ class SharePipeline implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $like;
|
protected $status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
|
@ -37,32 +44,32 @@ class SharePipeline implements ShouldQueue
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$status = $this->status;
|
$status = $this->status;
|
||||||
$actor = $this->status->profile;
|
$actor = $status->profile;
|
||||||
$target = $this->status->parent()->profile;
|
$target = $status->parent()->profile;
|
||||||
|
|
||||||
if ($status->url !== null) {
|
if ($status->uri !== null) {
|
||||||
// Ignore notifications to remote statuses
|
// Ignore notifications to remote statuses
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$exists = Notification::whereProfileId($status->profile_id)
|
$exists = Notification::whereProfileId($target->id)
|
||||||
->whereActorId($actor->id)
|
->whereActorId($status->profile_id)
|
||||||
->whereAction('like')
|
->whereAction('share')
|
||||||
->whereItemId($status->id)
|
->whereItemId($status->reblog_of_id)
|
||||||
->whereItemType('App\Status')
|
->whereItemType('App\Status')
|
||||||
->count();
|
->count();
|
||||||
|
|
||||||
if ($actor->id === $status->profile_id || $exists !== 0) {
|
if ($target->id === $status->profile_id || $exists !== 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$notification = new Notification();
|
$notification = new Notification;
|
||||||
$notification->profile_id = $status->profile_id;
|
$notification->profile_id = $target->id;
|
||||||
$notification->actor_id = $actor->id;
|
$notification->actor_id = $actor->id;
|
||||||
$notification->action = 'like';
|
$notification->action = 'share';
|
||||||
$notification->message = $like->toText();
|
$notification->message = $status->shareToText();
|
||||||
$notification->rendered = $like->toHtml();
|
$notification->rendered = $status->shareToHtml();
|
||||||
$notification->item_id = $status->id;
|
$notification->item_id = $status->id;
|
||||||
$notification->item_type = "App\Status";
|
$notification->item_type = "App\Status";
|
||||||
$notification->save();
|
$notification->save();
|
||||||
|
|
|
@ -16,7 +16,14 @@ class NewStatusPipeline implements ShouldQueue
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $status;
|
protected $status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,7 +18,14 @@ class StatusActivityPubDeliver implements ShouldQueue
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $status;
|
protected $status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,7 +19,14 @@ class StatusDelete implements ShouldQueue
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
protected $status;
|
protected $status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,7 +24,14 @@ class StatusEntityLexer implements ShouldQueue
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $entities;
|
protected $entities;
|
||||||
protected $autolink;
|
protected $autolink;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the job if its models no longer exist.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -49,7 +49,7 @@ class VideoThumbnail implements ShouldQueue
|
||||||
} elseif($video->getDurationInSeconds() < 5) {
|
} elseif($video->getDurationInSeconds() < 5) {
|
||||||
$video->getFrameFromSeconds(4);
|
$video->getFrameFromSeconds(4);
|
||||||
}
|
}
|
||||||
$video->export()
|
$video->export()
|
||||||
->save($save);
|
->save($save);
|
||||||
|
|
||||||
$media->thumbnail_path = $save;
|
$media->thumbnail_path = $save;
|
||||||
|
|
|
@ -17,13 +17,23 @@ class Media extends Model
|
||||||
*/
|
*/
|
||||||
protected $dates = ['deleted_at'];
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Status::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function profile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
if(!empty($this->remote_media) && $this->remote_url) {
|
if(!empty($this->remote_media) && $this->remote_url) {
|
||||||
$url = $this->remote_url;
|
$url = $this->remote_url;
|
||||||
} else {
|
} else {
|
||||||
$path = $this->media_path;
|
$path = $this->media_path;
|
||||||
$url = Storage::url($path);
|
$url = $this->cdn_url ?? Storage::url($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return url($url);
|
return url($url);
|
||||||
|
@ -37,6 +47,11 @@ class Media extends Model
|
||||||
return url($url);
|
return url($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function thumb()
|
||||||
|
{
|
||||||
|
return $this->thumbnailUrl();
|
||||||
|
}
|
||||||
|
|
||||||
public function mimeType()
|
public function mimeType()
|
||||||
{
|
{
|
||||||
return explode('/', $this->mime)[0];
|
return explode('/', $this->mime)[0];
|
||||||
|
|
18
app/OauthClient.php
Normal file
18
app/OauthClient.php
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
app/Page.php
Normal file
25
app/Page.php
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ class Profile extends Model
|
||||||
|
|
||||||
protected $dates = ['deleted_at'];
|
protected $dates = ['deleted_at'];
|
||||||
protected $hidden = ['private_key'];
|
protected $hidden = ['private_key'];
|
||||||
protected $visible = ['username', 'name'];
|
protected $visible = ['id', 'user_id', 'username', 'name'];
|
||||||
|
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
|
@ -274,4 +274,9 @@ class Profile extends Model
|
||||||
->unique()
|
->unique()
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function circles()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Circle::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,8 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
$this->registerPolicies();
|
$this->registerPolicies();
|
||||||
|
|
||||||
// Passport::routes();
|
Passport::routes();
|
||||||
|
Passport::tokensExpireIn(now()->addDays(15));
|
||||||
// Passport::tokensExpireIn(now()->addDays(15));
|
Passport::refreshTokensExpireIn(now()->addDays(30));
|
||||||
|
|
||||||
// Passport::refreshTokensExpireIn(now()->addDays(30));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class ReportComment extends Model
|
class ReportComment extends Model
|
||||||
{
|
{
|
||||||
//
|
public function profile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class ReportLog extends Model
|
class ReportLog extends Model
|
||||||
{
|
{
|
||||||
//
|
public function profile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,11 @@ class Status extends Model
|
||||||
$media = $this->firstMedia();
|
$media = $this->firstMedia();
|
||||||
$path = $media->media_path;
|
$path = $media->media_path;
|
||||||
$hash = is_null($media->processed_at) ? md5('unprocessed') : md5($media->created_at);
|
$hash = is_null($media->processed_at) ? md5('unprocessed') : md5($media->created_at);
|
||||||
$url = Storage::url($path)."?v={$hash}";
|
if(config('pixelfed.cloud_storage') == true) {
|
||||||
|
$url = Storage::disk(config('filesystems.cloud'))->url($path)."?v={$hash}";
|
||||||
|
} else {
|
||||||
|
$url = Storage::url($path)."?v={$hash}";
|
||||||
|
}
|
||||||
|
|
||||||
return url($url);
|
return url($url);
|
||||||
}
|
}
|
||||||
|
@ -270,6 +274,22 @@ class Status extends Model
|
||||||
__('notification.commented');
|
__('notification.commented');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shareToText()
|
||||||
|
{
|
||||||
|
$actorName = $this->profile->username;
|
||||||
|
|
||||||
|
return "{$actorName} ".__('notification.shared');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shareToHtml()
|
||||||
|
{
|
||||||
|
$actorName = $this->profile->username;
|
||||||
|
$actorUrl = $this->profile->url();
|
||||||
|
|
||||||
|
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
|
||||||
|
__('notification.shared');
|
||||||
|
}
|
||||||
|
|
||||||
public function recentComments()
|
public function recentComments()
|
||||||
{
|
{
|
||||||
return $this->comments()->orderBy('created_at', 'desc')->take(3);
|
return $this->comments()->orderBy('created_at', 'desc')->take(3);
|
||||||
|
|
|
@ -7,4 +7,14 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
class StatusHashtag extends Model
|
class StatusHashtag extends Model
|
||||||
{
|
{
|
||||||
public $fillable = ['status_id', 'hashtag_id'];
|
public $fillable = ['status_id', 'hashtag_id'];
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Status::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hashtag()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Hashtag::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,36 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Story extends Model
|
class Story extends Model
|
||||||
{
|
{
|
||||||
//
|
protected $visible = ['id'];
|
||||||
|
|
||||||
|
public function profile()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Profile::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function items()
|
||||||
|
{
|
||||||
|
return $this->hasMany(StoryItem::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reactions()
|
||||||
|
{
|
||||||
|
return $this->hasMany(StoryReaction::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function views()
|
||||||
|
{
|
||||||
|
return $this->hasMany(StoryView::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function seen($pid = false)
|
||||||
|
{
|
||||||
|
$id = $pid ?? Auth::user()->profile->id;
|
||||||
|
return $this->views()->whereProfileId($id)->exists();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
19
app/StoryItem.php
Normal file
19
app/StoryItem.php
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,5 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class StoryReaction extends Model
|
class StoryReaction extends Model
|
||||||
{
|
{
|
||||||
//
|
public function story()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Story::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
13
app/StoryView.php
Normal file
13
app/StoryView.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class StoryView extends Model
|
||||||
|
{
|
||||||
|
public function story()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Story::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ class StatusTransformer extends Fractal\TransformerAbstract
|
||||||
'type' => 'Document',
|
'type' => 'Document',
|
||||||
'mediaType' => $media->mime,
|
'mediaType' => $media->mime,
|
||||||
'url' => $media->url(),
|
'url' => $media->url(),
|
||||||
'name' => null,
|
'name' => $media->caption
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
'tag' => [],
|
'tag' => [],
|
||||||
|
|
|
@ -9,8 +9,9 @@ class AccountTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
public function transform(Profile $profile)
|
public function transform(Profile $profile)
|
||||||
{
|
{
|
||||||
|
$is_admin = $profile->domain ? false : $profile->user->is_admin;
|
||||||
return [
|
return [
|
||||||
'id' => $profile->id,
|
'id' => (string) $profile->id,
|
||||||
'username' => $profile->username,
|
'username' => $profile->username,
|
||||||
'acct' => $profile->username,
|
'acct' => $profile->username,
|
||||||
'display_name' => $profile->name,
|
'display_name' => $profile->name,
|
||||||
|
@ -28,6 +29,9 @@ class AccountTransformer extends Fractal\TransformerAbstract
|
||||||
'moved' => null,
|
'moved' => null,
|
||||||
'fields' => null,
|
'fields' => null,
|
||||||
'bot' => null,
|
'bot' => null,
|
||||||
|
'website' => $profile->website,
|
||||||
|
'software' => 'pixelfed',
|
||||||
|
'is_admin' => (bool) $is_admin
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ class AttachmentTransformer extends Fractal\TransformerAbstract
|
||||||
public function transform(Media $media)
|
public function transform(Media $media)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $media->id,
|
'id' => (string) $media->id,
|
||||||
'type' => $media->activityVerb(),
|
'type' => $media->activityVerb(),
|
||||||
'url' => $media->url(),
|
'url' => $media->url(),
|
||||||
'remote_url' => null,
|
'remote_url' => null,
|
||||||
|
|
|
@ -10,7 +10,7 @@ class MediaTransformer extends Fractal\TransformerAbstract
|
||||||
public function transform(Media $media)
|
public function transform(Media $media)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $media->id,
|
'id' => (string) $media->id,
|
||||||
'type' => $media->activityVerb(),
|
'type' => $media->activityVerb(),
|
||||||
'url' => $media->url(),
|
'url' => $media->url(),
|
||||||
'remote_url' => null,
|
'remote_url' => null,
|
||||||
|
|
|
@ -10,7 +10,7 @@ class MentionTransformer extends Fractal\TransformerAbstract
|
||||||
public function transform(Profile $profile)
|
public function transform(Profile $profile)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $profile->id,
|
'id' => (string) $profile->id,
|
||||||
'url' => $profile->url(),
|
'url' => $profile->url(),
|
||||||
'username' => $profile->username,
|
'username' => $profile->username,
|
||||||
'acct' => $profile->username,
|
'acct' => $profile->username,
|
||||||
|
|
|
@ -15,7 +15,7 @@ class NotificationTransformer extends Fractal\TransformerAbstract
|
||||||
public function transform(Notification $notification)
|
public function transform(Notification $notification)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $notification->id,
|
'id' => (string) $notification->id,
|
||||||
'type' => $this->replaceTypeVerb($notification->action),
|
'type' => $this->replaceTypeVerb($notification->action),
|
||||||
'created_at' => (string) $notification->created_at,
|
'created_at' => (string) $notification->created_at,
|
||||||
'account' => null,
|
'account' => null,
|
||||||
|
@ -44,6 +44,7 @@ class NotificationTransformer extends Fractal\TransformerAbstract
|
||||||
'follow' => 'follow',
|
'follow' => 'follow',
|
||||||
'mention' => 'mention',
|
'mention' => 'mention',
|
||||||
'reblog' => 'share',
|
'reblog' => 'share',
|
||||||
|
'share' => 'share',
|
||||||
'like' => 'favourite',
|
'like' => 'favourite',
|
||||||
'comment' => 'comment',
|
'comment' => 'comment',
|
||||||
];
|
];
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Transformer\Api;
|
namespace App\Transformer\Api;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use League\Fractal;
|
use League\Fractal;
|
||||||
|
|
||||||
|
@ -9,17 +10,18 @@ class RelationshipTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
public function transform(Profile $profile)
|
public function transform(Profile $profile)
|
||||||
{
|
{
|
||||||
|
$user = Auth::user()->profile;
|
||||||
return [
|
return [
|
||||||
'id' => $profile->id,
|
'id' => (string) $profile->id,
|
||||||
'following' => null,
|
'following' => $user->follows($profile),
|
||||||
'followed_by' => null,
|
'followed_by' => $user->followedBy($profile),
|
||||||
'blocking' => null,
|
'blocking' => null,
|
||||||
'muting' => null,
|
'muting' => null,
|
||||||
'muting_notifications' => null,
|
'muting_notifications' => null,
|
||||||
'requested' => null,
|
'requested' => null,
|
||||||
'domain_blocking' => null,
|
'domain_blocking' => null,
|
||||||
'showing_reblogs' => null,
|
'showing_reblogs' => null,
|
||||||
'endorsed' => null
|
'endorsed' => false
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,12 @@ class ResultsTransformer extends Fractal\TransformerAbstract
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $defaultIncludes = [
|
protected $defaultIncludes = [
|
||||||
'account',
|
'accounts',
|
||||||
'mentions',
|
'statuses',
|
||||||
'media_attachments',
|
'hashtags',
|
||||||
'tags',
|
|
||||||
];
|
];
|
||||||
public function transform()
|
|
||||||
|
public function transform($results)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'accounts' => [],
|
'accounts' => [],
|
||||||
|
@ -21,4 +21,22 @@ class ResultsTransformer extends Fractal\TransformerAbstract
|
||||||
'hashtags' => []
|
'hashtags' => []
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function includeAccounts($results)
|
||||||
|
{
|
||||||
|
$accounts = $results->accounts;
|
||||||
|
return $this->collection($accounts, new AccountTransformer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function includeStatuses($results)
|
||||||
|
{
|
||||||
|
$statuses = $results->statuses;
|
||||||
|
return $this->collection($statuses, new StatusTransformer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function includeTags($results)
|
||||||
|
{
|
||||||
|
$hashtags = $status->hashtags;
|
||||||
|
return $this->collection($hashtags, new HashtagTransformer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ class StatusTransformer extends Fractal\TransformerAbstract
|
||||||
public function transform(Status $status)
|
public function transform(Status $status)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => $status->id,
|
'id' => (string) $status->id,
|
||||||
'uri' => $status->url(),
|
'uri' => $status->url(),
|
||||||
'url' => $status->url(),
|
'url' => $status->url(),
|
||||||
'in_reply_to_id' => $status->in_reply_to_id,
|
'in_reply_to_id' => $status->in_reply_to_id,
|
||||||
|
|
27
app/Transformer/Api/StoryItemTransformer.php
Normal file
27
app/Transformer/Api/StoryItemTransformer.php
Normal file
|
@ -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(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
app/Transformer/Api/StoryTransformer.php
Normal file
34
app/Transformer/Api/StoryTransformer.php
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -62,6 +62,11 @@ class User extends Authenticatable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function filters()
|
||||||
|
{
|
||||||
|
return $this->hasMany(UserFilter::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function receivesBroadcastNotificationsOn()
|
public function receivesBroadcastNotificationsOn()
|
||||||
{
|
{
|
||||||
return 'App.User.'.$this->id;
|
return 'App.User.'.$this->id;
|
||||||
|
|
|
@ -21,7 +21,6 @@ class UserFilter extends Model
|
||||||
->pluck('filterable_id');
|
->pluck('filterable_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function blockedUserIds($profile_id)
|
public function blockedUserIds($profile_id)
|
||||||
{
|
{
|
||||||
return $this->whereUserId($profile_id)
|
return $this->whereUserId($profile_id)
|
||||||
|
@ -29,4 +28,9 @@ class UserFilter extends Model
|
||||||
->whereFilterType('block')
|
->whereFilterType('block')
|
||||||
->pluck('filterable_id');
|
->pluck('filterable_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function instance()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Instance::class, 'filterable_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
25
app/Util/ActivityPub/Validator/Follow.php
Normal file
25
app/Util/ActivityPub/Validator/Follow.php
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
25
app/Util/ActivityPub/Validator/Like.php
Normal file
25
app/Util/ActivityPub/Validator/Like.php
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,15 +17,6 @@ class RestrictedNames
|
||||||
'contact-us',
|
'contact-us',
|
||||||
'contact_us',
|
'contact_us',
|
||||||
'copyright',
|
'copyright',
|
||||||
'd',
|
|
||||||
'dashboard',
|
|
||||||
'dev',
|
|
||||||
'developer',
|
|
||||||
'developers',
|
|
||||||
'discover',
|
|
||||||
'discovers',
|
|
||||||
'doc',
|
|
||||||
'docs',
|
|
||||||
'download',
|
'download',
|
||||||
'domainadmin',
|
'domainadmin',
|
||||||
'domainadministrator',
|
'domainadministrator',
|
||||||
|
@ -41,10 +32,7 @@ class RestrictedNames
|
||||||
'guests',
|
'guests',
|
||||||
'hostmaster',
|
'hostmaster',
|
||||||
'hostmaster',
|
'hostmaster',
|
||||||
'image',
|
|
||||||
'images',
|
|
||||||
'imap',
|
'imap',
|
||||||
'img',
|
|
||||||
'info',
|
'info',
|
||||||
'info',
|
'info',
|
||||||
'is',
|
'is',
|
||||||
|
@ -57,7 +45,6 @@ class RestrictedNames
|
||||||
'mailerdaemon',
|
'mailerdaemon',
|
||||||
'marketing',
|
'marketing',
|
||||||
'me',
|
'me',
|
||||||
'media',
|
|
||||||
'mis',
|
'mis',
|
||||||
'mx',
|
'mx',
|
||||||
'new',
|
'new',
|
||||||
|
@ -82,7 +69,6 @@ class RestrictedNames
|
||||||
'pop3',
|
'pop3',
|
||||||
'postmaster',
|
'postmaster',
|
||||||
'pricing',
|
'pricing',
|
||||||
'privacy',
|
|
||||||
'root',
|
'root',
|
||||||
'sales',
|
'sales',
|
||||||
'security',
|
'security',
|
||||||
|
@ -96,7 +82,6 @@ class RestrictedNames
|
||||||
'sys',
|
'sys',
|
||||||
'sysadmin',
|
'sysadmin',
|
||||||
'system',
|
'system',
|
||||||
'terms',
|
|
||||||
'tutorial',
|
'tutorial',
|
||||||
'tutorials',
|
'tutorials',
|
||||||
'usenet',
|
'usenet',
|
||||||
|
@ -121,34 +106,68 @@ class RestrictedNames
|
||||||
'account',
|
'account',
|
||||||
'api',
|
'api',
|
||||||
'auth',
|
'auth',
|
||||||
|
'bartender',
|
||||||
'broadcast',
|
'broadcast',
|
||||||
'broadcaster',
|
'broadcaster',
|
||||||
|
'booth',
|
||||||
|
'bouncer',
|
||||||
|
'c',
|
||||||
'css',
|
'css',
|
||||||
'checkpoint',
|
'checkpoint',
|
||||||
'collection',
|
'collection',
|
||||||
'collections',
|
'collections',
|
||||||
'c',
|
'costar',
|
||||||
|
'costars',
|
||||||
'cdn',
|
'cdn',
|
||||||
|
'd',
|
||||||
'dashboard',
|
'dashboard',
|
||||||
'deck',
|
'deck',
|
||||||
|
'dev',
|
||||||
|
'developer',
|
||||||
|
'developers',
|
||||||
'discover',
|
'discover',
|
||||||
|
'discovers',
|
||||||
|
'dj',
|
||||||
|
'doc',
|
||||||
'docs',
|
'docs',
|
||||||
|
'docs',
|
||||||
|
'drive',
|
||||||
|
'driver',
|
||||||
'error',
|
'error',
|
||||||
'explore',
|
'explore',
|
||||||
|
'font',
|
||||||
'fonts',
|
'fonts',
|
||||||
|
'gdpr',
|
||||||
'home',
|
'home',
|
||||||
'help',
|
'help',
|
||||||
'helpcenter',
|
'helpcenter',
|
||||||
|
'help-center',
|
||||||
|
'help_center',
|
||||||
|
'help_center_',
|
||||||
|
'help-center-',
|
||||||
|
'help-center_',
|
||||||
|
'help_center-',
|
||||||
'i',
|
'i',
|
||||||
'img',
|
'img',
|
||||||
|
'imgs',
|
||||||
|
'image',
|
||||||
|
'images',
|
||||||
'js',
|
'js',
|
||||||
|
'legal',
|
||||||
'live',
|
'live',
|
||||||
'login',
|
'login',
|
||||||
'logout',
|
'logout',
|
||||||
'media',
|
'media',
|
||||||
|
'menu',
|
||||||
|
'oauth',
|
||||||
'official',
|
'official',
|
||||||
'p',
|
'p',
|
||||||
|
'page',
|
||||||
|
'pages',
|
||||||
|
'photo',
|
||||||
|
'photos',
|
||||||
'password',
|
'password',
|
||||||
|
'privacy',
|
||||||
'reset',
|
'reset',
|
||||||
'report',
|
'report',
|
||||||
'reports',
|
'reports',
|
||||||
|
@ -161,10 +180,14 @@ class RestrictedNames
|
||||||
'statuses',
|
'statuses',
|
||||||
'site',
|
'site',
|
||||||
'sites',
|
'sites',
|
||||||
|
'stage',
|
||||||
'static',
|
'static',
|
||||||
'story',
|
'story',
|
||||||
'stories',
|
'stories',
|
||||||
'support',
|
'support',
|
||||||
|
'svg',
|
||||||
|
'svgs',
|
||||||
|
'terms',
|
||||||
'telescope',
|
'telescope',
|
||||||
'timeline',
|
'timeline',
|
||||||
'timelines',
|
'timelines',
|
||||||
|
@ -174,9 +197,11 @@ class RestrictedNames
|
||||||
'username',
|
'username',
|
||||||
'usernames',
|
'usernames',
|
||||||
'vendor',
|
'vendor',
|
||||||
|
'waiter',
|
||||||
'ws',
|
'ws',
|
||||||
'wss',
|
'wss',
|
||||||
'www',
|
'www',
|
||||||
|
'valet',
|
||||||
'400',
|
'400',
|
||||||
'401',
|
'401',
|
||||||
'403',
|
'403',
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1.3",
|
"php": "^7.1.3",
|
||||||
|
"ext-bcmath": "*",
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-openssl": "*",
|
||||||
"beyondcode/laravel-self-diagnosis": "^1.0.2",
|
"beyondcode/laravel-self-diagnosis": "^1.0.2",
|
||||||
"bitverse/identicon": "^1.1",
|
"bitverse/identicon": "^1.1",
|
||||||
"doctrine/dbal": "^2.7",
|
"doctrine/dbal": "^2.7",
|
||||||
|
|
|
@ -42,7 +42,7 @@ return [
|
||||||
],
|
],
|
||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
'driver' => 'token',
|
'driver' => 'passport',
|
||||||
'provider' => 'users',
|
'provider' => 'users',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
|
@ -65,6 +65,21 @@ return [
|
||||||
'endpoint' => env('AWS_ENDPOINT'),
|
'endpoint' => env('AWS_ENDPOINT'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'spaces' => [
|
||||||
|
'driver' => 's3',
|
||||||
|
'key' => env('DO_SPACES_KEY'),
|
||||||
|
'secret' => env('DO_SPACES_SECRET'),
|
||||||
|
'endpoint' => env('DO_SPACES_ENDPOINT'),
|
||||||
|
'region' => env('DO_SPACES_REGION'),
|
||||||
|
'bucket' => env('DO_SPACES_BUCKET'),
|
||||||
|
'visibility' => 'public',
|
||||||
|
'options' => [
|
||||||
|
'CacheControl' => 'max-age=31536000'
|
||||||
|
],
|
||||||
|
'root' => env('DO_SPACES_ROOT','/'),
|
||||||
|
'url' => str_replace(env('DO_SPACES_REGION'),env('DO_SPACES_BUCKET').'.'.env('DO_SPACES_REGION'),str_replace("digitaloceanspaces","cdn.digitaloceanspaces",env('DO_SPACES_ENDPOINT'))),
|
||||||
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -23,7 +23,7 @@ return [
|
||||||
| This value is the version of your PixelFed instance.
|
| This value is the version of your PixelFed instance.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
'version' => '0.7.10',
|
'version' => '0.8.0rc1',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
@ -198,6 +198,46 @@ return [
|
||||||
*/
|
*/
|
||||||
'account_delete_after' => env('ACCOUNT_DELETE_AFTER', false),
|
'account_delete_after' => env('ACCOUNT_DELETE_AFTER', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Enable Cloud Storage
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Store media on object storage like S3, Digital Ocean Spaces, Rackspace
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
'cloud_storage' => env('PF_ENABLE_CLOUD', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Max User Limit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Allow a maximum number of user accounts. Default: off
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
'max_users' => env('PF_MAX_USERS', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Optimize Images
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Resize and optimize image uploads. Default: on
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
'optimize_image' => env('PF_OPTIMIZE_IMAGES', true),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Optimize Videos
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Resize and optimize video uploads. Default: on
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
'optimize_video' => env('PF_OPTIMIZE_VIDEOS', true),
|
||||||
|
|
||||||
|
|
||||||
'media_types' => env('MEDIA_TYPES', 'image/jpeg,image/png,image/gif'),
|
'media_types' => env('MEDIA_TYPES', 'image/jpeg,image/png,image/gif'),
|
||||||
'enforce_account_limit' => env('LIMIT_ACCOUNT_SIZE', true),
|
'enforce_account_limit' => env('LIMIT_ACCOUNT_SIZE', true),
|
||||||
|
|
|
@ -54,7 +54,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'HTML.Doctype' => 'XHTML 1.0 Strict',
|
'HTML.Doctype' => 'XHTML 1.0 Transitional',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
@ -67,7 +67,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'HTML.Allowed' => 'a[href|title|rel],p',
|
'HTML.Allowed' => 'a[href|title|rel],p,strong,em,i,u,h1,h2,h3,h4,h5,ul,ol,li',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
@ -17,7 +17,15 @@ RUN apt-get update \
|
||||||
&& docker-php-ext-install pdo_mysql pcntl gd exif bcmath \
|
&& docker-php-ext-install pdo_mysql pcntl gd exif bcmath \
|
||||||
&& pecl install imagick \
|
&& pecl install imagick \
|
||||||
&& docker-php-ext-enable imagick pcntl imagick gd exif \
|
&& docker-php-ext-enable imagick pcntl imagick gd exif \
|
||||||
&& a2enmod rewrite \
|
&& a2enmod rewrite remoteip \
|
||||||
|
&& {\
|
||||||
|
echo RemoteIPHeader X-Real-IP ;\
|
||||||
|
echo RemoteIPTrustedProxy 10.0.0.0/8 ;\
|
||||||
|
echo RemoteIPTrustedProxy 172.16.0.0/12 ;\
|
||||||
|
echo RemoteIPTrustedProxy 192.168.0.0/16 ;\
|
||||||
|
echo SetEnvIf X-Forwarded-Proto "https" HTTPS=on ;\
|
||||||
|
} > /etc/apache2/conf-available/remoteip.conf \
|
||||||
|
&& a2enconf remoteip \
|
||||||
&& curl -LsS https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar -o /usr/bin/composer \
|
&& curl -LsS https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar -o /usr/bin/composer \
|
||||||
&& echo "${COMPOSER_CHECKSUM} /usr/bin/composer" | sha256sum -c - \
|
&& echo "${COMPOSER_CHECKSUM} /usr/bin/composer" | sha256sum -c - \
|
||||||
&& chmod 755 /usr/bin/composer \
|
&& chmod 755 /usr/bin/composer \
|
||||||
|
|
77
database/migrations/2019_01_12_054413_stories.php
Normal file
77
database/migrations/2019_01_12_054413_stories.php
Normal file
|
@ -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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
40
database/migrations/2019_01_22_030129_create_pages_table.php
Normal file
40
database/migrations/2019_01_22_030129_create_pages_table.php
Normal file
|
@ -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 a new issue