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; namespace FireflyIII\Export\Collector;
use Amount; use Amount;
use Auth;
use Crypt; use Crypt;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Collection;
use Log; use Log;
use Storage;
/** /**
* Class AttachmentCollector * Class AttachmentCollector
@ -28,6 +30,12 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
{ {
/** @var string */ /** @var string */
private $explanationString = ''; private $explanationString = '';
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
/** @var AttachmentRepositoryInterface */
private $repository;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $uploadDisk;
/** /**
* AttachmentCollector constructor. * AttachmentCollector constructor.
@ -36,6 +44,11 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
*/ */
public function __construct(ExportJob $job) 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); parent::__construct($job);
} }
@ -45,35 +58,18 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
public function run() public function run()
{ {
// grab all the users attachments: // grab all the users attachments:
$attachments = Auth::user()->attachments()->get(); $attachments = $this->getAttachments();
Log::debug('Found ' . $attachments->count() . ' attachments.');
/** @var Attachment $attachment */ /** @var Attachment $attachment */
foreach ($attachments as $attachment) { foreach ($attachments as $attachment) {
$originalFile = storage_path('upload') . DIRECTORY_SEPARATOR . 'at-' . $attachment->id . '.data'; $this->exportAttachment($attachment);
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);
}
}
} }
// put the explanation string in a file and attach it as well. // 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 = $this->job->key . '-Source of all your attachments explained.txt';
file_put_contents($explanationFile, $this->explanationString); $this->exportDisk->put($file, $this->explanationString);
$this->getFiles()->push($explanationFile); 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; $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 FireflyIII\Models\ExportJob;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
use Log; use Log;
use Storage;
/** /**
* Class UploadCollector * Class UploadCollector
* *
@ -22,6 +24,12 @@ use Log;
*/ */
class UploadCollector extends BasicCollector implements CollectorInterface 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. * AttachmentCollector constructor.
@ -31,6 +39,11 @@ class UploadCollector extends BasicCollector implements CollectorInterface
public function __construct(ExportJob $job) public function __construct(ExportJob $job)
{ {
parent::__construct($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() public function run()
{ {
// grab upload directory. // grab upload directory.
$path = storage_path('upload'); $files = $this->uploadDisk->files();
$files = scandir($path); Log::debug('Found ' . count($files) . ' files in the upload directory.');
// only allow old uploads for this user:
$expected = 'csv-upload-' . Auth::user()->id . '-';
$len = strlen($expected);
foreach ($files as $entry) { foreach ($files as $entry) {
if (substr($entry, 0, $len) === $expected) { $this->processOldUpload($entry);
try { }
}
/**
* @param string $entry
*
* @return string
*/
private function getOriginalUploadDate(string $entry): string
{
// this is an original upload. // this is an original upload.
$parts = explode('-', str_replace(['.csv.encrypted', $expected], '', $entry)); $parts = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry));
$originalUpload = intval($parts[1]); $originalUpload = intval($parts[1]);
$date = date('Y-m-d \a\t H-i-s', $originalUpload); $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;
// write to file: return $date;
file_put_contents($fullPath, $content); }
// add entry to set: /**
$this->getFiles()->push($fullPath); * @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) { } catch (DecryptException $e) {
Log::error('Could not decrypt old CSV import file ' . $entry . '. Skipped.'); 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; namespace FireflyIII\Export;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use Log;
use Storage;
/** /**
* Class ConfigurationFile * Class ConfigurationFile
@ -19,6 +21,8 @@ use FireflyIII\Models\ExportJob;
*/ */
class ConfigurationFile class ConfigurationFile
{ {
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
/** @var ExportJob */ /** @var ExportJob */
private $job; private $job;
@ -30,6 +34,7 @@ class ConfigurationFile
public function __construct(ExportJob $job) 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) { foreach ($fields as $field) {
$configuration['roles'][] = $types[$field]; $configuration['roles'][] = $types[$field];
} }
$file = $this->job->key . '-configuration.json';
$file = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '-configuration.json'; Log::debug('Created JSON config file.');
file_put_contents($file, json_encode($configuration, JSON_PRETTY_PRINT)); Log::debug('Will put "' . $file . '" in the ZIP file.');
$this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT));
return $file; return $file;
} }

View File

@ -14,6 +14,7 @@ use FireflyIII\Export\Entry;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use League\Csv\Writer; use League\Csv\Writer;
use SplFileObject; use SplFileObject;
use Storage;
/** /**
* Class CsvExporter * Class CsvExporter
@ -25,9 +26,6 @@ class CsvExporter extends BasicExporter implements ExporterInterface
/** @var string */ /** @var string */
private $fileName; private $fileName;
/** @var resource */
private $handler;
/** /**
* CsvExporter constructor. * CsvExporter constructor.
* *
@ -36,6 +34,7 @@ class CsvExporter extends BasicExporter implements ExporterInterface
public function __construct(ExportJob $job) public function __construct(ExportJob $job)
{ {
parent::__construct($job); parent::__construct($job);
} }
/** /**
@ -54,9 +53,11 @@ class CsvExporter extends BasicExporter implements ExporterInterface
// create temporary file: // create temporary file:
$this->tempFile(); $this->tempFile();
// necessary for CSV writer:
$fullPath = storage_path('export') . DIRECTORY_SEPARATOR . $this->fileName;
// create CSV writer: // create CSV writer:
$writer = Writer::createFromPath(new SplFileObject($this->fileName, 'a+'), 'w'); $writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w');
//the $writer object open mode will be 'w'!!
// all rows: // all rows:
$rows = []; $rows = [];
@ -76,8 +77,6 @@ class CsvExporter extends BasicExporter implements ExporterInterface
private function tempFile() private function tempFile()
{ {
$fileName = $this->job->key . '-records.csv'; $this->fileName = $this->job->key . '-records.csv';
$this->fileName = storage_path('export') . DIRECTORY_SEPARATOR . $fileName;
$this->handler = fopen($this->fileName, 'w');
} }
} }

View File

@ -16,6 +16,8 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Storage;
use ZipArchive; use ZipArchive;
/** /**
@ -88,6 +90,13 @@ class Processor
$args = [$this->accounts, Auth::user(), $this->settings['startDate'], $this->settings['endDate']]; $args = [$this->accounts, Auth::user(), $this->settings['startDate'], $this->settings['endDate']];
$journalCollector = app('FireflyIII\Repositories\Journal\JournalCollector', $args); $journalCollector = app('FireflyIII\Repositories\Journal\JournalCollector', $args);
$this->journals = $journalCollector->collect(); $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() public function collectOldUploads()
@ -103,10 +112,13 @@ class Processor
*/ */
public function convertJournals() public function convertJournals()
{ {
$count = 0;
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($this->journals as $journal) { foreach ($this->journals as $journal) {
$this->exportEntries->push(Entry::fromJournal($journal)); $this->exportEntries->push(Entry::fromJournal($journal));
$count++;
} }
Log::debug('Converted ' . $count . ' journals to "Entry" objects.');
} }
public function createConfigFile() public function createConfigFile()
@ -118,24 +130,32 @@ class Processor
public function createZipFile() public function createZipFile()
{ {
$zip = new ZipArchive; $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.'); throw new FireflyException('Cannot store zip file.');
} }
// for each file in the collection, add it to the zip file. // for each file in the collection, add it to the zip file.
$search = storage_path('export') . DIRECTORY_SEPARATOR . $this->job->key . '-'; $disk = Storage::disk('export');
/** @var string $file */ foreach ($this->getFiles() as $entry) {
foreach ($this->getFiles() as $file) { // is part of this job?
$zipName = str_replace($search, '', $file); $zipFileName = str_replace($this->job->key . '-', '', $entry);
$zip->addFile($file, $zipName); $result = $zip->addFromString($zipFileName, $disk->get($entry));
if (!$result) {
Log::error('Could not add "' . $entry . '" into zip file as "' . $zipFileName . '".');
} }
}
$zip->close(); $zip->close();
// delete the files: // delete the files:
foreach ($this->getFiles() as $file) { 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); $exporterClass = Config::get('firefly.export_formats.' . $this->exportFormat);
$exporter = app($exporterClass, [$this->job]); $exporter = app($exporterClass, [$this->job]);
Log::debug('Going to export ' . $this->exportEntries->count() . ' export entries into ' . $this->exportFormat . ' format.');
$exporter->setEntries($this->exportEntries); $exporter->setEntries($this->exportEntries);
$exporter->run(); $exporter->run();
$this->files->push($exporter->getFileName()); $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 Illuminate\Support\MessageBag;
use Input; use Input;
use Log; use Log;
use Storage;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use TypeError; use TypeError;
@ -30,6 +31,9 @@ class AttachmentHelper implements AttachmentHelperInterface
/** @var int */ /** @var int */
protected $maxUploadSize; protected $maxUploadSize;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
protected $uploadDisk;
/** /**
* *
*/ */
@ -39,6 +43,7 @@ class AttachmentHelper implements AttachmentHelperInterface
$this->allowedMimes = Config::get('firefly.allowedMimes'); $this->allowedMimes = Config::get('firefly.allowedMimes');
$this->errors = new MessageBag; $this->errors = new MessageBag;
$this->messages = new MessageBag; $this->messages = new MessageBag;
$this->uploadDisk = Storage::disk('upload');
} }
/** /**
@ -148,15 +153,13 @@ class AttachmentHelper implements AttachmentHelperInterface
$attachment->uploaded = 0; $attachment->uploaded = 0;
$attachment->save(); $attachment->save();
$path = $file->getRealPath(); // encrypt and move file to storage. $fileObject = $file->openFile('r');
$content = file_get_contents($path); $fileObject->rewind();
$content = $fileObject->fread($file->getSize());
$encrypted = Crypt::encrypt($content); $encrypted = Crypt::encrypt($content);
// store it: // store it:
$upload = $this->getAttachmentLocation($attachment); $this->uploadDisk->put($attachment->fileName(), $encrypted);
if (is_writable(dirname($upload))) {
file_put_contents($upload, $encrypted);
}
$attachment->uploaded = 1; // update attachment $attachment->uploaded = 1; // update attachment
$attachment->save(); $attachment->save();

View File

@ -7,6 +7,7 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI; use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
@ -153,7 +154,7 @@ class CategoryController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function frontpage(CRI $repository) public function frontpage(CRI $repository, ARI $accountRepository)
{ {
$start = session('start', Carbon::now()->startOfMonth()); $start = session('start', Carbon::now()->startOfMonth());
@ -170,8 +171,9 @@ class CategoryController extends Controller
} }
// get data for categories (and "no category"): // get data for categories (and "no category"):
$set = $repository->spentForAccountsPerMonth(new Collection, $start, $end); $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
$outside = $repository->sumSpentNoCategory(new Collection, $start, $end); $set = $repository->spentForAccountsPerMonth($accounts, $start, $end);
$outside = $repository->sumSpentNoCategory($accounts, $start, $end);
// this is a "fake" entry for the "no category" entry. // this is a "fake" entry for the "no category" entry.
$entry = new stdClass(); $entry = new stdClass();

View File

@ -19,6 +19,7 @@ use FireflyIII\Http\Requests\ExportFormRequest;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI; use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI;
use Log;
use Preferences; use Preferences;
use Response; use Response;
use View; use View;
@ -51,7 +52,7 @@ class ExportController extends Controller
$quoted = sprintf('"%s"', addcslashes($name, '"\\')); $quoted = sprintf('"%s"', addcslashes($name, '"\\'));
$job->change('export_downloaded'); $job->change('export_downloaded');
Log::debug('Will send user file "' . $file . '".');
return response(file_get_contents($file), 200) return response(file_get_contents($file), 200)
->header('Content-Description', 'File Transfer') ->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']; 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. * Get all of the owning imageable models.
*/ */
@ -45,14 +61,30 @@ class Attachment extends Model
} }
/** /**
* @codeCoverageIgnore * Returns the expected filename for this attachment.
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo *
* @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 * @codeCoverageIgnore
@ -70,14 +102,6 @@ class Attachment extends Model
return Crypt::decrypt($value); return Crypt::decrypt($value);
} }
/**
* @param string $value
*/
public function setFilenameAttribute($value)
{
$this->attributes['filename'] = Crypt::encrypt($value);
}
/** /**
* @codeCoverageIgnore * @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); 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 * @param string $value
*/ */
@ -151,19 +159,19 @@ class Attachment extends Model
} }
/** /**
* @codeCoverageIgnore * @param string $value
*
* @param $value
*
* @return null|string
*/ */
public function getNotesAttribute($value) public function setFilenameAttribute($value)
{ {
if (is_null($value)) { $this->attributes['filename'] = Crypt::encrypt($value);
return null;
} }
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 * @param string $value
*
* @return Attachment
*/ */
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; * @codeCoverageIgnore
} * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
} */
throw new NotFoundHttpException; public function user()
{
return $this->belongsTo('FireflyIII\User');
} }
} }

View File

@ -12,6 +12,7 @@ namespace FireflyIII\Models;
use Auth; use Auth;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
@ -50,6 +51,7 @@ class ExportJob extends Model
*/ */
public function change($status) public function change($status)
{ {
Log::debug('Job ' . $this->key . ' to status "' . $status . '".');
$this->status = $status; $this->status = $status;
$this->save(); $this->save();
} }

View File

@ -3,7 +3,9 @@ declare(strict_types = 1);
namespace FireflyIII\Repositories\Attachment; namespace FireflyIII\Repositories\Attachment;
use Auth;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;
use Illuminate\Support\Collection;
/** /**
* Class AttachmentRepository * Class AttachmentRepository
@ -26,9 +28,18 @@ class AttachmentRepository implements AttachmentRepositoryInterface
$file = $helper->getAttachmentLocation($attachment); $file = $helper->getAttachmentLocation($attachment);
unlink($file); unlink($file);
$attachment->delete(); $attachment->delete();
return true; return true;
} }
/**
* @return Collection
*/
public function get(): Collection
{
return Auth::user()->attachments()->get();
}
/** /**
* @param Attachment $attachment * @param Attachment $attachment
* @param array $data * @param array $data

View File

@ -4,6 +4,7 @@ declare(strict_types = 1);
namespace FireflyIII\Repositories\Attachment; namespace FireflyIII\Repositories\Attachment;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;
use Illuminate\Support\Collection;
/** /**
* Interface AttachmentRepositoryInterface * Interface AttachmentRepositoryInterface
@ -20,6 +21,11 @@ interface AttachmentRepositoryInterface
*/ */
public function destroy(Attachment $attachment): bool; public function destroy(Attachment $attachment): bool;
/**
* @return Collection
*/
public function get(): Collection;
/** /**
* @param Attachment $attachment * @param Attachment $attachment
* @param array $attachmentData * @param array $attachmentData

View File

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

View File

@ -48,6 +48,15 @@ return [
'root' => storage_path('app'), 'root' => storage_path('app'),
], ],
'upload' => [
'driver' => 'local',
'root' => storage_path('upload'),
],
'export' => [
'driver' => 'local',
'root' => storage_path('export'),
],
'ftp' => [ 'ftp' => [
'driver' => 'ftp', 'driver' => 'ftp',
'host' => 'ftp.example.com', '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> <browserconfig>
<msapplication> <msapplication>
<tile> <tile>
<square70x70logo src="/mstile-70x70.png?v=Lb54KlrQnz"/> <square70x70logo src="mstile-70x70.png"/>
<square150x150logo src="/mstile-150x150.png?v=Lb54KlrQnz"/> <square150x150logo src="mstile-150x150.png"/>
<square310x310logo src="/mstile-310x310.png?v=Lb54KlrQnz"/> <square310x310logo src="mstile-310x310.png"/>
<wide310x150logo src="/mstile-310x150.png?v=Lb54KlrQnz"/> <wide310x150logo src="mstile-310x150.png"/>
<TileColor>#2d89ef</TileColor> <TileColor>#da532c</TileColor>
</tile> </tile>
</msapplication> </msapplication>
</browserconfig> </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", "name": "Firefly III",
"short_name": "Firefly III", "short_name": "Firefly III",
"icons": [ "icons": [
{ {
"src": "\/android-chrome-36x36.png?v=Lb54KlrQnz", "src": "\/android-chrome-36x36.png",
"sizes": "36x36", "sizes": "36x36",
"type": "image\/png", "type": "image\/png",
"density": "0.75" "density": 0.75
}, },
{ {
"src": "\/android-chrome-48x48.png?v=Lb54KlrQnz", "src": "\/android-chrome-48x48.png",
"sizes": "48x48", "sizes": "48x48",
"type": "image\/png", "type": "image\/png",
"density": "1.0" "density": 1
}, },
{ {
"src": "\/android-chrome-72x72.png?v=Lb54KlrQnz", "src": "\/android-chrome-72x72.png",
"sizes": "72x72", "sizes": "72x72",
"type": "image\/png", "type": "image\/png",
"density": "1.5" "density": 1.5
}, },
{ {
"src": "\/android-chrome-96x96.png?v=Lb54KlrQnz", "src": "\/android-chrome-96x96.png",
"sizes": "96x96", "sizes": "96x96",
"type": "image\/png", "type": "image\/png",
"density": "2.0" "density": 2
}, },
{ {
"src": "\/android-chrome-144x144.png?v=Lb54KlrQnz", "src": "\/android-chrome-144x144.png",
"sizes": "144x144", "sizes": "144x144",
"type": "image\/png", "type": "image\/png",
"density": "3.0" "density": 3
}, },
{ {
"src": "\/android-chrome-192x192.png?v=Lb54KlrQnz", "src": "\/android-chrome-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image\/png", "type": "image\/png",
"density": "4.0" "density": 4
} }
], ],
"start_url": "/", "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_error' => 'Error!',
'flash_info_multiple' => 'There is one message|There are :count messages', 'flash_info_multiple' => 'There is one message|There are :count messages',
'flash_error_multiple' => 'There is one error|There are :count errors', 'flash_error_multiple' => 'There is one error|There are :count errors',
'net_worth' => 'Net worth',
// export data: // export data:
@ -57,6 +58,7 @@ return [
'include_config_help' => 'For easy re-import into Firefly III', '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.', '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', 'do_export' => 'Export',
'export_status_never_started' => 'The export has not started yet',
'export_status_make_exporter' => 'Creating exporter thing...', 'export_status_make_exporter' => 'Creating exporter thing...',
'export_status_collecting_journals' => 'Collecting your transactions...', 'export_status_collecting_journals' => 'Collecting your transactions...',
'export_status_collected_journals' => 'Collected your transactions!', 'export_status_collected_journals' => 'Collected your transactions!',

View File

@ -44,6 +44,7 @@ return [
'flash_error' => 'Fout!', 'flash_error' => 'Fout!',
'flash_info_multiple' => 'Er is één melding|Er zijn :count meldingen', 'flash_info_multiple' => 'Er is één melding|Er zijn :count meldingen',
'flash_error_multiple' => 'Er is één fout|Er zijn :count fouten', 'flash_error_multiple' => 'Er is één fout|Er zijn :count fouten',
'net_worth' => 'Kapitaal',
// export data: // export data:
@ -74,7 +75,7 @@ return [
'export_status_created_zip_file' => 'Zipbestand gemaakt!', 'export_status_created_zip_file' => 'Zipbestand gemaakt!',
'export_status_finished' => 'Klaar met exportbestand! Hoera!', 'export_status_finished' => 'Klaar met exportbestand! Hoera!',
'export_data_please_wait' => 'Een ogenblik geduld...', '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
'rules' => 'Regels', 'rules' => 'Regels',

View File

@ -13,24 +13,7 @@
<link href="css/firefly.css" rel="stylesheet" type="text/css"/> <link href="css/firefly.css" rel="stylesheet" type="text/css"/>
<!-- favicons --> <!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png?v=Lb54KlrQnz"> {% include('partials/favicons.twig') %}
<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">
</head> </head>
<body class="ff-error-page"> <body class="ff-error-page">

View File

@ -30,24 +30,7 @@
<![endif]--> <![endif]-->
<!-- favicons --> <!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="apple-touch-icon-57x57.png?v=Lb54KlrQnz"> {% include('partials/favicons.twig') %}
<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">
</head> </head>
<body class="skin-blue-light sidebar-mini"> <body class="skin-blue-light sidebar-mini">

View File

@ -17,24 +17,7 @@
<![endif]--> <![endif]-->
<!-- favicons --> <!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="apple-touch-icon-57x57.png?v=Lb54KlrQnz"> {% include('partials/favicons.twig') %}
<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">
</head> </head>
<body class="login-page"> <body class="login-page">

View File

@ -18,24 +18,7 @@
<![endif]--> <![endif]-->
<!-- favicons --> <!-- favicons -->
<link rel="apple-touch-icon" sizes="57x57" href="apple-touch-icon-57x57.png?v=Lb54KlrQnz"> {% include('partials/favicons.twig') %}
<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">
</head> </head>
<body class="login-page"> <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">