1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-04-21 23:42:10 +02:00

Move html importer logic into action and improve imports via HTTP and console (refs #201)

This commit is contained in:
Kovah 2021-01-13 00:25:45 +01:00
parent 20ce10106d
commit 271386c3c8
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
6 changed files with 132 additions and 190 deletions

View File

@ -0,0 +1,91 @@
<?php
namespace App\Actions;
use App\Helper\HtmlMeta;
use App\Helper\LinkIconMapper;
use App\Models\Link;
use App\Models\Tag;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
class ImportHtmlBookmarks
{
protected $imported = 0;
protected $skipped = 0;
public function run(string $data, string $userId, bool $generateMeta = true): bool
{
$parser = new NetscapeBookmarkParser(true, [], '0', storage_path('logs'));
try {
$links = $parser->parseString($data);
} catch (\Exception $e) {
Log::error($e->getMessage());
return false;
}
if (empty($links)) {
// This will never be reached at the moment because the bookmark parser is not capable of handling
// empty bookmarks exports. See https://github.com/shaarli/netscape-bookmark-parser/issues/50
return false;
}
foreach ($links as $link) {
if (Link::whereUrl($link['uri'])->first()) {
$this->skipped++;
continue;
}
if ($generateMeta) {
$linkMeta = HtmlMeta::getFromUrl($link['uri']);
$title = $link['title'] ?: $linkMeta['title'];
$description = $link['note'] ?: $linkMeta['description'];
} else {
$title = $link['title'];
$description = $link['note'];
}
$newLink = Link::create([
'user_id' => $userId,
'url' => $link['uri'],
'title' => $title,
'description' => $description,
'icon' => LinkIconMapper::mapLink($link['uri']),
'is_private' => $link['pub'],
'created_at' => Carbon::createFromTimestamp($link['time']),
'updated_at' => Carbon::now(),
]);
if (!empty($link['tags'])) {
$tags = explode(' ', $link['tags']);
$newTags = [];
foreach ($tags as $tag) {
$newTag = Tag::firstOrCreate([
'user_id' => $userId,
'name' => $tag,
]);
$newTags[] = $newTag->id;
}
$newLink->tags()->sync($newTags);
}
$this->imported++;
}
return true;
}
public function getImportCount(): int
{
return $this->imported;
}
public function getSkippedCount(): int
{
return $this->skipped;
}
}

View File

@ -9,7 +9,7 @@ trait AsksForUser
/** @var User */
protected $user;
protected function askForUserEmail(): void
protected function askForUser(): void
{
do {
$email = $this->ask('Please enter the user email address');

View File

@ -2,15 +2,10 @@
namespace App\Console\Commands;
use App\Helper\HtmlMeta;
use App\Helper\LinkIconMapper;
use App\Models\Link;
use App\Models\Tag;
use Carbon\Carbon;
use App\Actions\ImportHtmlBookmarks;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
use Illuminate\Support\Facades\File;
/**
* Class ImportCommand
@ -19,132 +14,52 @@ use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
*/
class ImportCommand extends Command
{
use AsksForUser;
protected $signature = 'links:import
{filename : Bookmarks file to import}
{--skip-lookup : Whether the lookup should be skipped.}
{filepath : Bookmarks file to import}
{--skip-meta-generation : Whether the automatic generation of titles should be skipped.}
{--skip-check : Whether the links checking should be skipped afterwards}';
public function handle(): void
{
$lookup = true;
$check = true;
$lookupMeta = true;
if ($this->argument('filename')) {
$filename = $this->argument('filename');
} else {
// Check if option "-skip-lookup" is present
if ($this->option('skip-meta-generation')) {
$this->info('Skipping automatic meta generation.');
$lookupMeta = false;
}
$this->info('You will be asked to select a user who will be the owner of the imported bookmarks now.');
$this->askForUser();
$this->info('Reading file "' . $this->argument('filepath') . '"...');
$data = File::get(storage_path($this->argument('filepath')));
if ($data === false || empty($data)) {
$this->warn('The provided file is empty or could not be read!');
return;
}
$importer = new ImportHtmlBookmarks;
$result = $importer->run($data, $this->user->id, $lookupMeta);
// Check if option "-skip-lookup" is present
if ($this->option('skip-lookup')) {
$this->info("Skipping lookup");
$lookup = !$this->option('skip-lookup');
if ($result === false) {
$this->error('Error while importing bookmarks. Please check the application logs.');
return;
}
// Check if option "-skip-check" is present
if ($this->option('skip-check')) {
$this->info("Skipping link check");
$check = !$this->option('skip-check');
}
// Read file
$this->info('Reading file "' . $filename . '"...');
$data = file_get_contents($filename);
$parser = new NetscapeBookmarkParser(true, [], '0', storage_path('logs'));
try {
$links = $parser->parseString($data);
} catch (Exception $e) {
Log::error($e->getMessage());
$this->error(trans('import.import_error'));
return;
}
if (empty($links)) {
// This will never be reached at the moment because the bookmark parser is not capable of handling
// empty bookmarks exports. See https://github.com/shaarli/netscape-bookmark-parser/issues/50
$this->error(trans('import.import_empty'));
return;
}
$userId = 1;
$imported = 0;
$skipped = 0;
$bar = $this->output->createProgressBar(count($links));
$bar->start();
foreach ($links as $link) {
if (Link::whereUrl($link['uri'])->first()) {
$skipped++;
continue;
}
$bar->advance();
if ($lookup) {
$linkMeta = HtmlMeta::getFromUrl($link['uri']);
$title = $link['title'] ?: $linkMeta['title'];
$description = $link['note'] ?: $linkMeta['description'];
} else {
$title = $link['title'];
$description = $link['note'];
}
$newLink = Link::create([
'user_id' => $userId,
'url' => $link['uri'],
'title' => $title,
'description' => $description,
'icon' => LinkIconMapper::mapLink($link['uri']),
'is_private' => $link['pub'],
'created_at' => Carbon::createFromTimestamp($link['time']),
'updated_at' => Carbon::now(),
]);
// Get all tags
if (!empty($link['tags'])) {
$tags = explode(' ', $link['tags']);
$newTags = [];
foreach ($tags as $tag) {
$newTag = Tag::firstOrCreate([
'user_id' => $userId,
'name' => $tag,
]);
$newTags[] = $newTag->id;
}
$newLink->tags()->sync($newTags);
}
$imported++;
}
$bar->finish();
// Force new line
$this->comment(PHP_EOL);
if ($check) {
// Queue link check
$this->info('Skipping link check.');
} else {
Artisan::queue('links:check');
}
$this->info(trans('import.import_successfully', [
'imported' => $imported,
'skipped' => $skipped,
'imported' => $importer->getImportCount(),
'skipped' => $importer->getSkippedCount(),
]));
}
}

View File

@ -21,7 +21,7 @@ class ResetPasswordCommand extends Command
{
$this->line('This tool allows you to reset the password for any user.');
$this->askForUserEmail();
$this->askForUser();
$this->resetUserPassword();
}

View File

@ -21,7 +21,7 @@ class ViewRecoveryCodesCommand extends Command
{
$this->line('This tool allows you to view the 2FA recovery codes for any user.');
$this->askForUserEmail();
$this->askForUser();
$this->viewBackupCodes();
}

View File

@ -2,19 +2,12 @@
namespace App\Http\Controllers\App;
use App\Helper\HtmlMeta;
use App\Helper\LinkIconMapper;
use App\Actions\ImportHtmlBookmarks;
use App\Http\Controllers\Controller;
use App\Http\Requests\DoImportRequest;
use App\Models\Link;
use App\Models\Tag;
use Carbon\Carbon;
use Exception;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Contracts\View\View;
use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
use Illuminate\Http\JsonResponse;
class ImportController extends Controller
{
@ -41,78 +34,21 @@ class ImportController extends Controller
{
$data = $request->file('import-file')->get();
$parser = new NetscapeBookmarkParser(true, [], '0', storage_path('logs'));
$importer = new ImportHtmlBookmarks;
$result = $importer->run($data, auth()->id());
try {
$links = $parser->parseString($data);
} catch (Exception $e) {
Log::error($e->getMessage());
return response()->json([
if ($result === false) {
response()->json([
'success' => false,
'message' => trans('import.import_error'),
]);
}
if (empty($links)) {
// This will never be reached at the moment because the bookmark parser is not capable of handling
// empty bookmarks exports. See https://github.com/shaarli/netscape-bookmark-parser/issues/50
return response()->json([
'success' => false,
'message' => trans('import.import_empty'),
]);
}
$userId = auth()->id();
$imported = 0;
$skipped = 0;
foreach ($links as $link) {
if (Link::whereUrl($link['uri'])->first()) {
$skipped++;
continue;
}
$linkMeta = HtmlMeta::getFromUrl($link['uri']);
$title = $link['title'] ?: $linkMeta['title'];
$newLink = Link::create([
'user_id' => $userId,
'url' => $link['uri'],
'title' => $title,
'description' => $link['note'] ?: $linkMeta['description'],
'icon' => LinkIconMapper::mapLink($link['uri']),
'is_private' => $link['pub'],
'created_at' => Carbon::createFromTimestamp($link['time']),
'updated_at' => Carbon::now(),
]);
// Get all tags
if (!empty($link['tags'])) {
$tags = explode(' ', $link['tags']);
$newTags = [];
foreach ($tags as $tag) {
$newTag = Tag::firstOrCreate([
'user_id' => $userId,
'name' => $tag,
]);
$newTags[] = $newTag->id;
}
$newLink->tags()->sync($newTags);
}
$imported++;
}
return response()->json([
'success' => true,
'message' => trans('import.import_successfully', [
'imported' => $imported,
'skipped' => $skipped,
'imported' => $importer->getImportCount(),
'skipped' => $importer->getSkippedCount(),
]),
]);
}