kopia lustrzana https://github.com/pixelfed/pixelfed
commit
a781c73e33
|
@ -138,6 +138,7 @@
|
|||
- Update admin instances dashboard ([ecfc0766](https://github.com/pixelfed/pixelfed/commit/ecfc0766))
|
||||
- Update ap helpers, fix album order bug by setting media order ([871f798c](https://github.com/pixelfed/pixelfed/commit/871f798c))
|
||||
- Update image pipeline, dispatch jobs to mmo queue and add "replace_id" param to v2/media endpoint to dispatch delayed MediaDeletePipeline job for original media id to improve media gc on supported clients ([5a67e9f9](https://github.com/pixelfed/pixelfed/commit/5a67e9f9))
|
||||
- Update admin instance management, improve filtering/sorting and add import/export support ([d5d9500d](https://github.com/pixelfed/pixelfed/commit/d5d9500d))
|
||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||
|
||||
## [v0.11.4 (2022-10-04)](https://github.com/pixelfed/pixelfed/compare/v0.11.3...v0.11.4)
|
||||
|
|
|
@ -97,7 +97,7 @@ trait AdminInstanceController
|
|||
return AdminInstance::collection(
|
||||
Instance::where('domain', 'like', '%' . $q . '%')
|
||||
->orderByDesc('user_count')
|
||||
->cursorPaginate(20)
|
||||
->cursorPaginate(10)
|
||||
->withQueryString()
|
||||
);
|
||||
}
|
||||
|
@ -120,23 +120,47 @@ trait AdminInstanceController
|
|||
'all'
|
||||
])
|
||||
],
|
||||
'sort' => [
|
||||
'sometimes',
|
||||
'string',
|
||||
Rule::in([
|
||||
'id',
|
||||
'domain',
|
||||
'software',
|
||||
'user_count',
|
||||
'status_count',
|
||||
'banned',
|
||||
'auto_cw',
|
||||
'unlisted'
|
||||
])
|
||||
],
|
||||
'dir' => 'sometimes|in:desc,asc'
|
||||
]);
|
||||
$filter = $request->input('filter');
|
||||
$query = $request->input('q');
|
||||
$sortCol = $request->input('sort');
|
||||
$sortDir = $request->input('dir');
|
||||
|
||||
return AdminInstance::collection(Instance::when($query, function($q, $qq) use($query) {
|
||||
return $q->where('domain', 'like', '%' . $query . '%');
|
||||
})
|
||||
->when($filter, function($q, $f) use($filter) {
|
||||
if($filter == 'cw') { return $q->whereAutoCw(true)->orderByDesc('id'); }
|
||||
if($filter == 'unlisted') { return $q->whereUnlisted(true)->orderByDesc('id'); }
|
||||
if($filter == 'banned') { return $q->whereBanned(true)->orderByDesc('id'); }
|
||||
if($filter == 'cw') { return $q->whereAutoCw(true); }
|
||||
if($filter == 'unlisted') { return $q->whereUnlisted(true); }
|
||||
if($filter == 'banned') { return $q->whereBanned(true); }
|
||||
if($filter == 'new') { return $q->orderByDesc('id'); }
|
||||
if($filter == 'popular_users') { return $q->orderByDesc('user_count'); }
|
||||
if($filter == 'popular_statuses') { return $q->orderByDesc('status_count'); }
|
||||
return $q->orderByDesc('id');
|
||||
}, function($q) {
|
||||
return $q->orderByDesc('id');
|
||||
})
|
||||
->when($sortCol, function($q, $s) use($sortCol, $sortDir, $filter) {
|
||||
if(!in_array($filter, ['popular_users', 'popular_statuses'])) {
|
||||
return $q->whereNotNull($sortCol)->orderBy($sortCol, $sortDir);
|
||||
}
|
||||
}, function($q) use($filter) {
|
||||
if(!$filter || !in_array($filter, ['popular_users', 'popular_statuses'])) {
|
||||
return $q->orderByDesc('id');
|
||||
}
|
||||
})
|
||||
->cursorPaginate(10)
|
||||
->withQueryString());
|
||||
|
@ -222,4 +246,57 @@ trait AdminInstanceController
|
|||
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function downloadBackup(Request $request)
|
||||
{
|
||||
return response()->streamDownload(function () {
|
||||
$json = [
|
||||
'version' => 1,
|
||||
'auto_cw' => Instance::whereAutoCw(true)->pluck('domain')->toArray(),
|
||||
'unlisted' => Instance::whereUnlisted(true)->pluck('domain')->toArray(),
|
||||
'banned' => Instance::whereBanned(true)->pluck('domain')->toArray(),
|
||||
'created_at' => now()->format('c'),
|
||||
];
|
||||
$chk = hash('sha256', json_encode($json, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
|
||||
$json['_sha256'] = $chk;
|
||||
echo json_encode($json, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
}, 'pixelfed-instances-mod.json');
|
||||
}
|
||||
|
||||
public function importBackup(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'banned' => 'sometimes|array',
|
||||
'auto_cw' => 'sometimes|array',
|
||||
'unlisted' => 'sometimes|array',
|
||||
]);
|
||||
|
||||
$banned = $request->input('banned');
|
||||
$auto_cw = $request->input('auto_cw');
|
||||
$unlisted = $request->input('unlisted');
|
||||
|
||||
foreach($banned as $i) {
|
||||
Instance::updateOrCreate(
|
||||
['domain' => $i],
|
||||
['banned' => true]
|
||||
);
|
||||
}
|
||||
|
||||
foreach($auto_cw as $i) {
|
||||
Instance::updateOrCreate(
|
||||
['domain' => $i],
|
||||
['auto_cw' => true]
|
||||
);
|
||||
}
|
||||
|
||||
foreach($unlisted as $i) {
|
||||
Instance::updateOrCreate(
|
||||
['domain' => $i],
|
||||
['unlisted' => true]
|
||||
);
|
||||
}
|
||||
|
||||
InstanceService::refresh();
|
||||
return [200];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,6 +150,6 @@ class DeleteRemoteProfilePipeline implements ShouldQueue
|
|||
|
||||
// Delete profile
|
||||
Profile::findOrFail($profile->id)->delete();
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ class DeleteRemoteStatusPipeline implements ShouldQueue
|
|||
}
|
||||
|
||||
NetworkTimelineService::del($status->id);
|
||||
Cache::forget(StatusService::key($status->id));
|
||||
StatusService::del($status->id, true);
|
||||
Bookmark::whereStatusId($status->id)->delete();
|
||||
Notification::whereItemType('App\Status')
|
||||
->whereItemId($status->id)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -17,3 +17,5 @@
|
|||
*/
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */
|
||||
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
|
|
@ -1 +1 @@
|
|||
(()=>{"use strict";var e,r,t,n={},o={};function a(e){var r=o[e];if(void 0!==r)return r.exports;var t=o[e]={id:e,loaded:!1,exports:{}};return n[e].call(t.exports,t,t.exports,a),t.loaded=!0,t.exports}a.m=n,e=[],a.O=(r,t,n,o)=>{if(!t){var c=1/0;for(l=0;l<e.length;l++){for(var[t,n,o]=e[l],d=!0,i=0;i<t.length;i++)(!1&o||c>=o)&&Object.keys(a.O).every((e=>a.O[e](t[i])))?t.splice(i--,1):(d=!1,o<c&&(c=o));if(d){e.splice(l--,1);var s=n();void 0!==s&&(r=s)}}return r}o=o||0;for(var l=e.length;l>0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[t,n,o]},a.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return a.d(r,{a:r}),r},a.d=(e,r)=>{for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce(((r,t)=>(a.f[t](e,r),r)),[])),a.u=e=>"js/"+{1084:"profile~followers.bundle",1983:"kb.bundle",2470:"home.chunk",2521:"about.bundle",2530:"discover~myhashtags.chunk",2586:"compose.chunk",2732:"dms~message.chunk",3351:"discover~settings.chunk",3365:"dms.chunk",3623:"discover~findfriends.chunk",4028:"error404.bundle",4509:"static~privacy.bundle",4958:"discover.chunk",4965:"discover~memories.chunk",5865:"post.chunk",6053:"notifications.chunk",6869:"profile.chunk",7004:"help.bundle",7019:"discover~hashtag.bundle",8021:"contact.bundle",8250:"i18n.bundle",8517:"daci.chunk",8600:"changelog.bundle",8625:"profile~following.bundle",8900:"discover~serverfeed.chunk",9144:"static~tos.bundle"}[e]+"."+{1084:"54e01681585449b4",1983:"275433a4dc3ffda8",2470:"354924ec54100041",2521:"bad10a127433fdef",2530:"37f576a8a46a143c",2586:"377e944cece35cfa",2732:"a69815bfbd4d4598",3351:"1d06076fe2bf8e0a",3365:"4fb5b1ec270b18a1",3623:"d4e3c589be8a9497",4028:"8aa1c24f3ce999a6",4509:"26054b5ae4c0bed4",4958:"185909b64f3f8f1d",4965:"399db214c5e4f701",5865:"734a9056e41a9e23",6053:"ee310da155305164",6869:"33c4423c66be53b7",7004:"bed48f9410486673",7019:"31c30c11f6e84825",8021:"14773b169db68356",8250:"d69fe365e8c72323",8517:"939287af71234f59",8600:"099f13cf06b3200a",8625:"f9d69ec593f3d011",8900:"c854ec838bd539c3",9144:"478ba7e3a3536a1b"}[e]+".js",a.miniCssF=e=>({138:"css/spa",703:"css/admin",1242:"css/appdark",6170:"css/app",8737:"css/portfolio",9994:"css/landing"}[e]+".css"),a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="pixelfed:",a.l=(e,n,o,c)=>{if(r[e])r[e].push(n);else{var d,i;if(void 0!==o)for(var s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var u=s[l];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==t+o){d=u;break}}d||(i=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,a.nc&&d.setAttribute("nonce",a.nc),d.setAttribute("data-webpack",t+o),d.src=e),r[e]=[n];var f=(t,n)=>{d.onerror=d.onload=null,clearTimeout(b);var o=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),o&&o.forEach((e=>e(n))),t)return t(n)},b=setTimeout(f.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=f.bind(null,d.onerror),d.onload=f.bind(null,d.onload),i&&document.head.appendChild(d)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.p="/",(()=>{var e={8929:0,1242:0,6170:0,8737:0,703:0,9994:0,138:0};a.f.j=(r,t)=>{var n=a.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(1242|138|6170|703|8737|8929|9994)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var c=a.p+a.u(r),d=new Error;a.l(c,(t=>{if(a.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),c=t&&t.target&&t.target.src;d.message="Loading chunk "+r+" failed.\n("+o+": "+c+")",d.name="ChunkLoadError",d.type=o,d.request=c,n[1](d)}}),"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,t)=>{var n,o,[c,d,i]=t,s=0;if(c.some((r=>0!==e[r]))){for(n in d)a.o(d,n)&&(a.m[n]=d[n]);if(i)var l=i(a)}for(r&&r(t);s<c.length;s++)o=c[s],a.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return a.O(l)},t=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),a.nc=void 0})();
|
||||
(()=>{"use strict";var e,r,t,n={},o={};function a(e){var r=o[e];if(void 0!==r)return r.exports;var t=o[e]={id:e,loaded:!1,exports:{}};return n[e].call(t.exports,t,t.exports,a),t.loaded=!0,t.exports}a.m=n,e=[],a.O=(r,t,n,o)=>{if(!t){var c=1/0;for(l=0;l<e.length;l++){for(var[t,n,o]=e[l],d=!0,i=0;i<t.length;i++)(!1&o||c>=o)&&Object.keys(a.O).every((e=>a.O[e](t[i])))?t.splice(i--,1):(d=!1,o<c&&(c=o));if(d){e.splice(l--,1);var s=n();void 0!==s&&(r=s)}}return r}o=o||0;for(var l=e.length;l>0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[t,n,o]},a.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return a.d(r,{a:r}),r},a.d=(e,r)=>{for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce(((r,t)=>(a.f[t](e,r),r)),[])),a.u=e=>"js/"+{1084:"profile~followers.bundle",1983:"kb.bundle",2470:"home.chunk",2521:"about.bundle",2530:"discover~myhashtags.chunk",2586:"compose.chunk",2732:"dms~message.chunk",3351:"discover~settings.chunk",3365:"dms.chunk",3623:"discover~findfriends.chunk",4028:"error404.bundle",4509:"static~privacy.bundle",4958:"discover.chunk",4965:"discover~memories.chunk",5865:"post.chunk",6053:"notifications.chunk",6869:"profile.chunk",7004:"help.bundle",7019:"discover~hashtag.bundle",8021:"contact.bundle",8250:"i18n.bundle",8517:"daci.chunk",8600:"changelog.bundle",8625:"profile~following.bundle",8900:"discover~serverfeed.chunk",9144:"static~tos.bundle"}[e]+"."+{1084:"54e01681585449b4",1983:"275433a4dc3ffda8",2470:"354924ec54100041",2521:"bad10a127433fdef",2530:"37f576a8a46a143c",2586:"377e944cece35cfa",2732:"a69815bfbd4d4598",3351:"1d06076fe2bf8e0a",3365:"4fb5b1ec270b18a1",3623:"d4e3c589be8a9497",4028:"8aa1c24f3ce999a6",4509:"26054b5ae4c0bed4",4958:"185909b64f3f8f1d",4965:"399db214c5e4f701",5865:"52e6d50f600ac40a",6053:"ee310da155305164",6869:"33c4423c66be53b7",7004:"bed48f9410486673",7019:"31c30c11f6e84825",8021:"14773b169db68356",8250:"d69fe365e8c72323",8517:"939287af71234f59",8600:"099f13cf06b3200a",8625:"f9d69ec593f3d011",8900:"c854ec838bd539c3",9144:"478ba7e3a3536a1b"}[e]+".js",a.miniCssF=e=>({138:"css/spa",703:"css/admin",1242:"css/appdark",6170:"css/app",8737:"css/portfolio",9994:"css/landing"}[e]+".css"),a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="pixelfed:",a.l=(e,n,o,c)=>{if(r[e])r[e].push(n);else{var d,i;if(void 0!==o)for(var s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var u=s[l];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==t+o){d=u;break}}d||(i=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,a.nc&&d.setAttribute("nonce",a.nc),d.setAttribute("data-webpack",t+o),d.src=e),r[e]=[n];var f=(t,n)=>{d.onerror=d.onload=null,clearTimeout(b);var o=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),o&&o.forEach((e=>e(n))),t)return t(n)},b=setTimeout(f.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=f.bind(null,d.onerror),d.onload=f.bind(null,d.onload),i&&document.head.appendChild(d)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.p="/",(()=>{var e={8929:0,1242:0,6170:0,8737:0,703:0,9994:0,138:0};a.f.j=(r,t)=>{var n=a.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(1242|138|6170|703|8737|8929|9994)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var c=a.p+a.u(r),d=new Error;a.l(c,(t=>{if(a.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),c=t&&t.target&&t.target.src;d.message="Loading chunk "+r+" failed.\n("+o+": "+c+")",d.name="ChunkLoadError",d.type=o,d.request=c,n[1](d)}}),"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,t)=>{var n,o,[c,d,i]=t,s=0;if(c.some((r=>0!==e[r]))){for(n in d)a.o(d,n)&&(a.m[n]=d[n]);if(i)var l=i(a)}for(r&&r(t);s<c.length;s++)o=c[s],a.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return a.O(l)},t=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),a.nc=void 0})();
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -16,17 +16,17 @@
|
|||
"/js/profile-directory.js": "/js/profile-directory.js?id=24a2b981bffa2038f41284eed81eec71",
|
||||
"/js/story-compose.js": "/js/story-compose.js?id=cbb169145db25c0e92845710233771ac",
|
||||
"/js/direct.js": "/js/direct.js?id=5816111700ad8f8a89c932485f3a1e47",
|
||||
"/js/admin.js": "/js/admin.js?id=3abc28357565ed6c694ba2cc25820044",
|
||||
"/js/admin.js": "/js/admin.js?id=83cdc452ce6a5d0356688d22ed391049",
|
||||
"/js/live-player.js": "/js/live-player.js?id=c987b3cd69e432f1b3d52da2901ed305",
|
||||
"/js/spa.js": "/js/spa.js?id=d4900cb1ac07c0f5677b1584cda72666",
|
||||
"/js/stories.js": "/js/stories.js?id=4db94699502e85543192865879bece7d",
|
||||
"/js/portfolio.js": "/js/portfolio.js?id=646ebcbb4cab1dc0942dde3f8126940d",
|
||||
"/js/installer.js": "/js/installer.js?id=cd240ae970947b76ac49032ba95e0922",
|
||||
"/js/admin_invite.js": "/js/admin_invite.js?id=307a53250701e3b12164af9495e88447",
|
||||
"/js/manifest.js": "/js/manifest.js?id=a4b1e34f87f1cb21e78233ae8e2306f6",
|
||||
"/js/manifest.js": "/js/manifest.js?id=78f0d0a7d75fe83458bfeea680974724",
|
||||
"/js/home.chunk.354924ec54100041.js": "/js/home.chunk.354924ec54100041.js?id=63d1868b688b0f3cdc2feadf9473e837",
|
||||
"/js/compose.chunk.377e944cece35cfa.js": "/js/compose.chunk.377e944cece35cfa.js?id=6389b021170bc21b58fc5bc28920f9af",
|
||||
"/js/post.chunk.734a9056e41a9e23.js": "/js/post.chunk.734a9056e41a9e23.js?id=97bcade1b38a7c3ddd5fd9f94e384ae9",
|
||||
"/js/post.chunk.52e6d50f600ac40a.js": "/js/post.chunk.52e6d50f600ac40a.js?id=8f329d1f6a12369981bb4d9e16148e96",
|
||||
"/js/profile.chunk.33c4423c66be53b7.js": "/js/profile.chunk.33c4423c66be53b7.js?id=98796453d2016cb8704c5885f4572c95",
|
||||
"/js/discover~memories.chunk.399db214c5e4f701.js": "/js/discover~memories.chunk.399db214c5e4f701.js?id=069ec21e0c9316de51707bb570eeb8bb",
|
||||
"/js/discover~myhashtags.chunk.37f576a8a46a143c.js": "/js/discover~myhashtags.chunk.37f576a8a46a143c.js?id=1bebedd3778a9d58c0b02cce1cd1e979",
|
||||
|
|
|
@ -0,0 +1,406 @@
|
|||
<template>
|
||||
<div class="post-timeline-component web-wrapper">
|
||||
<div v-if="isLoaded" class="container-fluid mt-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3 d-md-block">
|
||||
<sidebar :user="user" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-8 col-lg-6">
|
||||
<div v-if="isReply" class="p-3 rounded-top mb-n3" style="background-color: var(--card-header-accent)">
|
||||
<p>
|
||||
<i class="fal fa-reply mr-1"></i> In reply to
|
||||
|
||||
<a
|
||||
:href="'/i/web/profile/' + reply.account.id"
|
||||
class="font-weight-bold primary"
|
||||
@click.prevent="goToProfile(reply.account)">
|
||||
@{{ reply.account.acct }}
|
||||
</a>
|
||||
|
||||
<button
|
||||
@click.prevent="goToPost(reply)"
|
||||
class="btn btn-primary font-weight-bold btn-sm px-3 float-right rounded-pill">
|
||||
View Post
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<status
|
||||
:key="post.id"
|
||||
:status="post"
|
||||
:profile="user"
|
||||
v-on:menu="openContextMenu()"
|
||||
v-on:like="likeStatus()"
|
||||
v-on:unlike="unlikeStatus()"
|
||||
v-on:likes-modal="openLikesModal()"
|
||||
v-on:shares-modal="openSharesModal()"
|
||||
v-on:bookmark="handleBookmark()"
|
||||
v-on:share="shareStatus()"
|
||||
v-on:unshare="unshareStatus()"
|
||||
v-on:counter-change="counterChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="d-none d-lg-block col-lg-3">
|
||||
<rightbar />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="postStateError" class="container-fluid mt-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3 d-md-block">
|
||||
<sidebar :user="user" />
|
||||
</div>
|
||||
<div class="col-md-8 col-lg-6">
|
||||
<div class="card card-body shadow-none border">
|
||||
<div class="d-flex align-self-center flex-column" style="max-width: 500px;">
|
||||
<p class="text-center">
|
||||
<i class="far fa-exclamation-triangle fa-3x text-lighter"></i>
|
||||
</p>
|
||||
<p class="text-center lead font-weight-bold">Error displaying post</p>
|
||||
<p class="mb-0">This can happen for a few reasons:</p>
|
||||
<ul class="text-lighter">
|
||||
<li>The url is invalid or has a typo</li>
|
||||
<li>The page has been flagged for review by our automated abuse detection systems</li>
|
||||
<li>The content may have been deleted</li>
|
||||
<li>You do not have permission to view this content</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-none d-lg-block col-lg-3">
|
||||
<rightbar />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<context-menu
|
||||
v-if="isLoaded"
|
||||
ref="contextMenu"
|
||||
:status="post"
|
||||
:profile="user"
|
||||
@report-modal="handleReport()"
|
||||
@delete="deletePost()"
|
||||
/>
|
||||
|
||||
<likes-modal
|
||||
v-if="showLikesModal"
|
||||
ref="likesModal"
|
||||
:status="post"
|
||||
:profile="user"
|
||||
/>
|
||||
|
||||
<shares-modal
|
||||
v-if="showSharesModal"
|
||||
ref="sharesModal"
|
||||
:status="post"
|
||||
:profile="profile"
|
||||
/>
|
||||
|
||||
<report-modal
|
||||
v-if="post"
|
||||
ref="reportModal"
|
||||
:status="post"
|
||||
/>
|
||||
|
||||
<drawer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Drawer from './partials/drawer.vue';
|
||||
import Rightbar from './partials/rightbar.vue';
|
||||
import Sidebar from './partials/sidebar.vue';
|
||||
import Status from './partials/TimelineStatus.vue';
|
||||
import ContextMenu from './partials/post/ContextMenu.vue';
|
||||
import MediaContainer from './partials/post/MediaContainer.vue';
|
||||
import LikesModal from './partials/post/LikeModal.vue';
|
||||
import SharesModal from './partials/post/ShareModal.vue';
|
||||
import ReportModal from './partials/modal/ReportPost.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
cachedStatus: {
|
||||
type: Object
|
||||
},
|
||||
|
||||
cachedProfile: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"status": Status,
|
||||
"context-menu": ContextMenu,
|
||||
"media-container": MediaContainer,
|
||||
"likes-modal": LikesModal,
|
||||
"shares-modal": SharesModal,
|
||||
"rightbar": Rightbar,
|
||||
"report-modal": ReportModal
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: false,
|
||||
user: undefined,
|
||||
profile: undefined,
|
||||
post: undefined,
|
||||
relationship: {},
|
||||
media: undefined,
|
||||
mediaIndex: 0,
|
||||
showLikesModal: false,
|
||||
isReply: false,
|
||||
reply: {},
|
||||
showSharesModal: false,
|
||||
postStateError: false
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
watch: {
|
||||
'$route': 'init'
|
||||
},
|
||||
|
||||
methods: {
|
||||
init() {
|
||||
if(this.cachedStatus && this.cachedProfile) {
|
||||
this.post = this.cachedStatus;
|
||||
this.media = this.post.media_attachments;
|
||||
this.profile = this.post.account;
|
||||
this.user = this.cachedProfile;
|
||||
if(this.post.in_reply_to_id) {
|
||||
this.fetchReply();
|
||||
} else {
|
||||
this.isReply = false;
|
||||
this.fetchRelationship();
|
||||
}
|
||||
} else {
|
||||
this.fetchSelf();
|
||||
}
|
||||
},
|
||||
|
||||
fetchSelf() {
|
||||
this.user = window._sharedData.user;
|
||||
this.fetchPost();
|
||||
},
|
||||
|
||||
fetchPost() {
|
||||
axios.get('/api/pixelfed/v1/statuses/'+this.$route.params.id)
|
||||
.then(res => {
|
||||
if(!res.data || !res.data.hasOwnProperty('id')) {
|
||||
this.$router.push('/i/web/404');
|
||||
}
|
||||
this.post = res.data;
|
||||
this.media = this.post.media_attachments;
|
||||
this.profile = this.post.account;
|
||||
if(this.post.in_reply_to_id) {
|
||||
this.fetchReply();
|
||||
} else {
|
||||
this.fetchRelationship();
|
||||
}
|
||||
}).catch(err => {
|
||||
switch(err.response.status) {
|
||||
case 403:
|
||||
case 404:
|
||||
this.$router.push('/i/web/404');
|
||||
break;
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
fetchReply() {
|
||||
axios.get('/api/pixelfed/v1/statuses/' + this.post.in_reply_to_id)
|
||||
.then(res => {
|
||||
this.reply = res.data;
|
||||
this.isReply = true;
|
||||
this.fetchRelationship();
|
||||
})
|
||||
.catch(err => {
|
||||
this.fetchRelationship();
|
||||
})
|
||||
},
|
||||
|
||||
fetchRelationship() {
|
||||
if(this.profile.id == this.user.id) {
|
||||
this.relationship = {};
|
||||
this.fetchState();
|
||||
return;
|
||||
}
|
||||
|
||||
axios.get('/api/pixelfed/v1/accounts/relationships', {
|
||||
params: {
|
||||
'id[]': this.profile.id
|
||||
}
|
||||
}).then(res => {
|
||||
this.relationship = res.data[0];
|
||||
this.fetchState();
|
||||
});
|
||||
},
|
||||
|
||||
fetchState() {
|
||||
axios.get('/api/v2/statuses/'+this.post.id+'/state')
|
||||
.then(res => {
|
||||
this.post.favourited = res.data.liked;
|
||||
this.post.reblogged = res.data.shared;
|
||||
this.post.bookmarked = res.data.bookmarked;
|
||||
if(!this.post.favourites_count && this.post.favourited) {
|
||||
this.post.favourites_count = 1;
|
||||
}
|
||||
this.isLoaded = true;
|
||||
}).catch(err => {
|
||||
this.isLoaded = false;
|
||||
this.postStateError = true;
|
||||
})
|
||||
},
|
||||
|
||||
goBack() {
|
||||
this.$router.push('/i/web');
|
||||
},
|
||||
|
||||
likeStatus() {
|
||||
let count = this.post.favourites_count;
|
||||
this.post.favourites_count = count + 1;
|
||||
this.post.favourited = !this.post.favourited;
|
||||
|
||||
axios.post('/api/v1/statuses/' + this.post.id + '/favourite')
|
||||
.then(res => {
|
||||
//
|
||||
}).catch(err => {
|
||||
this.post.favourites_count = count;
|
||||
this.post.favourited = false;
|
||||
})
|
||||
},
|
||||
|
||||
unlikeStatus() {
|
||||
let count = this.post.favourites_count;
|
||||
this.post.favourites_count = count - 1;
|
||||
this.post.favourited = !this.post.favourited;
|
||||
|
||||
axios.post('/api/v1/statuses/' + this.post.id + '/unfavourite')
|
||||
.then(res => {
|
||||
//
|
||||
}).catch(err => {
|
||||
this.post.favourites_count = count;
|
||||
this.post.favourited = false;
|
||||
})
|
||||
},
|
||||
|
||||
shareStatus() {
|
||||
let count = this.post.reblogs_count;
|
||||
this.post.reblogs_count = count + 1;
|
||||
this.post.reblogged = !this.post.reblogged;
|
||||
|
||||
axios.post('/api/v1/statuses/' + this.post.id + '/reblog')
|
||||
.then(res => {
|
||||
//
|
||||
}).catch(err => {
|
||||
this.post.reblogs_count = count;
|
||||
this.post.reblogged = false;
|
||||
})
|
||||
},
|
||||
|
||||
unshareStatus() {
|
||||
let count = this.post.reblogs_count;
|
||||
this.post.reblogs_count = count - 1;
|
||||
this.post.reblogged = !this.post.reblogged;
|
||||
|
||||
axios.post('/api/v1/statuses/' + this.post.id + '/unreblog')
|
||||
.then(res => {
|
||||
//
|
||||
}).catch(err => {
|
||||
this.post.reblogs_count = count;
|
||||
this.post.reblogged = false;
|
||||
})
|
||||
},
|
||||
|
||||
openContextMenu() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.contextMenu.open();
|
||||
});
|
||||
},
|
||||
|
||||
openLikesModal() {
|
||||
this.showLikesModal = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.likesModal.open();
|
||||
});
|
||||
},
|
||||
|
||||
openSharesModal() {
|
||||
this.showSharesModal = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.sharesModal.open();
|
||||
});
|
||||
},
|
||||
|
||||
deletePost() {
|
||||
this.$router.push('/i/web');
|
||||
},
|
||||
|
||||
goToPost(post) {
|
||||
this.$router.push({
|
||||
name: 'post',
|
||||
path: `/i/web/post/${post.id}`,
|
||||
params: {
|
||||
id: post.id,
|
||||
cachedStatus: post,
|
||||
cachedProfile: this.user
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
goToProfile(account) {
|
||||
this.$router.push({
|
||||
name: 'profile',
|
||||
path: `/i/web/profile/${account.id}`,
|
||||
params: {
|
||||
id: account.id,
|
||||
cachedProfile: account,
|
||||
cachedUser: this.user
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
handleBookmark() {
|
||||
axios.post('/i/bookmark', {
|
||||
item: this.post.id
|
||||
})
|
||||
.then(res => {
|
||||
this.post.bookmarked = !this.post.bookmarked;
|
||||
})
|
||||
.catch(err => {
|
||||
this.$bvToast.toast('Cannot bookmark post at this time.', {
|
||||
title: 'Bookmark Error',
|
||||
variant: 'danger',
|
||||
autoHideDelay: 5000
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
handleReport() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.reportModal.open();
|
||||
});
|
||||
},
|
||||
|
||||
counterChange(type) {
|
||||
switch(type) {
|
||||
case 'comment-increment':
|
||||
this.post.reply_count = this.post.reply_count + 1;
|
||||
break;
|
||||
|
||||
case 'comment-decrement':
|
||||
this.post.reply_count = this.post.reply_count - 1;
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -39,6 +39,21 @@
|
|||
<div class="col-xl-2 col-md-6">
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-outline-white btn-block btn-sm mt-1" @click.prevent="showAddModal = true">Create New Instance</button>
|
||||
<div v-if="showImportForm">
|
||||
<div class="form-group mt-3">
|
||||
<div class="custom-file">
|
||||
<input ref="importInput" type="file" class="custom-file-input" id="customFile" v-on:change="onImportUpload">
|
||||
<label class="custom-file-label" for="customFile">Choose file</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 mt-n3">
|
||||
<a href="#" class="text-white font-weight-bold small" @click.prevent="showImportForm = false">Cancel</a>
|
||||
</p>
|
||||
</div>
|
||||
<div v-else class="d-flex mt-1">
|
||||
<button class="btn btn-outline-white btn-sm mt-1" @click="openImportForm">Import</button>
|
||||
<button class="btn btn-outline-white btn-block btn-sm mt-1" @click="downloadBackup()">Download Backup</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -111,7 +126,7 @@
|
|||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col" class="cursor-pointer" v-html="buildColumn('ID', 'id')" @click="toggleCol('id')"></th>
|
||||
<th scope="col" class="cursor-pointer" v-html="buildColumn('Domain', 'name')" @click="toggleCol('name')"></th>
|
||||
<th scope="col" class="cursor-pointer" v-html="buildColumn('Domain', 'domain')" @click="toggleCol('domain')"></th>
|
||||
<th scope="col" class="cursor-pointer" v-html="buildColumn('Software', 'software')" @click="toggleCol('software')"></th>
|
||||
<th scope="col" class="cursor-pointer" v-html="buildColumn('User Count', 'user_count')" @click="toggleCol('user_count')"></th>
|
||||
<th scope="col" class="cursor-pointer" v-html="buildColumn('Status Count', 'status_count')" @click="toggleCol('status_count')"></th>
|
||||
|
@ -293,7 +308,62 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
|
||||
<b-modal
|
||||
v-model="showImportModal"
|
||||
title="Import Instance Backup"
|
||||
ok-title="Import"
|
||||
scrollable
|
||||
:ok-disabled="!importData || (!importData.banned.length && !importData.unlisted.length && !importData.auto_cw.length)"
|
||||
@ok="completeImport"
|
||||
@cancel="cancelImport">
|
||||
<div v-if="showImportModal && importData">
|
||||
<div v-if="importData.auto_cw && importData.auto_cw.length" class="mb-5">
|
||||
<p class="font-weight-bold text-center my-0">NSFW Instances ({{importData.auto_cw.length}})</p>
|
||||
<p class="small text-center text-muted mb-1">Tap on an instance to remove it.</p>
|
||||
<div class="list-group">
|
||||
<a v-for="(instance, idx) in importData.auto_cw" class="list-group-item d-flex align-items-center justify-content-between" href="#" @click.prevent="filterImportData('auto_cw', idx)">
|
||||
{{ instance }}
|
||||
|
||||
<span class="badge badge-warning">Auto CW</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="importData.unlisted && importData.unlisted.length" class="mb-5">
|
||||
<p class="font-weight-bold text-center my-0">Unlisted Instances ({{importData.unlisted.length}})</p>
|
||||
<p class="small text-center text-muted mb-1">Tap on an instance to remove it.</p>
|
||||
<div class="list-group">
|
||||
<a v-for="(instance, idx) in importData.unlisted" class="list-group-item d-flex align-items-center justify-content-between" href="#" @click.prevent="filterImportData('unlisted', idx)">
|
||||
{{ instance }}
|
||||
|
||||
<span class="badge badge-primary">Unlisted</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="importData.banned && importData.banned.length" class="mb-5">
|
||||
<p class="font-weight-bold text-center my-0">Banned Instances ({{importData.banned.length}})</p>
|
||||
<p class="small text-center text-muted mb-1">Review instances, tap on an instance to remove it.</p>
|
||||
<div class="list-group">
|
||||
<a v-for="(instance, idx) in importData.banned" class="list-group-item d-flex align-items-center justify-content-between" href="#" @click.prevent="filterImportData('banned', idx)">
|
||||
{{ instance }}
|
||||
|
||||
<span class="badge badge-danger">Banned</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!importData.banned.length && !importData.unlisted.length && !importData.auto_cw.length">
|
||||
<div class="text-center">
|
||||
<p>
|
||||
<i class="far fa-check-circle fa-4x text-success"></i>
|
||||
</p>
|
||||
<p class="lead">Nothing to import!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -347,7 +417,10 @@
|
|||
auto_cw: false,
|
||||
unlisted: false,
|
||||
notes: undefined
|
||||
}
|
||||
},
|
||||
showImportForm: false,
|
||||
showImportModal: false,
|
||||
importData: undefined,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -355,31 +428,47 @@
|
|||
this.fetchStats();
|
||||
|
||||
let u = new URLSearchParams(window.location.search);
|
||||
if(u.has('filter') || u.has('cursor') && !u.has('q')) {
|
||||
let url = '/i/admin/api/instances/get?';
|
||||
if(u.has('filter') && !u.has('q') && !u.has('sort')) {
|
||||
const url = new URL(window.location.origin + '/i/admin/api/instances/get');
|
||||
|
||||
let filter = u.get('filter');
|
||||
if(filter) {
|
||||
this.tabIndex = this.filterMap.indexOf(filter);
|
||||
url = url + 'filter=' + filter + '&';
|
||||
if(u.has('filter')) {
|
||||
this.tabIndex = this.filterMap.indexOf(u.get('filter'));
|
||||
url.searchParams.set('filter', u.get('filter'));
|
||||
}
|
||||
if(u.has('cursor')) {
|
||||
url.searchParams.set('cursor', u.get('cursor'));
|
||||
}
|
||||
|
||||
let cursor = u.get('cursor');
|
||||
if(cursor) {
|
||||
url = url + 'cursor=' + cursor;
|
||||
this.fetchInstances(url.toString());
|
||||
} else if(u.has('sort') && !u.has('q')) {
|
||||
const url = new URL(window.location.origin + '/i/admin/api/instances/get');
|
||||
url.searchParams.set('sort', u.get('sort'));
|
||||
|
||||
if(u.has('dir')) {
|
||||
url.searchParams.set('dir', u.get('dir'));
|
||||
}
|
||||
|
||||
this.fetchInstances(url);
|
||||
if(u.has('filter')) {
|
||||
url.searchParams.set('filter', u.get('filter'));
|
||||
}
|
||||
|
||||
if(u.has('cursor')) {
|
||||
url.searchParams.set('cursor', u.get('cursor'));
|
||||
}
|
||||
|
||||
this.fetchInstances(url.toString());
|
||||
} else if(u.has('q')) {
|
||||
this.tabIndex = -1;
|
||||
this.searchQuery = u.get('q');
|
||||
let cursor = u.has('cursor');
|
||||
let q = u.get('q');
|
||||
let url = '/i/admin/api/instances/query?q=' + q;
|
||||
if(cursor) {
|
||||
url = url + '&cursor=' + u.get('cursor');
|
||||
|
||||
const url = new URL(window.location.origin + '/i/admin/api/instances/query');
|
||||
url.searchParams.set('q', u.get('q'));
|
||||
|
||||
if(u.has('cursor')) {
|
||||
url.searchParams.set('cursor', u.get('cursor'));
|
||||
}
|
||||
this.fetchInstances(url);
|
||||
|
||||
this.fetchInstances(url.toString());
|
||||
} else {
|
||||
this.fetchInstances();
|
||||
}
|
||||
|
@ -470,19 +559,52 @@
|
|||
},
|
||||
|
||||
toggleCol(col) {
|
||||
// this.sortCol = col;
|
||||
if(this.filterMap[this.tabIndex] == col || this.searchQuery) {
|
||||
return;
|
||||
}
|
||||
this.sortCol = col;
|
||||
|
||||
// if(!this.sortDir) {
|
||||
// this.sortDir = 'desc';
|
||||
// } else {
|
||||
// this.sortDir = this.sortDir == 'asc' ? 'desc' : 'asc';
|
||||
// }
|
||||
if(!this.sortDir) {
|
||||
this.sortDir = 'desc';
|
||||
} else {
|
||||
this.sortDir = this.sortDir == 'asc' ? 'desc' : 'asc';
|
||||
}
|
||||
|
||||
// let url = '/i/admin/api/hashtags/query?sort=' + col + '&dir=' + this.sortDir;
|
||||
// this.fetchHashtags(url);
|
||||
const url = new URL(window.location.origin + '/i/admin/instances');
|
||||
url.searchParams.set('sort', col);
|
||||
url.searchParams.set('dir', this.sortDir);
|
||||
if(this.tabIndex != 0) {
|
||||
url.searchParams.set('filter', this.filterMap[this.tabIndex]);
|
||||
}
|
||||
history.pushState(null, '', url);
|
||||
|
||||
const apiUrl = new URL(window.location.origin + '/i/admin/api/instances/get');
|
||||
apiUrl.searchParams.set('sort', col);
|
||||
apiUrl.searchParams.set('dir', this.sortDir);
|
||||
if(this.tabIndex != 0) {
|
||||
apiUrl.searchParams.set('filter', this.filterMap[this.tabIndex]);
|
||||
}
|
||||
|
||||
this.fetchInstances(apiUrl.toString());
|
||||
},
|
||||
|
||||
buildColumn(name, col) {
|
||||
if([1, 5, 6].indexOf(this.tabIndex) != -1 || (this.searchQuery && this.searchQuery.length)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
if(this.tabIndex === 2 && col === 'banned') {
|
||||
return name;
|
||||
}
|
||||
|
||||
if(this.tabIndex === 3 && col === 'auto_cw') {
|
||||
return name;
|
||||
}
|
||||
|
||||
if(this.tabIndex === 4 && col === 'unlisted') {
|
||||
return name;
|
||||
}
|
||||
|
||||
let icon = `<i class="far fa-sort"></i>`;
|
||||
if(col == this.sortCol) {
|
||||
icon = this.sortDir == 'desc' ?
|
||||
|
@ -496,17 +618,26 @@
|
|||
event.currentTarget.blur();
|
||||
let apiUrl = dir == 'next' ? this.pagination.next : this.pagination.prev;
|
||||
let cursor = dir == 'next' ? this.pagination.next_cursor : this.pagination.prev_cursor;
|
||||
let url = '/i/admin/instances?';
|
||||
if(this.tabIndex && !this.searchQuery) {
|
||||
url = url + 'filter=' + this.filterMap[this.tabIndex] + '&';
|
||||
}
|
||||
|
||||
const url = new URL(window.location.origin + '/i/admin/instances');
|
||||
|
||||
if(cursor) {
|
||||
url = url + 'cursor=' + cursor;
|
||||
url.searchParams.set('cursor', cursor);
|
||||
}
|
||||
|
||||
if(this.searchQuery) {
|
||||
url = url + '&q=' + this.searchQuery;
|
||||
url.searchParams.set('q', this.searchQuery);
|
||||
}
|
||||
history.pushState(null, '', url);
|
||||
|
||||
if(this.sortCol) {
|
||||
url.searchParams.set('sort', this.sortCol);
|
||||
}
|
||||
|
||||
if(this.sortDir) {
|
||||
url.searchParams.set('dir', this.sortDir);
|
||||
}
|
||||
|
||||
history.pushState(null, '', url.toString());
|
||||
this.fetchInstances(apiUrl);
|
||||
},
|
||||
|
||||
|
@ -616,7 +747,146 @@
|
|||
this.showInstanceModal = false;
|
||||
this.instances = this.instances.filter(i => i.id != this.instanceModal.id);
|
||||
})
|
||||
.then(() => {
|
||||
setTimeout(() => this.fetchStats(), 1000);
|
||||
})
|
||||
},
|
||||
|
||||
openImportForm() {
|
||||
let el = document.createElement('p');
|
||||
el.classList.add('text-left');
|
||||
el.classList.add('mb-0');
|
||||
el.innerHTML = '<p class="lead mb-0">Import your instance moderation backup.</span></p><br /><p>Import Instructions:</p><ol><li>Press OK</li><li>Press "Choose File" on Import form input</li><li>Select your <kbd>pixelfed-instances-mod.json</kbd> file</li><li>Review instance moderation actions. Tap on an instance to remove it</li><li>Press "Import" button to finish importing</li></ol>';
|
||||
let wrapper = document.createElement('div');
|
||||
wrapper.appendChild(el);
|
||||
swal({
|
||||
title: 'Import Backup',
|
||||
content: wrapper,
|
||||
icon: 'info'
|
||||
})
|
||||
this.showImportForm = true;
|
||||
},
|
||||
|
||||
downloadBackup($event) {
|
||||
axios.get('/i/admin/api/instances/download-backup', {
|
||||
responseType: "blob"
|
||||
})
|
||||
.then(res => {
|
||||
let el = document.createElement('a');
|
||||
el.setAttribute('download', 'pixelfed-instances-mod.json')
|
||||
const href = URL.createObjectURL(res.data);
|
||||
el.href = href;
|
||||
el.setAttribute('target', '_blank');
|
||||
el.click();
|
||||
|
||||
swal(
|
||||
'Instance Backup Downloading',
|
||||
'Your instance moderation backup is downloading. Use this to import auto_cw, banned and unlisted instances to supported Pixelfed instances.',
|
||||
'success'
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
async onImportUpload(ev) {
|
||||
let res = await this.getParsedImport(ev.target.files[0]);
|
||||
|
||||
if(!res.hasOwnProperty('version') || res.version !== 1) {
|
||||
swal('Invalid Backup', 'We cannot validate this backup. Please try again later.', 'error');
|
||||
this.showImportForm = false;
|
||||
this.$refs.importInput.reset();
|
||||
return;
|
||||
}
|
||||
this.importData = res;
|
||||
this.showImportModal = true;
|
||||
},
|
||||
|
||||
async getParsedImport(ev) {
|
||||
try {
|
||||
return await this.parseJsonFile(ev);
|
||||
} catch(err) {
|
||||
let el = document.createElement('p');
|
||||
el.classList.add('text-left');
|
||||
el.classList.add('mb-0');
|
||||
el.innerHTML = '<p class="lead">An error occured when attempting to parse the import file. <span class="font-weight-bold">Please try again later.</span></p><br /><p class="small text-danger mb-0">Error message:</p><div class="card card-body"><code>' + err.message + '</code></div>';
|
||||
let wrapper = document.createElement('div');
|
||||
wrapper.appendChild(el);
|
||||
swal({
|
||||
title: 'Import Error',
|
||||
content: wrapper,
|
||||
icon: 'error'
|
||||
})
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
async promisedParseJSON(json) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
resolve(JSON.parse(json))
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async parseJsonFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fileReader = new FileReader()
|
||||
fileReader.onload = event => resolve(this.promisedParseJSON(event.target.result))
|
||||
fileReader.onerror = error => reject(error)
|
||||
fileReader.readAsText(file)
|
||||
})
|
||||
},
|
||||
|
||||
filterImportData(type, index) {
|
||||
switch(type) {
|
||||
case 'auto_cw':
|
||||
this.importData.auto_cw.splice(index, 1);
|
||||
break;
|
||||
|
||||
case 'unlisted':
|
||||
this.importData.unlisted.splice(index, 1);
|
||||
break;
|
||||
|
||||
case 'banned':
|
||||
this.importData.banned.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
completeImport() {
|
||||
this.showImportForm = false;
|
||||
|
||||
axios.post('/i/admin/api/instances/import-data', {
|
||||
'banned': this.importData.banned,
|
||||
'auto_cw': this.importData.auto_cw,
|
||||
'unlisted': this.importData.unlisted,
|
||||
})
|
||||
.then(res => {
|
||||
swal('Import Uploaded', 'Import successfully uploaded, please allow a few minutes to process.', 'success');
|
||||
})
|
||||
.then(() => {
|
||||
setTimeout(() => this.fetchStats(), 1000);
|
||||
})
|
||||
},
|
||||
|
||||
cancelImport(bvModalEvent) {
|
||||
if(this.importData.banned.length || this.importData.auto_cw.length || this.importData.unlisted.length) {
|
||||
if(!window.confirm('Are you sure you want to cancel importing?')) {
|
||||
bvModalEvent.preventDefault();
|
||||
return;
|
||||
} else {
|
||||
this.showImportForm = false;
|
||||
this.$refs.importInput.value = '';
|
||||
this.importData = {
|
||||
banned: [],
|
||||
auto_cw: [],
|
||||
unlisted: []
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -120,6 +120,8 @@ Route::domain(config('pixelfed.domain.admin'))->prefix('i/admin')->group(functio
|
|||
Route::post('instances/create', 'AdminController@postInstanceCreateNewApi');
|
||||
Route::post('instances/delete', 'AdminController@postInstanceDeleteApi');
|
||||
Route::post('instances/refresh-stats', 'AdminController@postInstanceRefreshStatsApi');
|
||||
Route::get('instances/download-backup', 'AdminController@downloadBackup');
|
||||
Route::post('instances/import-data', 'AdminController@importBackup');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue