Merge branch 'develop' into feature/rules-on-existing-transactions

This commit is contained in:
Robert Horlings 2016-02-23 13:17:07 +01:00
commit 3fd90a37fb
50 changed files with 408 additions and 239 deletions

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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';
}
}

View File

@ -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.');
}
/**

View File

@ -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();

View File

@ -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();

View File

@ -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')

View File

@ -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');
}
}

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -63,8 +63,8 @@ class Amount
/**
*
* @param TransactionJournal $journal
* @param bool $coloured
* @param \FireflyIII\Models\TransactionJournal $journal
* @param bool $coloured
*
* @return string
*/

View File

@ -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',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 933 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -2,11 +2,11 @@
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/mstile-70x70.png?v=Lb54KlrQnz"/>
<square150x150logo src="/mstile-150x150.png?v=Lb54KlrQnz"/>
<square310x310logo src="/mstile-310x310.png?v=Lb54KlrQnz"/>
<wide310x150logo src="/mstile-310x150.png?v=Lb54KlrQnz"/>
<TileColor>#2d89ef</TileColor>
<square70x70logo src="mstile-70x70.png"/>
<square150x150logo src="mstile-150x150.png"/>
<square310x310logo src="mstile-310x310.png"/>
<wide310x150logo src="mstile-310x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 608 B

BIN
public/favicon-194x194.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -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": "/",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,48 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M4090 5104 c-135 -37 -302 -170 -457 -362 -89 -111 -94 -115 -148
-123 -39 -6 -42 -4 -99 54 -113 114 -260 149 -425 101 -321 -93 -472 -354
-631 -1088 -22 -104 -45 -196 -49 -205 -22 -39 -102 -95 -231 -160 -25 -12
-58 -31 -75 -42 -16 -10 -35 -21 -42 -25 -17 -8 -44 -52 -49 -78 -4 -25 20
-66 59 -96 22 -18 46 -23 114 -28 74 -4 90 -9 115 -31 45 -40 51 -87 25 -221
-3 -14 -8 -43 -12 -65 -4 -22 -13 -73 -21 -113 -8 -40 -12 -83 -9 -95 4 -13
-6 -58 -21 -102 -18 -55 -27 -106 -29 -163 -11 -302 -17 -389 -36 -468 -6 -27
-12 -58 -15 -69 -2 -11 -30 -126 -63 -255 -33 -129 -64 -269 -70 -310 -12 -79
-19 -92 -131 -235 -95 -122 -177 -189 -345 -282 -132 -73 -141 -76 -224 -70
-85 5 -121 19 -235 91 -90 57 -147 74 -223 70 -143 -9 -347 -144 -409 -270
-23 -47 -27 -67 -26 -137 3 -151 8 -160 135 -224 76 -39 117 -53 162 -58 33
-3 89 -13 125 -22 37 -9 112 -16 175 -17 105 -1 116 1 230 41 77 26 144 43
187 46 117 8 179 49 416 281 88 86 246 284 323 406 57 89 126 230 153 310 26
80 33 123 41 260 5 75 52 281 101 440 23 77 35 142 39 215 5 83 14 212 20 275
3 25 7 81 10 125 3 44 8 103 10 130 3 28 7 73 10 100 2 28 7 77 10 110 3 33 8
80 10 104 16 160 28 201 72 231 33 22 43 24 147 22 140 -3 221 -10 268 -24 31
-9 44 -22 69 -67 29 -51 31 -62 31 -153 0 -139 -11 -204 -68 -398 -56 -192
-64 -231 -70 -330 -3 -52 -13 -131 -21 -175 -9 -44 -18 -129 -19 -190 -2 -92
-8 -124 -33 -195 -30 -85 -53 -209 -66 -350 -4 -41 -9 -86 -10 -100 -2 -14 -6
-63 -10 -110 -7 -104 -20 -185 -42 -268 -20 -79 -37 -93 -204 -177 -152 -76
-235 -130 -305 -200 -44 -43 -54 -61 -64 -108 -13 -66 -9 -85 26 -118 23 -21
33 -24 96 -21 39 1 111 11 162 22 50 11 115 22 145 25 66 6 79 10 159 46 35
16 66 29 69 29 3 0 19 7 36 15 106 48 181 72 256 81 31 3 63 8 71 9 8 2 44 6
80 10 151 16 322 104 331 170 2 11 0 37 -4 57 -13 66 -64 86 -227 94 -140 7
-162 13 -177 47 -5 12 -8 38 -7 57 2 35 42 400 49 455 2 17 7 57 10 90 3 33 8
79 11 103 3 23 -2 88 -11 144 -25 146 -19 241 35 528 53 282 61 344 64 460 1
63 5 124 8 135 4 11 25 76 47 144 23 68 41 129 41 137 0 71 88 104 291 107
112 2 141 6 179 24 53 25 86 58 94 93 13 55 14 80 6 85 -5 3 -12 27 -16 53 -7
49 -18 78 -40 100 -10 10 -56 13 -183 13 -221 0 -217 -3 -215 137 1 54 4 117
7 138 50 340 160 593 324 743 69 64 146 101 207 101 20 0 94 -27 182 -67 114
-51 159 -67 194 -67 81 1 257 89 305 152 16 22 19 44 20 137 2 128 -12 185
-54 232 -47 51 -100 81 -210 118 -91 30 -118 35 -239 39 -105 5 -150 2 -192
-10z m-903 -908 c17 -8 43 -30 58 -48 35 -42 33 -61 -39 -377 -31 -134 -56
-251 -56 -261 0 -26 -20 -39 -101 -61 -93 -26 -319 -28 -356 -4 -46 30 -56 69
-49 184 8 128 9 134 34 242 55 229 137 317 312 336 101 12 161 8 197 -11z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -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"',

View File

@ -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',

View File

@ -13,24 +13,7 @@
<link href="css/firefly.css" rel="stylesheet" type="text/css"/>
<!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png?v=Lb54KlrQnz">
<link rel="icon" type="image/png" href="/favicon-32x32.png?v=Lb54KlrQnz" sizes="32x32">
<link rel="icon" type="image/png" href="/android-chrome-192x192.png?v=Lb54KlrQnz" sizes="192x192">
<link rel="icon" type="image/png" href="/favicon-96x96.png?v=Lb54KlrQnz" sizes="96x96">
<link rel="icon" type="image/png" href="/favicon-16x16.png?v=Lb54KlrQnz" sizes="16x16">
<link rel="manifest" href="/manifest.json?v=Lb54KlrQnz">
<link rel="shortcut icon" href="/favicon.ico?v=Lb54KlrQnz">
<meta name="msapplication-TileColor" content="#2d89ef">
<meta name="msapplication-TileImage" content="/mstile-144x144.png?v=Lb54KlrQnz">
<meta name="theme-color" content="#ffffff">
{% include('partials/favicons.twig') %}
</head>
<body class="ff-error-page">

View File

@ -30,24 +30,7 @@
<![endif]-->
<!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="apple-touch-icon-57x57.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="60x60" href="apple-touch-icon-60x60.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="72x72" href="apple-touch-icon-72x72.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="76x76" href="apple-touch-icon-76x76.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="114x114" href="apple-touch-icon-114x114.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="120x120" href="apple-touch-icon-120x120.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="144x144" href="apple-touch-icon-144x144.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="152x152" href="apple-touch-icon-152x152.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon-180x180.png?v=Lb54KlrQnz">
<link rel="icon" type="image/png" href="favicon-32x32.png?v=Lb54KlrQnz" sizes="32x32">
<link rel="icon" type="image/png" href="android-chrome-192x192.png?v=Lb54KlrQnz" sizes="192x192">
<link rel="icon" type="image/png" href="favicon-96x96.png?v=Lb54KlrQnz" sizes="96x96">
<link rel="icon" type="image/png" href="favicon-16x16.png?v=Lb54KlrQnz" sizes="16x16">
<link rel="manifest" href="manifest.json?v=Lb54KlrQnz">
<link rel="shortcut icon" href="favicon.ico?v=Lb54KlrQnz">
<meta name="msapplication-TileColor" content="#2d89ef">
<meta name="msapplication-TileImage" content="mstile-144x144.png?v=Lb54KlrQnz">
<meta name="theme-color" content="#ffffff">
{% include('partials/favicons.twig') %}
</head>
<body class="skin-blue-light sidebar-mini">

View File

@ -17,24 +17,7 @@
<![endif]-->
<!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="apple-touch-icon-57x57.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="60x60" href="apple-touch-icon-60x60.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="72x72" href="apple-touch-icon-72x72.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="76x76" href="apple-touch-icon-76x76.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="114x114" href="apple-touch-icon-114x114.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="120x120" href="apple-touch-icon-120x120.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="144x144" href="apple-touch-icon-144x144.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="152x152" href="apple-touch-icon-152x152.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon-180x180.png?v=Lb54KlrQnz">
<link rel="icon" type="image/png" href="favicon-32x32.png?v=Lb54KlrQnz" sizes="32x32">
<link rel="icon" type="image/png" href="android-chrome-192x192.png?v=Lb54KlrQnz" sizes="192x192">
<link rel="icon" type="image/png" href="favicon-96x96.png?v=Lb54KlrQnz" sizes="96x96">
<link rel="icon" type="image/png" href="favicon-16x16.png?v=Lb54KlrQnz" sizes="16x16">
<link rel="manifest" href="manifest.json?v=Lb54KlrQnz">
<link rel="shortcut icon" href="favicon.ico?v=Lb54KlrQnz">
<meta name="msapplication-TileColor" content="#2d89ef">
<meta name="msapplication-TileImage" content="mstile-144x144.png?v=Lb54KlrQnz">
<meta name="theme-color" content="#ffffff">
{% include('partials/favicons.twig') %}
</head>
<body class="login-page">

View File

@ -18,24 +18,7 @@
<![endif]-->
<!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="apple-touch-icon-57x57.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="60x60" href="apple-touch-icon-60x60.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="72x72" href="apple-touch-icon-72x72.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="76x76" href="apple-touch-icon-76x76.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="114x114" href="apple-touch-icon-114x114.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="120x120" href="apple-touch-icon-120x120.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="144x144" href="apple-touch-icon-144x144.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="152x152" href="apple-touch-icon-152x152.png?v=Lb54KlrQnz">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon-180x180.png?v=Lb54KlrQnz">
<link rel="icon" type="image/png" href="favicon-32x32.png?v=Lb54KlrQnz" sizes="32x32">
<link rel="icon" type="image/png" href="android-chrome-192x192.png?v=Lb54KlrQnz" sizes="192x192">
<link rel="icon" type="image/png" href="favicon-96x96.png?v=Lb54KlrQnz" sizes="96x96">
<link rel="icon" type="image/png" href="favicon-16x16.png?v=Lb54KlrQnz" sizes="16x16">
<link rel="manifest" href="manifest.json?v=Lb54KlrQnz">
<link rel="shortcut icon" href="favicon.ico?v=Lb54KlrQnz">
<meta name="msapplication-TileColor" content="#2d89ef">
<meta name="msapplication-TileImage" content="mstile-144x144.png?v=Lb54KlrQnz">
<meta name="theme-color" content="#ffffff">
{% include('partials/favicons.twig') %}
</head>
<body class="login-page">

View File

@ -0,0 +1,18 @@
<link rel="apple-touch-icon" sizes="57x57" href="apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="manifest.json">
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#3c8dbc">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="mstile-144x144.png">
<meta name="theme-color" content="#3c8dbc">