Merge branch 'release/5.2.7'

This commit is contained in:
James Cole
2020-06-06 07:18:36 +02:00
424 changed files with 9518 additions and 9138 deletions

View File

@@ -58,13 +58,24 @@ APP_LOG_LEVEL=notice
# For other database types, please see the FAQ: https://docs.firefly-iii.org/support/faq # For other database types, please see the FAQ: https://docs.firefly-iii.org/support/faq
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE # If you use Docker or similar, you can set these variables from a file by appending them with _FILE
# Use "mysql" for MySQL and MariaDB. Use "sqlite" for SQLite. # Use "mysql" for MySQL and MariaDB. Use "sqlite" for SQLite.
DB_CONNECTION=pgsql DB_CONNECTION=mysql
DB_HOST=fireflyiiidb DB_HOST=fireflyiiidb
DB_PORT=5432 DB_PORT=3306
DB_DATABASE=firefly DB_DATABASE=firefly
DB_USERNAME=firefly DB_USERNAME=firefly
DB_PASSWORD=secret_firefly_password DB_PASSWORD=secret_firefly_password
# MySQL supports SSL. You can configure it here.
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
MYSQL_USE_SSL=false
MYSQL_SSL_VERIFY_SERVER_CERT=true
# You need to set at least of these options
MYSQL_SSL_CAPATH=/etc/ssl/certs/
MYSQL_SSL_CA=
MYSQL_SSL_CERT=
MYSQL_SSL_KEY=
MYSQL_SSL_CIPHER=
# PostgreSQL supports SSL. You can configure it here. # PostgreSQL supports SSL. You can configure it here.
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE # If you use Docker or similar, you can set these variables from a file by appending them with _FILE
PGSQL_SSL_MODE=prefer PGSQL_SSL_MODE=prefer
@@ -170,8 +181,16 @@ ADLDAP_PORT=389
ADLDAP_TIMEOUT=5 ADLDAP_TIMEOUT=5
ADLDAP_BASEDN="" ADLDAP_BASEDN=""
ADLDAP_FOLLOW_REFFERALS=false ADLDAP_FOLLOW_REFFERALS=false
# SSL/TLS settings
ADLDAP_USE_SSL=false ADLDAP_USE_SSL=false
ADLDAP_USE_TLS=false ADLDAP_USE_TLS=false
ADLDAP_SSL_CACERTDIR=
ADLDAP_SSL_CACERTFILE=
ADLDAP_SSL_CERTFILE=
ADLDAP_SSL_KEYFILE=
ADLDAP_SSL_CIPHER_SUITE=
ADLDAP_SSL_REQUIRE_CERT=
# You can set the following variables from a file by appending them with _FILE: # You can set the following variables from a file by appending them with _FILE:
ADLDAP_ADMIN_USERNAME= ADLDAP_ADMIN_USERNAME=
@@ -219,8 +238,9 @@ TRACKER_SITE_ID=
TRACKER_URL= TRACKER_URL=
# #
# Firefly III could (in the future) collect telemetry on how you use Firefly III. # Firefly III can collect telemetry on how you use Firefly III. This is opt-in.
# In order to allow this, change the following variable to true: # In order to allow this, change the following variable to true.
# To read more about this feature, go to this page: https://docs.firefly-iii.org/support/telemetry
SEND_TELEMETRY=false SEND_TELEMETRY=false
# You can fine tune the start-up of a Docker container by editing these environment variables. # You can fine tune the start-up of a Docker container by editing these environment variables.
@@ -268,7 +288,6 @@ DEMO_USERNAME=
DEMO_PASSWORD= DEMO_PASSWORD=
USE_ENCRYPTION=false USE_ENCRYPTION=false
IS_SANDSTORM=false IS_SANDSTORM=false
IS_DOCKER=false
IS_HEROKU=false IS_HEROKU=false
BUNQ_USE_SANDBOX=false BUNQ_USE_SANDBOX=false

View File

@@ -83,8 +83,6 @@ class CorrectDatabase extends Command
echo $result; echo $result;
} }
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -77,8 +77,6 @@ class CorrectOpeningBalanceCurrencies extends Command
$this->info('There was nothing to fix in the opening balance transactions.'); $this->info('There was nothing to fix in the opening balance transactions.');
} }
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -78,8 +78,6 @@ class CreateAccessTokens extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verify access tokens in %s seconds.', $end)); $this->info(sprintf('Verify access tokens in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -79,8 +79,6 @@ class CreateLinkTypes extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified link types in %s seconds', $end)); $this->info(sprintf('Verified link types in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -76,8 +76,6 @@ class DeleteEmptyGroups extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified empty groups in %s seconds', $end)); $this->info(sprintf('Verified empty groups in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -58,8 +58,6 @@ class DeleteEmptyJournals extends Command
$this->deleteUnevenJournals(); $this->deleteUnevenJournals();
$this->deleteEmptyJournals(); $this->deleteEmptyJournals();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -62,7 +62,6 @@ class DeleteOrphanedTransactions extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified orphans in %s seconds', $end)); $this->info(sprintf('Verified orphans in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -78,8 +78,6 @@ class DeleteZeroAmount extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified zero-amount integrity in %s seconds', $end)); $this->info(sprintf('Verified zero-amount integrity in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -101,8 +101,6 @@ class EnableCurrencies extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified currencies in %s seconds.', $end)); $this->info(sprintf('Verified currencies in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -106,7 +106,6 @@ class FixAccountTypes extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verifying account types took %s seconds', $end)); $this->info(sprintf('Verifying account types took %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -75,7 +75,6 @@ class FixLongDescriptions extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified all transaction group and journal title lengths in %s seconds.', $end)); $this->info(sprintf('Verified all transaction group and journal title lengths in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -98,7 +98,6 @@ class FixPiggies extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->line(sprintf('Verified the content of %d piggy bank events in %s seconds.', $set->count(), $end)); $this->line(sprintf('Verified the content of %d piggy bank events in %s seconds.', $set->count(), $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -67,7 +67,6 @@ class FixRecurringTransactions extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Corrected recurring transactions %s seconds.', $end)); $this->info(sprintf('Corrected recurring transactions %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -75,7 +75,6 @@ class FixUnevenAmount extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified amount integrity in %s seconds', $end)); $this->info(sprintf('Verified amount integrity in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -71,7 +71,6 @@ class RemoveBills extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified bills / journals in %s seconds', $end)); $this->info(sprintf('Verified bills / journals in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -83,7 +83,6 @@ class RenameMetaFields extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Renamed meta fields in %s seconds', $end)); $this->info(sprintf('Renamed meta fields in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -74,7 +74,6 @@ class TransferBudgets extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified budget/journals in %s seconds.', $end)); $this->info(sprintf('Verified budget/journals in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -123,7 +123,6 @@ class DecryptDatabase extends Command
} }
$this->info('Done!'); $this->info('Done!');
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -137,11 +137,11 @@ class ExportData extends Command
} catch (FireflyException $e) { } catch (FireflyException $e) {
$this->error(sprintf('Could not store data: %s', $e->getMessage())); $this->error(sprintf('Could not store data: %s', $e->getMessage()));
// app('telemetry')->feature('executed-command-with-error', $this->signature); app('telemetry')->feature('system.command.errored', $this->signature);
return 1; return 1;
} }
// app('telemetry')->feature('executed-command', $this->signature); app('telemetry')->feature('system.command.executed', $this->signature);
return 0; return 0;
} }

View File

@@ -63,279 +63,14 @@ class CreateCSVImport extends Command
{configuration? : The configuration file to use for the import.} {configuration? : The configuration file to use for the import.}
{--user=1 : The user ID that the import should import for.} {--user=1 : The user ID that the import should import for.}
{--token= : The user\'s access token.}'; {--token= : The user\'s access token.}';
/** @var ImportJob */
private $importJob;
/** @var ImportJobRepositoryInterface */
private $importRepository;
/** @var UserRepositoryInterface */
private $userRepository;
/** /**
* Run the command. * Run the command.
*/ */
public function handle(): int public function handle(): int
{ {
$this->stupidLaravel(); $this->error('This command is disabled.');
// @codeCoverageIgnoreStart
if (!$this->verifyAccessToken()) {
$this->errorLine('Invalid access token.');
return 1; return 1;
} }
if (!$this->validArguments()) {
$this->errorLine('Invalid arguments.');
return 1;
}
// @codeCoverageIgnoreEnd
/** @var User $user */
$user = $this->userRepository->findNull((int) $this->option('user'));
$file = (string) $this->argument('file');
$configuration = (string) $this->argument('configuration');
$this->importRepository->setUser($user);
$configurationData = json_decode(file_get_contents($configuration), true, 512, JSON_THROW_ON_ERROR);
$this->importJob = $this->importRepository->create('file');
// inform user (and log it)
$this->infoLine(sprintf('Import file : %s', $file));
$this->infoLine(sprintf('Configuration file : %s', $configuration));
$this->infoLine(sprintf('User : #%d (%s)', $user->id, $user->email));
$this->infoLine(sprintf('Job : %s', $this->importJob->key));
try {
$this->storeFile($file);
} catch (FireflyException $e) {
$this->errorLine($e->getMessage());
return 1;
}
// job is ready to go
$this->importRepository->setConfiguration($this->importJob, $configurationData);
$this->importRepository->setStatus($this->importJob, 'ready_to_run');
$this->infoLine('The import routine has started. The process is not visible. Please wait.');
Log::debug('Go for import!');
// keep repeating this call until job lands on "provider_finished"
try {
$this->processFile();
} catch (FireflyException $e) {
$this->errorLine($e->getMessage());
// app('telemetry')->feature('executed-command-with-error', $this->signature);
return 1;
}
// then store data:
try {
$this->storeData();
} catch (FireflyException $e) {
$this->errorLine($e->getMessage());
// app('telemetry')->feature('executed-command-with-error', $this->signature);
return 1;
}
// give feedback:
$this->giveFeedback();
// clear cache for user:
app('preferences')->setForUser($user, 'lastActivity', microtime());
// app('telemetry')->feature('executed-command', $this->signature);
return 0;
}
/**
* @param string $message
* @param array|null $data
*
* @codeCoverageIgnore
*/
private function errorLine(string $message, array $data = null): void
{
Log::error($message, $data ?? []);
$this->error($message);
}
/**
*
*/
private function giveFeedback(): void
{
$this->infoLine('Job has finished.');
if (null !== $this->importJob->tag) {
$this->infoLine(sprintf('%d transaction(s) have been imported.', $this->importJob->tag->transactionJournals->count()));
$this->infoLine(sprintf('You can find your transactions under tag "%s"', $this->importJob->tag->tag));
}
if (null === $this->importJob->tag) {
$this->errorLine('No transactions have been imported :(.');
}
if (count($this->importJob->errors) > 0) {
$this->infoLine(sprintf('%d error(s) occurred:', count($this->importJob->errors)));
foreach ($this->importJob->errors as $err) {
$this->errorLine('- ' . $err);
}
}
}
/**
* @param string $message
* @param array $data
*
* @codeCoverageIgnore
*/
private function infoLine(string $message, array $data = null): void
{
Log::info($message, $data ?? []);
$this->line($message);
}
/**
* Keep repeating import call until job lands on "provider_finished".
*
* @throws FireflyException
*/
private function processFile(): void
{
$className = config('import.routine.file');
$valid = ['provider_finished'];
$count = 0;
while (!in_array($this->importJob->status, $valid, true) && $count < 6) {
Log::debug(sprintf('Now in loop #%d.', $count + 1));
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setImportJob($this->importJob);
try {
$routine->run();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$this->importRepository->setStatus($this->importJob, 'error');
throw new FireflyException($message);
}
$count++;
}
$this->importRepository->setStatus($this->importJob, 'provider_finished');
$this->importJob->status = 'provider_finished';
}
/**
*
* @throws FireflyException
*/
private function storeData(): void
{
if ('provider_finished' === $this->importJob->status) {
$this->infoLine('Import has finished. Please wait for storage of data.');
// set job to be storing data:
$this->importRepository->setStatus($this->importJob, 'storing_data');
/** @var ImportArrayStorage $storage */
$storage = app(ImportArrayStorage::class);
$storage->setImportJob($this->importJob);
try {
$storage->store();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$this->importRepository->setStatus($this->importJob, 'error');
throw new FireflyException($message);
}
// set storage to be finished:
$this->importRepository->setStatus($this->importJob, 'storage_finished');
}
}
/**
* Store the supplied file as an attachment to this job.
*
* @param string $file
*
* @throws FireflyException
*/
private function storeFile(string $file): void
{
// store file as attachment.
if ('' !== $file) {
$messages = $this->importRepository->storeCLIUpload($this->importJob, 'import_file', $file);
if ($messages->count() > 0) {
throw new FireflyException($messages->first());
}
}
}
/**
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
* be called from the handle method instead of using the constructor to initialize the command.
*
* @codeCoverageIgnore
*/
private function stupidLaravel(): void
{
$this->userRepository = app(UserRepositoryInterface::class);
$this->importRepository = app(ImportJobRepositoryInterface::class);
}
/**
* Verify user inserts correct arguments.
*
* @noinspection MultipleReturnStatementsInspection
* @return bool
* @codeCoverageIgnore
*/
private function validArguments(): bool
{
$file = (string) $this->argument('file');
$configuration = (string) $this->argument('configuration');
$cwd = getcwd();
$enabled = (bool) config('import.enabled.file');
if (false === $enabled) {
$this->errorLine('CSV Provider is not enabled.');
return false;
}
if (!file_exists($file)) {
$this->errorLine(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd));
return false;
}
if (!file_exists($configuration)) {
$this->errorLine(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return false;
}
$configurationData = json_decode(file_get_contents($configuration), true, 512, JSON_THROW_ON_ERROR);
if (null === $configurationData) {
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return false;
}
return true;
}
} }

View File

@@ -64,7 +64,6 @@ class ReportEmptyObjects extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Report on empty objects finished in %s seconds', $end)); $this->info(sprintf('Report on empty objects finished in %s seconds', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -69,7 +69,6 @@ class ReportIntegrity extends Command
echo $result; echo $result;
} }
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -54,7 +54,6 @@ class ReportSum extends Command
{ {
$this->reportSum(); $this->reportSum();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -24,6 +24,7 @@ namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Support\System\OAuthKeys; use FireflyIII\Support\System\OAuthKeys;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Log;
/** /**
* Class RestoreOAuthKeys * Class RestoreOAuthKeys
@@ -52,7 +53,6 @@ class RestoreOAuthKeys extends Command
{ {
$this->restoreOAuthKeys(); $this->restoreOAuthKeys();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }
@@ -93,7 +93,9 @@ class RestoreOAuthKeys extends Command
*/ */
private function restoreOAuthKeys(): void private function restoreOAuthKeys(): void
{ {
Log::debug('Going to restoreOAuthKeys()');
if (!$this->keysInDatabase() && !$this->keysOnDrive()) { if (!$this->keysInDatabase() && !$this->keysOnDrive()) {
Log::debug('Keys are not in DB and keys are not on the drive.');
$this->generateKeys(); $this->generateKeys();
$this->storeKeysInDB(); $this->storeKeysInDB();
$this->line('Generated and stored new keys.'); $this->line('Generated and stored new keys.');
@@ -101,12 +103,14 @@ class RestoreOAuthKeys extends Command
return; return;
} }
if ($this->keysInDatabase() && !$this->keysOnDrive()) { if ($this->keysInDatabase() && !$this->keysOnDrive()) {
Log::debug('Keys are in DB and keys are not on the drive. Restore.');
$this->restoreKeysFromDB(); $this->restoreKeysFromDB();
$this->line('Restored OAuth keys from database.'); $this->line('Restored OAuth keys from database.');
return; return;
} }
if (!$this->keysInDatabase() && $this->keysOnDrive()) { if (!$this->keysInDatabase() && $this->keysOnDrive()) {
Log::debug('Keys are not in DB and keys are on the drive. Save in DB.');
$this->storeKeysInDB(); $this->storeKeysInDB();
$this->line('Stored OAuth keys in database.'); $this->line('Stored OAuth keys in database.');

View File

@@ -86,7 +86,7 @@ class ScanAttachments extends Command
$this->line(sprintf('Fixed attachment #%d', $attachment->id)); $this->line(sprintf('Fixed attachment #%d', $attachment->id));
} }
// app('telemetry')->feature('executed-command', $this->signature); app('telemetry')->feature('system.command.executed', $this->signature);
return 0; return 0;
} }
} }

View File

@@ -59,7 +59,7 @@ class SetLatestVersion extends Command
app('fireflyconfig')->set('ff3_version', config('firefly.version')); app('fireflyconfig')->set('ff3_version', config('firefly.version'));
$this->line('Updated version.'); $this->line('Updated version.');
// app('telemetry')->feature('executed-command', $this->signature); app('telemetry')->feature('system.command.executed', $this->signature);
return 0; return 0;
} }

View File

@@ -112,7 +112,7 @@ class ApplyRules extends Command
$result = $this->verifyInput(); $result = $this->verifyInput();
if (false === $result) { if (false === $result) {
// app('telemetry')->feature('executed-command-with-error', $this->signature); app('telemetry')->feature('system.command.errored', $this->signature);
return 1; return 1;
} }
@@ -131,7 +131,7 @@ class ApplyRules extends Command
$this->warn(' --rule_groups=1,2,...'); $this->warn(' --rule_groups=1,2,...');
$this->warn(' --all_rules'); $this->warn(' --all_rules');
// app('telemetry')->feature('executed-command-with-error', $this->signature); app('telemetry')->feature('system.command.errored', $this->signature);
return 1; return 1;
} }
@@ -167,7 +167,7 @@ class ApplyRules extends Command
$this->line(''); $this->line('');
$this->line('Done!'); $this->line('Done!');
// app('telemetry')->feature('executed-command', $this->signature); app('telemetry')->feature('system.command.executed', $this->signature);
return 0; return 0;
} }

View File

@@ -94,10 +94,10 @@ class Cron extends Command
} }
/* /*
* Fire telemetry cron job (disabled): * Fire telemetry cron job
*/ */
try { try {
//$this->telemetryCronJob($force, $date); $this->telemetryCronJob($force, $date);
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
@@ -106,7 +106,7 @@ class Cron extends Command
$this->info('More feedback on the cron jobs can be found in the log files.'); $this->info('More feedback on the cron jobs can be found in the log files.');
// app('telemetry')->feature('executed-command', $this->signature); app('telemetry')->feature('system.command.executed', $this->signature);
return 0; return 0;
} }

View File

@@ -87,7 +87,6 @@ class AccountCurrencies extends Command
$this->info(sprintf('Verified and fixed account currencies in %s seconds.', $end)); $this->info(sprintf('Verified and fixed account currencies in %s seconds.', $end));
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -78,8 +78,6 @@ class BackToJournals extends Command
$this->info(sprintf('Updated category and budget info for all transaction journals in %s seconds.', $end)); $this->info(sprintf('Updated category and budget info for all transaction journals in %s seconds.', $end));
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -92,8 +92,6 @@ class BudgetLimitCurrency extends Command
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -91,8 +91,6 @@ class CCLiabilities extends Command
$this->info(sprintf('Verified credit card liabilities in %s seconds', $end)); $this->info(sprintf('Verified credit card liabilities in %s seconds', $end));
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -101,8 +101,6 @@ class MigrateAttachments extends Command
$this->info(sprintf('Migrated attachment notes in %s seconds.', $end)); $this->info(sprintf('Migrated attachment notes in %s seconds.', $end));
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -100,8 +100,6 @@ class MigrateJournalNotes extends Command
$this->info(sprintf('Migrated notes in %s seconds.', $end)); $this->info(sprintf('Migrated notes in %s seconds.', $end));
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -72,8 +72,6 @@ class MigrateRecurrenceMeta extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Migrated recurrence meta data in %s seconds.', $end)); $this->info(sprintf('Migrated recurrence meta data in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -65,7 +65,6 @@ class MigrateTagLocations extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Migrated tag locations in %s seconds.', $end)); $this->info(sprintf('Migrated tag locations in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -112,8 +112,6 @@ class MigrateToGroups extends Command
$this->markAsMigrated(); $this->markAsMigrated();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -99,7 +99,6 @@ class MigrateToRules extends Command
$this->info(sprintf('Verified and fixed bills in %s seconds.', $end)); $this->info(sprintf('Verified and fixed bills in %s seconds.', $end));
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -90,7 +90,6 @@ class OtherCurrenciesCorrections extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified and fixed transaction currencies in %s seconds.', $end)); $this->info(sprintf('Verified and fixed transaction currencies in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -91,7 +91,6 @@ class RenameAccountMeta extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Fixed account meta data in %s seconds.', $end)); $this->info(sprintf('Fixed account meta data in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -101,7 +101,6 @@ class TransactionIdentifier extends Command
$this->info(sprintf('Verified and fixed transaction identifiers in %s seconds.', $end)); $this->info(sprintf('Verified and fixed transaction identifiers in %s seconds.', $end));
$this->markAsExecuted(); $this->markAsExecuted();
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -111,7 +111,6 @@ class TransferCurrenciesCorrections extends Command
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->info(sprintf('Verified and fixed currency information for transfers in %s seconds.', $end)); $this->info(sprintf('Verified and fixed currency information for transfers in %s seconds.', $end));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -117,7 +117,6 @@ class UpgradeDatabase extends Command
// index will set FF3 version. // index will set FF3 version.
app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); app('fireflyconfig')->set('ff3_version', (string) config('firefly.version'));
// app('telemetry')->feature('executed-command', $this->signature);
return 0; return 0;
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
use FireflyIII\Support\System\GeneratesInstallationId;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** /**
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
*/ */
class UpgradeFireflyInstructions extends Command class UpgradeFireflyInstructions extends Command
{ {
use GeneratesInstallationId;
/** /**
* The console command description. * The console command description.
* *
@@ -50,6 +53,7 @@ class UpgradeFireflyInstructions extends Command
*/ */
public function handle(): int public function handle(): int
{ {
$this->generateInstallationId();
if ('update' === (string) $this->argument('task')) { if ('update' === (string) $this->argument('task')) {
$this->updateInstructions(); $this->updateInstructions();
} }
@@ -57,7 +61,14 @@ class UpgradeFireflyInstructions extends Command
$this->installInstructions(); $this->installInstructions();
} }
// app('telemetry')->feature('executed-command', $this->signature); // collect system telemetry
$isDocker = true === env('IS_DOCKER', false) ? 'true' : 'false';
app('telemetry')->feature('system.php.version', PHP_VERSION);
app('telemetry')->feature('system.os.version', PHP_OS);
app('telemetry')->feature('system.database.driver', env('DB_CONNECTION', '(unknown)'));
app('telemetry')->feature('system.os.is_docker', $isDocker);
app('telemetry')->feature('system.command.executed', $this->signature);
return 0; return 0;
} }

View File

@@ -131,6 +131,7 @@ class LoginController extends Controller
return redirect(route('register')); // @codeCoverageIgnore return redirect(route('register')); // @codeCoverageIgnore
} }
// is allowed to? // is allowed to?
$singleUserMode = app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data; $singleUserMode = app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
$allowRegistration = true; $allowRegistration = true;
@@ -148,6 +149,9 @@ class LoginController extends Controller
$email = $request->old('email'); $email = $request->old('email');
$remember = $request->old('remember'); $remember = $request->old('remember');
// todo must forget 2FA if user ends up here.
return view('auth.login', compact('allowRegistration', 'email', 'remember', 'allowReset', 'title')); return view('auth.login', compact('allowRegistration', 'email', 'remember', 'allowReset', 'title'));
} }

View File

@@ -104,6 +104,9 @@ class RegisterController extends Controller
$this->registered($request, $user); $this->registered($request, $user);
// telemetry
\Telemetry::feature('system.users.count', User::count());
return redirect($this->redirectPath()); return redirect($this->redirectPath());
} }

View File

@@ -284,7 +284,7 @@ class BillController extends Controller
} }
$request->session()->flash('success', (string) trans('firefly.rescanned_bill', ['total' => $total])); $request->session()->flash('success', (string) trans_choice('firefly.rescanned_bill', $total));
app('preferences')->mark(); app('preferences')->mark();
return redirect(route('bills.show', [$bill->id])); return redirect(route('bills.show', [$bill->id]));

View File

@@ -177,12 +177,26 @@ class AvailableBudgetController extends Controller
$end = session()->get('end'); $end = session()->get('end');
Log::info($e->getMessage()); Log::info($e->getMessage());
} }
// validate amount
$amount = (string) $request->get('amount');
if ('' === $amount) {
session()->flash('error', trans('firefly.invalid_amount'));
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
}
if (0 === bccomp('0', $amount)) {
session()->flash('error', trans('firefly.invalid_amount'));
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
}
// find currency // find currency
$currency = $this->currencyRepos->find((int) $request->get('currency_id')); $currency = $this->currencyRepos->find((int) $request->get('currency_id'));
if (null === $currency) { if (null === $currency) {
session()->flash('error', trans('firefly.invalid_currency')); session()->flash('error', trans('firefly.invalid_currency'));
return redirect(route('budgets.index')); return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
} }
// find existing AB // find existing AB
@@ -190,7 +204,7 @@ class AvailableBudgetController extends Controller
if (null === $existing) { if (null === $existing) {
$this->abRepository->store( $this->abRepository->store(
[ [
'amount' => $request->get('amount'), 'amount' => $amount,
'currency' => $currency, 'currency' => $currency,
'start' => $start, 'start' => $start,
'end' => $end, 'end' => $end,
@@ -199,7 +213,7 @@ class AvailableBudgetController extends Controller
} }
if (null !== $existing) { if (null !== $existing) {
// update amount: // update amount:
$this->abRepository->update($existing, ['amount' => $request->get('amount')]); $this->abRepository->update($existing, ['amount' => $amount]);
} }
session()->flash('success', trans('firefly.set_ab')); session()->flash('success', trans('firefly.set_ab'));
@@ -217,7 +231,21 @@ class AvailableBudgetController extends Controller
*/ */
public function update(Request $request, AvailableBudget $availableBudget, Carbon $start, Carbon $end) public function update(Request $request, AvailableBudget $availableBudget, Carbon $start, Carbon $end)
{ {
$this->abRepository->update($availableBudget, ['amount' => $request->get('amount')]); // validate amount
$amount = (string) $request->get('amount');
if ('' === $amount) {
session()->flash('error', trans('firefly.invalid_amount'));
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
}
if (0 === bccomp('0', $amount)) {
session()->flash('error', trans('firefly.invalid_amount'));
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
}
$this->abRepository->update($availableBudget, ['amount' => $amount]);
session()->flash('success', trans('firefly.updated_ab')); session()->flash('success', trans('firefly.updated_ab'));
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));

View File

@@ -132,6 +132,7 @@ class BudgetLimitController extends Controller
*/ */
public function store(Request $request) public function store(Request $request)
{ {
Log::debug('Going to store new budget-limit.', $request->all());
// first search for existing one and update it if necessary. // first search for existing one and update it if necessary.
$currency = $this->currencyRepos->find((int) $request->get('transaction_currency_id')); $currency = $this->currencyRepos->find((int) $request->get('transaction_currency_id'));
$budget = $this->repository->findNull((int) $request->get('budget_id')); $budget = $this->repository->findNull((int) $request->get('budget_id'));
@@ -143,7 +144,6 @@ class BudgetLimitController extends Controller
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
Log::debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
$limit = $this->blRepository->find($budget, $currency, $start, $end); $limit = $this->blRepository->find($budget, $currency, $start, $end);

View File

@@ -121,11 +121,11 @@ class DebugController extends Controller
$search = ['~', '#']; $search = ['~', '#'];
$replace = ['\~', '# ']; $replace = ['\~', '# '];
$now = Carbon::now()->format('Y-m-d H:i:s e');
$installationId = app('fireflyconfig')->get('installation_id', '')->data; $installationId = app('fireflyconfig')->get('installation_id', '')->data;
$phpVersion = str_replace($search, $replace, PHP_VERSION); $phpVersion = str_replace($search, $replace, PHP_VERSION);
$phpOs = str_replace($search, $replace, PHP_OS); $phpOs = str_replace($search, $replace, PHP_OS);
$interface = PHP_SAPI; $interface = PHP_SAPI;
$now = Carbon::now()->format('Y-m-d H:i:s e');
$drivers = implode(', ', DB::availableDrivers()); $drivers = implode(', ', DB::availableDrivers());
$currentDriver = DB::getDriverName(); $currentDriver = DB::getDriverName();
$userAgent = $request->header('user-agent'); $userAgent = $request->header('user-agent');
@@ -137,7 +137,15 @@ class DebugController extends Controller
$logChannel = config('logging.default'); $logChannel = config('logging.default');
$appLogLevel = config('logging.level'); $appLogLevel = config('logging.level');
$cacheDriver = config('cache.default'); $cacheDriver = config('cache.default');
$loginProvider = config('auth.driver'); $loginProvider = config('auth.providers.users.driver');
// some new vars.
$telemetry = true === config('firefly.send_telemetry') && true === config('firefly.feature_flags.telemetry');
$defaultLanguage = (string) config('firefly.default_language');
$defaultLocale = (string) config('firefly.default_locale');
$userLanguage = app('steam')->getLanguage();
$userLocale = app('steam')->getLocale();
$isDocker = env('IS_DOCKER', false);
// set languages, see what happens: // set languages, see what happens:
$original = setlocale(LC_ALL, 0); $original = setlocale(LC_ALL, 0);
@@ -145,6 +153,7 @@ class DebugController extends Controller
$parts = app('steam')->getLocaleArray(app('steam')->getLocale()); $parts = app('steam')->getLocaleArray(app('steam')->getLocale());
foreach ($parts as $code) { foreach ($parts as $code) {
$code = trim($code); $code = trim($code);
Log::debug(sprintf('Trying to set %s', $code));
$localeAttempts[$code] = var_export(setlocale(LC_ALL, $code), true); $localeAttempts[$code] = var_export(setlocale(LC_ALL, $code), true);
} }
setlocale(LC_ALL, $original); setlocale(LC_ALL, $original);
@@ -194,7 +203,14 @@ class DebugController extends Controller
'interface', 'interface',
'logContent', 'logContent',
'cacheDriver', 'cacheDriver',
'trustedProxies' 'trustedProxies',
'telemetry',
'userLanguage',
'userLocale',
'defaultLanguage',
'defaultLocale',
'isDocker'
) )
); );
} }

View File

@@ -117,7 +117,7 @@ class HomeController extends Controller
if (0 === $count) { if (0 === $count) {
return redirect(route('new-user.index')); return redirect(route('new-user.index'));
} }
$subTitle = (string) trans('firefly.welcomeBack'); $subTitle = (string) trans('firefly.welcome_back');
$transactions = []; $transactions = [];
$frontPage = app('preferences')->get( $frontPage = app('preferences')->get(
'frontPageAccounts', 'frontPageAccounts',

View File

@@ -124,6 +124,11 @@ class NewUserController extends Controller
'invoice_date' => false, 'internal_reference' => false, 'notes' => true, 'attachments' => true,]; 'invoice_date' => false, 'internal_reference' => false, 'notes' => true, 'attachments' => true,];
app('preferences')->set('transaction_journal_optional_fields', $visibleFields); app('preferences')->set('transaction_journal_optional_fields', $visibleFields);
// telemetry: user language preference + default language.
app('telemetry')->feature('config.firefly.default_language', config('firefly.default_language', 'en_US'));
app('telemetry')->feature('user.preferences.language', app('steam')->getLanguage());
app('telemetry')->feature('user.preferences.locale', app('steam')->getLocale());
session()->flash('success', (string) trans('firefly.stored_new_accounts_new_user')); session()->flash('success', (string) trans('firefly.stored_new_accounts_new_user'));
app('preferences')->mark(); app('preferences')->mark();

View File

@@ -92,9 +92,7 @@ class PiggyBankController extends Controller
*/ */
public function add(PiggyBank $piggyBank) public function add(PiggyBank $piggyBank)
{ {
/** @var Carbon $date */ $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, new Carbon);
$date = session('end', Carbon::now()->endOfMonth());
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $date);
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank); $savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank);
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = min($leftOnAccount, $leftToSave); $maxAmount = min($leftOnAccount, $leftToSave);
@@ -113,7 +111,7 @@ class PiggyBankController extends Controller
public function addMobile(PiggyBank $piggyBank) public function addMobile(PiggyBank $piggyBank)
{ {
/** @var Carbon $date */ /** @var Carbon $date */
$date = session('end', Carbon::now()->endOfMonth()); $date = session('end', new Carbon);
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $date); $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $date);
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank); $savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank);
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);

View File

@@ -210,6 +210,11 @@ class PreferencesController extends Controller
session()->flash('success', (string) trans('firefly.saved_preferences')); session()->flash('success', (string) trans('firefly.saved_preferences'));
app('preferences')->mark(); app('preferences')->mark();
// telemetry: user language preference + default language.
app('telemetry')->feature('config.firefly.default_language', config('firefly.default_language', 'en_US'));
app('telemetry')->feature('user.preferences.language', app('steam')->getLanguage());
app('telemetry')->feature('user.preferences.locale', app('steam')->getLocale());
return redirect(route('preferences.index')); return redirect(route('preferences.index'));
} }
} }

View File

@@ -42,6 +42,7 @@ class CronController
$results = []; $results = [];
$results[] = $this->runRecurring(); $results[] = $this->runRecurring();
$results[] = $this->runAutoBudget(); $results[] = $this->runAutoBudget();
$results[] = $this->runTelemetry();
return implode("<br>\n", $results); return implode("<br>\n", $results);
} }

View File

@@ -217,7 +217,7 @@ class TagController extends Controller
$count++; $count++;
} }
} }
session()->flash('success', (string) trans('firefly.deleted_x_tags', ['count' => $count])); session()->flash('success', (string) trans_choice('firefly.deleted_x_tags', $count));
return redirect(route('tags.index')); return redirect(route('tags.index'));
} }

View File

@@ -118,7 +118,7 @@ class BulkController extends Controller
} }
} }
app('preferences')->mark(); app('preferences')->mark();
$request->session()->flash('success', (string) trans('firefly.mass_edited_transactions_success', ['amount' => $count])); $request->session()->flash('success', (string) trans_choice('firefly.mass_edited_transactions_success', $count));
// redirect to previous URL: // redirect to previous URL:
return redirect($this->getPreviousUri('transactions.bulk-edit.uri')); return redirect($this->getPreviousUri('transactions.bulk-edit.uri'));

View File

@@ -114,7 +114,7 @@ class MassController extends Controller
app('preferences')->mark(); app('preferences')->mark();
session()->flash('success', (string) trans('firefly.mass_deleted_transactions_success', ['amount' => $count])); session()->flash('success', (string) trans_choice('firefly.mass_deleted_transactions_success', $count));
// redirect to previous URL: // redirect to previous URL:
return redirect($this->getPreviousUri('transactions.mass-delete.uri')); return redirect($this->getPreviousUri('transactions.mass-delete.uri'));
@@ -188,7 +188,7 @@ class MassController extends Controller
} }
app('preferences')->mark(); app('preferences')->mark();
session()->flash('success', (string) trans('firefly.mass_edited_transactions_success', ['amount' => $count])); session()->flash('success', (string) trans_choice('firefly.mass_edited_transactions_success', $count ));
// redirect to previous URL: // redirect to previous URL:
return redirect($this->getPreviousUri('transactions.mass-edit.uri')); return redirect($this->getPreviousUri('transactions.mass-edit.uri'));

View File

@@ -99,8 +99,9 @@ class ShowController extends Controller
$transformer->setParameters(new ParameterBag); $transformer->setParameters(new ParameterBag);
$groupArray = $transformer->transformObject($transactionGroup); $groupArray = $transformer->transformObject($transactionGroup);
// do some amount calculations: // do some calculations:
$amounts = $this->getAmounts($groupArray); $amounts = $this->getAmounts($groupArray);
$accounts = $this->getAccounts($groupArray);
// make sure notes are escaped but not double escaped. // make sure notes are escaped but not double escaped.
foreach ($groupArray['transactions'] as $index => $transaction) { foreach ($groupArray['transactions'] as $index => $transaction) {
@@ -127,7 +128,8 @@ class ShowController extends Controller
'groupArray', 'groupArray',
'events', 'events',
'attachments', 'attachments',
'links' 'links',
'accounts',
) )
); );
} }
@@ -166,4 +168,31 @@ class ShowController extends Controller
return $amounts; return $amounts;
} }
/**
* @param array $group
*
* @return array
*/
private function getAccounts(array $group): array
{
$accounts = [];
foreach ($group['transactions'] as $index => $transaction) {
$accounts['source'][] = [
'type' => $transaction['source_type'],
'id' => $transaction['source_id'],
'name' => $transaction['source_name'],
'iban' => $transaction['source_iban'] ];
$accounts['destination'][] = [
'type' => $transaction['destination_type'],
'id' => $transaction['destination_id'],
'name' => $transaction['destination_name'],
'iban' => $transaction['destination_iban'] ];
}
$accounts['source'] = array_unique($accounts['source'], SORT_REGULAR);
$accounts['destination'] = array_unique($accounts['destination'], SORT_REGULAR);
return $accounts;
}
} }

View File

@@ -26,8 +26,7 @@ namespace FireflyIII\Http\Middleware;
use Closure; use Closure;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use Log; use FireflyIII\Support\System\GeneratesInstallationId;
use Ramsey\Uuid\Uuid;
/** /**
* *
@@ -35,6 +34,7 @@ use Ramsey\Uuid\Uuid;
*/ */
class InstallationId class InstallationId
{ {
use GeneratesInstallationId;
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
@@ -48,13 +48,7 @@ class InstallationId
*/ */
public function handle($request, Closure $next) public function handle($request, Closure $next)
{ {
$config = app('fireflyconfig')->get('installation_id', null); $this->generateInstallationId();
if (null === $config) {
$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_URL, 'firefly-iii.org');
$uniqueId = (string) $uuid5;
Log::info(sprintf('Created Firefly III installation ID %s', $uniqueId));
app('fireflyconfig')->set('installation_id', $uniqueId);
}
return $next($request); return $next($request);
} }

View File

@@ -28,6 +28,7 @@ use Closure;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Http\Controllers\RequestInformation; use FireflyIII\Support\Http\Controllers\RequestInformation;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Log;
/** /**
* Class SessionFilter. * Class SessionFilter.
@@ -87,6 +88,7 @@ class Range
// send error to view if could not set money format // send error to view if could not set money format
if (false === $moneyResult) { if (false === $moneyResult) {
Log::error('Could not set locale. The following array doesnt work: ', $localeArray);
app('view')->share('invalidMonetaryLocale', true); // @codeCoverageIgnore app('view')->share('invalidMonetaryLocale', true); // @codeCoverageIgnore
} }

View File

@@ -84,7 +84,7 @@ class MailError extends Job implements ShouldQueue
$args, $args,
function (Message $message) use ($email) { function (Message $message) use ($email) {
if ('mail@example.com' !== $email) { if ('mail@example.com' !== $email) {
$message->to($email, $email)->subject('Caught an error in Firefly III'); $message->to($email, $email)->subject((string) trans('email.error_subject'));
} }
} }
); );

View File

@@ -24,6 +24,7 @@ namespace FireflyIII\Jobs;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Telemetry; use FireflyIII\Models\Telemetry;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
@@ -33,7 +34,9 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use JsonException;
use Log; use Log;
use Exception;
/** /**
* Class SubmitTelemetryData * Class SubmitTelemetryData
@@ -61,7 +64,7 @@ class SubmitTelemetryData implements ShouldQueue
} }
/** /**
* * @throws FireflyException
*/ */
public function handle(): void public function handle(): void
{ {
@@ -77,9 +80,17 @@ class SubmitTelemetryData implements ShouldQueue
} }
$json = $this->parseJson($telemetry); $json = $this->parseJson($telemetry);
try {
$body = json_encode($json, JSON_THROW_ON_ERROR, 512);
} catch (JsonException $e) {
Log::error($e->getMessage());
Log::error('Could not parse JSON.');
throw new FireflyException(sprintf('Could not parse telemetry JSON: %s', $e->getMessage()));
}
$client = new Client; $client = new Client;
$options = [ $options = [
'body' => json_encode($json, JSON_THROW_ON_ERROR, 512), 'body' => $body,
'headers' => [ 'headers' => [
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
'Accept' => 'application/json', 'Accept' => 'application/json',
@@ -89,11 +100,11 @@ class SubmitTelemetryData implements ShouldQueue
]; ];
try { try {
$result = $client->post($url, $options); $result = $client->post($url, $options);
} catch (GuzzleException $e) { } catch (GuzzleException|Exception $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
Log::error('Could not submit telemetry.'); Log::error('Could not submit telemetry.');
return; throw new FireflyException(sprintf('Could not submit telemetry: %s', $e->getMessage()));
} }
$body = (string) $result->getBody(); $body = (string) $result->getBody();
$statusCode = $result->getStatusCode(); $statusCode = $result->getStatusCode();
@@ -156,6 +167,7 @@ class SubmitTelemetryData implements ShouldQueue
foreach ($telemetry as $entry) { foreach ($telemetry as $entry) {
$array[] = [ $array[] = [
'installation_id' => $entry->installation_id, 'installation_id' => $entry->installation_id,
'collected_at' => $entry->created_at->format('r'),
'type' => $entry->type, 'type' => $entry->type,
'key' => $entry->key, 'key' => $entry->key,
'value' => $entry->value, 'value' => $entry->value,

View File

@@ -63,6 +63,6 @@ class AccessTokenCreatedMail extends Mailable
public function build(): self public function build(): self
{ {
return $this->view('emails.access-token-created-html')->text('emails.access-token-created-text') return $this->view('emails.access-token-created-html')->text('emails.access-token-created-text')
->subject('A new access token was created'); ->subject((string) trans('email.access_token_created_subject'));
} }
} }

View File

@@ -62,6 +62,6 @@ class AdminTestMail extends Mailable
public function build(): self public function build(): self
{ {
return $this->view('emails.admin-test-html')->text('emails.admin-test-text') return $this->view('emails.admin-test-html')->text('emails.admin-test-text')
->subject('A test message from your Firefly III installation'); ->subject((string) trans('email.admin_test_subject'));
} }
} }

View File

@@ -70,6 +70,6 @@ class ConfirmEmailChangeMail extends Mailable
public function build(): self public function build(): self
{ {
return $this->view('emails.confirm-email-change-html')->text('emails.confirm-email-change-text') return $this->view('emails.confirm-email-change-html')->text('emails.confirm-email-change-text')
->subject('Your Firefly III email address has changed'); ->subject((string) trans('email.email_change_subject'));
} }
} }

View File

@@ -67,6 +67,6 @@ class OAuthTokenCreatedMail extends Mailable
public function build(): self public function build(): self
{ {
return $this->view('emails.oauth-client-created-html')->text('emails.oauth-client-created-text') return $this->view('emails.oauth-client-created-html')->text('emails.oauth-client-created-text')
->subject('A new OAuth client has been created'); ->subject((string) trans('email.oauth_created_subject'));
} }
} }

View File

@@ -61,6 +61,6 @@ class RegisteredUser extends Mailable
*/ */
public function build(): self public function build(): self
{ {
return $this->view('emails.registered-html')->text('emails.registered-text')->subject('Welcome to Firefly III!'); return $this->view('emails.registered-html')->text('emails.registered-text')->subject((string) trans('email.registered_subject'));
} }
} }

View File

@@ -79,7 +79,7 @@ class ReportNewJournalsMail extends Mailable
$this->transform(); $this->transform();
return $this->view('emails.report-new-journals-html')->text('emails.report-new-journals-text') return $this->view('emails.report-new-journals-html')->text('emails.report-new-journals-text')
->subject($subject); ->subject((string) trans_choice('email.new_journals_subject', $this->groups->count()));
} }
private function transform(): void private function transform(): void

View File

@@ -60,6 +60,6 @@ class RequestedNewPassword extends Mailable
*/ */
public function build(): self public function build(): self
{ {
return $this->view('emails.password-html')->text('emails.password-text')->subject('Your password reset request'); return $this->view('emails.password-html')->text('emails.password-text')->subject((string) trans('email.reset_pw_subject'));
} }
} }

View File

@@ -68,6 +68,6 @@ class UndoEmailChangeMail extends Mailable
public function build(): self public function build(): self
{ {
return $this->view('emails.undo-email-change-html')->text('emails.undo-email-change-text') return $this->view('emails.undo-email-change-html')->text('emails.undo-email-change-text')
->subject('Your Firefly III email address has changed'); ->subject((string) trans('email.email_change_subject'));
} }
} }

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class ObjectGroup
*/
class ObjectGroup extends Model
{
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function piggyBanks()
{
return $this->morphedByMany(PiggyBank::class, 'object_groupable');
}
}

View File

@@ -126,6 +126,14 @@ class PiggyBank extends Model
throw new NotFoundHttpException; throw new NotFoundHttpException;
} }
/**
* Get all of the tags for the post.
*/
public function objectGroups()
{
return $this->morphToMany(ObjectGroup::class, 'object_groupable');
}
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
* @return MorphMany * @return MorphMany

View File

@@ -330,7 +330,7 @@ class BudgetRepository implements BudgetRepositoryInterface
// create initial budget limit. // create initial budget limit.
$today = new Carbon; $today = new Carbon;
$start = app('navigation')->startOfPeriod($today, $autoBudget->period); $start = app('navigation')->startOfPeriod($today, $autoBudget->period);
$end = app('navigation')->startOfPeriod($start, $autoBudget->period); $end = app('navigation')->endOfPeriod($start, $autoBudget->period);
$limitRepos = app(BudgetLimitRepositoryInterface::class); $limitRepos = app(BudgetLimitRepositoryInterface::class);
$limitRepos->setUser($this->user); $limitRepos->setUser($this->user);

View File

@@ -172,6 +172,7 @@ class UpdateRequest implements UpdateRequestInterface
'headers' => [ 'headers' => [
'User-Agent' => sprintf('FireflyIII/%s/%s', config('firefly.version'), $channel), 'User-Agent' => sprintf('FireflyIII/%s/%s', config('firefly.version'), $channel),
], ],
'timeout' => 3.1415
]; ];
$res = $client->request('GET', $uri, $options); $res = $client->request('GET', $uri, $options);
} catch (GuzzleException|Exception $e) { } catch (GuzzleException|Exception $e) {

View File

@@ -52,11 +52,10 @@ class AccountList implements BinderInterface
/** @var Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->accounts() $collection = auth()->user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.type', AccountType::ASSET) ->whereIn('account_types.type', [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE])
->orderBy('accounts.name', 'ASC') ->orderBy('accounts.name', 'ASC')
->get(['accounts.*']); ->get(['accounts.*']);
} }
if ('allAssetAccounts' !== $value) { if ('allAssetAccounts' !== $value) {
$incoming = array_map('\intval', explode(',', $value)); $incoming = array_map('\intval', explode(',', $value));
$list = array_merge(array_unique($incoming), [0]); $list = array_merge(array_unique($incoming), [0]);

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\Cronjobs; namespace FireflyIII\Support\Cronjobs;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\SubmitTelemetryData; use FireflyIII\Jobs\SubmitTelemetryData;
use FireflyIII\Models\Configuration; use FireflyIII\Models\Configuration;
use Log; use Log;
@@ -35,9 +36,17 @@ class TelemetryCronjob extends AbstractCronjob
/** /**
* @inheritDoc * @inheritDoc
* @throws FireflyException
*/ */
public function fire(): bool public function fire(): bool
{ {
// do not fire if telemetry is disabled.
if (false === config('firefly.send_telemetry') || false === config('firefly.feature_flags.telemetry')) {
Log::warning('Telemetry is disabled. The cron job will do nothing.');
return false;
}
/** @var Configuration $config */ /** @var Configuration $config */
$config = app('fireflyconfig')->get('last_tm_job', 0); $config = app('fireflyconfig')->get('last_tm_job', 0);
$lastTime = (int) $config->data; $lastTime = (int) $config->data;
@@ -46,8 +55,8 @@ class TelemetryCronjob extends AbstractCronjob
if (0 === $lastTime) { if (0 === $lastTime) {
Log::info('Telemetry cron-job has never fired before.'); Log::info('Telemetry cron-job has never fired before.');
} }
// less than half a day ago: // less than a week ago:
if ($lastTime > 0 && $diff <= 43200) { if ($lastTime > 0 && $diff <= 604800) {
Log::info(sprintf('It has been %s since the telemetry cron-job has fired.', $diffForHumans)); Log::info(sprintf('It has been %s since the telemetry cron-job has fired.', $diffForHumans));
if (false === $this->force) { if (false === $this->force) {
Log::info('The cron-job will not fire now.'); Log::info('The cron-job will not fire now.');
@@ -60,8 +69,8 @@ class TelemetryCronjob extends AbstractCronjob
Log::info('Execution of the telemetry cron-job has been FORCED.'); Log::info('Execution of the telemetry cron-job has been FORCED.');
} }
} }
// more than a week ago.
if ($lastTime > 0 && $diff > 43200) { if ($lastTime > 0 && $diff > 604799) {
Log::info(sprintf('It has been %s since the telemetry cron-job has fired. It will fire now!', $diffForHumans)); Log::info(sprintf('It has been %s since the telemetry cron-job has fired. It will fire now!', $diffForHumans));
} }
@@ -74,7 +83,7 @@ class TelemetryCronjob extends AbstractCronjob
/** /**
* * @throws FireflyException
*/ */
private function fireTelemetry(): void private function fireTelemetry(): void
{ {
@@ -84,6 +93,9 @@ class TelemetryCronjob extends AbstractCronjob
$job->setDate($this->date); $job->setDate($this->date);
$job->setForce($this->force); $job->setForce($this->force);
$job->handle(); $job->handle();
// TODO remove old, submitted telemetry data.
app('fireflyconfig')->set('last_tm_job', (int) $this->date->format('U')); app('fireflyconfig')->set('last_tm_job', (int) $this->date->format('U'));
Log::info('Done with telemetry cron job task.'); Log::info('Done with telemetry cron job task.');
} }

View File

@@ -436,4 +436,33 @@ class ExpandedForm
return $html; return $html;
} }
/**
* @param null $value
* @param array|null $options
*
* @return string
*/
public function objectGroup($value = null, array $options = null): string
{
$name = 'object_group';
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['rows'] = 4;
if (null === $value) {
$value = '';
}
try {
$html = view('form.object_group', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render objectGroup(): %s', $e->getMessage()));
$html = 'Could not render objectGroup.';
}
return $html;
}
} }

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Support\Http\Controllers;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Cronjobs\AutoBudgetCronjob; use FireflyIII\Support\Cronjobs\AutoBudgetCronjob;
use FireflyIII\Support\Cronjobs\RecurringCronjob; use FireflyIII\Support\Cronjobs\RecurringCronjob;
use FireflyIII\Support\Cronjobs\TelemetryCronjob;
/** /**
* Trait CronRunner * Trait CronRunner
@@ -51,6 +52,24 @@ trait CronRunner
return 'The recurring transaction cron job fired successfully.'; return 'The recurring transaction cron job fired successfully.';
} }
/**
* @return string
*/
protected function runTelemetry(): string {
/** @var TelemetryCronjob $telemetry */
$telemetry = app(TelemetryCronjob::class);
try {
$result = $telemetry->fire();
} catch (FireflyException $e) {
return $e->getMessage();
}
if (false === $result) {
return 'The telemetry cron job did not fire.';
}
return 'The telemetry cron job fired successfully.';
}
/** /**
* @return string * @return string
*/ */

View File

@@ -123,7 +123,7 @@ class Steam
$transactions = $account->transactions() $transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currencyId) ->where('transactions.transaction_currency_id', $currencyId)
->get(['transactions.amount'])->toArray(); ->get(['transactions.amount'])->toArray();
$nativeBalance = $this->sumTransactions($transactions, 'amount'); $nativeBalance = $this->sumTransactions($transactions, 'amount');
@@ -131,7 +131,7 @@ class Steam
// get all balances in foreign currency: // get all balances in foreign currency:
$transactions = $account->transactions() $transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $currencyId) ->where('transactions.foreign_currency_id', $currencyId)
->where('transactions.transaction_currency_id', '!=', $currencyId) ->where('transactions.transaction_currency_id', '!=', $currencyId)
->get(['transactions.foreign_amount'])->toArray(); ->get(['transactions.foreign_amount'])->toArray();

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Support\System;
use Log;
use Ramsey\Uuid\Uuid;
/**
* Trait GeneratesInstallationId
*/
trait GeneratesInstallationId
{
/**
*
*/
protected function generateInstallationId(): void
{
$config = app('fireflyconfig')->get('installation_id', null);
if (null === $config) {
$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_URL, 'firefly-iii.org');
$uniqueId = (string) $uuid5;
Log::info(sprintf('Created Firefly III installation ID %s', $uniqueId));
app('fireflyconfig')->set('installation_id', $uniqueId);
}
}
}

View File

@@ -24,6 +24,7 @@ namespace FireflyIII\Support;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Telemetry as TelemetryModel; use FireflyIII\Models\Telemetry as TelemetryModel;
use FireflyIII\Support\System\GeneratesInstallationId;
use JsonException; use JsonException;
use Log; use Log;
@@ -32,6 +33,7 @@ use Log;
*/ */
class Telemetry class Telemetry
{ {
use GeneratesInstallationId;
/** /**
* Feature telemetry stores a $value for the given $feature. * Feature telemetry stores a $value for the given $feature.
* Will only store the given $feature / $value combination once. * Will only store the given $feature / $value combination once.
@@ -166,9 +168,12 @@ class Telemetry
*/ */
private function storeEntry(string $type, string $name, string $value): void private function storeEntry(string $type, string $name, string $value): void
{ {
$this->generateInstallationId();
$config = app('fireflyconfig')->get('installation_id', null);
$installationId = null !== $config ? $config->data : 'empty';
TelemetryModel::create( TelemetryModel::create(
[ [
'installation_id' => \FireflyConfig::get('installation_id', '')->data, 'installation_id' => $installationId,
'key' => $name, 'key' => $name,
'type' => $type, 'type' => $type,
'value' => $value, 'value' => $value,

View File

@@ -0,0 +1,99 @@
<?php
/**
* ForeignCurrencyIs.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\TransactionRules\Triggers;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Log;
/**
* Class ForeignCurrencyIs.
*/
final class ForeignCurrencyIs extends AbstractTrigger implements TriggerInterface
{
/**
* A trigger is said to "match anything", or match any given transaction,
* when the trigger value is very vague or has no restrictions. Easy examples
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
* has an amount of more than zero! Other examples are all the "Description"-triggers
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
*
* If the user tries to create such a trigger, this method MUST return true so Firefly III
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
* (even if it will still include 99.9% of the users transactions), this method MUST return
* false.
*
* @param mixed $value
*
* @return bool
*/
public static function willMatchEverything($value = null): bool
{
if (null !== $value) {
return false;
}
Log::error(sprintf('Cannot use %s with a null value.', self::class));
return true;
}
/**
* Returns true when description is X
*
* @param TransactionJournal $journal
*
* @return bool
*/
public function triggered(TransactionJournal $journal): bool
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
// if currency name contains " ("
if (0 === strpos($this->triggerValue, ' (')) {
$parts = explode(' (', $this->triggerValue);
$this->triggerValue = $parts[0];
}
$currency = $repository->findByNameNull($this->triggerValue);
$hit = true;
if (null !== $currency) {
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
if ((int)$transaction->foreign_currency_id !== (int)$currency->id) {
Log::debug(
sprintf(
'Trigger ForeignCurrencyIs: Transaction #%d in journal #%d uses currency %d instead of sought for #%d. No hit!',
$transaction->id, $journal->id, $transaction->foreign_currency_id, $currency->id
)
);
$hit = false;
}
}
}
return $hit;
}
}

View File

@@ -2,10 +2,45 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [5.2.7 (API 1.1.0) - 2020-06-01
### Added
- Firefly III **optional + opt-in** telemetry can now be enabled, if you want to. Read more about it [here](https://docs.firefly-iii.org/support/telemetry).
- [Issue 3133](https://github.com/firefly-iii/firefly-iii/issues/3133) You can remove attachments before you create a transaction.
- [Issue 3395](https://github.com/firefly-iii/firefly-iii/issues/3395) Emails sent by Firefly III have been translated. See the note at the bottom. Thanks to @sephrat
- [Issue 3393](https://github.com/firefly-iii/firefly-iii/issues/3393) New SSL options for LDAP and MySQL. Thanks to @sephrat
- [Issue 3413](https://github.com/firefly-iii/firefly-iii/issues/3413) Better string pluralization. Thanks to @sephrat
- [Issue 3297](https://github.com/firefly-iii/firefly-iii/issues/3297) Rule trigger for foreign currency ID
### Changed
- The default Docker Compose configuration and documentation switched from PostgreSQL to MariaDB. This will NOT affect existing installations unless you
change your docker compose file.
- [Issue 3404](https://github.com/firefly-iii/firefly-iii/issues/3404) The profile page has been translated. See the note at the bottom. Thanks to @sephrat
- [Issue 3405](https://github.com/firefly-iii/firefly-iii/issues/3405) All error pages have been translated. See the note at the bottom. Thanks to @sephrat
### Deprecated
- Initial release.
### Removed
- Initial release.
### Fixed
- [Issue 3309](https://github.com/firefly-iii/firefly-iii/issues/3309) New budgets would create bad budget limits.
- [Issue 3390](https://github.com/firefly-iii/firefly-iii/issues/3390) Typos and minor text inconsistencies fixed by @sephrat
- [Issue 3407](https://github.com/firefly-iii/firefly-iii/issues/3407) [issue 3408](https://github.com/firefly-iii/firefly-iii/issues/3408) The total transaction amount displayed is no longer empty for opening balances by @sephrat
- [Issue 3409](https://github.com/firefly-iii/firefly-iii/issues/3409) [issue 3420](https://github.com/firefly-iii/firefly-iii/issues/3420) Double accounts no longer listed by @sephrat
- [Issue 3427](https://github.com/firefly-iii/firefly-iii/issues/3427) Add a time-out to version update check. More improvements are coming.
- [Issue 3419](https://github.com/firefly-iii/firefly-iii/issues/3419) Error fixed which would prevent you from adding money to a piggy bank, by @sephrat
- [Issue 3425](https://github.com/firefly-iii/firefly-iii/issues/3425) Budget amount had no validation.
- [Issue 3428](https://github.com/firefly-iii/firefly-iii/issues/3428) Reconciliation "select all"-button would miscalculate.
- [Issue 3415](https://github.com/firefly-iii/firefly-iii/issues/3415) New error views
A note about new translations: text you see in errors and emails may still be in English. This is not a bug. Translated text is sometimes generated outside of
what's called the user's "session". When Firefly III operates outside of your session, it can't access your preferences or your data. It doesn't know what
language to pick. You can set the `DEFAULT_LANGUAGE`-environment variable. But user specific preferences may be ignored.
## [5.2.6 (API 1.1.0)] - 2020-05-22 ## [5.2.6 (API 1.1.0)] - 2020-05-22
## [3.4.2] - 2015-05-25
### Added ### Added
- [Issue 3049](https://github.com/firefly-iii/firefly-iii/issues/3049) New transaction triggers for dates. - [Issue 3049](https://github.com/firefly-iii/firefly-iii/issues/3049) New transaction triggers for dates.
- Warning if recurring transactions no longer run. - Warning if recurring transactions no longer run.

View File

@@ -94,11 +94,7 @@
"laravelcollective/html": "6.*", "laravelcollective/html": "6.*",
"league/commonmark": "1.*", "league/commonmark": "1.*",
"league/csv": "9.*", "league/csv": "9.*",
"league/flysystem-replicate-adapter": "1.*",
"league/flysystem-sftp": "1.*",
"league/fractal": "0.*", "league/fractal": "0.*",
"litipk/flysystem-fallback-adapter": "0.*",
"mschindler83/fints-hbci-php": "1.*",
"pragmarx/google2fa": "^7.0", "pragmarx/google2fa": "^7.0",
"pragmarx/recovery": "^0.1.0", "pragmarx/recovery": "^0.1.0",
"predis/predis": "^1.1", "predis/predis": "^1.1",

745
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,38 @@ if (!(false === $databaseUrl)) {
$database = substr($options['path'] ?? '/firefly', 1); $database = substr($options['path'] ?? '/firefly', 1);
} }
/*
* Get SSL parameters from .env file.
*/
$mysql_ssl_ca_dir = envNonEmpty('MYSQL_SSL_CAPATH', null);
$mysql_ssl_ca_file = envNonEmpty('MYSQL_SSL_CA', null);
$mysql_ssl_cert = envNonEmpty('MYSQL_SSL_CERT', null);
$mysql_ssl_key = envNonEmpty('MYSQL_SSL_KEY', null);
$mysql_ssl_ciphers = envNonEmpty('MYSQL_SSL_CIPHER', null);
$mysql_ssl_verify = envNonEmpty('MYSQL_SSL_VERIFY_SERVER_CERT', null);
$mySqlSSLOptions = [];
if (false !== envNonEmpty('MYSQL_USE_SSL', false)) {
if (null !== $mysql_ssl_ca_dir) {
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CAPATH] = $mysql_ssl_ca_dir;
}
if (null !== $mysql_ssl_ca_file) {
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ssl_ca_file;
}
if (null !== $mysql_ssl_cert) {
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CERT] = $mysql_ssl_cert;
}
if (null !== $mysql_ssl_key) {
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_KEY] = $mysql_ssl_key;
}
if (null !== $mysql_ssl_ciphers) {
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CIPHER] = $mysql_ssl_ciphers;
}
if (null !== $mysql_ssl_verify) {
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $mysql_ssl_verify;
}
}
return [ return [
'default' => envNonEmpty('DB_CONNECTION', 'pgsql'), 'default' => envNonEmpty('DB_CONNECTION', 'pgsql'),
'connections' => [ 'connections' => [
@@ -60,6 +92,7 @@ return [
'prefix' => '', 'prefix' => '',
'strict' => true, 'strict' => true,
'engine' => 'InnoDB', 'engine' => 'InnoDB',
'options' => $mySqlSSLOptions,
], ],
'pgsql' => [ 'pgsql' => [
'driver' => 'pgsql', 'driver' => 'pgsql',

View File

@@ -85,6 +85,7 @@ use FireflyIII\TransactionRules\Triggers\AmountMore;
use FireflyIII\TransactionRules\Triggers\BudgetIs; use FireflyIII\TransactionRules\Triggers\BudgetIs;
use FireflyIII\TransactionRules\Triggers\CategoryIs; use FireflyIII\TransactionRules\Triggers\CategoryIs;
use FireflyIII\TransactionRules\Triggers\CurrencyIs; use FireflyIII\TransactionRules\Triggers\CurrencyIs;
use FireflyIII\TransactionRules\Triggers\ForeignCurrencyIs;
use FireflyIII\TransactionRules\Triggers\DateIs; use FireflyIII\TransactionRules\Triggers\DateIs;
use FireflyIII\TransactionRules\Triggers\DateBefore; use FireflyIII\TransactionRules\Triggers\DateBefore;
use FireflyIII\TransactionRules\Triggers\DateAfter; use FireflyIII\TransactionRules\Triggers\DateAfter;
@@ -139,11 +140,11 @@ return [
], ],
'feature_flags' => [ 'feature_flags' => [
'export' => true, 'export' => true,
'telemetry' => false, 'telemetry' => true,
], ],
'encryption' => null === env('USE_ENCRYPTION') || true === env('USE_ENCRYPTION'), 'encryption' => null === env('USE_ENCRYPTION') || true === env('USE_ENCRYPTION'),
'version' => '5.2.6', 'version' => '5.2.7',
'api_version' => '1.1.0', 'api_version' => '1.1.0',
'db_version' => 13, 'db_version' => 13,
'maxUploadSize' => 15242880, 'maxUploadSize' => 15242880,
@@ -479,6 +480,7 @@ return [
'budget_is' => BudgetIs::class, 'budget_is' => BudgetIs::class,
'tag_is' => TagIs::class, 'tag_is' => TagIs::class,
'currency_is' => CurrencyIs::class, 'currency_is' => CurrencyIs::class,
'foreign_currency_is' => ForeignCurrencyIs::class,
'has_attachments' => HasAttachment::class, 'has_attachments' => HasAttachment::class,
'has_no_category' => HasNoCategory::class, 'has_no_category' => HasNoCategory::class,
'has_any_category' => HasAnyCategory::class, 'has_any_category' => HasAnyCategory::class,

View File

@@ -38,6 +38,36 @@ if ('ActiveDirectory' === envNonEmpty('ADLDAP_CONNECTION_SCHEME', 'OpenLDAP')) {
$schema = ActiveDirectory::class; $schema = ActiveDirectory::class;
} }
/*
* Get SSL parameters from .env file.
*/
$ssl_ca_dir = envNonEmpty('ADLDAP_SSL_CACERTDIR', null);
$ssl_ca_file = envNonEmpty('ADLDAP_SSL_CACERTFILE', null);
$ssl_cert = envNonEmpty('ADLDAP_SSL_CERTFILE', null);
$ssl_key = envNonEmpty('ADLDAP_SSL_KEYFILE', null);
$ssl_ciphers = envNonEmpty('ADLDAP_SSL_CIPHER_SUITE', null);
$ssl_require = envNonEmpty('ADLDAP_SSL_REQUIRE_CERT', null);
$sslOptions = [];
if (null !== $ssl_ca_dir) {
$sslOptions[LDAP_OPT_X_TLS_CACERTDIR] = $ssl_ca_dir;
}
if (null !== $ssl_ca_file) {
$sslOptions[LDAP_OPT_X_TLS_CACERTFILE] = $ssl_ca_file;
}
if (null !== $ssl_cert) {
$sslOptions[LDAP_OPT_X_TLS_CERTFILE] = $ssl_cert;
}
if (null !== $ssl_key) {
$sslOptions[LDAP_OPT_X_TLS_KEYFILE] = $ssl_key;
}
if (null !== $ssl_ciphers) {
$sslOptions[LDAP_OPT_X_TLS_CIPHER_SUITE] = $ssl_ciphers;
}
if (null !== $ssl_require) {
$sslOptions[LDAP_OPT_X_TLS_REQUIRE_CERT] = $ssl_require;
}
return [ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -254,6 +284,7 @@ return [
'use_ssl' => env('ADLDAP_USE_SSL', false), 'use_ssl' => env('ADLDAP_USE_SSL', false),
'use_tls' => env('ADLDAP_USE_TLS', false), 'use_tls' => env('ADLDAP_USE_TLS', false),
'custom_options' => $sslOptions,
], ],
], ],

View File

@@ -108,26 +108,6 @@ body.waiting * {
padding: 0; padding: 0;
} }
.ff-error-page {
width: 1000px;
margin: 20px auto 0 auto;
}
.ff-error-page > .error-content {
margin-left: 190px;
display: block;
}
.ff-error-page > .error-content > h3 {
font-weight: 300;
font-size: 25px;
}
.ff-error-box {
width: 460px;
margin: 7% auto;
}
/* cursors */ /* cursors */
.rule-triggers { .rule-triggers {
cursor: move; cursor: move;
@@ -152,7 +132,7 @@ body.waiting * {
} }
.loading { .loading {
background: url('/images/loading-small.gif') no-repeat center center; background: url('/v1/images/loading-small.gif') no-repeat center center;
min-height: 30px; min-height: 30px;
} }

2
public/v1/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -90,6 +90,10 @@ function selectAllReconcile(e) {
var identifier = 'checked_' + journalId; var identifier = 'checked_' + journalId;
console.log('in selectAllReconcile(' + journalId + ') with amount ' + amount + ' and selected amount ' + selectedAmount); console.log('in selectAllReconcile(' + journalId + ') with amount ' + amount + ' and selected amount ' + selectedAmount);
// do nothing if line is already in target state
if (check.prop('checked') === doCheck )
return;
check.prop('checked', doCheck); check.prop('checked', doCheck);
// if checked, add to selected amount // if checked, add to selected amount
if (doCheck === true && check.data('younger') === false) { if (doCheck === true && check.data('younger') === false) {

View File

@@ -282,10 +282,10 @@ function reportOnErrors(data) {
if (data.errors.length === 1) { if (data.errors.length === 1) {
$('#import-status-error-intro').text(langImportSingleError); $('#import-status-error-intro').text(langImportSingleError);
//'An error has occured during the import. The import can continue, however.' //'An error has occurred during the import. The import can continue, however.'
} }
if (data.errors.length > 1) { if (data.errors.length > 1) {
// 'Errors have occured during the import. The import can continue, however.' // 'Errors have occurred during the import. The import can continue, however.'
$('#import-status-error-intro').text(langImportMultiError); $('#import-status-error-intro').text(langImportMultiError);
} }
$('.info_errors').show(); $('.info_errors').show();

View File

@@ -350,6 +350,7 @@ function updateTriggerInput(selectList) {
inputResult.typeahead('destroy'); inputResult.typeahead('destroy');
break; break;
case 'currency_is': case 'currency_is':
case 'foreign_currency_is':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.'); console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/currency-names'); createAutoComplete(inputResult, 'json/currency-names');
break; break;

File diff suppressed because one or more lines are too long

View File

@@ -1,139 +0,0 @@
<!--
- Index.vue
- Copyright (c) 2019 james@firefly-iii.org
-
- This file is part of Firefly III (https://github.com/firefly-iii).
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<template>
<div>
<table class="table table-hover sortable">
<caption>Bills table</caption>
<thead>
<tr>
<th scope="col" class="hidden-sm hidden-xs" data-defaultsort="disabled">&nbsp;</th>
<th scope="col">{{ 'list.name' | trans }}</th>
<th scope="col"data-defaultsign="az" class="hidden-sm hidden-md hidden-xs">{{ 'list.matchesOn' | trans }}</th>
<th scope="col" data-defaultsign="_19" colspan="2">{{ 'list.amount' | trans }}</th>
<th scope="col" data-defaultsign="month" class="hidden-sm hidden-xs">{{ 'list.paid_current_period' | trans }}</th>
<th scope="col" data-defaultsign="month" class="hidden-sm hidden-xs">{{ 'list.next_expected_match' | trans }}</th>
<th scope="col" class="hidden-sm hidden-xs hidden-md">{{ 'list.active' | trans }}</th>
<th scope="col" class="hidden-sm hidden-xs hidden-md">{{ 'list.automatch' | trans }}</th>
<th scope="col" data-defaultsign="az" class="hidden-sm hidden-xs">{{ 'list.repeat_freq' | trans }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(bill, index) in list">
<td class="hidden-sm hidden-xs">
<div class="btn-group btn-group-xs edit_tr_buttons"><a href="x" class="btn btn-default btn-xs"><i class="fa fa-fw fa-pencil"></i></a><a href="x" class="btn btn-danger btn-xs"><i class="fa fa-fw fa-trash-o"></i></a></div>
</td>
<td :data-value="bill.attributes.name">
<a href="x" :title="bill.attributes.name">{{ bill.attributes.name }}</a>
<i v-if='bill.attributes.attachments_count > 0' class="fa fa-paperclip"></i>
</td>
<td class="hidden-sm hidden-md hidden-xs">
<span v-for="(word) in bill.attributes.match"><span class="label label-info">{{ word }}</span>&nbsp;</span>
</td>
<td :data-value="bill.attributes.amount_min" style="text-align: right;">
<span style="margin-right:5px;" v-html="formatAmount(bill.attributes.amount_min)"></span>
</td>
<td :data-value="bill.attributes.amount_max" style="text-align: right;">
<span style="margin-right:5px;" v-html="formatAmount(bill.attributes.amount_max)"></span>
</td>
<!-- first two -->
<td v-if="bill.attributes.paid_dates.length == 0 && bill.attributes.pay_dates.length == 0 && bill.attributes.active" class="paid_in_period text-muted">
{{ 'components.not_expected_period' | trans }}
</td>
<td v-if="bill.attributes.paid_dates.length == 0 && bill.attributes.pay_dates.length == 0 && bill.attributes.active" class="expected_in_period hidden-sm hidden-xs">
{{ bill.attributes.next_expected_match|formatDate }}
</td>
<!-- second set -->
<td v-if="bill.attributes.paid_dates.length == 0 && bill.attributes.pay_dates.length > 0 && bill.attributes.active" class="paid_in_period text-danger">
{{ 'components.not_or_not_yet' | trans }}
</td>
<td v-if="bill.attributes.paid_dates.length == 0 && bill.attributes.pay_dates.length > 0 && bill.attributes.active" class="expected_in_period hidden-sm hidden-xs">
{{ bill.attributes.next_expected_match|formatDate }}
</td>
<!-- third set -->
<td v-if="bill.attributes.paid_dates.length > 0 && bill.attributes.active" class="paid_in_period text-success">
<span v-for="date in bill.attributes.paid_dates">{{ date|formatDate }}<br /></span>
</td>
<td v-if="bill.attributes.paid_dates.length > 0 && bill.attributes.active" class="expected_in_period hidden-sm hidden-xs">
{{ bill.attributes.next_expected_match|formatDate }}
</td>
<!-- last set -->
<td v-if="bill.attributes.active === false" class="paid_in_period text-muted" data-value="0000-00-00 00-00-00">
~
</td>
<td v-if="bill.attributes.active === false" class="expected_in_period text-muted hidden-sm hidden-xs" data-value="0">
~
</td>
<td class="hidden-sm hidden-xs hidden-md" :data-value="bill.attributes.active">
<i v-if="bill.attributes.active === true" class="fa fa-fw fa-check"></i>
<i v-if="bill.attributes.active === false" class="fa fa-fw fa-ban"></i>
</td>
<td class="hidden-sm hidden-xs hidden-md" :data-value="bill.attributes.automatch">
<i v-if="bill.attributes.automatch === true" class="fa fa-fw fa-check"></i>
<i v-if="bill.attributes.automatch === false" class="fa fa-fw fa-ban"></i>
</td>
<td class="hidden-sm hidden-xs" :data-value="bill.attributes.repeat_freq + bill.attributes.skip">
{{ bill.attributes.repeat_freq }}
<span v-if="bill.attributes.skip > 0">Skips over {{ bill.attributes.skip }}</span>
</td>
</tr>
<!--<button @click="deleteBill(bill.attributes.id)" class="btn btn-danger btn-xs pull-right">Delete</button>-->
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
list: [],
bill: {
id: '',
name: ''
}
};
},
created() {
this.fetchBillList();
},
methods: {
formatAmount: Vue.filter('formatAmount'),
trans: Vue.filter('trans'),
fetchBillList() {
axios.get('api/v1/bill', {params: {start: window.sessionStart, end: window.sessionEnd}}).then((res) => {
this.list = res.data.data;
});
},
deleteBill(id) {
axios.delete('api/bills/' + id)
.then((res) => {
this.fetchBillList()
})
.catch((err) => console.error(err));
},
}
}
</script>

Some files were not shown because too many files have changed in this diff Show More