diff --git a/app/Export/Collector/AttachmentCollector.php b/app/Export/Collector/AttachmentCollector.php index 706fc12ab8..2f67d299c7 100644 --- a/app/Export/Collector/AttachmentCollector.php +++ b/app/Export/Collector/AttachmentCollector.php @@ -11,13 +11,15 @@ declare(strict_types = 1); namespace FireflyIII\Export\Collector; use Amount; -use Auth; use Crypt; use FireflyIII\Models\Attachment; use FireflyIII\Models\ExportJob; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use Illuminate\Contracts\Encryption\DecryptException; +use Illuminate\Support\Collection; use Log; +use Storage; /** * Class AttachmentCollector @@ -28,6 +30,12 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface { /** @var string */ private $explanationString = ''; + /** @var \Illuminate\Contracts\Filesystem\Filesystem */ + private $exportDisk; + /** @var AttachmentRepositoryInterface */ + private $repository; + /** @var \Illuminate\Contracts\Filesystem\Filesystem */ + private $uploadDisk; /** * AttachmentCollector constructor. @@ -36,6 +44,11 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface */ public function __construct(ExportJob $job) { + $this->repository = app('FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface'); + // make storage: + $this->uploadDisk = Storage::disk('upload'); + $this->exportDisk = Storage::disk('export'); + parent::__construct($job); } @@ -45,35 +58,18 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface public function run() { // grab all the users attachments: - $attachments = Auth::user()->attachments()->get(); - - Log::debug('Found ' . $attachments->count() . ' attachments.'); + $attachments = $this->getAttachments(); /** @var Attachment $attachment */ foreach ($attachments as $attachment) { - $originalFile = storage_path('upload') . DIRECTORY_SEPARATOR . 'at-' . $attachment->id . '.data'; - if (file_exists($originalFile)) { - Log::debug('Stored 1 attachment'); - try { - $decrypted = Crypt::decrypt(file_get_contents($originalFile)); - $newFile = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '-Attachment nr. ' . $attachment->id . ' - ' - . $attachment->filename; - file_put_contents($newFile, $decrypted); - $this->getFiles()->push($newFile); - - // explain: - $this->explain($attachment); - } catch (DecryptException $e) { - Log::error('Catchable error: could not decrypt attachment #' . $attachment->id); - } - - } + $this->exportAttachment($attachment); } // put the explanation string in a file and attach it as well. - $explanationFile = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '-Source of all your attachments explained.txt'; - file_put_contents($explanationFile, $this->explanationString); - $this->getFiles()->push($explanationFile); + $file = $this->job->key . '-Source of all your attachments explained.txt'; + $this->exportDisk->put($file, $this->explanationString); + Log::debug('Also put explanation file "' . $file . '" in the zip.'); + $this->getFiles()->push($file); } /** @@ -96,4 +92,57 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface $this->explanationString .= $string; } + + /** + * @param Attachment $attachment + * + * @return bool + */ + private function exportAttachment(Attachment $attachment): bool + { + $file = $attachment->fileName(); + Log::debug('Original file is at "' . $file . '".'); + if ($this->uploadDisk->exists($file)) { + try { + $decrypted = Crypt::decrypt($this->uploadDisk->get($file)); + $exportFile = $this->exportFileName($attachment); + $this->exportDisk->put($exportFile, $decrypted); + $this->getFiles()->push($exportFile); + Log::debug('Stored file content in new file "' . $exportFile . '", which will be in the final zip file.'); + + // explain: + $this->explain($attachment); + } catch (DecryptException $e) { + Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage()); + } + + } + + return true; + } + + /** + * Returns the new file name for the export file. + * + * @param $attachment + * + * @return string + */ + private function exportFileName($attachment): string + { + + return sprintf('%s-Attachment nr. %s - %s', $this->job->key, strval($attachment->id), $attachment->filename); + } + + /** + * @return Collection + */ + private function getAttachments(): Collection + { + $attachments = $this->repository->get(); + + Log::debug('Found ' . $attachments->count() . ' attachments.'); + + return $attachments; + } } diff --git a/app/Export/Collector/UploadCollector.php b/app/Export/Collector/UploadCollector.php index 817385fc93..48cd660287 100644 --- a/app/Export/Collector/UploadCollector.php +++ b/app/Export/Collector/UploadCollector.php @@ -15,6 +15,8 @@ use Crypt; use FireflyIII\Models\ExportJob; use Illuminate\Contracts\Encryption\DecryptException; use Log; +use Storage; + /** * Class UploadCollector * @@ -22,6 +24,12 @@ use Log; */ class UploadCollector extends BasicCollector implements CollectorInterface { + /** @var string */ + private $expected; + /** @var \Illuminate\Contracts\Filesystem\Filesystem */ + private $exportDisk; + /** @var \Illuminate\Contracts\Filesystem\Filesystem */ + private $uploadDisk; /** * AttachmentCollector constructor. @@ -31,6 +39,11 @@ class UploadCollector extends BasicCollector implements CollectorInterface public function __construct(ExportJob $job) { parent::__construct($job); + + // make storage: + $this->uploadDisk = Storage::disk('upload'); + $this->exportDisk = Storage::disk('export'); + $this->expected = 'csv-upload-' . Auth::user()->id . '-'; } /** @@ -39,31 +52,68 @@ class UploadCollector extends BasicCollector implements CollectorInterface public function run() { // grab upload directory. - $path = storage_path('upload'); - $files = scandir($path); - // only allow old uploads for this user: - $expected = 'csv-upload-' . Auth::user()->id . '-'; - $len = strlen($expected); + $files = $this->uploadDisk->files(); + Log::debug('Found ' . count($files) . ' files in the upload directory.'); + foreach ($files as $entry) { - if (substr($entry, 0, $len) === $expected) { - try { - // this is an original upload. - $parts = explode('-', str_replace(['.csv.encrypted', $expected], '', $entry)); - $originalUpload = intval($parts[1]); - $date = date('Y-m-d \a\t H-i-s', $originalUpload); - $newFileName = 'Old CSV import dated ' . $date . '.csv'; - $content = Crypt::decrypt(file_get_contents($path . DIRECTORY_SEPARATOR . $entry)); - $fullPath = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '-' . $newFileName; + $this->processOldUpload($entry); + } + } - // write to file: - file_put_contents($fullPath, $content); + /** + * @param string $entry + * + * @return string + */ + private function getOriginalUploadDate(string $entry): string + { + // this is an original upload. + $parts = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry)); + $originalUpload = intval($parts[1]); + $date = date('Y-m-d \a\t H-i-s', $originalUpload); - // add entry to set: - $this->getFiles()->push($fullPath); - } catch (DecryptException $e) { - Log::error('Could not decrypt old CSV import file ' . $entry . '. Skipped.'); - } + return $date; + } + + /** + * @param string $entry + * + * @return bool + */ + private function isValidFile(string $entry): bool + { + $len = strlen($this->expected); + if (substr($entry, 0, $len) === $this->expected) { + Log::debug($entry . ' is part of this users original uploads.'); + + return true; + } + Log::debug($entry . ' is not part of this users original uploads.'); + + return false; + } + + /** + * @param $entry + */ + private function processOldUpload(string $entry) + { + $content = ''; + + if ($this->isValidFile($entry)) { + try { + $content = Crypt::decrypt($this->uploadDisk->get($entry)); + } catch (DecryptException $e) { + Log::error('Could not decrypt old CSV import file ' . $entry . '. Skipped because ' . $e->getMessage()); } } + if (strlen($content) > 0) { + // continue with file: + $date = $this->getOriginalUploadDate($entry); + $file = $this->job->key . '-Old CSV import dated ' . $date . '.csv'; + Log::debug('Will put "' . $file . '" in the zip file.'); + $this->exportDisk->put($file, $content); + $this->getFiles()->push($file); + } } } diff --git a/app/Export/ConfigurationFile.php b/app/Export/ConfigurationFile.php index a7c989b8d9..c78e6501b7 100644 --- a/app/Export/ConfigurationFile.php +++ b/app/Export/ConfigurationFile.php @@ -11,6 +11,8 @@ declare(strict_types = 1); namespace FireflyIII\Export; use FireflyIII\Models\ExportJob; +use Log; +use Storage; /** * Class ConfigurationFile @@ -19,6 +21,8 @@ use FireflyIII\Models\ExportJob; */ class ConfigurationFile { + /** @var \Illuminate\Contracts\Filesystem\Filesystem */ + private $exportDisk; /** @var ExportJob */ private $job; @@ -29,7 +33,8 @@ class ConfigurationFile */ public function __construct(ExportJob $job) { - $this->job = $job; + $this->job = $job; + $this->exportDisk = Storage::disk('export'); } /** @@ -51,9 +56,10 @@ class ConfigurationFile foreach ($fields as $field) { $configuration['roles'][] = $types[$field]; } - - $file = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '-configuration.json'; - file_put_contents($file, json_encode($configuration, JSON_PRETTY_PRINT)); + $file = $this->job->key . '-configuration.json'; + Log::debug('Created JSON config file.'); + Log::debug('Will put "' . $file . '" in the ZIP file.'); + $this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT)); return $file; } diff --git a/app/Export/Exporter/CsvExporter.php b/app/Export/Exporter/CsvExporter.php index 9a8d414a06..f0d2675cf8 100644 --- a/app/Export/Exporter/CsvExporter.php +++ b/app/Export/Exporter/CsvExporter.php @@ -14,6 +14,7 @@ use FireflyIII\Export\Entry; use FireflyIII\Models\ExportJob; use League\Csv\Writer; use SplFileObject; +use Storage; /** * Class CsvExporter @@ -25,9 +26,6 @@ class CsvExporter extends BasicExporter implements ExporterInterface /** @var string */ private $fileName; - /** @var resource */ - private $handler; - /** * CsvExporter constructor. * @@ -36,6 +34,7 @@ class CsvExporter extends BasicExporter implements ExporterInterface public function __construct(ExportJob $job) { parent::__construct($job); + } /** @@ -54,9 +53,11 @@ class CsvExporter extends BasicExporter implements ExporterInterface // create temporary file: $this->tempFile(); + // necessary for CSV writer: + $fullPath = storage_path('export') . DIRECTORY_SEPARATOR . $this->fileName; + // create CSV writer: - $writer = Writer::createFromPath(new SplFileObject($this->fileName, 'a+'), 'w'); - //the $writer object open mode will be 'w'!! + $writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w'); // all rows: $rows = []; @@ -76,8 +77,6 @@ class CsvExporter extends BasicExporter implements ExporterInterface private function tempFile() { - $fileName = $this->job->key . '-records.csv'; - $this->fileName = storage_path('export') . DIRECTORY_SEPARATOR . $fileName; - $this->handler = fopen($this->fileName, 'w'); + $this->fileName = $this->job->key . '-records.csv'; } } diff --git a/app/Export/Processor.php b/app/Export/Processor.php index 9f59c7e2b4..41adf0722d 100644 --- a/app/Export/Processor.php +++ b/app/Export/Processor.php @@ -16,6 +16,8 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ExportJob; use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; +use Log; +use Storage; use ZipArchive; /** @@ -88,6 +90,13 @@ class Processor $args = [$this->accounts, Auth::user(), $this->settings['startDate'], $this->settings['endDate']]; $journalCollector = app('FireflyIII\Repositories\Journal\JournalCollector', $args); $this->journals = $journalCollector->collect(); + Log::debug( + 'Collected ' . + $this->journals->count() . ' journals (between ' . + $this->settings['startDate']->format('Y-m-d') . ' and ' . + $this->settings['endDate']->format('Y-m-d') + . ').' + ); } public function collectOldUploads() @@ -103,10 +112,13 @@ class Processor */ public function convertJournals() { + $count = 0; /** @var TransactionJournal $journal */ foreach ($this->journals as $journal) { $this->exportEntries->push(Entry::fromJournal($journal)); + $count++; } + Log::debug('Converted ' . $count . ' journals to "Entry" objects.'); } public function createConfigFile() @@ -118,24 +130,32 @@ class Processor public function createZipFile() { $zip = new ZipArchive; - $filename = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '.zip'; + $file = $this->job->key . '.zip'; + $fullPath = storage_path('export') . '/' . $file; + Log::debug('Will create zip file at ' . $fullPath); - if ($zip->open($filename, ZipArchive::CREATE) !== true) { + if ($zip->open($fullPath, ZipArchive::CREATE) !== true) { throw new FireflyException('Cannot store zip file.'); } // for each file in the collection, add it to the zip file. - $search = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '-'; - /** @var string $file */ - foreach ($this->getFiles() as $file) { - $zipName = str_replace($search, '', $file); - $zip->addFile($file, $zipName); + $disk = Storage::disk('export'); + foreach ($this->getFiles() as $entry) { + // is part of this job? + $zipFileName = str_replace($this->job->key . '-', '', $entry); + $result = $zip->addFromString($zipFileName, $disk->get($entry)); + if (!$result) { + Log::error('Could not add "' . $entry . '" into zip file as "' . $zipFileName . '".'); + } } + $zip->close(); // delete the files: foreach ($this->getFiles() as $file) { - unlink($file); + Log::debug('Will now delete file "' . $file . '".'); + $disk->delete($file); } + Log::debug('Done!'); } /** @@ -145,9 +165,11 @@ class Processor { $exporterClass = Config::get('firefly.export_formats.' . $this->exportFormat); $exporter = app($exporterClass, [$this->job]); + Log::debug('Going to export ' . $this->exportEntries->count() . ' export entries into ' . $this->exportFormat . ' format.'); $exporter->setEntries($this->exportEntries); $exporter->run(); $this->files->push($exporter->getFileName()); + Log::debug('Added "' . $exporter->getFileName() . '" to the list of files to include in the zip.'); } /** diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 08e9086af4..d23e74b35f 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\MessageBag; use Input; use Log; +use Storage; use Symfony\Component\HttpFoundation\File\UploadedFile; use TypeError; @@ -30,6 +31,9 @@ class AttachmentHelper implements AttachmentHelperInterface /** @var int */ protected $maxUploadSize; + /** @var \Illuminate\Contracts\Filesystem\Filesystem */ + protected $uploadDisk; + /** * */ @@ -39,6 +43,7 @@ class AttachmentHelper implements AttachmentHelperInterface $this->allowedMimes = Config::get('firefly.allowedMimes'); $this->errors = new MessageBag; $this->messages = new MessageBag; + $this->uploadDisk = Storage::disk('upload'); } /** @@ -148,15 +153,13 @@ class AttachmentHelper implements AttachmentHelperInterface $attachment->uploaded = 0; $attachment->save(); - $path = $file->getRealPath(); // encrypt and move file to storage. - $content = file_get_contents($path); + $fileObject = $file->openFile('r'); + $fileObject->rewind(); + $content = $fileObject->fread($file->getSize()); $encrypted = Crypt::encrypt($content); // store it: - $upload = $this->getAttachmentLocation($attachment); - if (is_writable(dirname($upload))) { - file_put_contents($upload, $encrypted); - } + $this->uploadDisk->put($attachment->fileName(), $encrypted); $attachment->uploaded = 1; // update attachment $attachment->save(); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 409d20a21a..bdefe371f0 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -7,6 +7,7 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; +use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI; use FireflyIII\Support\CacheProperties; @@ -153,7 +154,7 @@ class CategoryController extends Controller * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(CRI $repository) + public function frontpage(CRI $repository, ARI $accountRepository) { $start = session('start', Carbon::now()->startOfMonth()); @@ -170,8 +171,9 @@ class CategoryController extends Controller } // get data for categories (and "no category"): - $set = $repository->spentForAccountsPerMonth(new Collection, $start, $end); - $outside = $repository->sumSpentNoCategory(new Collection, $start, $end); + $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); + $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); + $outside = $repository->sumSpentNoCategory($accounts, $start, $end); // this is a "fake" entry for the "no category" entry. $entry = new stdClass(); diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index 27e65214af..b0f15e5d1a 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -19,6 +19,7 @@ use FireflyIII\Http\Requests\ExportFormRequest; use FireflyIII\Models\ExportJob; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI; +use Log; use Preferences; use Response; use View; @@ -51,7 +52,7 @@ class ExportController extends Controller $quoted = sprintf('"%s"', addcslashes($name, '"\\')); $job->change('export_downloaded'); - + Log::debug('Will send user file "' . $file . '".'); return response(file_get_contents($file), 200) ->header('Content-Description', 'File Transfer') diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 4eeaad603c..8397fda56d 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -36,6 +36,22 @@ class Attachment extends Model protected $fillable = ['attachable_id', 'attachable_type', 'user_id', 'md5', 'filename', 'mime', 'title', 'notes', 'description', 'size', 'uploaded']; + /** + * @param Attachment $value + * + * @return Attachment + */ + public static function routeBinder(Attachment $value) + { + if (Auth::check()) { + + if ($value->user_id == Auth::user()->id) { + return $value; + } + } + throw new NotFoundHttpException; + } + /** * Get all of the owning imageable models. */ @@ -45,14 +61,30 @@ class Attachment extends Model } /** - * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * Returns the expected filename for this attachment. + * + * @return string */ - public function user() + public function fileName(): string { - return $this->belongsTo('FireflyIII\User'); + return sprintf('at-%s.data', strval($this->id)); } + /** + * @codeCoverageIgnore + * + * @param $value + * + * @return null|string + */ + public function getDescriptionAttribute($value) + { + if (is_null($value)) { + return null; + } + + return Crypt::decrypt($value); + } /** * @codeCoverageIgnore @@ -70,14 +102,6 @@ class Attachment extends Model return Crypt::decrypt($value); } - /** - * @param string $value - */ - public function setFilenameAttribute($value) - { - $this->attributes['filename'] = Crypt::encrypt($value); - } - /** * @codeCoverageIgnore * @@ -95,11 +119,19 @@ class Attachment extends Model } /** - * @param string $value + * @codeCoverageIgnore + * + * @param $value + * + * @return null|string */ - public function setMimeAttribute($value) + public function getNotesAttribute($value) { - $this->attributes['mime'] = Crypt::encrypt($value); + if (is_null($value)) { + return null; + } + + return Crypt::decrypt($value); } /** @@ -118,30 +150,6 @@ class Attachment extends Model return Crypt::decrypt($value); } - /** - * @param string $value - */ - public function setTitleAttribute($value) - { - $this->attributes['title'] = Crypt::encrypt($value); - } - - /** - * @codeCoverageIgnore - * - * @param $value - * - * @return null|string - */ - public function getDescriptionAttribute($value) - { - if (is_null($value)) { - return null; - } - - return Crypt::decrypt($value); - } - /** * @param string $value */ @@ -151,19 +159,19 @@ class Attachment extends Model } /** - * @codeCoverageIgnore - * - * @param $value - * - * @return null|string + * @param string $value */ - public function getNotesAttribute($value) + public function setFilenameAttribute($value) { - if (is_null($value)) { - return null; - } + $this->attributes['filename'] = Crypt::encrypt($value); + } - return Crypt::decrypt($value); + /** + * @param string $value + */ + public function setMimeAttribute($value) + { + $this->attributes['mime'] = Crypt::encrypt($value); } /** @@ -175,19 +183,20 @@ class Attachment extends Model } /** - * @param Attachment $value - * - * @return Attachment + * @param string $value */ - public static function routeBinder(Attachment $value) + public function setTitleAttribute($value) { - if (Auth::check()) { + $this->attributes['title'] = Crypt::encrypt($value); + } - if ($value->user_id == Auth::user()->id) { - return $value; - } - } - throw new NotFoundHttpException; + /** + * @codeCoverageIgnore + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo('FireflyIII\User'); } } diff --git a/app/Models/ExportJob.php b/app/Models/ExportJob.php index a03e1bca94..17583ef18c 100644 --- a/app/Models/ExportJob.php +++ b/app/Models/ExportJob.php @@ -12,6 +12,7 @@ namespace FireflyIII\Models; use Auth; use Illuminate\Database\Eloquent\Model; +use Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -50,6 +51,7 @@ class ExportJob extends Model */ public function change($status) { + Log::debug('Job ' . $this->key . ' to status "' . $status . '".'); $this->status = $status; $this->save(); } diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index d8520d0d54..5165d497f8 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -3,7 +3,9 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Attachment; +use Auth; use FireflyIII\Models\Attachment; +use Illuminate\Support\Collection; /** * Class AttachmentRepository @@ -26,9 +28,18 @@ class AttachmentRepository implements AttachmentRepositoryInterface $file = $helper->getAttachmentLocation($attachment); unlink($file); $attachment->delete(); + return true; } + /** + * @return Collection + */ + public function get(): Collection + { + return Auth::user()->attachments()->get(); + } + /** * @param Attachment $attachment * @param array $data diff --git a/app/Repositories/Attachment/AttachmentRepositoryInterface.php b/app/Repositories/Attachment/AttachmentRepositoryInterface.php index 0cd01dff73..2c25be8baa 100644 --- a/app/Repositories/Attachment/AttachmentRepositoryInterface.php +++ b/app/Repositories/Attachment/AttachmentRepositoryInterface.php @@ -4,6 +4,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Attachment; use FireflyIII\Models\Attachment; +use Illuminate\Support\Collection; /** * Interface AttachmentRepositoryInterface @@ -20,6 +21,11 @@ interface AttachmentRepositoryInterface */ public function destroy(Attachment $attachment): bool; + /** + * @return Collection + */ + public function get(): Collection; + /** * @param Attachment $attachment * @param array $attachmentData diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 3eb21e4522..49f5f6d0cc 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -63,8 +63,8 @@ class Amount /** * - * @param TransactionJournal $journal - * @param bool $coloured + * @param \FireflyIII\Models\TransactionJournal $journal + * @param bool $coloured * * @return string */ diff --git a/config/filesystems.php b/config/filesystems.php index 3fffcf0a2f..20f5c4d2e3 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -48,6 +48,15 @@ return [ 'root' => storage_path('app'), ], + 'upload' => [ + 'driver' => 'local', + 'root' => storage_path('upload'), + ], + 'export' => [ + 'driver' => 'local', + 'root' => storage_path('export'), + ], + 'ftp' => [ 'driver' => 'ftp', 'host' => 'ftp.example.com', diff --git a/public/android-chrome-144x144.png b/public/android-chrome-144x144.png index 5bbc2e7a61..0fbc935ef5 100644 Binary files a/public/android-chrome-144x144.png and b/public/android-chrome-144x144.png differ diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png index 325f76a32c..fb8549caef 100644 Binary files a/public/android-chrome-192x192.png and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-36x36.png b/public/android-chrome-36x36.png index 8bbc22da5c..c101427bd7 100644 Binary files a/public/android-chrome-36x36.png and b/public/android-chrome-36x36.png differ diff --git a/public/android-chrome-48x48.png b/public/android-chrome-48x48.png index 948730fdca..3817a33ab7 100644 Binary files a/public/android-chrome-48x48.png and b/public/android-chrome-48x48.png differ diff --git a/public/android-chrome-72x72.png b/public/android-chrome-72x72.png index 55a7375e8c..92ae9f437b 100644 Binary files a/public/android-chrome-72x72.png and b/public/android-chrome-72x72.png differ diff --git a/public/android-chrome-96x96.png b/public/android-chrome-96x96.png index 7ecebf0a2e..3602979589 100644 Binary files a/public/android-chrome-96x96.png and b/public/android-chrome-96x96.png differ diff --git a/public/apple-touch-icon-114x114.png b/public/apple-touch-icon-114x114.png index aa4623e402..498ea65bd1 100644 Binary files a/public/apple-touch-icon-114x114.png and b/public/apple-touch-icon-114x114.png differ diff --git a/public/apple-touch-icon-120x120.png b/public/apple-touch-icon-120x120.png index 69f186adff..542d5330e4 100644 Binary files a/public/apple-touch-icon-120x120.png and b/public/apple-touch-icon-120x120.png differ diff --git a/public/apple-touch-icon-144x144.png b/public/apple-touch-icon-144x144.png index b02e2e380d..d4aab1e910 100644 Binary files a/public/apple-touch-icon-144x144.png and b/public/apple-touch-icon-144x144.png differ diff --git a/public/apple-touch-icon-152x152.png b/public/apple-touch-icon-152x152.png index 927af49f12..9e51ddfb64 100644 Binary files a/public/apple-touch-icon-152x152.png and b/public/apple-touch-icon-152x152.png differ diff --git a/public/apple-touch-icon-180x180.png b/public/apple-touch-icon-180x180.png index f3eb5fb0e0..c0f50f32f5 100644 Binary files a/public/apple-touch-icon-180x180.png and b/public/apple-touch-icon-180x180.png differ diff --git a/public/apple-touch-icon-57x57.png b/public/apple-touch-icon-57x57.png index f2141ad174..414e32f2ee 100644 Binary files a/public/apple-touch-icon-57x57.png and b/public/apple-touch-icon-57x57.png differ diff --git a/public/apple-touch-icon-60x60.png b/public/apple-touch-icon-60x60.png index dd7b9d7f8c..e05470b64b 100644 Binary files a/public/apple-touch-icon-60x60.png and b/public/apple-touch-icon-60x60.png differ diff --git a/public/apple-touch-icon-72x72.png b/public/apple-touch-icon-72x72.png index 360ad6b514..d2080d08e2 100644 Binary files a/public/apple-touch-icon-72x72.png and b/public/apple-touch-icon-72x72.png differ diff --git a/public/apple-touch-icon-76x76.png b/public/apple-touch-icon-76x76.png index 25cd70ee50..47bd65a56d 100644 Binary files a/public/apple-touch-icon-76x76.png and b/public/apple-touch-icon-76x76.png differ diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png index d37f2828ac..0b6bb71456 100644 Binary files a/public/apple-touch-icon-precomposed.png and b/public/apple-touch-icon-precomposed.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index f3eb5fb0e0..c0f50f32f5 100644 Binary files a/public/apple-touch-icon.png and b/public/apple-touch-icon.png differ diff --git a/public/browserconfig.xml b/public/browserconfig.xml index d01e2c2194..6d6066d3f2 100644 --- a/public/browserconfig.xml +++ b/public/browserconfig.xml @@ -2,11 +2,11 @@ - - - - - #2d89ef + + + + + #da532c diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png index 03953f23ac..496df9f39a 100644 Binary files a/public/favicon-16x16.png and b/public/favicon-16x16.png differ diff --git a/public/favicon-194x194.png b/public/favicon-194x194.png new file mode 100644 index 0000000000..25d5884833 Binary files /dev/null and b/public/favicon-194x194.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png index 80c79d7ae2..c729437261 100644 Binary files a/public/favicon-32x32.png and b/public/favicon-32x32.png differ diff --git a/public/favicon-96x96.png b/public/favicon-96x96.png index 7ecebf0a2e..555cb24669 100644 Binary files a/public/favicon-96x96.png and b/public/favicon-96x96.png differ diff --git a/public/manifest.json b/public/manifest.json index 412c8c6bad..5881f59e5c 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,43 +1,42 @@ { "name": "Firefly III", "short_name": "Firefly III", - "icons": [ { - "src": "\/android-chrome-36x36.png?v=Lb54KlrQnz", + "src": "\/android-chrome-36x36.png", "sizes": "36x36", "type": "image\/png", - "density": "0.75" + "density": 0.75 }, { - "src": "\/android-chrome-48x48.png?v=Lb54KlrQnz", + "src": "\/android-chrome-48x48.png", "sizes": "48x48", "type": "image\/png", - "density": "1.0" + "density": 1 }, { - "src": "\/android-chrome-72x72.png?v=Lb54KlrQnz", + "src": "\/android-chrome-72x72.png", "sizes": "72x72", "type": "image\/png", - "density": "1.5" + "density": 1.5 }, { - "src": "\/android-chrome-96x96.png?v=Lb54KlrQnz", + "src": "\/android-chrome-96x96.png", "sizes": "96x96", "type": "image\/png", - "density": "2.0" + "density": 2 }, { - "src": "\/android-chrome-144x144.png?v=Lb54KlrQnz", + "src": "\/android-chrome-144x144.png", "sizes": "144x144", "type": "image\/png", - "density": "3.0" + "density": 3 }, { - "src": "\/android-chrome-192x192.png?v=Lb54KlrQnz", + "src": "\/android-chrome-192x192.png", "sizes": "192x192", "type": "image\/png", - "density": "4.0" + "density": 4 } ], "start_url": "/", diff --git a/public/mstile-144x144.png b/public/mstile-144x144.png index 9b58f4fff1..d27ae1b177 100644 Binary files a/public/mstile-144x144.png and b/public/mstile-144x144.png differ diff --git a/public/mstile-150x150.png b/public/mstile-150x150.png index f8a3ee894d..e03a93a3fc 100644 Binary files a/public/mstile-150x150.png and b/public/mstile-150x150.png differ diff --git a/public/mstile-310x150.png b/public/mstile-310x150.png index 8293e6f1ab..2c17b9eee8 100644 Binary files a/public/mstile-310x150.png and b/public/mstile-310x150.png differ diff --git a/public/mstile-310x310.png b/public/mstile-310x310.png index ef0c267960..ef719f84ca 100644 Binary files a/public/mstile-310x310.png and b/public/mstile-310x310.png differ diff --git a/public/mstile-70x70.png b/public/mstile-70x70.png index 9f99178f61..3f334b60a3 100644 Binary files a/public/mstile-70x70.png and b/public/mstile-70x70.png differ diff --git a/public/safari-pinned-tab.svg b/public/safari-pinned-tab.svg new file mode 100644 index 0000000000..5803c22658 --- /dev/null +++ b/public/safari-pinned-tab.svg @@ -0,0 +1,48 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index e9ed71aa0c..b6b8f96d18 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -44,6 +44,7 @@ return [ 'flash_error' => 'Error!', 'flash_info_multiple' => 'There is one message|There are :count messages', 'flash_error_multiple' => 'There is one error|There are :count errors', + 'net_worth' => 'Net worth', // export data: @@ -57,6 +58,7 @@ return [ 'include_config_help' => 'For easy re-import into Firefly III', 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.', 'do_export' => 'Export', + 'export_status_never_started' => 'The export has not started yet', 'export_status_make_exporter' => 'Creating exporter thing...', 'export_status_collecting_journals' => 'Collecting your transactions...', 'export_status_collected_journals' => 'Collected your transactions!', @@ -114,23 +116,23 @@ return [ 'default_rule_action_prepend' => 'Bought the world from ', 'default_rule_action_set_category' => 'Large expenses', - 'trigger' => 'Trigger', - 'trigger_value' => 'Trigger on value', - 'stop_processing_other_triggers' => 'Stop processing other triggers', - 'add_rule_trigger' => 'Add new trigger', - 'action' => 'Action', - 'action_value' => 'Action value', - 'stop_executing_other_actions' => 'Stop executing other actions', - 'add_rule_action' => 'Add new action', - 'edit_rule' => 'Edit rule ":title"', - 'delete_rule' => 'Delete rule ":title"', - 'update_rule' => 'Update rule', + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger on value', + 'stop_processing_other_triggers' => 'Stop processing other triggers', + 'add_rule_trigger' => 'Add new trigger', + 'action' => 'Action', + 'action_value' => 'Action value', + 'stop_executing_other_actions' => 'Stop executing other actions', + 'add_rule_action' => 'Add new action', + 'edit_rule' => 'Edit rule ":title"', + 'delete_rule' => 'Delete rule ":title"', + 'update_rule' => 'Update rule', 'test_rule_triggers' => 'See matching transactions', 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', 'warning_no_valid_triggers' => 'No valid triggers provided.', - + // actions and triggers 'rule_trigger_user_action' => 'User action is ":trigger_value"', 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index e903fdb754..825880e100 100755 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -44,6 +44,7 @@ return [ 'flash_error' => 'Fout!', 'flash_info_multiple' => 'Er is één melding|Er zijn :count meldingen', 'flash_error_multiple' => 'Er is één fout|Er zijn :count fouten', + 'net_worth' => 'Kapitaal', // export data: @@ -74,7 +75,7 @@ return [ 'export_status_created_zip_file' => 'Zipbestand gemaakt!', 'export_status_finished' => 'Klaar met exportbestand! Hoera!', 'export_data_please_wait' => 'Een ogenblik geduld...', - 'attachment_explanation' => 'Het bestand \':attachment_name\' (#:attachment_id) werd oorspronkelijk geüpload naar :type (Engels) \':description\' (#:journal_id), met datum :datum en bedrag :bedrag.', + 'attachment_explanation' => 'Het bestand \':attachment_name\' (#:attachment_id) werd oorspronkelijk geüpload naar (Engels) :type \':description\' (#:journal_id), met datum :date en bedrag :amount.', // rules 'rules' => 'Regels', diff --git a/resources/views/errors/FireflyException.twig b/resources/views/errors/FireflyException.twig index b77ee64798..ce2e5801ab 100644 --- a/resources/views/errors/FireflyException.twig +++ b/resources/views/errors/FireflyException.twig @@ -13,24 +13,7 @@ - - - - - - - - - - - - - - - - - - + {% include('partials/favicons.twig') %} diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 7ee974b939..700f9b11dc 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -30,24 +30,7 @@ - - - - - - - - - - - - - - - - - - + {% include('partials/favicons.twig') %} diff --git a/resources/views/layout/empty.twig b/resources/views/layout/empty.twig index 8f4a0faad0..0e82247ef3 100644 --- a/resources/views/layout/empty.twig +++ b/resources/views/layout/empty.twig @@ -17,24 +17,7 @@ - - - - - - - - - - - - - - - - - - + {% include('partials/favicons.twig') %} diff --git a/resources/views/layout/guest.twig b/resources/views/layout/guest.twig index 8b59e6af25..61c936f989 100644 --- a/resources/views/layout/guest.twig +++ b/resources/views/layout/guest.twig @@ -18,24 +18,7 @@ - - - - - - - - - - - - - - - - - - + {% include('partials/favicons.twig') %} diff --git a/resources/views/partials/favicons.twig b/resources/views/partials/favicons.twig new file mode 100644 index 0000000000..868737260f --- /dev/null +++ b/resources/views/partials/favicons.twig @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file