This commit is contained in:
Richard Dern
2022-01-12 00:35:37 +01:00
commit 400e3d01f1
1363 changed files with 57778 additions and 0 deletions

170
app/Services/Exporter.php Executable file
View File

@@ -0,0 +1,170 @@
<?php
namespace App\Services;
use App\Models\Folder;
use App\Models\User;
/**
* Exports data from Cyca.
*/
class Exporter
{
/**
* Should we include highlights in the export ?
*
* @var bool
*/
protected $withHighlights = true;
/**
* Should we include bookmarks in the export ?
*
* @var bool
*/
protected $withBookmarks = true;
/**
* User to import data for.
*
* @var \App\Models\User
*/
protected $forUser;
/**
* Folder to export bookmarks and feeds from.
*
* @var \App\Models\Folder
*/
protected $fromFolder;
/**
* Should we ignore highlights during export ? By default, highlights will
* be exported as well.
*
* @return self
*/
public function withoutHighlights()
{
$this->withHighlights = false;
return $this;
}
/**
* Should we ignore bookmarks during export ? By default, bookmarks will
* be exported as well.
*
* @return self
*/
public function withoutBookmarks()
{
$this->withoutBookmarks = false;
return $this;
}
/**
* Defines which user to export data for. If not defined, user will be
* extracted from request.
*
* @return self
*/
public function forUser(User $user)
{
$this->forUser = $user;
return $this;
}
/**
* Defines from which folder data will be exported. If not defined, root
* folder attached to specified user will be used.
*
* @return self
*/
public function fromFolder(Folder $folder)
{
$this->fromFolder = $folder;
return $this;
}
/**
* Export specified user's data into a PHP array.
*
* @return array
*/
public function export()
{
if (empty($this->fromFolder)) {
$this->fromFolder = $this->forUser->groups()->active()->first()->folders()->ofType('root')->first();
}
$rootArray = [
'documents' => [],
'folders' => $this->exportTree($this->fromFolder->children()->get()),
];
foreach ($this->fromFolder->documents()->get() as $document) {
$documentArray = [
'url' => $document->url,
'feeds' => [],
];
foreach ($document->feeds()->get() as $feed) {
$documentArray['feeds'] = [
'url' => $feed->url,
'is_ignored' => $feed->is_ignored,
];
}
$rootArray['documents'][] = $documentArray;
}
return [
'highlights' => $this->forUser->highlights()->select(['expression', 'color'])->get(),
'bookmarks' => $rootArray,
];
}
/**
* Export a single tree branch.
*
* @param mixed $folders
*/
protected function exportTree($folders)
{
$array = [];
foreach ($folders as $folder) {
$folderArray = [
'title' => $folder->title,
'documents' => [],
'folders' => [],
];
foreach ($folder->documents()->get() as $document) {
$documentArray = [
'url' => $document->url,
'feeds' => [],
];
foreach ($document->feeds()->get() as $feed) {
$documentArray['feeds'][] = [
'url' => $feed->url,
'is_ignored' => $feed->is_ignored,
];
}
$folderArray['documents'][] = $documentArray;
}
$folderArray['folders'] = $this->exportTree(($folder->children()->get()));
$array[] = $folderArray;
}
return $array;
}
}

321
app/Services/Importer.php Executable file
View File

@@ -0,0 +1,321 @@
<?php
namespace App\Services;
use App\Models\Document;
use App\Models\Feed;
use App\Models\Folder;
use App\Models\Group;
use App\Models\Highlight;
use App\Models\IgnoredFeed;
use App\Models\User;
use Illuminate\Http\Request;
/**
* Imports data in Cyca.
*/
class Importer
{
/**
* Adapter used to import data.
*
* @var \App\Contracts\ImportAdapter
*/
protected $importAdapter;
/**
* User to import data for.
*
* @var \App\Models\User
*/
protected $forUser;
/**
* Folder to import bookmarks and feeds to.
*
* @var \App\Models\Folder
*/
protected $inFolder;
/**
* Should we import highlights as well ?
*
* @var bool
*/
protected $withHighlights = true;
/**
* Data to be imported as an array.
*
* @var array
*/
protected $dataArray = [];
/**
* Group to import data to.
*
* @var \App\Models\Group
*/
protected $inGroup;
/**
* Indicates which adapter to use for importation.
*
* @param string $adapterName
*
* @return self
*/
public function using($adapterName)
{
$className = config(sprintf('importers.adapters.%s.adapter', $adapterName));
if (empty($className)) {
abort(422, sprintf('Unknown import adapter %s', $className));
}
$this->importAdapter = new $className();
return $this;
}
/**
* Defines which user to import data for. If not defined, user will be
* extracted from request.
*
* @return self
*/
public function forUser(User $user)
{
$this->forUser = $user;
return $this;
}
/**
* Defines the group to import data to.
*
* @return self
*/
public function inGroup(Group $group)
{
$this->inGroup = $group;
return $this;
}
/**
* Defines in which folder data will be imported to. If not defined, root
* folder attached to specified user will be used.
*
* @return self
*/
public function inFolder(Folder $folder)
{
$this->inFolder = $folder;
return $this;
}
/**
* Should we ignore highlights during import ? By default, highlights will
* be imported as well.
*
* @return self
*/
public function withoutHighlights()
{
$this->withHighlights = false;
return $this;
}
/**
* Import data from specified file. Must be a valid json file, valid from
* Cyca's architecture point of view.
*
* @param string $path Full path to file to import
*
* @return self
*/
public function fromFile($path)
{
if (empty($this->forUser)) {
abort(422, 'Target user not specified');
}
$contents = file_get_contents($path);
if (empty($contents)) {
abort(422, 'File does not exists');
}
$this->dataArray = json_decode($contents, true);
return $this;
}
/**
* Import data using current request informations.
*
* @return self
*/
public function fromRequest(Request $request)
{
if (empty($this->importAdapter)) {
$this->using($request->input('importer'));
}
if (empty($this->importAdapter)) {
abort(422, 'An import adapter must be specified');
}
if (empty($this->forUser)) {
$this->forUser = $request->user();
}
$this->dataArray = $this->importAdapter->importFromRequest($request);
return $this;
}
/**
* Perform the import.
*/
public function import()
{
if (empty($this->inGroup)) {
$this->inGroup = $this->forUser->groups()->wherePivot('status', '=', 'own')->first();
}
if (empty($this->inFolder)) {
$this->inFolder = $this->inGroup->folders()->ofType('root')->first();
}
if ($this->withHighlights && !empty($this->dataArray['highlights'])) {
$this->importHighlights($this->dataArray['highlights']);
}
if (!empty($this->dataArray['bookmarks'])) {
$this->importBookmarks($this->dataArray['bookmarks']);
}
}
/**
* Import highlights from specified array.
*
* @param array $highlights
*/
protected function importHighlights($highlights)
{
foreach ($highlights as $highlightData) {
$highlight = Highlight::where('user_id', $this->forUser->id)->where('expression', $highlightData['expression'])->first();
if (!$highlight) {
$highlight = new Highlight();
$highlight->user_id = $this->forUser->id;
$highlight->expression = $highlightData['expression'];
$highlight->color = $highlightData['color'];
$highlight->save();
}
}
}
/**
* Import bookmarks from specified array.
*
* @param array $bookmarks
*/
protected function importBookmarks($bookmarks)
{
$this->importDocuments($this->inFolder, $bookmarks['documents'] ?: []);
$this->importFolders($this->inFolder, $bookmarks['folders'] ?: []);
}
/**
* Import folders.
*
* @param \App\Models\Folder Destination folder
* @param array $foldersData Array of sub-folders definitions
* @param mixed $folder
*/
protected function importFolders($folder, $foldersData)
{
foreach ($foldersData as $folderData) {
$children = $this->inGroup->folders()->save(new Folder([
'title' => $folderData['title'],
'parent_id' => $folder->id,
'user_id' => $this->forUser->id,
]));
$this->importDocuments($children, $folderData['documents']);
$this->importFolders($children, $folderData['folders']);
}
}
/**
* Import documents.
*
* @param \App\Models\Folder Destination folder
* @param array $documentsData Array of documents definitions
* @param mixed $folder
*/
protected function importDocuments($folder, $documentsData)
{
foreach ($documentsData as $docData) {
if (empty($docData['url'])) {
continue;
}
$url = urldecode($docData['url']);
$document = Document::firstOrCreate(['url' => $url]);
if (array_key_exists('feeds', $docData)) {
$this->importFeeds($document, $docData['feeds']);
}
$folder->documents()->save($document, [
'initial_url' => $url,
]);
}
}
/**
* Import feeds.
*
* @param \App\Models\Document $document Destination document
* @param array $feedsData Array of feeds definitions
*/
protected function importFeeds($document, $feedsData)
{
$feedsToAttach = $document->feeds()->get()->pluck('id')->all();
foreach ($feedsData as $feedData) {
if (empty($feedData['url'])) {
continue;
}
$feedUrl = urldecode($feedData['url']);
$feed = Feed::firstOrCreate(['url' => $feedUrl]);
$feedsToAttach[] = $feed->id;
if ($feedData['is_ignored']) {
$ignoredFeed = IgnoredFeed::where('user_id', $this->forUser->id)->where('feed_id', $feed->id)->first();
if (!$ignoredFeed) {
$ignoredFeed = new IgnoredFeed();
$ignoredFeed->user()->associate($this->forUser);
$ignoredFeed->feed()->associate($feed);
$ignoredFeed->save();
}
}
}
$document->feeds()->sync($feedsToAttach);
}
}