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

Merge pull request #1708 from pixelfed/staging

v0.10.4
parents 66d4b9a0 813703af
......@@ -48,19 +48,6 @@ MAX_CAPTION_LENGTH=150
MAX_ALBUM_LENGTH=4
ACTIVITY_PUB=false
REMOTE_FOLLOW=false
ACTIVITYPUB_INBOX=false
ACTIVITYPUB_SHAREDINBOX=false
# Set these "true" to enable federation.
# You might need to also run:
# php artisan cache:clear
# php artisan config:cache
AP_REMOTE_FOLLOW=false
AP_INBOX=false
PF_COSTAR_ENABLED=false
CS_BLOCKED_DOMAINS='gab.com,gab.ai,develop.gab.com'
CS_CW_DOMAINS='switter.at'
CS_UNLISTED_DOMAINS='example.org,example.net,example.com'
## Optional
#HORIZON_DARKMODE=false # Horizon theme darkmode
#HORIZON_EMBED=false # Single Docker Container mode
......@@ -3,20 +3,33 @@
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.10.3...dev)
### Added
- Added Welsh translations [#1706](https://github.com/pixelfed/pixelfed/pull/1706)
- Added Api v1 controller [85835f5a](https://github.com/pixelfed/pixelfed/commit/85835f5a6712dea0562df4be897087de5305750f)
- Added database migration that adds a language column to the users table [c87d8c16](https://github.com/pixelfed/pixelfed/commit/c87d8c16)
- Added persistent preferred language [18bc9c30](https://github.com/pixelfed/pixelfed/commit/18bc9c30)
### Fixed
- Fixed count bug in StatusHashtagService [#1694](https://github.com/pixelfed/pixelfed/pull/1694)
- Fixed private account bug [#1699](https://github.com/pixelfed/pixelfed/pull/1699)
- Fixed comments on MomentUI posts [#1704](https://github.com/pixelfed/pixelfed/pull/1704)
### Changed
- Updated EmailService, added new domains [#1690](https://github.com/pixelfed/pixelfed/pull/1690)
- Updated quill.js to v1.3.7 [#1692](https://github.com/pixelfed/pixelfed/pull/1690)
- Updated quill.js to v1.3.7 [#1692](https://github.com/pixelfed/pixelfed/pull/1692)
- Cache ProfileController [#1700](https://github.com/pixelfed/pixelfed/pull/1700)
- Updated ComposeUI v4, made cropping optional [#1702](https://github.com/pixelfed/pixelfed/pull/1702)
- Updated DiscoverController, limit Loops to local only posts [#1703](https://github.com/pixelfed/pixelfed/pull/1703)
- Namespaced internal apis [3c306c5e](https://github.com/pixelfed/pixelfed/commit/3c306c5e179d35dbe19a6a1bd9533350e4b96524)
- Updated .env.example with proper remote follow variable [0697f780](https://github.com/pixelfed/pixelfed/commit/0697f780d3a5cba72148f0a767d5a35124a3d9b4)
- Updated show all comments view [0a5eaa31](https://github.com/pixelfed/pixelfed/pull/1708/commits/0a5eaa3118cb09c61d3e5442fe3bf8439a2a12af)
- Updated language page layout [01fb5af](https://github.com/pixelfed/pixelfed/pull/1708/commits/01fb5af19e803488c5794b545d218771f6fce6d7)
- Updated privacy policy page layout [a4229d5](https://github.com/pixelfed/pixelfed/pull/1708/commits/a4229d5d30faea11e7a72d122c4a5762d867aaf3)
- Updated terms page layout [4f8c5e5](https://github.com/pixelfed/pixelfed/pull/1708/commits/4f8c5e5519949c63c702c724a00d8575db4e0014)
- Update v1 API, added /api/v1/instance endpoint [951b6fa0](https://github.com/pixelfed/pixelfed/commit/951b6fa0) [9dc2234b](https://github.com/pixelfed/pixelfed/commit/99dc2234b)
## Deprecated
- Remove deprecated profile following/followers [#1697](https://github.com/pixelfed/pixelfed/pull/1697)
- Remove old comment permalink [05f6598](https://github.com/pixelfed/pixelfed/pull/1708/commits/05f659896d903e1ff41dba810f125d721fa057e7)
## [v0.10.3 (2019-09-08)](https://github.com/pixelfed/pixelfed/compare/v0.10.2...v0.10.3)
......@@ -43,7 +56,7 @@
### Fixed
- Typo in Inbox prevented proper federation support
- Typo in Inbox prevented proper federation support [#1664](https://github.com/pixelfed/pixelfed/pull/1664)
## [v0.10.1 (2019-09-06)](https://github.com/pixelfed/pixelfed/compare/v0.10.0...v0.10.1)
......@@ -53,19 +66,19 @@
- Compose UI v4: a rework of the v3 flow to allow basic cropping and better support future post types
- Profile badges show if a user is following you or is an admin
- Show confirmation message when muting or blocking a user from a post
- Allow "read more" to be disabled on posts
- Allow "read more" to be disabled on posts [#1545](https://github.com/pixelfed/pixelfed/pull/1545)
- Loops! Discover short videos
- Preliminary support for profile PropertyValue metadata
- Preliminary support for Direct Messages
- Places! Run the artisan task `import:cities`
- Emails are now validated and banned email domains are disallowed at signup. Artisan task `email:bancheck` will validate existing users.
- .env vars `REDIS_SCHEME` and `REDIS_PATH` allow for using Redis over a Unix socket instead of TCP
- .env vars `REDIS_SCHEME` and `REDIS_PATH` allow for using Redis over a Unix socket instead of TCP [#1602](https://github.com/pixelfed/pixelfed/pull/1602)
- .env var `IMAGE_DRIVER` allows using imagick instead of gd
### Fixed
- Show delete button while composing video posts
- Show delete button while composing video posts [#1529](https://github.com/pixelfed/pixelfed/pull/1529)
- Show pending follow requests on private profiles
- Allow muted users to comment on your posts
- Allow muted users to comment on your posts [#1537](https://github.com/pixelfed/pixelfed/pull/1537)
- Bugs with carousel cursor and tooltips
- Collections can now be deleted from collection page
- Compose modal now indicates album media limits
......@@ -73,7 +86,7 @@
- Don't show Register link in navbar when registrations are closed
### Changed
- Use vue-masonry for Moment UI layout
- Use vue-masonry for Moment UI layout [#1536](https://github.com/pixelfed/pixelfed/pull/1536)
- User post limit changed from 20/hr to 50/hr
- Better mobile profile layout
- Dark mode is now a bit bluer
......@@ -85,17 +98,17 @@
## [v0.10.0 (2019-07-17)](https://github.com/pixelfed/pixelfed/compare/v0.9.6...v0.10.0)
### Added
- Collections! Add posts to Collections, similar to categories.
- Profile donate links: add links to Patreon, Liberapay, and OpenCollective on your profile
- Collections! Add posts to Collections, similar to categories. [#1511](https://github.com/pixelfed/pixelfed/pull/1511)
- Profile donate links: add links to Patreon, Liberapay, and OpenCollective on your profile [#1500](https://github.com/pixelfed/pixelfed/pull/1500)
### Fixed
- Show correct mode when viewing followers / following
### Changed
- Profile model now uses snowflake id
- Profile model now uses snowflake id [#1502](https://github.com/pixelfed/pixelfed/pull/1502)
### Removed
- OStatus legacy code has been removed
- OStatus legacy code has been removed [#1510](https://github.com/pixelfed/pixelfed/pull/1510)
## [v0.9.6 (2019-07-10)](https://github.com/pixelfed/pixelfed/compare/v0.9.5...v0.9.6)
......
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Str;
use App\Jobs\StatusPipeline\StatusDelete;
use Laravel\Passport\Passport;
use Auth, Cache, DB;
use App\{
Like,
Media,
Profile,
Status
};
use League\Fractal;
use App\Transformer\Api\{
AccountTransformer,
RelationshipTransformer,
StatusTransformer,
};
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Services\NotificationService;
class ApiV1Controller extends Controller
{
protected $fractal;
public function __construct()
{
$this->fractal = new Fractal\Manager();
$this->fractal->setSerializer(new ArraySerializer());
}
public function apps(Request $request)
{
abort_if(!config('pixelfed.oauth_enabled'), 404);
$this->validate($request, [
'client_name' => 'required',
'redirect_uris' => 'required',
'scopes' => 'nullable',
'website' => 'nullable'
]);
$client = Passport::client()->forceFill([
'user_id' => null,
'name' => e($request->client_name),
'secret' => Str::random(40),
'redirect' => $request->redirect_uris,
'personal_access_client' => false,
'password_client' => false,
'revoked' => false,
]);
$client->save();
$res = [
'id' => $client->id,
'name' => $client->name,
'website' => null,
'redirect_uri' => $client->redirect,
'client_id' => $client->id,
'client_secret' => $client->secret,
'vapid_key' => null
];
return $res;
}
public function accountById(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 statusById(Request $request, $id)
{
$status = Status::whereVisibility('public')->findOrFail($id);
$resource = new Fractal\Resource\Item($status, new StatusTransformer());
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res);
}
public function instance(Request $request)
{
$res = [
'description' => 'Pixelfed - Photo sharing for everyone',
'email' => config('instance.email'),
'languages' => ['en'],
'max_toot_chars' => config('pixelfed.max_caption_length'),
'registrations' => config('pixelfed.open_registration'),
'stats' => [
'user_count' => 0,
'status_count' => 0,
'domain_count' => 0
],
'thumbnail' => config('app.url') . '/img/pixelfed-icon-color.png',
'title' => 'Pixelfed (' . config('pixelfed.domain.app') . ')',
'uri' => config('app.url'),
'urls' => [],
'version' => '2.7.2 (compatible; Pixelfed ' . config('pixelfed.version') . ')'
];
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
}
public function filters(Request $request)
{
// Pixelfed does not yet support keyword filters
return response()->json([]);
}
public function context(Request $request)
{
// todo
$res = [
'ancestors' => [],
'descendants' => []
];
return response()->json($res);
}
}
\ No newline at end of file
......@@ -40,40 +40,34 @@ class BaseApiController extends Controller
public function __construct()
{
$this->middleware('auth');
// $this->middleware('auth');
$this->fractal = new Fractal\Manager();
$this->fractal->setSerializer(new ArraySerializer());
}
public function notifications(Request $request)
{
$pid = Auth::user()->profile->id;
$pg = $request->input('pg');
if($pg == true) {
$timeago = Carbon::now()->subMonths(6);
$notifications = Notification::whereProfileId($pid)
->whereDate('created_at', '>', $timeago)
->latest()
->simplePaginate(10);
$resource = new Fractal\Resource\Collection($notifications, new NotificationTransformer());
$res = $this->fractal->createData($resource)->toArray();
} else {
$this->validate($request, [
'page' => 'nullable|integer|min:1|max:10',
'limit' => 'nullable|integer|min:1|max:10'
]);
$limit = $request->input('limit') ?? 10;
$page = $request->input('page') ?? 1;
$end = (int) $page * $limit;
$start = (int) $end - $limit;
$res = NotificationService::get($pid, $start, $end);
}
abort_if(!$request->user(), 403);
$pid = $request->user()->profile_id;
$this->validate($request, [
'page' => 'nullable|integer|min:1|max:10',
'limit' => 'nullable|integer|min:1|max:40'
]);
$limit = $request->input('limit') ?? 10;
$timeago = Carbon::now()->subMonths(6);
$notifications = Notification::whereProfileId($pid)
->whereDate('created_at', '>', $timeago)
->latest()
->simplePaginate($limit);
$resource = new Fractal\Resource\Collection($notifications, new NotificationTransformer());
$res = $this->fractal->createData($resource)->toArray();
return response()->json($res);
}
public function accounts(Request $request, $id)
{
abort_if(!$request->user(), 403);
$profile = Profile::findOrFail($id);
$resource = new Fractal\Resource\Item($profile, new AccountTransformer());
$res = $this->fractal->createData($resource)->toArray();
......@@ -83,6 +77,7 @@ class BaseApiController extends Controller
public function accountFollowers(Request $request, $id)
{
abort_if(!$request->user(), 403);
$profile = Profile::findOrFail($id);
$followers = $profile->followers;
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer());
......@@ -93,6 +88,7 @@ class BaseApiController extends Controller
public function accountFollowing(Request $request, $id)
{
abort_if(!$request->user(), 403);
$profile = Profile::findOrFail($id);
$following = $profile->following;
$resource = new Fractal\Resource\Collection($following, new AccountTransformer());
......@@ -103,6 +99,7 @@ class BaseApiController extends Controller
public function accountStatuses(Request $request, $id)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'only_media' => 'nullable',
'pinned' => 'nullable',
......@@ -152,6 +149,7 @@ class BaseApiController extends Controller
public function avatarUpdate(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'upload' => 'required|mimes:jpeg,png,gif|max:'.config('pixelfed.max_avatar_size'),
]);
......@@ -188,6 +186,7 @@ class BaseApiController extends Controller
public function showTempMedia(Request $request, int $profileId, $mediaId)
{
abort_if(!$request->user(), 403);
abort_if(!$request->hasValidSignature(), 404);
abort_if(Auth::user()->profile_id !== $profileId, 404);
$media = Media::whereProfileId(Auth::user()->profile_id)->findOrFail($mediaId);
......@@ -197,6 +196,7 @@ class BaseApiController extends Controller
public function uploadMedia(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'file.*' => function() {
return [
......@@ -278,6 +278,7 @@ class BaseApiController extends Controller
public function deleteMedia(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'id' => 'required|integer|min:1|exists:media,id'
]);
......@@ -299,12 +300,21 @@ class BaseApiController extends Controller
public function verifyCredentials(Request $request)
{
abort_if(!$request->user(), 403);
$id = Auth::id();
$res = Cache::remember('user:account:id:'.$id, now()->addHours(6), function() use($id) {
$profile = Profile::whereNull('status')->whereUserId($id)->firstOrFail();
$resource = new Fractal\Resource\Item($profile, new AccountTransformer());
return $this->fractal->createData($resource)->toArray();
$res = $this->fractal->createData($resource)->toArray();
$res['source'] = [
'privacy' => $profile->is_private ? 'private' : 'public',
'sensitive' => $profile->cw ? true : false,
'language' => 'en',
'note' => '',
'fields' => []
];
return $res;
});
return response()->json($res);
......
......@@ -21,21 +21,13 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
class CommentController extends Controller
{
public function show(Request $request, $username, int $id, int $cid)
{
$user = Profile::whereUsername($username)->firstOrFail();
$status = Status::whereProfileId($user->id)->whereInReplyToId($id)->findOrFail($cid);
return view('status.reply', compact('user', 'status'));
}
public function showAll(Request $request, $username, int $id)
{
$user = Profile::whereUsername($username)->firstOrFail();
$status = Status::whereProfileId($user->id)->findOrFail($id);
$replies = Status::whereInReplyToId($id)->paginate(40);
$profile = Profile::whereNull(['status', 'domain'])->whereUsername($username)->firstOrFail();
$status = Status::whereProfileId($profile->id)->findOrFail($id);
$replies = Status::whereInReplyToId($id)->simplePaginate(40);
return view('status.comments', compact('user', 'status', 'replies'));
return view('status.comments', compact('profile', 'status', 'replies'));
}
public function store(Request $request)
......
......@@ -129,7 +129,7 @@ class FederationController extends Controller
$res['openRegistrations'] = config('pixelfed.open_registration');
return response()->json($res, 200, [
'Access-Control-Allow-Origin' => '*'
]);
], JSON_PRETTY_PRINT);
}
public function webfinger(Request $request)
......
......@@ -38,7 +38,6 @@ class FollowerController extends Controller
{
$user = Auth::user()->profile;
$target = Profile::where('id', '!=', $user->id)->whereNull('status')->findOrFail($item);
$private = (bool) $target->is_private;
$remote = (bool) $target->domain;
......@@ -54,7 +53,7 @@ class FollowerController extends Controller
$isFollowing = Follower::whereProfileId($user->id)->whereFollowingId($target->id)->exists();
if($private == true && $isFollowing == 0 || $remote == true) {
if($private == true && $isFollowing == 0 && $remote == true) {
if($user->following()->count() >= Follower::MAX_FOLLOWING) {
abort(400, 'You cannot follow more than ' . Follower::MAX_FOLLOWING . ' accounts');
}
......
......@@ -223,7 +223,7 @@ class PublicApiController extends Controller
'page' => 'nullable|integer|max:40',
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'limit' => 'nullable|integer|max:20'
'limit' => 'nullable|integer|max:30'
]);
if(config('instance.timeline.local.is_public') == false && !Auth::check()) {
......@@ -332,7 +332,7 @@ class PublicApiController extends Controller
'page' => 'nullable|integer|max:40',
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
'limit' => 'nullable|integer|max:20'
'limit' => 'nullable|integer|max:40'
]);
$page = $request->input('page');
......
......@@ -38,12 +38,14 @@ trait HomeSettings
'name' => 'required|string|max:'.config('pixelfed.max_name_length'),
'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'),
'website' => 'nullable|url',
'language' => 'nullable|string|min:2|max:5'
]);
$changes = false;
$name = strip_tags(Purify::clean($request->input('name')));
$bio = $request->filled('bio') ? strip_tags(Purify::clean($request->input('bio'))) : null;
$website = $request->input('website');
$language = $request->input('language');
$user = Auth::user();
$profile = $user->profile;
$layout = $request->input('profile_layout');
......@@ -51,10 +53,10 @@ trait HomeSettings
$layout = !in_array($layout, ['metro', 'moment']) ? 'metro' : $layout;
}
$validate = config('pixelfed.enforce_email_verification');
$enforceEmailVerification = config('pixelfed.enforce_email_verification');
// Only allow email to be updated if not yet verified
if (!$validate || !$changes && $user->email_verified_at) {
if (!$enforceEmailVerification || !$changes && $user->email_verified_at) {
if ($profile->name != $name) {
$changes = true;
$user->name = $name;
......@@ -71,9 +73,12 @@ trait HomeSettings
$profile->bio = $bio;
}
if ($profile->profile_layout != $layout) {
if($user->language != $language &&
in_array($language, \App\Util\Localization\Localization::languages())
) {
$changes = true;
$profile->profile_layout = $layout;
$user->language = $language;
session()->put('locale', $language);
}
}
......
......@@ -34,6 +34,11 @@ class SiteController extends Controller
// todo: add other locales after pushing new l10n strings
$locales = Localization::languages();
if(in_array($locale, $locales)) {
if($request->user()) {
$user = $request->user();
$user->language = $locale;
$user->save();
}
session()->put('locale', $locale);
}
......
......@@ -36,6 +36,7 @@ class AuthLogin
$this->userState($user);
$this->userDevice($user);
$this->userProfileId($user);
$this->userLanguage($user);
}
protected function userProfile($user)
......@@ -132,4 +133,9 @@ class AuthLogin
});
}
}
protected function userLanguage($user)
{
session()->put('locale', $user->language ?? 'en');
}
}
......@@ -4,6 +4,7 @@ namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;
use Gate;
class AuthServiceProvider extends ServiceProvider
{
......@@ -32,14 +33,22 @@ class AuthServiceProvider extends ServiceProvider
Passport::enableImplicitGrant();
Passport::setDefaultScope([
'user:read',
'user:write'
'read',
'write',
'follow',
'push'
]);
Passport::tokensCan([
'user:read' => 'Read a user’s profile info and media',
'user:write' => 'This scope lets an app "Change your profile information"',
'read' => 'Full read access to your account',
'write' => 'Full write access to your account',
'follow' => 'Ability to follow other profiles',
'push' => ''
]);
}
Gate::define('viewWebSocketsDashboard', function ($user = null) {
return $user->is_admin;
});
}
}
......@@ -14,29 +14,32 @@ class AccountTransformer extends Fractal\TransformerAbstract
public function transform(Profile $profile)
{
$is_admin = $profile->domain ? false : $profile->user->is_admin;
$local = $profile->domain == null;
$is_admin = !$local ? false : $profile->user->is_admin;
$acct = $local ? $profile->username : substr($profile->username, 1);
$username = $local ? $profile->username : explode('@', $acct)[0];
return [
'id' => (string) $profile->id,
'username' => $profile->username,
'acct' => $profile->username,
'username' => $username,
'acct' => $acct,
'display_name' => $profile->name,
'locked' => (bool) $profile->is_private,
'created_at' => null,
'created_at' => $profile->created_at->format('c'),
'followers_count' => $profile->followerCount(),
'following_count' => $profile->followingCount(),
'statuses_count' => (int) $profile->statusCount(),
'note' => $profile->bio,
'note' => $profile->bio ?? '',
'url' => $profile->url(),
'avatar' => $profile->avatarUrl(),
'avatar_static' => $profile->avatarUrl(),
'header' => null,
'header_static' => null,
'header' => '',
'header_static' => '',
'header_bg' => $profile->header_bg,
'emojis' => [],
'moved' => null,
'fields' => null,
'bot' => null,
'fields' => [],
'bot' => false,
'website' => $profile->website,
'software' => 'pixelfed',