mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-26 16:23:16 +00:00
commit
7f0476f02e
8 changed files with 212 additions and 28 deletions
|
@ -12,6 +12,7 @@
|
|||
- Updated RemoteProfile, fix missing content warnings ([e487527a](https://github.com/pixelfed/pixelfed/commit/e487527a))
|
||||
- Updated RemotePost component, fix missing like button on comments ([7ef90565](https://github.com/pixelfed/pixelfed/commit/7ef90565))
|
||||
- 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))
|
||||
|
||||
|
||||
## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9)
|
||||
|
|
15
README.md
15
README.md
|
@ -2,7 +2,6 @@
|
|||
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/pixelfed/pixelfed"><img src="https://circleci.com/gh/pixelfed/pixelfed.svg?style=svg" alt="Build Status"></a>
|
||||
<a href="https://codeclimate.com/github/pixelfed/pixelfed/maintainability"><img src="https://api.codeclimate.com/v1/badges/0b038ebb94ab9080ef59/maintainability" /></a>
|
||||
<a href="https://packagist.org/packages/pixelfed/pixelfed"><img src="https://poser.pugx.org/pixelfed/pixelfed/v/stable.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/pixelfed/pixelfed"><img src="https://poser.pugx.org/pixelfed/pixelfed/license.svg" alt="License"></a>
|
||||
</p>
|
||||
|
@ -29,25 +28,13 @@ The ways you can communicate on the project are below. Before interacting, pleas
|
|||
read through the [Code Of Conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
* IRC: [#pixelfed](irc://chat.freenode.net/pixelfed) on irc.freenode.net
|
||||
* Matrix: [#pixelfed:matrix.org](https://matrix.to/#/#pixelfed:matrix.org)
|
||||
* Mastodon: [@pixelfed@mastodon.social](https://mastodon.social/@pixelfed)
|
||||
* E-mail: [hello@pixelfed.org](mailto:hello@pixelfed.org)
|
||||
|
||||
### Development
|
||||
|
||||
For more development focused communication methods, please visit the following:
|
||||
|
||||
* IRC: [#pixelfed-dev](irc://chat.freenode.net/pixelfed-dev) on irc.freenode.net
|
||||
* Matrix: [#pixelfed-dev:matrix.org](https://matrix.to/#/#pixelfed-dev:matrix.org)
|
||||
* Mastodon: [@pixeldev@mastodon.social](https://mastodon.social/@pixeldev)
|
||||
|
||||
|
||||
## Pixelfed Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Pixelfed development. If you are interested in becoming a sponsor, please visit the Pixelfed [Patreon Page](https://www.patreon.com/dansup/overview)
|
||||
|
||||
- [NLnet Foundation](https://nlnet.nl) and [NGI0
|
||||
Discovery](https://nlnet.nl/discovery/), part of the [Next Generation
|
||||
Internet](https://ngi.eu) initiative.
|
||||
- [<img src="https://td-misc-public.s3.amazonaws.com/OscillasLogo.png" width="100px">](https://oscillas.com/)
|
||||
- Managed Pixelfed Hosting by [Spacebear](https://app.spacebear.ee/)
|
||||
Internet](https://ngi.eu) initiative.
|
|
@ -40,8 +40,14 @@ class FixUsernames extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->line(' ');
|
||||
$this->info('Collecting data ...');
|
||||
$this->line(' ');
|
||||
$this->restrictedCheck();
|
||||
}
|
||||
|
||||
protected function restrictedCheck()
|
||||
{
|
||||
$affected = collect([]);
|
||||
|
||||
$restricted = RestrictedNames::get();
|
||||
|
@ -61,6 +67,7 @@ class FixUsernames extends Command
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
if($affected->count() > 0) {
|
||||
$this->info('Found: ' . $affected->count() . ' affected usernames');
|
||||
|
||||
|
@ -118,7 +125,40 @@ class FixUsernames extends Command
|
|||
|
||||
$this->info('Fixed ' . $affected->count() . ' usernames!');
|
||||
} else {
|
||||
$this->info('No affected usernames found!');
|
||||
$this->info('No restricted usernames found!');
|
||||
}
|
||||
$this->line(' ');
|
||||
$this->versionZeroTenNineFix();
|
||||
}
|
||||
|
||||
protected function versionZeroTenNineFix()
|
||||
{
|
||||
$profiles = Profile::whereNotNull('domain')
|
||||
->whereNull('private_key')
|
||||
->where('username', 'not like', '@%@%')
|
||||
->get();
|
||||
|
||||
$count = $profiles->count();
|
||||
|
||||
if($count > 0) {
|
||||
$this->info("Found {$count} remote usernames to fix ...");
|
||||
$this->line(' ');
|
||||
} else {
|
||||
$this->info('No remote fixes found!');
|
||||
$this->line(' ');
|
||||
return;
|
||||
}
|
||||
foreach($profiles as $p) {
|
||||
$this->info("Fixed $p->username => $p->webfinger");
|
||||
$p->username = $p->webfinger ?? "@{$p->username}@{$p->domain}";
|
||||
if(Profile::whereUsername($p->username)->exists()) {
|
||||
return;
|
||||
}
|
||||
$p->save();
|
||||
}
|
||||
if($count > 0) {
|
||||
$this->line(' ');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,10 @@ class SearchController extends Controller
|
|||
$this->webfingerSearch();
|
||||
break;
|
||||
|
||||
case 'remote':
|
||||
$this->remoteLookupSearch();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -179,24 +183,15 @@ class SearchController extends Controller
|
|||
'following' => $item->followedBy(Auth::user()->profile),
|
||||
'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id),
|
||||
'thumb' => $item->avatarUrl(),
|
||||
'local' => (bool) !$item->domain
|
||||
'local' => (bool) !$item->domain,
|
||||
'post_count' => $item->statuses()->count()
|
||||
]
|
||||
]];
|
||||
return $tokens;
|
||||
});
|
||||
}
|
||||
}
|
||||
// elseif( Str::containsAll($tag, ['@', '.'])
|
||||
// config('federation.activitypub.enabled') == true &&
|
||||
// config('federation.activitypub.remoteFollow') == true
|
||||
// ) {
|
||||
// if(substr_count($tag, '@') == 2) {
|
||||
// $domain = last(explode('@', sub_str($u, 1)));
|
||||
// } else {
|
||||
// $domain = last(explode('@', $u));
|
||||
// }
|
||||
|
||||
// }
|
||||
else {
|
||||
$this->tokens['profiles'] = Cache::remember($key, $ttl, function() use($tag) {
|
||||
$users = Profile::select('domain', 'username', 'name', 'id')
|
||||
|
@ -268,4 +263,53 @@ class SearchController extends Controller
|
|||
];
|
||||
return;
|
||||
}
|
||||
|
||||
protected function remotePostLookup()
|
||||
{
|
||||
$tag = $this->term;
|
||||
$hash = hash('sha256', $tag);
|
||||
$local = Helpers::validateLocalUrl($tag);
|
||||
$valid = Helpers::validateUrl($tag);
|
||||
|
||||
if($valid == false || $local == true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Status::whereUri($tag)->whereLocal(false)->exists()) {
|
||||
$item = Status::whereUri($tag)->first();
|
||||
$this->tokens['posts'] = [[
|
||||
'count' => 0,
|
||||
'url' => "/i/web/post/_/$item->profile_id/$item->id",
|
||||
'type' => 'status',
|
||||
'username' => $item->profile->username,
|
||||
'caption' => $item->rendered ?? $item->caption,
|
||||
'thumb' => $item->firstMedia()->remote_url,
|
||||
'timestamp' => $item->created_at->diffForHumans()
|
||||
]];
|
||||
}
|
||||
|
||||
$remote = Helpers::fetchFromUrl($tag);
|
||||
|
||||
if(isset($remote['type']) && $remote['type'] == 'Note') {
|
||||
$item = Helpers::statusFetch($tag);
|
||||
$this->tokens['posts'] = [[
|
||||
'count' => 0,
|
||||
'url' => "/i/web/post/_/$item->profile_id/$item->id",
|
||||
'type' => 'status',
|
||||
'username' => $item->profile->username,
|
||||
'caption' => $item->rendered ?? $item->caption,
|
||||
'thumb' => $item->firstMedia()->remote_url,
|
||||
'timestamp' => $item->created_at->diffForHumans()
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
protected function remoteLookupSearch()
|
||||
{
|
||||
if(!Helpers::validateUrl($this->term)) {
|
||||
return;
|
||||
}
|
||||
$this->getProfiles();
|
||||
$this->remotePostLookup();
|
||||
}
|
||||
}
|
|
@ -416,7 +416,7 @@ class Helpers {
|
|||
if(!$profile) {
|
||||
$profile = new Profile();
|
||||
$profile->domain = strtolower($domain);
|
||||
$profile->username = strtolower(Purify::clean($remoteUsername));
|
||||
$profile->username = strtolower(Purify::clean($webfinger));
|
||||
$profile->name = isset($res['name']) ? Purify::clean($res['name']) : 'user';
|
||||
$profile->bio = isset($res['summary']) ? Purify::clean($res['summary']) : null;
|
||||
$profile->sharedInbox = isset($res['endpoints']) && isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : null;
|
||||
|
|
BIN
public/js/search.js
vendored
BIN
public/js/search.js
vendored
Binary file not shown.
Binary file not shown.
|
@ -175,6 +175,82 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="analysis == 'remote'" class="row">
|
||||
<div class="col-12 mb-5">
|
||||
<p class="h5 font-weight-bold text-dark">Showing results for <i>{{query}}</i></p>
|
||||
<hr>
|
||||
</div>
|
||||
<div v-if="results.profiles.length" class="col-md-6 offset-3">
|
||||
<a v-for="(profile, index) in results.profiles" class="mb-2 result-card" :href="buildUrl('profile', profile)">
|
||||
<div class="pb-3">
|
||||
<div class="media align-items-center py-2 pr-3">
|
||||
<img class="mr-3 rounded-circle border" :src="profile.entity.thumb" width="50px" height="50px" onerror="this.onerror=null;this.src='/storage/avatars/default.png';">
|
||||
<div class="media-body">
|
||||
<p class="mb-0 text-truncate text-dark font-weight-bold" data-toggle="tooltip" :title="profile.value">
|
||||
{{profile.value}}
|
||||
</p>
|
||||
<p class="mb-0 small font-weight-bold text-muted text-uppercase">
|
||||
{{profile.entity.post_count}} Posts
|
||||
</p>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<a v-if="profile.entity.following" class="btn btn-primary btn-sm font-weight-bold text-uppercase py-0" :href="buildUrl('profile', profile)">Following</a>
|
||||
<a v-else class="btn btn-outline-primary btn-sm font-weight-bold text-uppercase py-0" :href="buildUrl('profile', profile)">View</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="results.statuses.length" class="col-md-6 offset-3">
|
||||
<a v-for="(status, index) in results.statuses" class="mr-2 result-card" :href="buildUrl('status', status)">
|
||||
<img :src="status.thumb" width="90px" height="90px" class="mb-2" onerror="this.onerror=null;this.src='/storage/no-preview.png';">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="analysis == 'remotePost'" class="row">
|
||||
<div class="col-12 mb-5">
|
||||
<p class="h5 font-weight-bold text-dark">Showing results for <i>{{query}}</i></p>
|
||||
<hr>
|
||||
</div>
|
||||
<div class="col-md-6 offset-md-3">
|
||||
<div v-if="results.statuses.length">
|
||||
<div v-for="(status, index) in results.statuses" class="card mb-4 shadow-none border">
|
||||
<div class="card-header p-0 m-0">
|
||||
<div style="width: 100%;height: 200px;background: #fff">
|
||||
<div class="pt-4 text-center">
|
||||
<img :src="status.thumb" class="img-fluid border" style="max-height: 140px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mt-n4 mb-2">
|
||||
<div class="media">
|
||||
|
||||
<img class="rounded-circle p-1 mr-2 border mt-n3 bg-white shadow" src="/storage/avatars/default.png" width="70px" height="70px;" onerror="this.onerror=null;this.src='/storage/avatars/default.png';">
|
||||
<div class="media-body pt-3">
|
||||
<p class="font-weight-bold mb-0">{{status.username}}</p>
|
||||
</div>
|
||||
<div class="float-right pt-3">
|
||||
<p class="small mb-0 text-muted">{{status.timestamp}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-center mb-3 lead" v-html="status.caption"></p>
|
||||
<!-- <p class="text-center text-muted small text-uppercase mb-4">2 likes</p> -->
|
||||
<!-- <div class="d-flex justify-content-center">
|
||||
<a class="btn btn-primary btn-sm py-1 px-4 text-uppercase font-weight-bold" :href="status.url" style="font-weight: 500">View Post</a>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a class="btn btn-primary btn-block font-weight-bold rounded-0" :href="status.url">View Post</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="border py-3 text-center font-weight-bold">No results found</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="col-12">
|
||||
<p class="text-center text-muted lead font-weight-bold">No results found</p>
|
||||
</div>
|
||||
|
@ -232,6 +308,14 @@ export default {
|
|||
},
|
||||
|
||||
fetchSearchResults() {
|
||||
if(this.analysis == 'remote') {
|
||||
let term = this.query;
|
||||
let parsed = new URL(term);
|
||||
if(parsed.host === window.location.host) {
|
||||
window.location.href = term;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.searchContext(this.analysis);
|
||||
},
|
||||
|
||||
|
@ -266,7 +350,7 @@ export default {
|
|||
return 'webfinger';
|
||||
}
|
||||
|
||||
if(q.startsWith('@') || q.search('@') != -1) {
|
||||
if(q.startsWith('@')) {
|
||||
return 'profile';
|
||||
}
|
||||
|
||||
|
@ -320,6 +404,34 @@ export default {
|
|||
});
|
||||
break;
|
||||
|
||||
case 'remote':
|
||||
axios.get('/api/search', {
|
||||
params: {
|
||||
'q': this.query,
|
||||
'src': 'metro',
|
||||
'v': 1,
|
||||
'scope': 'remote'
|
||||
}
|
||||
}).then(res => {
|
||||
let results = res.data;
|
||||
this.results.hashtags = results.hashtags ? results.hashtags : [];
|
||||
this.results.profiles = results.profiles ? results.profiles : [];
|
||||
this.results.statuses = results.posts ? results.posts : [];
|
||||
|
||||
if(this.results.profiles.length) {
|
||||
this.analysis = 'profile';
|
||||
}
|
||||
if(this.results.statuses.length) {
|
||||
this.analysis = 'remotePost';
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(err => {
|
||||
this.loading = false;
|
||||
console.log(err);
|
||||
this.networkError = true;
|
||||
});
|
||||
break;
|
||||
|
||||
case 'hashtag':
|
||||
axios.get('/api/search', {
|
||||
params: {
|
||||
|
|
Loading…
Reference in a new issue