mirror of https://github.com/pixelfed/pixelfed
commit
7353bc2d8c
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\MediaTag;
|
||||
use App\Profile;
|
||||
use App\UserFilter;
|
||||
use App\User;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MediaTagController extends Controller
|
||||
{
|
||||
public function usernameLookup(Request $request)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'q' => 'required|string|min:1|max:50'
|
||||
]);
|
||||
|
||||
$q = $request->input('q');
|
||||
|
||||
if(Str::of($q)->startsWith('@')) {
|
||||
if(strlen($q) < 3) {
|
||||
return [];
|
||||
}
|
||||
$q = mb_substr($q, 1);
|
||||
}
|
||||
|
||||
$blocked = UserFilter::whereFilterableType('App\Profile')
|
||||
->whereFilterType('block')
|
||||
->whereFilterableId($request->user()->profile_id)
|
||||
->pluck('user_id');
|
||||
|
||||
$blocked->push($request->user()->profile_id);
|
||||
|
||||
$results = Profile::select('id','domain','username')
|
||||
->whereNotIn('id', $blocked)
|
||||
->whereNull('domain')
|
||||
->where('username','like','%'.$q.'%')
|
||||
->limit(15)
|
||||
->get()
|
||||
->map(function($r) {
|
||||
return [
|
||||
'id' => (string) $r->id,
|
||||
'name' => $r->username,
|
||||
'privacy' => true,
|
||||
'avatar' => $r->avatarUrl()
|
||||
];
|
||||
});
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MediaTag extends Model
|
||||
{
|
||||
public function status()
|
||||
{
|
||||
return $this->belongsTo(Status::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Cache;
|
||||
|
||||
class HashidService {
|
||||
|
||||
public const MIN_LIMIT = 15;
|
||||
public const CMAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
||||
|
||||
public static function encode($id)
|
||||
{
|
||||
if(!is_numeric($id) || $id > PHP_INT_MAX || strlen($id) < self::MIN_LIMIT) {
|
||||
return null;
|
||||
}
|
||||
$key = "hashids:{$id}";
|
||||
return Cache::remember($key, now()->hours(48), function() use($id) {
|
||||
$cmap = self::CMAP;
|
||||
$base = strlen($cmap);
|
||||
$shortcode = '';
|
||||
while($id) {
|
||||
$id = ($id - ($r = $id % $base)) / $base;
|
||||
$shortcode = $cmap{$r} . $shortcode;
|
||||
};
|
||||
return $shortcode;
|
||||
});
|
||||
}
|
||||
|
||||
public static function decode($short)
|
||||
{
|
||||
$len = strlen($short);
|
||||
if($len < 3 || $len > 11) {
|
||||
return null;
|
||||
}
|
||||
$id = 0;
|
||||
foreach(str_split($short) as $needle) {
|
||||
$pos = strpos(self::CMAP, $needle);
|
||||
// if(!$pos) {
|
||||
// return null;
|
||||
// }
|
||||
$id = ($id*64) + $pos;
|
||||
}
|
||||
if(strlen($id) < self::MIN_LIMIT) {
|
||||
return null;
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Media;
|
||||
use App\Profile;
|
||||
use App\User;
|
||||
|
||||
class MediaPathService {
|
||||
|
||||
public static function get($account, $version = 1)
|
||||
{
|
||||
$mh = hash('sha256', date('Y').'-.-'.date('m'));
|
||||
|
||||
if($account instanceOf User) {
|
||||
switch ($version) {
|
||||
// deprecated
|
||||
case 1:
|
||||
$monthHash = hash('sha1', date('Y').date('m'));
|
||||
$userHash = hash('sha1', $account->id . (string) $account->created_at);
|
||||
$path = "public/m/{$monthHash}/{$userHash}";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$monthHash = substr($mh, 0, 9).'-'.substr($mh, 9, 6);
|
||||
$userHash = $account->profile_id;
|
||||
$random = Str::random(12);
|
||||
$path = "public/m/_v2/{$userHash}/{$monthHash}/{$random}";
|
||||
break;
|
||||
|
||||
default:
|
||||
$monthHash = substr($mh, 0, 9).'-'.substr($mh, 9, 6);
|
||||
$userHash = $account->profile_id;
|
||||
$random = Str::random(12);
|
||||
$path = "public/m/_v2/{$userHash}/{$monthHash}/{$random}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($account instanceOf Profile) {
|
||||
$monthHash = substr($mh, 0, 9).'-'.substr($mh, 9, 6);
|
||||
$userHash = $account->id;
|
||||
$random = Str::random(12);
|
||||
$path = "public/m/_v2/{$userHash}/{$monthHash}/{$random}";
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Cache;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\Notification;
|
||||
use App\MediaTag;
|
||||
use League\Fractal;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
|
||||
class MediaTagService
|
||||
{
|
||||
const CACHE_KEY = 'pf:services:media_tags:id:';
|
||||
|
||||
public static function get($mediaId, $usernames = true)
|
||||
{
|
||||
$k = 'pf:services:media_tags:get:sid:' . $mediaId;
|
||||
return Cache::remember($k, now()->addMinutes(60), function() use($mediaId, $usernames) {
|
||||
$key = self::CACHE_KEY . $mediaId;
|
||||
if(Redis::zCount($key, '-inf', '+inf') == 0) {
|
||||
return [];
|
||||
}
|
||||
$res = Redis::zRange($key, 0, -1);
|
||||
if(!$usernames) {
|
||||
return $res;
|
||||
}
|
||||
$usernames = [];
|
||||
foreach ($res as $k) {
|
||||
$username = (new self)->idToUsername($k);
|
||||
array_push($usernames, $username);
|
||||
}
|
||||
|
||||
return $usernames;
|
||||
});
|
||||
}
|
||||
|
||||
public static function set($mediaId, $profileId)
|
||||
{
|
||||
$key = self::CACHE_KEY . $mediaId;
|
||||
Redis::zAdd($key, $profileId, $profileId);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function idToUsername($id)
|
||||
{
|
||||
$profile = ProfileService::build()->profileId($id);
|
||||
|
||||
if(!$profile) {
|
||||
return 'unavailable';
|
||||
}
|
||||
|
||||
return [
|
||||
'username' => $profile->username,
|
||||
'avatar' => $profile->avatarUrl()
|
||||
];
|
||||
}
|
||||
|
||||
public static function sendNotification(MediaTag $tag)
|
||||
{
|
||||
$p = $tag->status->profile;
|
||||
$actor = $p->username;
|
||||
$message = "{$actor} tagged you in a post.";
|
||||
$rendered = "<a href='/{$actor}' class='profile-link'>{$actor}</a> tagged you in a post.";
|
||||
$n = new Notification;
|
||||
$n->profile_id = $tag->profile_id;
|
||||
$n->actor_id = $p->id;
|
||||
$n->item_id = $tag->id;
|
||||
$n->item_type = 'App\MediaTag';
|
||||
$n->action = 'tagged';
|
||||
$n->message = $message;
|
||||
$n->rendered = $rendered;
|
||||
$n->save();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateMediaTagsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('media_tags', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->bigInteger('status_id')->unsigned()->index()->nullable();
|
||||
$table->bigInteger('media_id')->unsigned()->index();
|
||||
$table->bigInteger('profile_id')->unsigned()->index();
|
||||
$table->string('tagged_username')->nullable();
|
||||
$table->boolean('is_public')->default(true)->index();
|
||||
$table->json('metadata')->nullable();
|
||||
$table->unique(['media_id', 'profile_id']);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('media_tags');
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,29 @@
|
||||
@extends('site.help.partial.template', ['breadcrumb'=>'Tagging People'])
|
||||
|
||||
@section('section')
|
||||
|
||||
<div class="title">
|
||||
<h3 class="font-weight-bold">Tagging People</h3>
|
||||
</div>
|
||||
<hr>
|
||||
<p class="lead">Tag people in your posts without mentioning them in the caption.</p>
|
||||
<div class="py-4">
|
||||
<p class="font-weight-bold h5 pb-3">Tagging People in Posts</p>
|
||||
<ul>
|
||||
<li class="mb-3 ">You can only tag <span class="font-weight-bold">local</span> and <span class="font-weight-bold">public</span> accounts who haven't blocked you.</li>
|
||||
<li class="mb-3 ">You can tag up to <span class="font-weight-bold">10</span> people.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="card bg-primary border-primary" style="box-shadow: none !important;border: 3px solid #08d!important;">
|
||||
<div class="card-header text-light font-weight-bold h4 p-4 bg-primary">Tagging Tips</div>
|
||||
<div class="card-body bg-white p-3">
|
||||
<ul class="pt-3">
|
||||
<li class="lead mb-4">Tagging someone will send them a notification.</li>
|
||||
<li class="lead mb-4">You can untag yourself from posts.</li>
|
||||
<li class="lead ">Only tag people you know.</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
Loading…
Reference in New Issue