Merge pull request #2147 from pixelfed/staging

Bug fixes
This commit is contained in:
daniel 2020-04-27 21:39:06 -06:00 committed by GitHub
commit 2617b15ffd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 593 additions and 69 deletions

View file

@ -2,7 +2,9 @@
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.10.9...dev)
### Added
- ActivityPubFetchService for signed GET requests ([8763bfc5](https://github.com/pixelfed/pixelfed/commit/8763bfc5))
- Custom content warnings for remote posts ([6afc61a4](https://github.com/pixelfed/pixelfed/commit/6afc61a4))
- Thai translations ([https://github.com/pixelfed/pixelfed/commit/74cd536](https://github.com/pixelfed/pixelfed/commit/74cd536))
### Updated
- Updated PostComponent, fix remote urls ([42716ccc](https://github.com/pixelfed/pixelfed/commit/42716ccc))
@ -14,6 +16,17 @@
- Updated PublicApiControllers, fix block/mutes filtering on public timeline ([08383dd4](https://github.com/pixelfed/pixelfed/commit/08383dd4))
- Updated FixUsernames command, fixes remote username search ([0f943f67](https://github.com/pixelfed/pixelfed/commit/0f943f67))
- Updated Timeline component, fix mod tools ([b1d5eb05](https://github.com/pixelfed/pixelfed/commit/b1d5eb05))
- Updated Profile.vue component, fix pagination bug ([46767810](https://github.com/pixelfed/pixelfed/commit/46767810))
- Updated purify config, fix microformats support ([877023fb](https://github.com/pixelfed/pixelfed/commit/877023fb))
- Updated LikeController, fix likes_count bug ([996866cb](https://github.com/pixelfed/pixelfed/commit/996866cb))
- Updated AccountController, added followRequestJson method ([483548e2](https://github.com/pixelfed/pixelfed/commit/483548e2))
- Updated UserInvite model, added sender relation ([591a1929](https://github.com/pixelfed/pixelfed/commit/591a1929))
- Updated migrations, added UIKit ([fcab5010](https://github.com/pixelfed/pixelfed/commit/fcab5010))
- Updated AccountTransformer, added last_fetched_at attribute ([38b0233e](https://github.com/pixelfed/pixelfed/commit/38b0233e))
- Updated StoryItemTransformer, increase story length to 5 seconds ([924e424c](https://github.com/pixelfed/pixelfed/commit/924e424c))
- Updated StatusController, fix reblog_count bug ([1dc65e93](https://github.com/pixelfed/pixelfed/commit/1dc65e93))
- Updated NotificationCard.vue component, add follow requests at top of card, remove card-header ([5e48ffca](https://github.com/pixelfed/pixelfed/commit/5e48ffca))
- Updated RemoteProfile.vue component, add warning for empty profiles and last_fetched_at ([66f44a9d](https://github.com/pixelfed/pixelfed/commit/66f44a9d))
## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9)

View file

@ -327,6 +327,27 @@ class AccountController extends Controller
return view('account.follow-requests', compact('followers'));
}
public function followRequestsJson(Request $request)
{
$pid = Auth::user()->profile_id;
$followers = FollowRequest::whereFollowingId($pid)->orderBy('id','desc')->whereIsRejected(0)->get();
$res = [
'count' => $followers->count(),
'accounts' => $followers->take(10)->map(function($a) {
$actor = $a->actor;
return [
'id' => $actor->id,
'username' => $actor->username,
'avatar' => $actor->avatarUrl(),
'url' => $actor->url(),
'local' => $actor->domain == null,
'following' => $actor->followedBy(Auth::user()->profile)
];
})
];
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
}
public function followRequestHandle(Request $request)
{
$this->validate($request, [

View file

@ -27,7 +27,7 @@ class LikeController extends Controller
$profile = $user->profile;
$status = Status::findOrFail($request->input('item'));
$count = $status->likes_count;
$count = $status->likes()->count();
if ($status->likes()->whereProfileId($profile->id)->count() !== 0) {
$like = Like::whereProfileId($profile->id)->whereStatusId($status->id)->firstOrFail();

View file

@ -175,7 +175,7 @@ class StatusController extends Controller
->whereIn('scope', ['public', 'unlisted'])
->findOrFail($request->input('item'));
$count = $status->shares_count;
$count = $status->shares()->count();
$exists = Status::whereProfileId(Auth::user()->profile->id)
->whereReblogOfId($status->id)

View file

@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UIKitController extends Controller
{
//
}

View file

@ -8,6 +8,8 @@ class ModLog extends Model
{
protected $visible = ['id'];
protected $fillable = ['*'];
public function admin()
{
return $this->belongsTo(User::class, 'user_id');

View file

@ -18,7 +18,10 @@ class Profile extends Model
*/
public $incrementing = false;
protected $dates = ['deleted_at'];
protected $dates = [
'deleted_at',
'last_fetched_at'
];
protected $hidden = ['private_key'];
protected $visible = ['id', 'user_id', 'username', 'name'];
protected $fillable = ['user_id'];

View file

@ -0,0 +1,59 @@
<?php
namespace App\Services;
use Zttp\Zttp;
use App\Profile;
use App\Util\ActivityPub\Helpers;
use App\Util\ActivityPub\HttpSignature;
class ActivityPubFetchService
{
public $signed = true;
public $actor;
public $url;
public $headers = [
'Accept' => 'application/activity+json, application/json',
'User-Agent' => 'PixelfedBot - https://pixelfed.org'
];
public static function queue()
{
return new self;
}
public function signed($signed = true)
{
$this->signed = $signed;
return $this;
}
public function actor($profile)
{
$this->actor = $profile;
return $this;
}
public function url($url)
{
if(!Helpers::validateUrl($url)) {
throw new \Exception('Invalid URL');
}
$this->url = $url;
return $this;
}
public function get()
{
if($this->signed == true && $this->actor == null) {
throw new \Exception('Cannot sign request without actor');
}
return $this->signedRequest();
}
protected function signedRequest()
{
$this->headers = HttpSignature::sign($this->actor, $this->url, false, $this->headers);
return Zttp::withHeaders($this->headers)->get($this->url)->body();
}
}

View file

@ -34,7 +34,8 @@ class AccountTransformer extends Fractal\TransformerAbstract
'local' => (bool) $local,
'is_admin' => (bool) $is_admin,
'created_at' => $profile->created_at->toJSON(),
'header_bg' => $profile->header_bg
'header_bg' => $profile->header_bg,
'last_fetched_at' => optional($profile->last_fetched_at)->toJSON()
];
}

View file

@ -14,7 +14,7 @@ class StoryItemTransformer extends Fractal\TransformerAbstract
return [
'id' => (string) $item->id,
'type' => $item->type,
'length' => $item->duration != 0 ? $item->duration : 3,
'length' => 5,
'src' => $item->url(),
'preview' => null,
'link' => null,

21
app/UIKit.php Normal file
View file

@ -0,0 +1,21 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class UIKit extends Model
{
protected $table = 'uikit';
protected $fillable = [
'k',
'v',
'defv',
'dhis'
];
public static function section($k)
{
return (new self)->where('k', $k)->first()->v;
}
}

View file

@ -6,10 +6,13 @@ use Illuminate\Database\Eloquent\Model;
class UserInvite extends Model
{
public function sender()
{
return $this->belongsTo(Profile::class, 'profile_id');
}
public function url()
{
$path = '/i/invite/code';
$url = url($path, [$this->key, $this->token]);
return $url;
return url("/i/invite/code/{$this->key}/{$this->token}");
}
}

View file

@ -68,8 +68,8 @@ return [
*/
'HTML.Allowed' => env('RESTRICT_HTML_TYPES', true) ?
'a[href|title|rel],p,span,br' :
'a[href|title|rel],p,span,strong,em,del,b,i,s,strike,h1,h2,h3,h4,h5,h6,ul,ol,li,br',
'a[href|title|rel|class],p[class],span[class],br' :
'a[href|title|rel|class],p[class],span[class],strong,em,del,b,i,s,strike,h1,h2,h3,h4,h5,h6,ul,ol,li,br',
/*
@ -133,6 +133,27 @@ return [
'AutoFormat.RemoveEmpty' => false,
'Attr.AllowedClasses' => [
'h-feed',
'h-entry',
'h-cite',
'h-card',
'p-author',
'p-name',
'p-in-reply-to',
'p-repost-of',
'p-comment',
'u-photo',
'u-uid',
'u-url',
'dt-published',
'e-content',
'mention',
'hashtag',
'ellipsis',
'invisible'
],
'Attr.AllowedRel' => [
'noreferrer',
'noopener',

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUikitTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('uikit', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('k')->unique()->index();
$table->text('v')->nullable();
$table->json('meta')->nullable();
// default value for rollbacks
$table->text('defv')->nullable();
// delta history
$table->text('dhis')->nullable();
$table->unsignedInteger('edit_count')->default(0)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('uikit');
}
}

BIN
public/js/app.js vendored

Binary file not shown.

BIN
public/js/profile.js vendored

Binary file not shown.

BIN
public/js/rempro.js vendored

Binary file not shown.

BIN
public/js/status.js vendored

Binary file not shown.

BIN
public/js/timeline.js vendored

Binary file not shown.

Binary file not shown.

View file

@ -69,6 +69,31 @@ window.App.util = {
return 0;
}
return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count);
}),
timeAgo: (function(ts) {
let date = Date.parse(ts);
let seconds = Math.floor((new Date() - date) / 1000);
let interval = Math.floor(seconds / 31536000);
if (interval >= 1) {
return interval + "y";
}
interval = Math.floor(seconds / 604800);
if (interval >= 1) {
return interval + "w";
}
interval = Math.floor(seconds / 86400);
if (interval >= 1) {
return interval + "d";
}
interval = Math.floor(seconds / 3600);
if (interval >= 1) {
return interval + "h";
}
interval = Math.floor(seconds / 60);
if (interval >= 1) {
return interval + "m";
}
return Math.floor(seconds) + "s";
})
},
filters: [

View file

@ -2,19 +2,19 @@
<div>
<transition name="fade">
<div class="card notification-card shadow-none border">
<div class="card-header bg-white">
<p class="mb-0 d-flex align-items-center justify-content-between">
<span data-toggle="tooltip" data-placement="bottom"><i class="fas fa-redo fa-lg text-white"></i></span>
<span class="small text-dark text-uppercase font-weight-bold">Alerts</span>
<a class="text-decoration-none text-muted" href="/account/activity"><i class="fas fa-inbox fa-lg"></i></a>
</p>
</div>
<div class="card-body loader text-center" style="height: 200px;">
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<div class="card-body pt-2 px-0 py-0 contents" style="max-height: 200px; overflow-y: scroll;">
<div v-if="notifications.length > 0" class="card-body px-0 py-0 contents" style="max-height: 240px; overflow-y: scroll;">
<div v-if="profile.locked" class="media align-items-center mt-n2 px-3 py-2 border-bottom border-lighter bg-light cursor-pointer" @click="redirect('/account/follow-requests')">
<div class="media-body font-weight-light pt-2 small d-flex align-items-center justify-content-between">
<p class="mb-0 text-lighter"><i class="fas fa-cog text-light"></i></p>
<p class="text-center pt-1 mb-1 text-dark font-weight-bold"><strong>{{followRequests.count}}</strong> Follow Requests</p>
<p class="mb-0 text-lighter"><i class="fas fa-chevron-right"></i></p>
</div>
</div>
<div v-if="notifications.length > 0" class="media align-items-center px-3 py-2 border-bottom border-light" v-for="(n, index) in notifications">
<img class="mr-2 rounded-circle" style="border:1px solid #ccc" :src="n.account.avatar" alt="" width="32px" height="32px" onerror="this.onerror=null;this.src='/storage/avatars/default.png';">
<div class="media-body font-weight-light small">
@ -76,11 +76,20 @@
notifications: {},
notificationCursor: 2,
notificationMaxId: 0,
profile: {
locked: false
},
followRequests: null
};
},
mounted() {
let self = this;
this.fetchNotifications();
setTimeout(function() {
self.profile = window._sharedData.curUser;
self.fetchFollowRequests();
}, 500);
},
updated() {
@ -138,29 +147,7 @@
},
timeAgo(ts) {
let date = Date.parse(ts);
let seconds = Math.floor((new Date() - date) / 1000);
let interval = Math.floor(seconds / 31536000);
if (interval >= 1) {
return interval + "y";
}
interval = Math.floor(seconds / 604800);
if (interval >= 1) {
return interval + "w";
}
interval = Math.floor(seconds / 86400);
if (interval >= 1) {
return interval + "d";
}
interval = Math.floor(seconds / 3600);
if (interval >= 1) {
return interval + "h";
}
interval = Math.floor(seconds / 60);
if (interval >= 1) {
return interval + "m";
}
return Math.floor(seconds) + "s";
return window.App.util.format.timeAgo(ts);
},
mentionUrl(status) {
@ -219,6 +206,19 @@
}
}
});
},
fetchFollowRequests() {
if(window._sharedData.curUser.locked == true) {
axios.get('/account/follow-requests.json')
.then(res => {
this.followRequests = res.data;
})
}
},
redirect(url) {
window.location.href = url;
}
}
}

View file

@ -1066,29 +1066,7 @@ export default {
},
timeAgo(ts) {
let date = Date.parse(ts);
let seconds = Math.floor((new Date() - date) / 1000);
let interval = Math.floor(seconds / 31536000);
if (interval >= 1) {
return interval + "y";
}
interval = Math.floor(seconds / 604800);
if (interval >= 1) {
return interval + "w";
}
interval = Math.floor(seconds / 86400);
if (interval >= 1) {
return interval + "d";
}
interval = Math.floor(seconds / 3600);
if (interval >= 1) {
return interval + "h";
}
interval = Math.floor(seconds / 60);
if (interval >= 1) {
return interval + "m";
}
return Math.floor(seconds) + "s";
return App.util.format.timeAgo(ts);
},
emojiReaction() {

View file

@ -760,8 +760,13 @@
self.ids.push(d.id);
}
});
let max = Math.min(...this.ids);
if(max == this.max_id) {
$state.complete();
return;
}
this.min_id = Math.max(...this.ids);
this.max_id = Math.min(...this.ids);
this.max_id = max;
$state.loaded();
this.loading = false;
} else {

View file

@ -33,8 +33,7 @@
</span>
</div>
<p class="pl-2 h4 font-weight-bold mb-1">{{profile.display_name}}</p>
<p class="pl-2 font-weight-bold mb-1 text-muted">{{profile.acct}}</p>
<p class="pl-2 text-muted small pt-3" v-html="profile.note"></p>
<p class="pl-2 font-weight-bold mb-2 text-muted">{{profile.acct}}</p>
<p class="pl-2 text-muted small d-flex justify-content-between">
<span>
<span class="font-weight-bold text-dark">{{profile.statuses_count}}</span>
@ -49,8 +48,10 @@
<span>Followers</span>
</span>
</p>
<p class="pl-2 text-muted small pt-2" v-html="profile.note"></p>
</div>
</div>
<p class="small text-lighter p-2">Last updated: <time :datetime="profile.last_fetched_at">{{timeAgo(profile.last_fetched_at, 'ago')}}</time></p>
</div>
<div class="col-12 col-md-8 pt-5">
<div class="row">
@ -110,9 +111,18 @@
</div>
</div>
<!-- <div class="col-12 mt-4">
<div v-if="feed.length == 0" class="col-12 mb-2">
<div class="d-flex justify-content-center align-items-center bg-white border rounded" style="height:60vh;">
<div class="text-center">
<p class="mb-0 lead">No posts found.</p>
<p class="">We haven't seen any posts from this account.</p>
</div>
</div>
</div>
<div v-else class="col-12 mt-4">
<p class="text-center mb-0 px-0"><button class="btn btn-outline-primary btn-block font-weight-bold">Load More</button></p>
</div> -->
</div>
</div>
</div>
</div>
@ -191,6 +201,8 @@
warning: false,
ctxMenuStatus: false,
ctxMenuRelationship: false,
fetchingRemotePosts: false,
showMutualFollowers: false
}
},
@ -451,6 +463,24 @@
swal('Error', 'Something went wrong. Please try again later.', 'error');
});
},
manuallyFetchRemotePosts($event) {
this.fetchingRemotePosts = true;
event.target.blur();
swal(
'Fetching Remote Posts',
'Check back in a few minutes!',
'info'
);
},
timeAgo(ts, suffix = false) {
if(ts == null) {
return 'never';
}
suffix = suffix ? ' ' + suffix : '';
return App.util.format.timeAgo(ts) + suffix;
},
}
}
</script>

View file

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'ข้อมูลไม่ตรงกับบันทึกของเรา',
'throttle' => 'ลงชื่อเข้าหลายครั้งเกินไป โปรดลองอีกครั้งภายใน :seconds วินาที',
];

View file

@ -0,0 +1,11 @@
<?php
return [
'compose' => [
'invalid' => [
'album' => 'ต้องมีอย่างรูปหรือวีดิโอเป็นอย่างน้อย',
],
],
];

View file

@ -0,0 +1,26 @@
<?php
return [
'helpcenter' => 'Help Center',
'whatsnew' => 'What\'s New',
'gettingStarted' => 'Getting Started',
'sharingMedia' => 'Sharing Media',
'profile' => 'Profile',
'stories' => 'Stories',
'hashtags' => 'Hashtags',
'discover' => 'Discover',
'directMessages' => 'Direct Messages',
'timelines' => 'Timelines',
'embed' => 'Embed',
'communityGuidelines' => 'Community Guidelines',
'whatIsTheFediverse' => 'What is the fediverse?',
'controllingVisibility' => 'Controlling Visibility',
'blockingAccounts' => 'Blocking Accounts',
'safetyTips' => 'Safety Tips',
'reportSomething' => 'Report Something',
'dataPolicy' => 'Data Policy'
];

View file

@ -0,0 +1,19 @@
<?php
return [
'search' => 'ค้นหา',
'home' => 'หน้าหลัก',
'local' => 'Local',
'network' => 'Network',
'discover' => 'Discover',
'viewMyProfile' => 'ดูโพรไฟล์',
'myProfile' => 'โพรไฟล์ของฉัน',
'myTimeline' => 'ทามไลน์ของฉัน',
'publicTimeline' => 'ทามไลน์สาธารณะ',
'remoteFollow' => 'Remote Follow',
'settings' => 'ตั้งค่า',
'admin' => 'ผู้ดูแล',
'logout' => 'ออกจากระบบ',
'directMessages' => 'ส่งข้อความ',
'composePost' => 'สร้างโพสต์',
];

View file

@ -0,0 +1,12 @@
<?php
return [
'likedPhoto' => 'ถูกใจโพสต์ของคุณ',
'likedComment' => 'ถูกใจความเห็นของคุณ',
'startedFollowingYou' => 'ได้ติดตามคุณแล้ว',
'commented' => 'ได้แสดงความเห็นโพสต์ของคุณ',
'mentionedYou' => 'พูดถึงคุณ',
'shared' => 'แชร์โพสต์ของคุณ',
];

View file

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '« ก่อนหน้า',
'next' => 'ถัดไป »',
];

View file

@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'รหัสผ่านจำเป็นต้องมีอย่างน้อยหกตัวอักษร',
'reset' => 'คุณได้เปลี่ยนรหัสผ่านเรียบร้อยแล้ว!',
'sent' => 'หากอีเมลของคุณอยู่ในฐานข้อมูลของเราแล้ว ในอีกไม่กี่นาทีคุณจะได้รับอีเมลเพื่อแก้ใขรหัสผ่าน หากไม่พบอีเมล โปรดไปตรวจดูที่ถังขยะ',
'token' => 'ใช้รหัสผ่านนี้ไม่ได้',
'user' => 'หากอีเมลของคุณอยู่ในฐานข้อมูลของเราแล้ว ในอีกไม่กี่นาทีคุณจะได้รับอีเมลเพื่อแก้ใขรหัสผ่าน หากไม่พบอีเมล โปรดไปตรวจดูที่ถังขยะ',
];

View file

@ -0,0 +1,15 @@
<?php
return [
'emptyTimeline' => 'ผู้ใช้นี้ยังไม่มีโพสต์อะไร!',
'emptyFollowers' => 'ผู้ใช้นี้ยังไม่มีผู้ติดตาม!',
'emptyFollowing' => 'ผู้ใช้นี้ยังไม่ได้ติดตามใครเลย!',
'emptySaved' => 'คุณยังไม่ได้บันทึกโพสต์ใด ๆ!',
'savedWarning' => 'คุณเท่านั้นที่สามารถดูโพสต์นี้ได้',
'privateProfileWarning' => 'บัญชีนี้เป็นบัญชีส่วนบุคคล',
'alreadyFollow' => ':username ได้ติดตาม',
'loginToSeeProfile' => 'ดูรูปและวีดิโอ',
'status.disabled.header' => 'บัญชีไม่สามารถใช้งานได้',
'status.disabled.body' => 'เสียใจด้วยนะ บัญชีนี้ไม่สามารถใช้งานได้ โปรดลองใหม่อีกครั้ง',
];

View file

@ -0,0 +1,20 @@
<?php
return [
'about' => 'เกี่ยวกับ',
'help' => 'ช่วยเหลือ',
'language' => 'ภาษา',
'fediverse' => 'Fediverse',
'opensource' => 'Open Source',
'terms' => 'ข้อกำหนด',
'privacy' => 'ความเป็นส่วนตัว',
'l10nWip' => 'เรากำลังอยู่ดำเนินการแปลเป็นภาษาของคุณ',
'currentLocale' => 'สถานที่ปัจจุบัน',
'selectLocale' => 'เลือกภาษาที่ได้รับการสนับสนุน',
'contact' => 'ติดต่อ',
'contact-us' => 'ติดต่อเรา',
'places' => 'สถานที่',
'profiles' => 'โพรไฟล์',
];

View file

@ -0,0 +1,7 @@
<?php
return [
'emptyPersonalTimeline' => 'ทามไลน์ของคุณยังไม่มีอะไร!',
];

View file

@ -0,0 +1,122 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute must be accepted.',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
'alpha' => 'The :attribute may only contain letters.',
'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.',
'alpha_num' => 'The :attribute may only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'before' => 'The :attribute must be a date before :date.',
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
'between' => [
'numeric' => 'The :attribute must be between :min and :max.',
'file' => 'The :attribute must be between :min and :max kilobytes.',
'string' => 'The :attribute must be between :min and :max characters.',
'array' => 'The :attribute must have between :min and :max items.',
],
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'max' => [
'numeric' => 'The :attribute may not be greater than :max.',
'file' => 'The :attribute may not be greater than :max kilobytes.',
'string' => 'The :attribute may not be greater than :max characters.',
'array' => 'The :attribute may not have more than :max items.',
],
'mimes' => 'The :attribute must be a file of type: :values.',
'mimetypes' => 'The :attribute must be a file of type: :values.',
'min' => [
'numeric' => 'The :attribute must be at least :min.',
'file' => 'The :attribute must be at least :min kilobytes.',
'string' => 'The :attribute must be at least :min characters.',
'array' => 'The :attribute must have at least :min items.',
],
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values is present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
'size' => [
'numeric' => 'The :attribute must be :size.',
'file' => 'The :attribute must be :size kilobytes.',
'string' => 'The :attribute must be :size characters.',
'array' => 'The :attribute must contain :size items.',
],
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid zone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [],
];

View file

@ -272,6 +272,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::get('activity', 'AccountController@notifications')->name('notifications');
Route::get('follow-requests', 'AccountController@followRequests')->name('follow-requests');
Route::post('follow-requests', 'AccountController@followRequestHandle');
Route::get('follow-requests.json', 'AccountController@followRequestsJson');
});
Route::group(['prefix' => 'settings'], function () {