Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop

This commit is contained in:
James Cole 2018-09-26 20:32:38 +02:00
commit e38b64547f
61 changed files with 6322 additions and 587 deletions

View File

@ -1,6 +1,6 @@
language: php
php:
- 7.1.18
- 7.2
cache:
directories:

View File

@ -98,6 +98,7 @@ class UpgradeDatabase extends Command
$this->migrateAttachmentData();
$this->migrateBillsToRules();
$this->budgetLimitCurrency();
$this->removeCCLiabilities();
$this->info('Firefly III database is up to date.');
@ -535,6 +536,28 @@ class UpgradeDatabase extends Command
}
}
/**
*
*/
private function removeCCLiabilities(): void
{
$ccType = AccountType::where('type', AccountType::CREDITCARD)->first();
$debtType =AccountType::where('type', AccountType::DEBT)->first();
if(null === $ccType || null === $debtType) {
return;
}
/** @var Collection $accounts */
$accounts = Account::where('account_type_id', $ccType->id)->get();
foreach($accounts as $account) {
$account->account_type_id = $debtType->id;
$account->save();
$this->line(sprintf('Converted credit card liability account "%s" (#%d) to generic debt liability.', $account->name, $account->id));
}
if($accounts->count() > 0) {
$this->info('Credit card liability types are no longer supported and have been converted to generic debts. See: http://bit.ly/FF3-credit-cards');
}
}
/**
* This method makes sure that the transaction journal uses the currency given in the transaction.
*

View File

@ -108,6 +108,7 @@ class TransactionFactory
{
Log::debug('Start of TransactionFactory::createPair()', $data);
// all this data is the same for both transactions:
Log::debug('Searching for currency info.');
$currency = $this->findCurrency($data['currency_id'], $data['currency_code']);
$description = $journal->description === $data['description'] ? null : $data['description'];
@ -164,6 +165,7 @@ class TransactionFactory
}
// set foreign currency
Log::debug('Trying to find foreign currency information.');
$foreign = $this->findCurrency($data['foreign_currency_id'], $data['foreign_currency_code']);
$this->setForeignCurrency($source, $foreign);
$this->setForeignCurrency($dest, $foreign);

View File

@ -36,6 +36,11 @@ use Log;
*/
class TransactionJournalFactory
{
/** @var User The user */
private $user;
use JournalServiceTrait, TransactionTypeTrait;
/**
* Constructor.
*/
@ -46,10 +51,6 @@ class TransactionJournalFactory
}
}
use JournalServiceTrait, TransactionTypeTrait;
/** @var User The user */
private $user;
/**
* Store a new transaction journal.
*
@ -67,13 +68,15 @@ class TransactionJournalFactory
$type = $this->findTransactionType($data['type']);
$defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user);
Log::debug(sprintf('Going to store a %s', $type->type));
$description = app('steam')->cleanString($data['description']);
$description = str_replace(["\n", "\t", "\r"], "\x20", $description);
$journal = TransactionJournal::create(
[
'user_id' => $data['user'],
'transaction_type_id' => $type->id,
'bill_id' => null,
'transaction_currency_id' => $defaultCurrency->id,
'description' => $data['description'],
'description' => $description,
'date' => $data['date']->format('Y-m-d'),
'order' => 0,
'tag_count' => 0,
@ -114,7 +117,7 @@ class TransactionJournalFactory
// store date meta fields (if present):
$fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date',
'due_date', 'recurrence_id', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id', 'importHash', 'importHashV2',
'external_id', 'sepa-batch-id','original-source'];
'external_id', 'sepa-batch-id', 'original-source'];
foreach ($fields as $field) {
$this->storeMeta($journal, $data, $field);

View File

@ -182,13 +182,17 @@ class AttachmentHelper implements AttachmentHelperInterface
/**
* Save attachments that get uploaded with models, through the app.
*
* @param Model $model
* @param object $model
* @param array|null $files
*
* @return bool
* @throws \Illuminate\Contracts\Encryption\EncryptException
*/
public function saveAttachmentsForModel(Model $model, ?array $files): bool
public function saveAttachmentsForModel(object $model, ?array $files): bool
{
if(!($model instanceof Model)) {
return false;
}
Log::debug(sprintf('Now in saveAttachmentsForModel for model %s', \get_class($model)));
if (\is_array($files)) {
Log::debug('$files is an array.');

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Attachments;
use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
@ -84,10 +83,10 @@ interface AttachmentHelperInterface
/**
* Save attachments that got uploaded.
*
* @param Model $model
* @param object $model
* @param null|array $files
*
* @return bool
*/
public function saveAttachmentsForModel(Model $model, ?array $files): bool;
public function saveAttachmentsForModel(object $model, ?array $files): bool;
}

View File

@ -83,12 +83,10 @@ class CreateController extends Controller
$debt = $this->repository->getAccountTypeByType(AccountType::DEBT);
$loan = $this->repository->getAccountTypeByType(AccountType::LOAN);
$mortgage = $this->repository->getAccountTypeByType(AccountType::MORTGAGE);
$creditCard = $this->repository->getAccountTypeByType(AccountType::CREDITCARD);
$liabilityTypes = [
$debt->id => (string)trans('firefly.account_type_' . AccountType::DEBT),
$loan->id => (string)trans('firefly.account_type_' . AccountType::LOAN),
$mortgage->id => (string)trans('firefly.account_type_' . AccountType::MORTGAGE),
$creditCard->id => (string)trans('firefly.account_type_' . AccountType::CREDITCARD),
];
asort($liabilityTypes);

View File

@ -90,12 +90,10 @@ class EditController extends Controller
$debt = $this->repository->getAccountTypeByType(AccountType::DEBT);
$loan = $this->repository->getAccountTypeByType(AccountType::LOAN);
$mortgage = $this->repository->getAccountTypeByType(AccountType::MORTGAGE);
$creditCard = $this->repository->getAccountTypeByType(AccountType::CREDITCARD);
$liabilityTypes = [
$debt->id => (string)trans('firefly.account_type_' . AccountType::DEBT),
$loan->id => (string)trans('firefly.account_type_' . AccountType::LOAN),
$mortgage->id => (string)trans('firefly.account_type_' . AccountType::MORTGAGE),
$creditCard->id => (string)trans('firefly.account_type_' . AccountType::CREDITCARD),
];
asort($liabilityTypes);

View File

@ -47,6 +47,8 @@ class JobStatusController extends Controller
public function __construct()
{
parent::__construct();
// set time limit to zero to prevent timeouts.
set_time_limit(0);
$this->middleware(
function ($request, $next) {

View File

@ -36,8 +36,10 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* TODO refactor so each auto-complete thing is a function call because lots of code duplication.
* Class AutoCompleteController.
*
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
@ -49,18 +51,40 @@ class AutoCompleteController extends Controller
/**
* Returns a JSON list of all accounts.
*
* @param Request $request
* @param AccountRepositoryInterface $repository
*
* @return JsonResponse
*/
public function allAccounts(AccountRepositoryInterface $repository): JsonResponse
public function allAccounts(Request $request, AccountRepositoryInterface $repository): JsonResponse
{
$return = array_unique(
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-all-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$return = array_values(
array_unique(
$repository->getAccountsByType(
[AccountType::REVENUE, AccountType::EXPENSE, AccountType::BENEFICIARY, AccountType::DEFAULT, AccountType::ASSET]
)->pluck('name')->toArray()
)
);
sort($return);
if ('' !== $search) {
$return = array_values(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
);
}
$cache->store($return);
return response()->json($return);
}
@ -68,15 +92,86 @@ class AutoCompleteController extends Controller
/**
* List of all journals.
*
* @param Request $request
* @param TransactionCollectorInterface $collector
*
* @return JsonResponse
*/
public function allTransactionJournals(TransactionCollectorInterface $collector): JsonResponse
public function allTransactionJournals(Request $request, TransactionCollectorInterface $collector): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-all-journals');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$collector->setLimit(250)->setPage(1);
$return = array_unique($collector->getTransactions()->pluck('description')->toArray());
sort($return);
$return = array_values(array_unique($collector->getTransactions()->pluck('description')->toArray()));
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
/**
* List of revenue accounts.
*
* @param Request $request
* @param AccountRepositoryInterface $repository
*
* @return JsonResponse
*/
public function assetAccounts(Request $request, AccountRepositoryInterface $repository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-asset-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$set = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$filtered = $set->filter(
function (Account $account) {
if (true === $account->active) {
return $account;
}
return false; // @codeCoverageIgnore
}
);
$return = array_values(array_unique($filtered->pluck('name')->toArray()));
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
@ -84,16 +179,37 @@ class AutoCompleteController extends Controller
/**
* Returns a JSON list of all bills.
*
* @param Request $request
* @param BillRepositoryInterface $repository
*
* @return JsonResponse
*/
public function bills(BillRepositoryInterface $repository): JsonResponse
public function bills(Request $request, BillRepositoryInterface $repository): JsonResponse
{
$return = array_unique(
$repository->getActiveBills()->pluck('name')->toArray()
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-bills');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$return = array_unique($repository->getActiveBills()->pluck('name')->toArray());
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
sort($return);
}
$cache->store($return);
return response()->json($return);
}
@ -101,14 +217,37 @@ class AutoCompleteController extends Controller
/**
* List of budgets.
*
* @param Request $request
* @param BudgetRepositoryInterface $repository
*
* @return JsonResponse
*/
public function budgets(BudgetRepositoryInterface $repository): JsonResponse
public function budgets(Request $request, BudgetRepositoryInterface $repository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-budgets');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$return = array_unique($repository->getBudgets()->pluck('name')->toArray());
sort($return);
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
@ -116,14 +255,34 @@ class AutoCompleteController extends Controller
/**
* Returns a list of categories.
*
* @param Request $request
* @param CategoryRepositoryInterface $repository
*
* @return JsonResponse
*/
public function categories(CategoryRepositoryInterface $repository): JsonResponse
public function categories(Request $request, CategoryRepositoryInterface $repository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-categories');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$return = array_unique($repository->getCategories()->pluck('name')->toArray());
sort($return);
if ('' !== $search) {
$return = array_values(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
);
}
$cache->store($return);
return response()->json($return);
}
@ -131,27 +290,62 @@ class AutoCompleteController extends Controller
/**
* List of currency names.
*
* @param Request $request
* @param CurrencyRepositoryInterface $repository
*
* @return JsonResponse
*/
public function currencyNames(CurrencyRepositoryInterface $repository): JsonResponse
public function currencyNames(Request $request, CurrencyRepositoryInterface $repository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-currency-names');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$return = $repository->get()->pluck('name')->toArray();
sort($return);
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
/**
* Returns a JSON list of all beneficiaries.
*
* @param Request $request
* @param AccountRepositoryInterface $repository
*
* @return JsonResponse
*/
public function expenseAccounts(AccountRepositoryInterface $repository): JsonResponse
public function expenseAccounts(Request $request, AccountRepositoryInterface $repository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-expense-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$set = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
$filtered = $set->filter(
function (Account $account) {
@ -166,27 +360,43 @@ class AutoCompleteController extends Controller
sort($return);
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
/**
* List of journals with their ID.
*
* @param Request $request
* @param TransactionCollectorInterface $collector
* @param TransactionJournal $except
*
* @return JsonResponse
*/
public function journalsWithId(TransactionCollectorInterface $collector, TransactionJournal $except): JsonResponse
public function journalsWithId(Request $request, TransactionCollectorInterface $collector, TransactionJournal $except): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('recent-journals-id');
$cache->addProperty('ac-expense-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
return response()->json($cache->get());
}
// find everything:
$collector->setLimit(400)->setPage(1);
$set = $collector->getTransactions()->pluck('description', 'journal_id')->toArray();
$return = [];
@ -200,6 +410,19 @@ class AutoCompleteController extends Controller
}
}
sort($return);
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
@ -208,12 +431,23 @@ class AutoCompleteController extends Controller
/**
* List of revenue accounts.
*
* @param Request $request
* @param AccountRepositoryInterface $repository
*
* @return JsonResponse
*/
public function revenueAccounts(AccountRepositoryInterface $repository): JsonResponse
public function revenueAccounts(Request $request, AccountRepositoryInterface $repository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-revenue-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$set = $repository->getAccountsByType([AccountType::REVENUE]);
$filtered = $set->filter(
function (Account $account) {
@ -227,30 +461,18 @@ class AutoCompleteController extends Controller
$return = array_unique($filtered->pluck('name')->toArray());
sort($return);
return response()->json($return);
}
/**
* List of revenue accounts.
*
* @param AccountRepositoryInterface $repository
*
* @return JsonResponse
*/
public function assetAccounts(AccountRepositoryInterface $repository): JsonResponse
{
$set = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$filtered = $set->filter(
function (Account $account) {
if (true === $account->active) {
return $account;
}
return false; // @codeCoverageIgnore
}
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
$return = array_unique($filtered->pluck('name')->toArray());
sort($return);
}
$cache->store($return);
return response()->json($return);
}
@ -258,28 +480,63 @@ class AutoCompleteController extends Controller
/**
* Returns a JSON list of all beneficiaries.
*
* @param Request $request
* @param TagRepositoryInterface $tagRepository
*
* @return JsonResponse
*/
public function tags(TagRepositoryInterface $tagRepository): JsonResponse
public function tags(Request $request, TagRepositoryInterface $tagRepository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-revenue-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$return = array_unique($tagRepository->get()->pluck('tag')->toArray());
sort($return);
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
/**
* List of journals by type.
*
* @param Request $request
* @param TransactionCollectorInterface $collector
* @param string $what
*
* @return JsonResponse
*/
public function transactionJournals(TransactionCollectorInterface $collector, string $what): JsonResponse
public function transactionJournals(Request $request, TransactionCollectorInterface $collector, string $what): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-revenue-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$type = config('firefly.transactionTypesByWhat.' . $what);
$types = [$type];
@ -287,21 +544,60 @@ class AutoCompleteController extends Controller
$return = array_unique($collector->getTransactions()->pluck('description')->toArray());
sort($return);
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
/**
* List if transaction types.
*
* @param Request $request
* @param JournalRepositoryInterface $repository
*
* @return JsonResponse
*/
public function transactionTypes(JournalRepositoryInterface $repository): JsonResponse
public function transactionTypes(Request $request, JournalRepositoryInterface $repository): JsonResponse
{
$search = (string)$request->get('search');
$cache = new CacheProperties;
$cache->addProperty('ac-revenue-accounts');
// very unlikely a user will actually search for this string.
$key = '' === $search ? 'skjf0893j89fj2398hd89dh289h2398hr7isd8900828u209ujnxs88929282u' : $search;
$cache->addProperty($key);
if ($cache->has()) {
return response()->json($cache->get());
}
// find everything:
$return = array_unique($repository->getTransactionTypes()->pluck('type')->toArray());
sort($return);
if ('' !== $search) {
$return = array_values(
array_unique(
array_filter(
$return, function (string $value) use ($search) {
return !(false === stripos($value, $search));
}, ARRAY_FILTER_USE_BOTH
)
)
);
}
$cache->store($return);
return response()->json($return);
}
}

View File

@ -68,7 +68,7 @@ class Authenticate
*/
public function handle($request, Closure $next, ...$guards)
{
$this->authenticate($guards);
$this->authenticate($request, $guards);
return $next($request);
}
@ -86,7 +86,7 @@ class Authenticate
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function authenticate(array $guards)
protected function authenticate($request, array $guards)
{
if (empty($guards)) {

View File

@ -57,7 +57,7 @@ class SecureHeaders
"form-action 'self'",
"font-src 'self'",
"connect-src 'self'",
"img-src 'self' data:",
"img-src 'self' data: https://api.tiles.mapbox.com",
];
$featurePolicies = [

View File

@ -92,58 +92,7 @@ class Request extends FormRequest
*/
public function string(string $field): string
{
$string = $this->get($field) ?? '';
$search = [
"\u{0001}", // start of heading
"\u{0002}", // start of text
"\u{0003}", // end of text
"\u{0004}", // end of transmission
"\u{0005}", // enquiry
"\u{0006}", // ACK
"\u{0007}", // BEL
"\u{0008}", // backspace
"\u{000E}", // shift out
"\u{000F}", // shift in
"\u{0010}", // data link escape
"\u{0011}", // DC1
"\u{0012}", // DC2
"\u{0013}", // DC3
"\u{0014}", // DC4
"\u{0015}", // NAK
"\u{0016}", // SYN
"\u{0017}", // ETB
"\u{0018}", // CAN
"\u{0019}", // EM
"\u{001A}", // SUB
"\u{001B}", // escape
"\u{001C}", // file separator
"\u{001D}", // group separator
"\u{001E}", // record separator
"\u{001F}", // unit separator
"\u{007F}", // DEL
"\u{00A0}", // non-breaking space
"\u{1680}", // ogham space mark
"\u{180E}", // mongolian vowel separator
"\u{2000}", // en quad
"\u{2001}", // em quad
"\u{2002}", // en space
"\u{2003}", // em space
"\u{2004}", // three-per-em space
"\u{2005}", // four-per-em space
"\u{2006}", // six-per-em space
"\u{2007}", // figure space
"\u{2008}", // punctuation space
"\u{2009}", // thin space
"\u{200A}", // hair space
"\u{200B}", // zero width space
"\u{202F}", // narrow no-break space
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
];
$replace = "\x20"; // plain old normal space
$string = str_replace($search, $replace, $string);
return trim($string);
return app('steam')->cleanString($this->get($field) ?? '');
}
/**

View File

@ -79,7 +79,7 @@ class ChooseAccountsHandler implements BunqJobConfigurationInterface
$config = $this->repository->getConfiguration($this->importJob);
$accounts = $config['accounts'] ?? [];
$mapping = $data['account_mapping'] ?? [];
$applyRules = 1 === (int)$data['apply_rules'];
$applyRules = 1 === (int)($data['apply_rules'] ?? 0);
$final = [];
/*

View File

@ -30,7 +30,6 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Collection;
use Log;
use stdClass;
/**
@ -38,6 +37,7 @@ use stdClass;
*/
class Steam
{
/**
* @param \FireflyIII\Models\Account $account
* @param \Carbon\Carbon $date
@ -315,6 +315,68 @@ class Steam
return $result;
}
/**
* Remove weird chars from strings.
*
* @param string $string
*
* @return string
*/
public function cleanString(string $string): string
{
$search = [
"\u{0001}", // start of heading
"\u{0002}", // start of text
"\u{0003}", // end of text
"\u{0004}", // end of transmission
"\u{0005}", // enquiry
"\u{0006}", // ACK
"\u{0007}", // BEL
"\u{0008}", // backspace
"\u{000E}", // shift out
"\u{000F}", // shift in
"\u{0010}", // data link escape
"\u{0011}", // DC1
"\u{0012}", // DC2
"\u{0013}", // DC3
"\u{0014}", // DC4
"\u{0015}", // NAK
"\u{0016}", // SYN
"\u{0017}", // ETB
"\u{0018}", // CAN
"\u{0019}", // EM
"\u{001A}", // SUB
"\u{001B}", // escape
"\u{001C}", // file separator
"\u{001D}", // group separator
"\u{001E}", // record separator
"\u{001F}", // unit separator
"\u{007F}", // DEL
"\u{00A0}", // non-breaking space
"\u{1680}", // ogham space mark
"\u{180E}", // mongolian vowel separator
"\u{2000}", // en quad
"\u{2001}", // em quad
"\u{2002}", // en space
"\u{2003}", // em space
"\u{2004}", // three-per-em space
"\u{2005}", // four-per-em space
"\u{2006}", // six-per-em space
"\u{2007}", // figure space
"\u{2008}", // punctuation space
"\u{2009}", // thin space
"\u{200A}", // hair space
"\u{200B}", // zero width space
"\u{202F}", // narrow no-break space
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
];
$replace = "\x20"; // plain old normal space
$string = str_replace($search, $replace, $string);
return trim($string);
}
/**
* @param int $isEncrypted
* @param $value

View File

@ -46,21 +46,21 @@
}
],
"require": {
"php": ">=7.1.0",
"php": ">=7.2.0",
"ext-bcmath": "*",
"ext-curl": "*",
"ext-gd": "*",
"ext-intl": "*",
"ext-xml": "*",
"ext-zip": "*",
"bacon/bacon-qr-code": "1.*",
"bacon/bacon-qr-code": "2.*",
"bunq/sdk_php": "dev-master#8c1faefc111d9b970168a1837ca165d854954941",
"davejamesmiller/laravel-breadcrumbs": "5.*",
"doctrine/dbal": "2.*",
"fideloper/proxy": "4.*",
"laravel/framework": "5.6.*",
"laravel/passport": "^5.0",
"laravelcollective/html": "5.6.*",
"laravel/framework": "5.7.*",
"laravel/passport": "^7.0",
"laravelcollective/html": "5.7.*",
"league/commonmark": "0.*",
"league/csv": "9.*",
"league/fractal": "^0.17.0",
@ -74,9 +74,9 @@
"filp/whoops": "2.*",
"fzaninotto/faker": "1.*",
"johnkary/phpunit-speedtrap": "^3.0",
"mockery/mockery": "^1.0",
"mockery/mockery": "1.*",
"php-coveralls/php-coveralls": "^2.0",
"phpunit/phpunit": "~7.0",
"phpunit/phpunit": "7.*",
"roave/security-advisories": "dev-master"
},
"autoload": {
@ -112,13 +112,13 @@
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump"
],
"post-update-cmd": [
"php artisan firefly:upgrade-database",
"php artisan firefly:verify",
"php artisan firefly:instructions update",
"php artisan passport:install"
"@php artisan firefly:upgrade-database",
"@php artisan firefly:verify",
"@php artisan firefly:instructions update",
"@php artisan passport:install"
],
"post-install-cmd": [
"php artisan firefly:instructions install"
"@php artisan firefly:instructions install"
]
},
"config": {

411
composer.lock generated
View File

@ -4,36 +4,39 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "dcaf20ad3436c4fc4cbebeee09c9de1f",
"content-hash": "364135d1fab7e47564deae52ea0c12c2",
"packages": [
{
"name": "bacon/bacon-qr-code",
"version": "1.0.3",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
"reference": "5a91b62b9d37cee635bbf8d553f4546057250bee"
"reference": "eaac909da3ccc32b748a65b127acd8918f58d9b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/5a91b62b9d37cee635bbf8d553f4546057250bee",
"reference": "5a91b62b9d37cee635bbf8d553f4546057250bee",
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/eaac909da3ccc32b748a65b127acd8918f58d9b0",
"reference": "eaac909da3ccc32b748a65b127acd8918f58d9b0",
"shasum": ""
},
"require": {
"dasprid/enum": "^1.0",
"ext-iconv": "*",
"php": "^5.4|^7.0"
"php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8"
"phly/keep-a-changelog": "^1.4",
"phpunit/phpunit": "^6.4",
"squizlabs/php_codesniffer": "^3.1"
},
"suggest": {
"ext-gd": "to generate QR code images"
"ext-imagick": "to generate QR code images"
},
"type": "library",
"autoload": {
"psr-0": {
"BaconQrCode": "src/"
"psr-4": {
"BaconQrCode\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -50,7 +53,7 @@
],
"description": "BaconQrCode is a QR code generator for PHP.",
"homepage": "https://github.com/Bacon/BaconQrCode",
"time": "2017-10-17T09:59:25+00:00"
"time": "2018-04-25T17:53:56+00:00"
},
{
"name": "bunq/sdk_php",
@ -117,27 +120,69 @@
"time": "2018-09-01T12:54:04+00:00"
},
{
"name": "davejamesmiller/laravel-breadcrumbs",
"version": "5.1.0",
"name": "dasprid/enum",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/davejamesmiller/laravel-breadcrumbs.git",
"reference": "2a02abfeca13acba6d528f4fcd385570452308f7"
"url": "https://github.com/DASPRiD/Enum.git",
"reference": "631ef6e638e9494b0310837fa531bedd908fc22b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/davejamesmiller/laravel-breadcrumbs/zipball/2a02abfeca13acba6d528f4fcd385570452308f7",
"reference": "2a02abfeca13acba6d528f4fcd385570452308f7",
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/631ef6e638e9494b0310837fa531bedd908fc22b",
"reference": "631ef6e638e9494b0310837fa531bedd908fc22b",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "^6.4",
"squizlabs/php_codesniffer": "^3.1"
},
"type": "library",
"autoload": {
"psr-4": {
"DASPRiD\\Enum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/"
}
],
"description": "PHP 7.1 enum implementation",
"keywords": [
"enum",
"map"
],
"time": "2017-10-25T22:45:27+00:00"
},
{
"name": "davejamesmiller/laravel-breadcrumbs",
"version": "5.1.2",
"source": {
"type": "git",
"url": "https://github.com/davejamesmiller/laravel-breadcrumbs.git",
"reference": "f24853b97d9f973a9b936d2692f93b11924415e2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/davejamesmiller/laravel-breadcrumbs/zipball/f24853b97d9f973a9b936d2692f93b11924415e2",
"reference": "f24853b97d9f973a9b936d2692f93b11924415e2",
"shasum": ""
},
"require": {
"illuminate/support": "5.6.*",
"illuminate/view": "5.6.*",
"illuminate/support": "5.6.*|5.7.*",
"illuminate/view": "5.6.*|5.7.*",
"php": ">=7.1.3"
},
"require-dev": {
"laravel/framework": "5.6.*",
"orchestra/testbench": "3.6.*",
"laravel/framework": "5.6.*|5.7.*",
"orchestra/testbench": "3.6.*|3.7.*",
"php-coveralls/php-coveralls": "^1.0",
"phpunit/phpunit": "7.*"
},
@ -173,7 +218,7 @@
"keywords": [
"laravel"
],
"time": "2018-05-05T19:30:03+00:00"
"time": "2018-09-14T06:29:58+00:00"
},
{
"name": "defuse/php-encryption",
@ -1022,42 +1067,42 @@
},
{
"name": "laravel/framework",
"version": "v5.6.38",
"version": "v5.7.4",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "38d838bab9434af79e8ab274ae63f52f7ed45d6e"
"reference": "f6e8f6562120e0063313e81716666cfcd362569b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/38d838bab9434af79e8ab274ae63f52f7ed45d6e",
"reference": "38d838bab9434af79e8ab274ae63f52f7ed45d6e",
"url": "https://api.github.com/repos/laravel/framework/zipball/f6e8f6562120e0063313e81716666cfcd362569b",
"reference": "f6e8f6562120e0063313e81716666cfcd362569b",
"shasum": ""
},
"require": {
"doctrine/inflector": "~1.1",
"dragonmantank/cron-expression": "~2.0",
"erusev/parsedown": "~1.7",
"doctrine/inflector": "^1.1",
"dragonmantank/cron-expression": "^2.0",
"erusev/parsedown": "^1.7",
"ext-mbstring": "*",
"ext-openssl": "*",
"league/flysystem": "^1.0.8",
"monolog/monolog": "~1.12",
"nesbot/carbon": "1.25.*",
"monolog/monolog": "^1.12",
"nesbot/carbon": "^1.26.3",
"php": "^7.1.3",
"psr/container": "~1.0",
"psr/container": "^1.0",
"psr/simple-cache": "^1.0",
"ramsey/uuid": "^3.7",
"swiftmailer/swiftmailer": "~6.0",
"symfony/console": "~4.0",
"symfony/debug": "~4.0",
"symfony/finder": "~4.0",
"symfony/http-foundation": "~4.0",
"symfony/http-kernel": "~4.0",
"symfony/process": "~4.0",
"symfony/routing": "~4.0",
"symfony/var-dumper": "~4.0",
"swiftmailer/swiftmailer": "^6.0",
"symfony/console": "^4.1",
"symfony/debug": "^4.1",
"symfony/finder": "^4.1",
"symfony/http-foundation": "^4.1",
"symfony/http-kernel": "^4.1",
"symfony/process": "^4.1",
"symfony/routing": "^4.1",
"symfony/var-dumper": "^4.1",
"tijsverkoyen/css-to-inline-styles": "^2.2.1",
"vlucas/phpdotenv": "~2.2"
"vlucas/phpdotenv": "^2.2"
},
"conflict": {
"tightenco/collect": "<5.5.33"
@ -1093,43 +1138,45 @@
"illuminate/view": "self.version"
},
"require-dev": {
"aws/aws-sdk-php": "~3.0",
"doctrine/dbal": "~2.6",
"aws/aws-sdk-php": "^3.0",
"doctrine/dbal": "^2.6",
"filp/whoops": "^2.1.4",
"league/flysystem-cached-adapter": "~1.0",
"mockery/mockery": "~1.0",
"league/flysystem-cached-adapter": "^1.0",
"mockery/mockery": "^1.0",
"moontoast/math": "^1.1",
"orchestra/testbench-core": "3.6.*",
"pda/pheanstalk": "~3.0",
"phpunit/phpunit": "~7.0",
"orchestra/testbench-core": "3.7.*",
"pda/pheanstalk": "^3.0",
"phpunit/phpunit": "^7.0",
"predis/predis": "^1.1.1",
"symfony/css-selector": "~4.0",
"symfony/dom-crawler": "~4.0"
"symfony/css-selector": "^4.1",
"symfony/dom-crawler": "^4.1",
"true/punycode": "^2.1"
},
"suggest": {
"aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).",
"doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.6).",
"aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (^3.0).",
"doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).",
"ext-pcntl": "Required to use all features of the queue worker.",
"ext-posix": "Required to use all features of the queue worker.",
"fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).",
"guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~6.0).",
"laravel/tinker": "Required to use the tinker console command (~1.0).",
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).",
"league/flysystem-cached-adapter": "Required to use the Flysystem cache (~1.0).",
"league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0).",
"league/flysystem-sftp": "Required to use the Flysystem SFTP driver (~1.0).",
"nexmo/client": "Required to use the Nexmo transport (~1.0).",
"pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).",
"predis/predis": "Required to use the redis cache and queue drivers (~1.0).",
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~3.0).",
"symfony/css-selector": "Required to use some of the crawler integration testing tools (~4.0).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (~4.0).",
"symfony/psr-http-message-bridge": "Required to psr7 bridging features (~1.0)."
"fzaninotto/faker": "Required to use the eloquent factory builder (^1.4).",
"guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (^6.0).",
"laravel/tinker": "Required to use the tinker console command (^1.0).",
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).",
"league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).",
"league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (^1.0).",
"league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
"moontoast/math": "Required to use ordered UUIDs (^1.1).",
"nexmo/client": "Required to use the Nexmo transport (^1.0).",
"pda/pheanstalk": "Required to use the beanstalk queue driver (^3.0).",
"predis/predis": "Required to use the redis cache and queue drivers (^1.0).",
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).",
"symfony/css-selector": "Required to use some of the crawler integration testing tools (^4.1).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (^4.1).",
"symfony/psr-http-message-bridge": "Required to psr7 bridging features (^1.0)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.7-dev"
}
},
"autoload": {
@ -1157,20 +1204,20 @@
"framework",
"laravel"
],
"time": "2018-09-04T13:15:09+00:00"
"time": "2018-09-18T13:34:05+00:00"
},
{
"name": "laravel/passport",
"version": "v5.0.3",
"version": "v7.0.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/passport.git",
"reference": "9dbcc3d6f8f20f1a83cbef73bbb563ce59e43ded"
"reference": "90124969cdd4ff39d4cd5a608c23bbe16e772f7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/passport/zipball/9dbcc3d6f8f20f1a83cbef73bbb563ce59e43ded",
"reference": "9dbcc3d6f8f20f1a83cbef73bbb563ce59e43ded",
"url": "https://api.github.com/repos/laravel/passport/zipball/90124969cdd4ff39d4cd5a608c23bbe16e772f7e",
"reference": "90124969cdd4ff39d4cd5a608c23bbe16e772f7e",
"shasum": ""
},
"require": {
@ -1184,8 +1231,8 @@
"illuminate/encryption": "~5.6",
"illuminate/http": "~5.6",
"illuminate/support": "~5.6",
"league/oauth2-server": "^6.0",
"php": ">=7.0",
"league/oauth2-server": "^7.0",
"php": ">=7.1",
"phpseclib/phpseclib": "^2.0",
"symfony/psr-http-message-bridge": "~1.0",
"zendframework/zend-diactoros": "~1.0"
@ -1226,39 +1273,39 @@
"oauth",
"passport"
],
"time": "2018-03-15T12:39:02+00:00"
"time": "2018-08-13T14:45:04+00:00"
},
{
"name": "laravelcollective/html",
"version": "v5.6.10",
"version": "v5.7.1",
"source": {
"type": "git",
"url": "https://github.com/LaravelCollective/html.git",
"reference": "974605fcd22a7e4d19f0b2ef635a0d1d7400387d"
"reference": "777b6d390811ba249255ed5750bf17a019cd88a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/LaravelCollective/html/zipball/974605fcd22a7e4d19f0b2ef635a0d1d7400387d",
"reference": "974605fcd22a7e4d19f0b2ef635a0d1d7400387d",
"url": "https://api.github.com/repos/LaravelCollective/html/zipball/777b6d390811ba249255ed5750bf17a019cd88a5",
"reference": "777b6d390811ba249255ed5750bf17a019cd88a5",
"shasum": ""
},
"require": {
"illuminate/http": "5.6.*",
"illuminate/routing": "5.6.*",
"illuminate/session": "5.6.*",
"illuminate/support": "5.6.*",
"illuminate/view": "5.6.*",
"illuminate/http": "5.7.*",
"illuminate/routing": "5.7.*",
"illuminate/session": "5.7.*",
"illuminate/support": "5.7.*",
"illuminate/view": "5.7.*",
"php": ">=7.1.3"
},
"require-dev": {
"illuminate/database": "5.6.*",
"illuminate/database": "5.7.*",
"mockery/mockery": "~1.0",
"phpunit/phpunit": "~7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.6-dev"
"dev-master": "5.7-dev"
},
"laravel": {
"providers": [
@ -1294,7 +1341,7 @@
],
"description": "HTML and Form Builders for the Laravel Framework",
"homepage": "https://laravelcollective.com",
"time": "2018-06-18T15:04:16+00:00"
"time": "2018-09-05T18:32:53+00:00"
},
{
"name": "lcobucci/jwt",
@ -1356,16 +1403,16 @@
},
{
"name": "league/commonmark",
"version": "0.17.5",
"version": "0.18.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "82d7ab62d7f68391cb9d323f3ccce50be24a5369"
"reference": "006af077d4b1b7eb1d9760964f9f984ba188632c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/82d7ab62d7f68391cb9d323f3ccce50be24a5369",
"reference": "82d7ab62d7f68391cb9d323f3ccce50be24a5369",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/006af077d4b1b7eb1d9760964f9f984ba188632c",
"reference": "006af077d4b1b7eb1d9760964f9f984ba188632c",
"shasum": ""
},
"require": {
@ -1394,7 +1441,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.18-dev"
"dev-master": "0.19-dev"
}
},
"autoload": {
@ -1421,7 +1468,7 @@
"markdown",
"parser"
],
"time": "2018-03-29T14:35:19+00:00"
"time": "2018-09-18T13:13:55+00:00"
},
{
"name": "league/csv",
@ -1542,26 +1589,26 @@
},
{
"name": "league/flysystem",
"version": "1.0.46",
"version": "1.0.47",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "f3e0d925c18b92cf3ce84ea5cc58d62a1762a2b2"
"reference": "a11e4a75f256bdacf99d20780ce42d3b8272975c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/f3e0d925c18b92cf3ce84ea5cc58d62a1762a2b2",
"reference": "f3e0d925c18b92cf3ce84ea5cc58d62a1762a2b2",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a11e4a75f256bdacf99d20780ce42d3b8272975c",
"reference": "a11e4a75f256bdacf99d20780ce42d3b8272975c",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
"php": ">=5.5.9"
},
"conflict": {
"league/flysystem-sftp": "<1.0.6"
},
"require-dev": {
"ext-fileinfo": "*",
"phpspec/phpspec": "^3.4",
"phpunit/phpunit": "^5.7.10"
},
@ -1622,7 +1669,7 @@
"sftp",
"storage"
],
"time": "2018-08-22T07:45:22+00:00"
"time": "2018-09-14T15:30:29+00:00"
},
{
"name": "league/fractal",
@ -1690,34 +1737,36 @@
},
{
"name": "league/oauth2-server",
"version": "6.1.1",
"version": "7.2.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth2-server.git",
"reference": "a0cabb573c7cd5ee01803daec992d6ee3677c4ae"
"reference": "8184f771d43ea7305ddbb893d0daf6f0352ec5fd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/a0cabb573c7cd5ee01803daec992d6ee3677c4ae",
"reference": "a0cabb573c7cd5ee01803daec992d6ee3677c4ae",
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/8184f771d43ea7305ddbb893d0daf6f0352ec5fd",
"reference": "8184f771d43ea7305ddbb893d0daf6f0352ec5fd",
"shasum": ""
},
"require": {
"defuse/php-encryption": "^2.1",
"ext-openssl": "*",
"lcobucci/jwt": "^3.1",
"lcobucci/jwt": "^3.2.2",
"league/event": "^2.1",
"paragonie/random_compat": "^2.0",
"php": ">=5.6.0",
"psr/http-message": "^1.0"
"php": ">=7.0.0",
"psr/http-message": "^1.0.1"
},
"replace": {
"league/oauth2server": "*",
"lncd/oauth2": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.8.38 || ^5.7.21",
"zendframework/zend-diactoros": "^1.0"
"phpstan/phpstan": "^0.9.2",
"phpstan/phpstan-phpunit": "^0.9.4",
"phpstan/phpstan-strict-rules": "^0.9.0",
"phpunit/phpunit": "^6.3 || ^7.0",
"zendframework/zend-diactoros": "^1.3.2"
},
"type": "library",
"autoload": {
@ -1754,7 +1803,7 @@
"secure",
"server"
],
"time": "2017-12-23T23:33:42+00:00"
"time": "2018-06-23T16:57:59+00:00"
},
{
"name": "monolog/monolog",
@ -1836,16 +1885,16 @@
},
{
"name": "nesbot/carbon",
"version": "1.25.0",
"version": "1.33.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4"
"reference": "55667c1007a99e82030874b1bb14d24d07108413"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cbcf13da0b531767e39eb86e9687f5deba9857b4",
"reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/55667c1007a99e82030874b1bb14d24d07108413",
"reference": "55667c1007a99e82030874b1bb14d24d07108413",
"shasum": ""
},
"require": {
@ -1858,13 +1907,15 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.23-dev"
"laravel": {
"providers": [
"Carbon\\Laravel\\ServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Carbon\\": "src/Carbon/"
"": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -1885,7 +1936,7 @@
"datetime",
"time"
],
"time": "2018-03-19T15:50:49+00:00"
"time": "2018-08-07T08:39:47+00:00"
},
{
"name": "paragonie/constant_time_encoding",
@ -1951,33 +2002,29 @@
},
{
"name": "paragonie/random_compat",
"version": "v2.0.17",
"version": "v9.99.99",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d"
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d",
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
"php": "^7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
@ -1996,7 +2043,7 @@
"pseudorandom",
"random"
],
"time": "2018-07-04T16:31:37+00:00"
"time": "2018-07-02T15:55:56+00:00"
},
{
"name": "phpseclib/phpseclib",
@ -2500,22 +2547,22 @@
},
{
"name": "rcrowe/twigbridge",
"version": "v0.9.6",
"version": "v0.9.7",
"source": {
"type": "git",
"url": "https://github.com/rcrowe/TwigBridge.git",
"reference": "c3579440a3ca47ca45bfb0ed854bc0ff9b086bf5"
"reference": "d0c998ae6d39f154c4e2d01ef914e49d0b7d5d68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/c3579440a3ca47ca45bfb0ed854bc0ff9b086bf5",
"reference": "c3579440a3ca47ca45bfb0ed854bc0ff9b086bf5",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/d0c998ae6d39f154c4e2d01ef914e49d0b7d5d68",
"reference": "d0c998ae6d39f154c4e2d01ef914e49d0b7d5d68",
"shasum": ""
},
"require": {
"illuminate/support": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*",
"illuminate/view": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*|5.5.*|5.6.*",
"php": ">=5.4.0",
"illuminate/support": "5.5.*|5.6.*|5.7.*",
"illuminate/view": "5.5.*|5.6.*|5.7.*",
"php": ">=7",
"twig/twig": "~1.30"
},
"require-dev": {
@ -2568,20 +2615,20 @@
"laravel",
"twig"
],
"time": "2018-02-08T15:59:23+00:00"
"time": "2018-08-31T13:30:10+00:00"
},
{
"name": "swiftmailer/swiftmailer",
"version": "v6.1.2",
"version": "v6.1.3",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "7d760881d266d63c5e7a1155cbcf2ac656a31ca8"
"reference": "8ddcb66ac10c392d3beb54829eef8ac1438595f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/7d760881d266d63c5e7a1155cbcf2ac656a31ca8",
"reference": "7d760881d266d63c5e7a1155cbcf2ac656a31ca8",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8ddcb66ac10c392d3beb54829eef8ac1438595f4",
"reference": "8ddcb66ac10c392d3beb54829eef8ac1438595f4",
"shasum": ""
},
"require": {
@ -2627,7 +2674,7 @@
"mail",
"mailer"
],
"time": "2018-07-13T07:04:35+00:00"
"time": "2018-09-11T07:12:52+00:00"
},
{
"name": "symfony/console",
@ -3835,16 +3882,16 @@
},
{
"name": "zendframework/zend-diactoros",
"version": "1.8.5",
"version": "1.8.6",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-diactoros.git",
"reference": "3e4edb822c942f37ade0d09579cfbab11e2fee87"
"reference": "20da13beba0dde8fb648be3cc19765732790f46e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/3e4edb822c942f37ade0d09579cfbab11e2fee87",
"reference": "3e4edb822c942f37ade0d09579cfbab11e2fee87",
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e",
"reference": "20da13beba0dde8fb648be3cc19765732790f46e",
"shasum": ""
},
"require": {
@ -3857,6 +3904,7 @@
"require-dev": {
"ext-dom": "*",
"ext-libxml": "*",
"php-http/psr7-integration-tests": "dev-master",
"phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7",
"zendframework/zend-coding-standard": "~1.0"
},
@ -3894,22 +3942,22 @@
"psr",
"psr-7"
],
"time": "2018-08-10T14:16:32+00:00"
"time": "2018-09-05T19:29:37+00:00"
}
],
"packages-dev": [
{
"name": "barryvdh/laravel-ide-helper",
"version": "v2.5.0",
"version": "v2.5.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
"reference": "09db8c9a76711e98c61af0795934fb15955223fb"
"reference": "7db1843473e1562d8e0490b51db847d3a1415140"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/09db8c9a76711e98c61af0795934fb15955223fb",
"reference": "09db8c9a76711e98c61af0795934fb15955223fb",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/7db1843473e1562d8e0490b51db847d3a1415140",
"reference": "7db1843473e1562d8e0490b51db847d3a1415140",
"shasum": ""
},
"require": {
@ -3970,7 +4018,7 @@
"phpstorm",
"sublime"
],
"time": "2018-08-31T13:28:09+00:00"
"time": "2018-09-06T18:41:09+00:00"
},
{
"name": "barryvdh/reflection-docblock",
@ -4380,16 +4428,16 @@
},
{
"name": "filp/whoops",
"version": "2.2.0",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a"
"reference": "e79cd403fb77fc8963a99ecc30e80ddd885b3311"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/181c4502d8f34db7aed7bfe88d4f87875b8e947a",
"reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a",
"url": "https://api.github.com/repos/filp/whoops/zipball/e79cd403fb77fc8963a99ecc30e80ddd885b3311",
"reference": "e79cd403fb77fc8963a99ecc30e80ddd885b3311",
"shasum": ""
},
"require": {
@ -4408,7 +4456,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
"dev-master": "2.2-dev"
}
},
"autoload": {
@ -4437,7 +4485,7 @@
"throwable",
"whoops"
],
"time": "2018-03-03T17:56:25+00:00"
"time": "2018-06-30T13:14:06+00:00"
},
{
"name": "fzaninotto/faker",
@ -5230,21 +5278,24 @@
},
{
"name": "phpunit/php-file-iterator",
"version": "2.0.1",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c"
"reference": "050bedf145a257b1ff02746c31894800e5122946"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cecbc684605bb0cc288828eb5d65d93d5c676d3c",
"reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
"reference": "050bedf145a257b1ff02746c31894800e5122946",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.1"
},
"type": "library",
"extra": {
"branch-alias": {
@ -5273,7 +5324,7 @@
"filesystem",
"iterator"
],
"time": "2018-06-11T11:44:00+00:00"
"time": "2018-09-13T20:33:42+00:00"
},
{
"name": "phpunit/php-text-template",
@ -5416,16 +5467,16 @@
},
{
"name": "phpunit/phpunit",
"version": "7.3.3",
"version": "7.3.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "1bd5629cccfb2c0a9ef5474b4ff772349e1ec898"
"reference": "7b331efabbb628c518c408fdfcaf571156775de2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1bd5629cccfb2c0a9ef5474b4ff772349e1ec898",
"reference": "1bd5629cccfb2c0a9ef5474b4ff772349e1ec898",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7b331efabbb628c518c408fdfcaf571156775de2",
"reference": "7b331efabbb628c518c408fdfcaf571156775de2",
"shasum": ""
},
"require": {
@ -5496,7 +5547,7 @@
"testing",
"xunit"
],
"time": "2018-09-01T15:49:55+00:00"
"time": "2018-09-08T15:14:29+00:00"
},
{
"name": "roave/security-advisories",
@ -5508,6 +5559,7 @@
"amphp/http": "<1.0.1",
"asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
"aws/aws-sdk-php": ">=3,<3.2.1",
"brightlocal/phpwhois": "<=4.2.5",
"bugsnag/bugsnag-laravel": ">=2,<2.0.2",
"cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4",
"cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4",
@ -5519,6 +5571,7 @@
"contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8",
"contao/listing-bundle": ">=4,<4.4.8",
"contao/newsletter-bundle": ">=4,<4.1",
"david-garcia/phpwhois": "<=4.3.1",
"doctrine/annotations": ">=1,<1.2.7",
"doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2",
"doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1",
@ -5533,6 +5586,7 @@
"drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3",
"erusev/parsedown": "<1.7",
"ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1",
"ezyang/htmlpurifier": "<4.1.1",
"firebase/php-jwt": "<2",
"friendsofsymfony/rest-bundle": ">=1.2,<1.2.2",
"friendsofsymfony/user-bundle": ">=1.2,<1.3.5",
@ -5544,7 +5598,11 @@
"illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
"illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29",
"illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15",
"ivankristianto/phpwhois": "<=4.3",
"james-heinrich/getid3": "<1.9.9",
"joomla/session": "<1.3.1",
"jsmitty12/phpwhois": "<5.1",
"kazist/phpwhois": "<=4.2.6",
"kreait/firebase-php": ">=3.2,<3.8.1",
"laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
"laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10",
@ -5554,6 +5612,7 @@
"monolog/monolog": ">=1.8,<1.12",
"namshi/jose": "<2.2",
"onelogin/php-saml": "<2.10.4",
"openid/php-openid": "<2.3",
"oro/crm": ">=1.7,<1.7.4",
"oro/platform": ">=1.7,<1.7.4",
"padraic/humbug_get_contents": "<1.1.2",
@ -5562,21 +5621,25 @@
"paypal/merchant-sdk-php": "<3.12",
"phpmailer/phpmailer": ">=5,<5.2.24",
"phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3",
"phpwhois/phpwhois": "<=4.2.5",
"phpxmlrpc/extras": "<0.6.1",
"propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7",
"propel/propel1": ">=1,<=1.7.1",
"pusher/pusher-php-server": "<2.2.1",
"sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9",
"sensiolabs/connect": "<4.2.3",
"serluck/phpwhois": "<=4.2.6",
"shopware/shopware": "<5.3.7",
"silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11",
"silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
"silverstripe/framework": ">=3,<3.3",
"silverstripe/userforms": "<3",
"simple-updates/phpwhois": "<=1",
"simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4",
"simplesamlphp/simplesamlphp": "<1.15.2",
"simplesamlphp/simplesamlphp-module-infocard": "<1.0.1",
"slim/slim": "<2.6",
"smarty/smarty": "<3.1.33",
"socalnick/scn-social-auth": "<1.15.2",
"squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
"stormpath/sdk": ">=0,<9.9.99",
@ -5603,8 +5666,10 @@
"symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4",
"symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7",
"thelia/backoffice-default-template": ">=2.1,<2.1.2",
"thelia/thelia": ">=2.1,<2.1.2|>=2.1.0-beta1,<2.1.3",
"thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2",
"theonedemon/phpwhois": "<=4.2.5",
"titon/framework": ">=0,<9.9.99",
"truckersmp/phpwhois": "<=4.3.1",
"twig/twig": "<1.20",
"typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.30|>=8,<8.7.17|>=9,<9.3.2",
"typo3/cms-core": ">=8,<8.7.17|>=9,<9.3.2",
@ -5657,7 +5722,7 @@
}
],
"description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
"time": "2018-08-14T15:39:17+00:00"
"time": "2018-09-17T20:20:31+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -6636,7 +6701,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.1.0",
"php": ">=7.2.0",
"ext-bcmath": "*",
"ext-curl": "*",
"ext-gd": "*",

View File

@ -31,6 +31,7 @@ return [
'4.7.3' => 'This version of Firefly III handles bills differently. See http://bit.ly/FF3-new-bills for more information.',
'4.7.4' => 'This version of Firefly III has a new import routine. See http://bit.ly/FF3-new-import for more information.',
'4.7.6' => 'This will be the last version to require PHP7.1. Future versions will require PHP7.2 minimum.',
'4.7.7' => 'This version of Firefly III requires PHP7.2.',
],
'install' =>
[
@ -40,6 +41,7 @@ return [
'4.7.3' => 'This version of Firefly III handles bills differently. See http://bit.ly/FF3-new-bills for more information.',
'4.7.4' => 'This version of Firefly III has a new import routine. See http://bit.ly/FF3-new-import for more information.',
'4.7.6' => 'This will be the last version to require PHP7.1. Future versions will require PHP7.2 minimum.',
'4.7.7' => 'This version of Firefly III requires PHP7.2.',
],
],
];

View File

@ -28,7 +28,7 @@ use Illuminate\Database\Seeder;
*/
class AccountTypeSeeder extends Seeder
{
public function run()
public function run(): void
{
$types = [
AccountType::DEFAULT,
@ -42,8 +42,7 @@ class AccountTypeSeeder extends Seeder
AccountType::LOAN,
AccountType::RECONCILIATION,
AccountType::DEBT,
AccountType::MORTGAGE,
AccountType::CREDITCARD,
AccountType::MORTGAGE
];
foreach ($types as $type) {
try {

View File

@ -65,6 +65,9 @@ class TransactionCurrencySeeder extends Seeder
$currencies[] = ['code' => 'BCH', 'name' => 'Bitcoin cash', 'symbol' => '₿C', 'decimal_places' => 8];
$currencies[] = ['code' => 'ETH', 'name' => 'Ethereum', 'symbol' => 'Ξ', 'decimal_places' => 12];
// PLEASE ADD NEW CURRENCIES BELOW THIS LINE
$currencies[] = ['code' => 'ILS', 'name' => 'Israeli new shekel', 'symbol' => '₪', 'decimal_places' => 2];
foreach ($currencies as $currency) {
try {
TransactionCurrency::create($currency);

View File

@ -1,5 +1,5 @@
/*
* bootstrap-tagsinput v0.8.0
* bootstrap-tagsinput v0.9.1
*
*/
@ -16,7 +16,6 @@
line-height: 22px;
cursor: text;
}
.bootstrap-tagsinput input {
border: none;
box-shadow: none;
@ -27,44 +26,35 @@
width: auto;
max-width: inherit;
}
.bootstrap-tagsinput.form-control input::-moz-placeholder {
color: #777;
opacity: 1;
}
.bootstrap-tagsinput.form-control input:-ms-input-placeholder {
color: #777;
}
.bootstrap-tagsinput.form-control input::-webkit-input-placeholder {
color: #777;
}
.bootstrap-tagsinput input:focus {
border: none;
box-shadow: none;
}
.bootstrap-tagsinput .tag {
margin-right: 2px;
color: white;
}
.bootstrap-tagsinput .tag [data-role="remove"] {
margin-left: 8px;
cursor: pointer;
}
.bootstrap-tagsinput .tag [data-role="remove"]:after {
content: "x";
padding: 0px 2px;
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}

View File

@ -170,3 +170,77 @@ span.info-box-text a:hover, span.info-box-number a:hover {
overflow-y: auto;
overflow-x: hidden;
}
.bootstrap-tagsinput {
width: 100%;
}
.accordion {
margin-bottom:-3px;
}
.accordion-group {
border: none;
}
.twitter-typeahead {
width:100%;
}
span.twitter-typeahead {
display: inline !important;width:100%;
}
.tt-input {
background-color:#fff !important;
}
.twitter-typeahead .tt-query,
.twitter-typeahead .tt-hint {
margin-bottom: 0;
}
.twitter-typeahead .tt-hint
{
display: none;
}
.tt-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
font-size: 14px;
background-color: #ffffff;
border: 1px solid #cccccc;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
background-clip: padding-box;
cursor: pointer;
}
.tt-suggestion {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.428571429;
color: #333333;
white-space: nowrap;
}
.tt-suggestion:hover,
.tt-suggestion:focus {
color: #ffffff;
text-decoration: none;
outline: 0;
background-color: #428bca;
}

View File

@ -18,8 +18,6 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** global: what, Modernizr, selectsForeignCurrency, convertForeignToNative, validateCurrencyForTransfer, convertSourceToDestination, journalData, journal, accountInfo, exchangeRateInstructions, currencyInfo */
$(document).ready(function () {
"use strict";
setAutocompletes();
@ -30,24 +28,7 @@ $(document).ready(function () {
* Set the auto-complete JSON things.
*/
function setAutocompletes() {
$.getJSON('json/categories').done(function (data) {
$('input[name="category"]').typeahead({source: data, autoSelect: false});
});
$.getJSON('json/tags').done(function (data) {
var opt = {
typeahead: {
source: data,
afterSelect: function () {
this.$element.val("");
},
autoSelect: false,
}
};
$('input[name="tags"]').tagsinput(
opt
);
});
initCategoryAC();
initTagsAC();
}

165
public/js/ff/common/autocomplete.js vendored Normal file
View File

@ -0,0 +1,165 @@
/*
* autocomplete.js
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Do tags auto complete.
*/
function initTagsAC() {
console.log('initTagsAC()');
var tagTags = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/tags',
filter: function (list) {
return $.map(list, function (tagTag) {
return {name: tagTag};
});
}
},
remote: {
url: 'json/tags?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
tagTags.initialize();
$('input[name="tags"]').tagsinput({
typeaheadjs: {
hint: true,
highlight: true,
name: 'tags',
displayKey: 'name',
valueKey: 'name',
source: tagTags.ttAdapter()
}
});
}
/**
* Do destination name (expense accounts) auto complete.
*/
function initExpenseAC() {
initExpenseACField('destination_name');
}
/**
* Do destination name (expense accounts) auto complete.
*/
function initExpenseACField(fieldName) {
console.log('initExpenseACField("' + fieldName + '")');
if ($('input[name="' + fieldName + '"]').length > 0) {
var destNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/expense-accounts',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/expense-accounts?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
destNames.initialize();
$('input[name="' + fieldName + '"]').typeahead({hint: true, highlight: true,}, {source: destNames, displayKey: 'name', autoSelect: false});
}
}
/**
* Do source name (revenue accounts) auto complete.
*/
function initRevenueAC() {
initRevenueACField('source_name');
}
/**
* Do source name (revenue accounts) auto complete.
*/
function initRevenueACField(fieldName) {
console.log('initRevenueACField("' + fieldName + '")');
if ($('input[name="' + fieldName + '"]').length > 0) {
var sourceNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/revenue-accounts',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/revenue-accounts?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
sourceNames.initialize();
$('input[name="' + fieldName + '"]').typeahead({hint: true, highlight: true,}, {source: sourceNames, displayKey: 'name', autoSelect: false});
}
}
/**
* Do categories auto complete.
*/
function initCategoryAC() {
var categories = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/categories',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/categories?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
categories.initialize();
$('input[name="category"]').typeahead({hint: true, highlight: true,}, {source: categories, displayKey: 'name', autoSelect: false});
}

View File

@ -137,39 +137,10 @@ function parseRepetitionSuggestions(data) {
}
function initializeAutoComplete() {
// auto complete things:
$.getJSON('json/tags').done(function (data) {
var opt = {
typeahead: {
source: data,
afterSelect: function () {
this.$element.val("");
},
autoSelect: false,
},
autoSelect: false,
};
$('input[name="tags"]').tagsinput(
opt
);
});
if ($('input[name="destination_name"]').length > 0) {
$.getJSON('json/expense-accounts').done(function (data) {
$('input[name="destination_name"]').typeahead({source: data, autoSelect: false});
});
}
if ($('input[name="source_name"]').length > 0) {
$.getJSON('json/revenue-accounts').done(function (data) {
$('input[name="source_name"]').typeahead({source: data, autoSelect: false});
});
}
$.getJSON('json/categories').done(function (data) {
$('input[name="category"]').typeahead({source: data, autoSelect: false});
});
initTagsAC();
initExpenseAC();
initRevenueAC();
initCategoryAC();
}
/**

View File

@ -109,11 +109,11 @@ function respondToFirstDateChange() {
// preselected value:
var preSelected = currentRepType;
if(preSelected === '') {
if (preSelected === '') {
preSelected = select.val();
}
$.getJSON(suggestUri, {date: date,pre_select: preSelected,past:true}).fail(function () {
$.getJSON(suggestUri, {date: date, pre_select: preSelected, past: true}).fail(function () {
console.error('Could not load repetition suggestions');
alert('Could not load repetition suggestions');
}).done(parseRepetitionSuggestions);
@ -128,8 +128,8 @@ function parseRepetitionSuggestions(data) {
if (data.hasOwnProperty(k)) {
console.log('label: ' + data[k].label + ', selected: ' + data[k].selected);
opt = $('<option>').val(k).attr('label', data[k].label).text(data[k].label);
if(data[k].selected) {
opt.attr('selected','selected');
if (data[k].selected) {
opt.attr('selected', 'selected');
}
select.append(opt);
}
@ -138,39 +138,10 @@ function parseRepetitionSuggestions(data) {
}
function initializeAutoComplete() {
// auto complete things:
$.getJSON('json/tags').done(function (data) {
var opt = {
typeahead: {
source: data,
afterSelect: function () {
this.$element.val("");
},
autoSelect: false,
},
autoSelect: false,
};
$('input[name="tags"]').tagsinput(
opt
);
});
if ($('input[name="destination_name"]').length > 0) {
$.getJSON('json/expense-accounts').done(function (data) {
$('input[name="destination_name"]').typeahead({source: data, autoSelect: false});
});
}
if ($('input[name="source_name"]').length > 0) {
$.getJSON('json/revenue-accounts').done(function (data) {
$('input[name="source_name"]').typeahead({source: data, autoSelect: false});
});
}
$.getJSON('json/categories').done(function (data) {
$('input[name="category"]').typeahead({source: data, autoSelect: false});
});
initTagsAC();
initExpenseAC();
initRevenueAC();
initCategoryAC();
}
/**

View File

@ -360,15 +360,32 @@ function updateTriggerInput(selectList) {
* @param URI
*/
function createAutoComplete(input, URI) {
console.log('Now in createAutoComplete().')
console.log('Now in createAutoComplete("' + URI + '").');
input.typeahead('destroy');
$.getJSON(URI).done(function (data) {
console.log('Input now has auto complete from URI ' + URI);
input.typeahead({source: data, autoSelect: false});
}).fail(function () {
console.log('Could not grab URI ' + URI + ' so autocomplete will not work');
});
var source = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: URI,
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: URI + '?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
source.initialize();
input.typeahead({hint: true, highlight: true,}, {source: source, displayKey: 'name', autoSelect: false});
}
function testRuleTriggers() {

34
public/js/ff/transactions/convert.js vendored Normal file
View File

@ -0,0 +1,34 @@
/*
* convert.js
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
$(document).ready(function () {
"use strict";
setAutocompletes();
});
/**
* Set the auto-complete JSON things.
*/
function setAutocompletes() {
initRevenueACField('source_account_revenue');
initExpenseACField('destination_account_expense');
}

View File

@ -67,7 +67,7 @@ function goToMassEdit() {
baseHref = bases[0].href;
}
window.location.href = baseHref + '/transactions/mass/edit/' + checkedArray;
window.location.href = baseHref + 'transactions/mass/edit/' + checkedArray;
return false;
}
@ -87,7 +87,7 @@ function goToBulkEdit() {
baseHref = bases[0].href;
}
window.location.href = baseHref + '/transactions/bulk/edit/' + checkedArray;
window.location.href = baseHref + 'transactions/bulk/edit/' + checkedArray;
return false;
}
@ -106,7 +106,7 @@ function goToMassDelete() {
if (bases.length > 0) {
baseHref = bases[0].href;
}
window.location.href = baseHref + '/transactions/mass/delete/' + checkedArray;
window.location.href = baseHref + 'transactions/mass/delete/' + checkedArray;
return false;
}

View File

@ -22,21 +22,6 @@
$(document).ready(function () {
"use strict";
$.getJSON('json/categories').done(function (data) {
$('input[name="category"]').typeahead({source: data, autoSelect: false});
});
$.getJSON('json/tags').done(function (data) {
var opt = {
source: data,
afterSelect: function () {
this.$element.val("");
},
autoSelect: false
};
$('input[name="tags"]').tagsinput(
opt
);
});
initTagsAC();
initCategoryAC();
});

View File

@ -25,19 +25,83 @@ $(document).ready(function () {
// destination account names:
if ($('input[name^="destination_name["]').length > 0) {
$.getJSON('json/expense-accounts').done(function (data) {
$('input[name^="destination_name["]').typeahead({source: data, autoSelect: false});
var destNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/expense-accounts',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/expense-accounts?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
destNames.initialize();
$('input[name^="destination_name["]').typeahead({hint: true, highlight: true,}, {source: destNames, displayKey: 'name', autoSelect: false});
}
// source account name
if ($('input[name^="source_name["]').length > 0) {
$.getJSON('json/revenue-accounts').done(function (data) {
$('input[name^="source_name["]').typeahead({source: data, autoSelect: false});
var sourceNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/revenue-accounts',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
$.getJSON('json/categories').done(function (data) {
$('input[name^="category["]').typeahead({source: data, autoSelect: false});
},
remote: {
url: 'json/revenue-accounts?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
sourceNames.initialize();
$('input[name^="source_name["]').typeahead({hint: true, highlight: true,}, {source: sourceNames, displayKey: 'name', autoSelect: false});
}
var categories = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/categories',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/categories?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
categories.initialize();
$('input[name^="category["]').typeahead({hint: true, highlight: true,}, {source: categories, displayKey: 'name', autoSelect: false});
});

View File

@ -22,19 +22,37 @@
$(function () {
"use strict";
$.getJSON(autoCompleteUri).done(function (data) {
var $input = $("#link_other");
$input.typeahead({
source: data,
autoSelect: true
var transactions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: autoCompleteUri,
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
$input.change(function () {
var current = $input.typeahead("getActive");
}
},
remote: {
url: autoCompleteUri + '?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
transactions.initialize();
var input=$("#link_other");
input.typeahead({hint: true, highlight: true,}, {source: transactions, displayKey: 'name', autoSelect: false});
input.change(function () {
var current = input.typeahead("getActive");
if (current) {
// Some item from your model is active!
if (current.name.toLowerCase() === $input.val().toLowerCase()) {
if (current.name.toLowerCase() ===
input.val().toLowerCase()) {
// This means the exact match is found. Use toLowerCase() if you want case insensitive match.
$('input[name="link_journal_id"]').val(current.id);
} else {
@ -44,7 +62,4 @@ $(function () {
$('input[name="link_journal_id"]').val(0);
}
});
});
});

View File

@ -47,39 +47,17 @@ function runModernizer() {
*/
function setCommonAutocomplete() {
console.log('In setCommonAutoComplete()');
$.getJSON('json/tags').done(function (data) {
var opt = {
typeahead: {
source: data,
afterSelect: function () {
this.$element.val("");
},
autoSelect: false,
},
autoSelect: false,
};
$('input[name="tags"]').tagsinput(
opt
);
});
// do tags auto complete:
initTagsAC();
// do destination name (expense accounts):
initExpenseAC();
// do source name (revenue accounts):
initRevenueAC();
if ($('input[name="destination_name"]').length > 0) {
$.getJSON('json/expense-accounts').done(function (data) {
$('input[name="destination_name"]').typeahead({source: data, autoSelect: false});
});
}
if ($('input[name="source_name"]').length > 0) {
$.getJSON('json/revenue-accounts').done(function (data) {
$('input[name="source_name"]').typeahead({source: data, autoSelect: false});
});
}
$.getJSON('json/categories').done(function (data) {
$('input[name="category"]').typeahead({source: data, autoSelect: false});
});
// do categories auto complete:
initCategoryAC();
}
/**
@ -108,7 +86,7 @@ function selectsForeignCurrency() {
// both holders are shown to the user:
$('#exchange_rate_instruction_holder').show();
if(what !== 'transfer') {
if (what !== 'transfer') {
console.log('Show native amount holder.');
$('#native_amount_holder').show();
}

View File

@ -60,7 +60,7 @@ $(document).ready(function () {
// overrule click on currency:
if(useAccountCurrency === false) {
if (useAccountCurrency === false) {
$('.currency-option[data-id="' + overruleCurrency + '"]').click();
$('[data-toggle="dropdown"]').parent().removeClass('open');
}
@ -136,9 +136,31 @@ function getExchangeInstructions() {
*
*/
function updateDescription() {
$.getJSON('json/transaction-journals/' + what).done(function (data) {
$('input[name="description"]').typeahead('destroy').typeahead({source: data, autoSelect: false});
// do description auto complete:
var journalNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/transaction-journals/' + what,
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/transaction-journals/' + what + '?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
journalNames.initialize();
$('input[name="description"]').typeahead('destroy').typeahead({hint: true, highlight: true,}, {source: journalNames, displayKey: 'name', autoSelect: false});
$('#ffInput_description').focus();
}

View File

@ -148,9 +148,31 @@ function getAccountId() {
* Set the auto-complete JSON things.
*/
function setAutocompletes() {
$.getJSON('json/transaction-journals/' + what).done(function (data) {
$('input[name="description"]').typeahead({source: data, autoSelect: false});
// do description auto complete:
var journalNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/transaction-journals/' + what,
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/transaction-journals/' + what + '?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
journalNames.initialize();
$('input[name="description"]').typeahead({hint: true, highlight: true,}, {source: journalNames, displayKey: 'name', autoSelect: false});
}
/**

View File

@ -21,53 +21,152 @@
/** global: originalSum,originalForeignSum, accounting, what, Modernizr, currencySymbol, foreignCurrencySymbol */
var destAccounts = {};
var srcAccounts = {};
var categories = {};
var descriptions = {};
var destNames;
var sourceNames;
var categories;
var journalNames;
$(document).ready(function () {
"use strict";
$('.btn-do-split').click(cloneDivRow);
$('.remove-current-split').click(removeDivRow);
$.getJSON('json/expense-accounts').done(function (data) {
destAccounts = data;
$('input[name$="destination_name]"]').typeahead({source: destAccounts, autoSelect: false});
// auto complete destination name (expense accounts):
destNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/expense-accounts',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
$.getJSON('json/revenue-accounts').done(function (data) {
srcAccounts = data;
$('input[name$="source_name]"]').typeahead({source: srcAccounts, autoSelect: false});
});
$.getJSON('json/categories').done(function (data) {
categories = data;
$('input[name$="category_name]"]').typeahead({source: categories, autoSelect: false});
});
$.getJSON('json/transaction-journals/' + what).done(function (data) {
descriptions = data;
$('input[name="journal_description"]').typeahead({source: descriptions, autoSelect: false});
$('input[name$="transaction_description]"]').typeahead({source: descriptions, autoSelect: false});
});
$.getJSON('json/tags').done(function (data) {
var opt = {
typeahead: {
source: data,
afterSelect: function () {
this.$element.val("");
},
autoSelect: false
}
};
$('input[name="tags"]').tagsinput(
opt
);
},
remote: {
url: 'json/expense-accounts?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
destNames.initialize();
$('input[name$="destination_name]"]').typeahead({hint: true, highlight: true,}, {source: destNames, displayKey: 'name', autoSelect: false});
// auto complete source name (revenue accounts):
sourceNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/revenue-accounts',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/revenue-accounts?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
sourceNames.initialize();
$('input[name$="source_name]"]').typeahead({hint: true, highlight: true,}, {source: sourceNames, displayKey: 'name', autoSelect: false});
// auto complete category fields:
categories = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/categories',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/categories?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
categories.initialize();
$('input[name$="category_name]"]').typeahead({hint: true, highlight: true,}, {source: categories, displayKey: 'name', autoSelect: false});
// get transaction journal name things:
journalNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/transaction-journals/' + what,
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
},
remote: {
url: 'json/transaction-journals/' + what + '?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
journalNames.initialize();
$('input[name="journal_description"]').typeahead({hint: true, highlight: true,}, {source: journalNames, displayKey: 'name', autoSelect: false});
$('input[name$="transaction_description]"]').typeahead({hint: true, highlight: true,}, {source: journalNames, displayKey: 'name', autoSelect: false});
// get tags:
console.log('initTagsAC()');
var tagTags = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/tags',
filter: function (list) {
return $.map(list, function (tagTag) {
return {name: tagTag};
});
}
},
remote: {
url: 'json/tags?search=%QUERY',
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
});
}
}
});
tagTags.initialize();
$('input[name="tags"]').tagsinput({
typeaheadjs: {
hint: true,
highlight: true,
name: 'tags',
displayKey: 'name',
valueKey: 'name',
source: tagTags.ttAdapter()
}
});
$('input[name$="][amount]"]').on('change', calculateBothSums);
$('input[name$="][foreign_amount]"]').on('change', calculateBothSums);
@ -128,18 +227,18 @@ function cloneDivRow() {
source.find('input[name$="][amount]"]').val("").on('change', calculateBothSums);
source.find('input[name$="][foreign_amount]"]').val("").on('change', calculateBothSums);
if (destAccounts.length > 0) {
source.find('input[name$="destination_name]"]').typeahead({source: destAccounts, autoSelect: false});
if (destNames) {
source.find('input[name$="destination_name]"]').typeahead({hint: true, highlight: true,}, {source: destNames, displayKey: 'name', autoSelect: false});
}
if (srcAccounts.length > 0) {
source.find('input[name$="source_name]"]').typeahead({source: srcAccounts, autoSelect: false});
if (sourceNames) {
source.find('input[name$="source_name]"]').typeahead({hint: true, highlight: true,}, {source: sourceNames, displayKey: 'name', autoSelect: false});
}
if (categories.length > 0) {
source.find('input[name$="category_name]"]').typeahead({source: categories, autoSelect: false});
if (categories) {
source.find('input[name$="category_name]"]').typeahead({hint: true, highlight: true,}, {source: categories, displayKey: 'name', autoSelect: false});
}
if (descriptions.length > 0) {
source.find('input[name$="transaction_description]"]').typeahead({source: descriptions, autoSelect: false});
if (journalNames) {
source.find('input[name$="transaction_description]"]').typeahead({hint: true, highlight: true,}, {source: journalNames, displayKey: 'name', autoSelect: false});
}
$('div.split_row_holder').append(source);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
Options -Indexes

918
public/js/lib/typeahead/bloodhound.js vendored Normal file
View File

@ -0,0 +1,918 @@
/*!
* typeahead.js 0.11.1
* https://github.com/twitter/typeahead.js
* Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT
*/
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define("bloodhound", [ "jquery" ], function(a0) {
return root["Bloodhound"] = factory(a0);
});
} else if (typeof exports === "object") {
module.exports = factory(require("jquery"));
} else {
root["Bloodhound"] = factory(jQuery);
}
})(this, function($) {
var _ = function() {
"use strict";
return {
isMsie: function() {
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
},
isBlankString: function(str) {
return !str || /^\s*$/.test(str);
},
escapeRegExChars: function(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
isString: function(obj) {
return typeof obj === "string";
},
isNumber: function(obj) {
return typeof obj === "number";
},
isArray: $.isArray,
isFunction: $.isFunction,
isObject: $.isPlainObject,
isUndefined: function(obj) {
return typeof obj === "undefined";
},
isElement: function(obj) {
return !!(obj && obj.nodeType === 1);
},
isJQuery: function(obj) {
return obj instanceof $;
},
toStr: function toStr(s) {
return _.isUndefined(s) || s === null ? "" : s + "";
},
bind: $.proxy,
each: function(collection, cb) {
$.each(collection, reverseArgs);
function reverseArgs(index, value) {
return cb(value, index);
}
},
map: $.map,
filter: $.grep,
every: function(obj, test) {
var result = true;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (!(result = test.call(null, val, key, obj))) {
return false;
}
});
return !!result;
},
some: function(obj, test) {
var result = false;
if (!obj) {
return result;
}
$.each(obj, function(key, val) {
if (result = test.call(null, val, key, obj)) {
return false;
}
});
return !!result;
},
mixin: $.extend,
identity: function(x) {
return x;
},
clone: function(obj) {
return $.extend(true, {}, obj);
},
getIdGenerator: function() {
var counter = 0;
return function() {
return counter++;
};
},
templatify: function templatify(obj) {
return $.isFunction(obj) ? obj : template;
function template() {
return String(obj);
}
},
defer: function(fn) {
setTimeout(fn, 0);
},
debounce: function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments, later, callNow;
later = function() {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
}
};
callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
}
return result;
};
},
throttle: function(func, wait) {
var context, args, timeout, result, previous, later;
previous = 0;
later = function() {
previous = new Date();
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date(), remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
},
stringify: function(val) {
return _.isString(val) ? val : JSON.stringify(val);
},
noop: function() {}
};
}();
var VERSION = "0.11.1";
var tokenizers = function() {
"use strict";
return {
nonword: nonword,
whitespace: whitespace,
obj: {
nonword: getObjTokenizer(nonword),
whitespace: getObjTokenizer(whitespace)
}
};
function whitespace(str) {
str = _.toStr(str);
return str ? str.split(/\s+/) : [];
}
function nonword(str) {
str = _.toStr(str);
return str ? str.split(/\W+/) : [];
}
function getObjTokenizer(tokenizer) {
return function setKey(keys) {
keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);
return function tokenize(o) {
var tokens = [];
_.each(keys, function(k) {
tokens = tokens.concat(tokenizer(_.toStr(o[k])));
});
return tokens;
};
};
}
}();
var LruCache = function() {
"use strict";
function LruCache(maxSize) {
this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
this.reset();
if (this.maxSize <= 0) {
this.set = this.get = $.noop;
}
}
_.mixin(LruCache.prototype, {
set: function set(key, val) {
var tailItem = this.list.tail, node;
if (this.size >= this.maxSize) {
this.list.remove(tailItem);
delete this.hash[tailItem.key];
this.size--;
}
if (node = this.hash[key]) {
node.val = val;
this.list.moveToFront(node);
} else {
node = new Node(key, val);
this.list.add(node);
this.hash[key] = node;
this.size++;
}
},
get: function get(key) {
var node = this.hash[key];
if (node) {
this.list.moveToFront(node);
return node.val;
}
},
reset: function reset() {
this.size = 0;
this.hash = {};
this.list = new List();
}
});
function List() {
this.head = this.tail = null;
}
_.mixin(List.prototype, {
add: function add(node) {
if (this.head) {
node.next = this.head;
this.head.prev = node;
}
this.head = node;
this.tail = this.tail || node;
},
remove: function remove(node) {
node.prev ? node.prev.next = node.next : this.head = node.next;
node.next ? node.next.prev = node.prev : this.tail = node.prev;
},
moveToFront: function(node) {
this.remove(node);
this.add(node);
}
});
function Node(key, val) {
this.key = key;
this.val = val;
this.prev = this.next = null;
}
return LruCache;
}();
var PersistentStorage = function() {
"use strict";
var LOCAL_STORAGE;
try {
LOCAL_STORAGE = window.localStorage;
LOCAL_STORAGE.setItem("~~~", "!");
LOCAL_STORAGE.removeItem("~~~");
} catch (err) {
LOCAL_STORAGE = null;
}
function PersistentStorage(namespace, override) {
this.prefix = [ "__", namespace, "__" ].join("");
this.ttlKey = "__ttl__";
this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
this.ls = override || LOCAL_STORAGE;
!this.ls && this._noop();
}
_.mixin(PersistentStorage.prototype, {
_prefix: function(key) {
return this.prefix + key;
},
_ttlKey: function(key) {
return this._prefix(key) + this.ttlKey;
},
_noop: function() {
this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
},
_safeSet: function(key, val) {
try {
this.ls.setItem(key, val);
} catch (err) {
if (err.name === "QuotaExceededError") {
this.clear();
this._noop();
}
}
},
get: function(key) {
if (this.isExpired(key)) {
this.remove(key);
}
return decode(this.ls.getItem(this._prefix(key)));
},
set: function(key, val, ttl) {
if (_.isNumber(ttl)) {
this._safeSet(this._ttlKey(key), encode(now() + ttl));
} else {
this.ls.removeItem(this._ttlKey(key));
}
return this._safeSet(this._prefix(key), encode(val));
},
remove: function(key) {
this.ls.removeItem(this._ttlKey(key));
this.ls.removeItem(this._prefix(key));
return this;
},
clear: function() {
var i, keys = gatherMatchingKeys(this.keyMatcher);
for (i = keys.length; i--; ) {
this.remove(keys[i]);
}
return this;
},
isExpired: function(key) {
var ttl = decode(this.ls.getItem(this._ttlKey(key)));
return _.isNumber(ttl) && now() > ttl ? true : false;
}
});
return PersistentStorage;
function now() {
return new Date().getTime();
}
function encode(val) {
return JSON.stringify(_.isUndefined(val) ? null : val);
}
function decode(val) {
return $.parseJSON(val);
}
function gatherMatchingKeys(keyMatcher) {
var i, key, keys = [], len = LOCAL_STORAGE.length;
for (i = 0; i < len; i++) {
if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
keys.push(key.replace(keyMatcher, ""));
}
}
return keys;
}
}();
var Transport = function() {
"use strict";
var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
function Transport(o) {
o = o || {};
this.cancelled = false;
this.lastReq = null;
this._send = o.transport;
this._get = o.limiter ? o.limiter(this._get) : this._get;
this._cache = o.cache === false ? new LruCache(0) : sharedCache;
}
Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
maxPendingRequests = num;
};
Transport.resetCache = function resetCache() {
sharedCache.reset();
};
_.mixin(Transport.prototype, {
_fingerprint: function fingerprint(o) {
o = o || {};
return o.url + o.type + $.param(o.data || {});
},
_get: function(o, cb) {
var that = this, fingerprint, jqXhr;
fingerprint = this._fingerprint(o);
if (this.cancelled || fingerprint !== this.lastReq) {
return;
}
if (jqXhr = pendingRequests[fingerprint]) {
jqXhr.done(done).fail(fail);
} else if (pendingRequestsCount < maxPendingRequests) {
pendingRequestsCount++;
pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
} else {
this.onDeckRequestArgs = [].slice.call(arguments, 0);
}
function done(resp) {
cb(null, resp);
that._cache.set(fingerprint, resp);
}
function fail() {
cb(true);
}
function always() {
pendingRequestsCount--;
delete pendingRequests[fingerprint];
if (that.onDeckRequestArgs) {
that._get.apply(that, that.onDeckRequestArgs);
that.onDeckRequestArgs = null;
}
}
},
get: function(o, cb) {
var resp, fingerprint;
cb = cb || $.noop;
o = _.isString(o) ? {
url: o
} : o || {};
fingerprint = this._fingerprint(o);
this.cancelled = false;
this.lastReq = fingerprint;
if (resp = this._cache.get(fingerprint)) {
cb(null, resp);
} else {
this._get(o, cb);
}
},
cancel: function() {
this.cancelled = true;
}
});
return Transport;
}();
var SearchIndex = window.SearchIndex = function() {
"use strict";
var CHILDREN = "c", IDS = "i";
function SearchIndex(o) {
o = o || {};
if (!o.datumTokenizer || !o.queryTokenizer) {
$.error("datumTokenizer and queryTokenizer are both required");
}
this.identify = o.identify || _.stringify;
this.datumTokenizer = o.datumTokenizer;
this.queryTokenizer = o.queryTokenizer;
this.reset();
}
_.mixin(SearchIndex.prototype, {
bootstrap: function bootstrap(o) {
this.datums = o.datums;
this.trie = o.trie;
},
add: function(data) {
var that = this;
data = _.isArray(data) ? data : [ data ];
_.each(data, function(datum) {
var id, tokens;
that.datums[id = that.identify(datum)] = datum;
tokens = normalizeTokens(that.datumTokenizer(datum));
_.each(tokens, function(token) {
var node, chars, ch;
node = that.trie;
chars = token.split("");
while (ch = chars.shift()) {
node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
node[IDS].push(id);
}
});
});
},
get: function get(ids) {
var that = this;
return _.map(ids, function(id) {
return that.datums[id];
});
},
search: function search(query) {
var that = this, tokens, matches;
tokens = normalizeTokens(this.queryTokenizer(query));
_.each(tokens, function(token) {
var node, chars, ch, ids;
if (matches && matches.length === 0) {
return false;
}
node = that.trie;
chars = token.split("");
while (node && (ch = chars.shift())) {
node = node[CHILDREN][ch];
}
if (node && chars.length === 0) {
ids = node[IDS].slice(0);
matches = matches ? getIntersection(matches, ids) : ids;
} else {
matches = [];
return false;
}
});
return matches ? _.map(unique(matches), function(id) {
return that.datums[id];
}) : [];
},
all: function all() {
var values = [];
for (var key in this.datums) {
values.push(this.datums[key]);
}
return values;
},
reset: function reset() {
this.datums = {};
this.trie = newNode();
},
serialize: function serialize() {
return {
datums: this.datums,
trie: this.trie
};
}
});
return SearchIndex;
function normalizeTokens(tokens) {
tokens = _.filter(tokens, function(token) {
return !!token;
});
tokens = _.map(tokens, function(token) {
return token.toLowerCase();
});
return tokens;
}
function newNode() {
var node = {};
node[IDS] = [];
node[CHILDREN] = {};
return node;
}
function unique(array) {
var seen = {}, uniques = [];
for (var i = 0, len = array.length; i < len; i++) {
if (!seen[array[i]]) {
seen[array[i]] = true;
uniques.push(array[i]);
}
}
return uniques;
}
function getIntersection(arrayA, arrayB) {
var ai = 0, bi = 0, intersection = [];
arrayA = arrayA.sort();
arrayB = arrayB.sort();
var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
while (ai < lenArrayA && bi < lenArrayB) {
if (arrayA[ai] < arrayB[bi]) {
ai++;
} else if (arrayA[ai] > arrayB[bi]) {
bi++;
} else {
intersection.push(arrayA[ai]);
ai++;
bi++;
}
}
return intersection;
}
}();
var Prefetch = function() {
"use strict";
var keys;
keys = {
data: "data",
protocol: "protocol",
thumbprint: "thumbprint"
};
function Prefetch(o) {
this.url = o.url;
this.ttl = o.ttl;
this.cache = o.cache;
this.prepare = o.prepare;
this.transform = o.transform;
this.transport = o.transport;
this.thumbprint = o.thumbprint;
this.storage = new PersistentStorage(o.cacheKey);
}
_.mixin(Prefetch.prototype, {
_settings: function settings() {
return {
url: this.url,
type: "GET",
dataType: "json"
};
},
store: function store(data) {
if (!this.cache) {
return;
}
this.storage.set(keys.data, data, this.ttl);
this.storage.set(keys.protocol, location.protocol, this.ttl);
this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
},
fromCache: function fromCache() {
var stored = {}, isExpired;
if (!this.cache) {
return null;
}
stored.data = this.storage.get(keys.data);
stored.protocol = this.storage.get(keys.protocol);
stored.thumbprint = this.storage.get(keys.thumbprint);
isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
return stored.data && !isExpired ? stored.data : null;
},
fromNetwork: function(cb) {
var that = this, settings;
if (!cb) {
return;
}
settings = this.prepare(this._settings());
this.transport(settings).fail(onError).done(onResponse);
function onError() {
cb(true);
}
function onResponse(resp) {
cb(null, that.transform(resp));
}
},
clear: function clear() {
this.storage.clear();
return this;
}
});
return Prefetch;
}();
var Remote = function() {
"use strict";
function Remote(o) {
this.url = o.url;
this.prepare = o.prepare;
this.transform = o.transform;
this.transport = new Transport({
cache: o.cache,
limiter: o.limiter,
transport: o.transport
});
}
_.mixin(Remote.prototype, {
_settings: function settings() {
return {
url: this.url,
type: "GET",
dataType: "json"
};
},
get: function get(query, cb) {
var that = this, settings;
if (!cb) {
return;
}
query = query || "";
settings = this.prepare(query, this._settings());
return this.transport.get(settings, onResponse);
function onResponse(err, resp) {
err ? cb([]) : cb(that.transform(resp));
}
},
cancelLastRequest: function cancelLastRequest() {
this.transport.cancel();
}
});
return Remote;
}();
var oParser = function() {
"use strict";
return function parse(o) {
var defaults, sorter;
defaults = {
initialize: true,
identify: _.stringify,
datumTokenizer: null,
queryTokenizer: null,
sufficient: 5,
sorter: null,
local: [],
prefetch: null,
remote: null
};
o = _.mixin(defaults, o || {});
!o.datumTokenizer && $.error("datumTokenizer is required");
!o.queryTokenizer && $.error("queryTokenizer is required");
sorter = o.sorter;
o.sorter = sorter ? function(x) {
return x.sort(sorter);
} : _.identity;
o.local = _.isFunction(o.local) ? o.local() : o.local;
o.prefetch = parsePrefetch(o.prefetch);
o.remote = parseRemote(o.remote);
return o;
};
function parsePrefetch(o) {
var defaults;
if (!o) {
return null;
}
defaults = {
url: null,
ttl: 24 * 60 * 60 * 1e3,
cache: true,
cacheKey: null,
thumbprint: "",
prepare: _.identity,
transform: _.identity,
transport: null
};
o = _.isString(o) ? {
url: o
} : o;
o = _.mixin(defaults, o);
!o.url && $.error("prefetch requires url to be set");
o.transform = o.filter || o.transform;
o.cacheKey = o.cacheKey || o.url;
o.thumbprint = VERSION + o.thumbprint;
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
return o;
}
function parseRemote(o) {
var defaults;
if (!o) {
return;
}
defaults = {
url: null,
cache: true,
prepare: null,
replace: null,
wildcard: null,
limiter: null,
rateLimitBy: "debounce",
rateLimitWait: 300,
transform: _.identity,
transport: null
};
o = _.isString(o) ? {
url: o
} : o;
o = _.mixin(defaults, o);
!o.url && $.error("remote requires url to be set");
o.transform = o.filter || o.transform;
o.prepare = toRemotePrepare(o);
o.limiter = toLimiter(o);
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
delete o.replace;
delete o.wildcard;
delete o.rateLimitBy;
delete o.rateLimitWait;
return o;
}
function toRemotePrepare(o) {
var prepare, replace, wildcard;
prepare = o.prepare;
replace = o.replace;
wildcard = o.wildcard;
if (prepare) {
return prepare;
}
if (replace) {
prepare = prepareByReplace;
} else if (o.wildcard) {
prepare = prepareByWildcard;
} else {
prepare = idenityPrepare;
}
return prepare;
function prepareByReplace(query, settings) {
settings.url = replace(settings.url, query);
return settings;
}
function prepareByWildcard(query, settings) {
settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
return settings;
}
function idenityPrepare(query, settings) {
return settings;
}
}
function toLimiter(o) {
var limiter, method, wait;
limiter = o.limiter;
method = o.rateLimitBy;
wait = o.rateLimitWait;
if (!limiter) {
limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
}
return limiter;
function debounce(wait) {
return function debounce(fn) {
return _.debounce(fn, wait);
};
}
function throttle(wait) {
return function throttle(fn) {
return _.throttle(fn, wait);
};
}
}
function callbackToDeferred(fn) {
return function wrapper(o) {
var deferred = $.Deferred();
fn(o, onSuccess, onError);
return deferred;
function onSuccess(resp) {
_.defer(function() {
deferred.resolve(resp);
});
}
function onError(err) {
_.defer(function() {
deferred.reject(err);
});
}
};
}
}();
var Bloodhound = function() {
"use strict";
var old;
old = window && window.Bloodhound;
function Bloodhound(o) {
o = oParser(o);
this.sorter = o.sorter;
this.identify = o.identify;
this.sufficient = o.sufficient;
this.local = o.local;
this.remote = o.remote ? new Remote(o.remote) : null;
this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
this.index = new SearchIndex({
identify: this.identify,
datumTokenizer: o.datumTokenizer,
queryTokenizer: o.queryTokenizer
});
o.initialize !== false && this.initialize();
}
Bloodhound.noConflict = function noConflict() {
window && (window.Bloodhound = old);
return Bloodhound;
};
Bloodhound.tokenizers = tokenizers;
_.mixin(Bloodhound.prototype, {
__ttAdapter: function ttAdapter() {
var that = this;
return this.remote ? withAsync : withoutAsync;
function withAsync(query, sync, async) {
return that.search(query, sync, async);
}
function withoutAsync(query, sync) {
return that.search(query, sync);
}
},
_loadPrefetch: function loadPrefetch() {
var that = this, deferred, serialized;
deferred = $.Deferred();
if (!this.prefetch) {
deferred.resolve();
} else if (serialized = this.prefetch.fromCache()) {
this.index.bootstrap(serialized);
deferred.resolve();
} else {
this.prefetch.fromNetwork(done);
}
return deferred.promise();
function done(err, data) {
if (err) {
return deferred.reject();
}
that.add(data);
that.prefetch.store(that.index.serialize());
deferred.resolve();
}
},
_initialize: function initialize() {
var that = this, deferred;
this.clear();
(this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
return this.initPromise;
function addLocalToIndex() {
that.add(that.local);
}
},
initialize: function initialize(force) {
return !this.initPromise || force ? this._initialize() : this.initPromise;
},
add: function add(data) {
this.index.add(data);
return this;
},
get: function get(ids) {
ids = _.isArray(ids) ? ids : [].slice.call(arguments);
return this.index.get(ids);
},
search: function search(query, sync, async) {
var that = this, local;
local = this.sorter(this.index.search(query));
sync(this.remote ? local.slice() : local);
if (this.remote && local.length < this.sufficient) {
this.remote.get(query, processRemote);
} else if (this.remote) {
this.remote.cancelLastRequest();
}
return this;
function processRemote(remote) {
var nonDuplicates = [];
_.each(remote, function(r) {
!_.some(local, function(l) {
return that.identify(r) === that.identify(l);
}) && nonDuplicates.push(r);
});
async && async(nonDuplicates);
}
},
all: function all() {
return this.index.all();
},
clear: function clear() {
this.index.reset();
return this;
},
clearPrefetchCache: function clearPrefetchCache() {
this.prefetch && this.prefetch.clear();
return this;
},
clearRemoteCache: function clearRemoteCache() {
Transport.resetCache();
return this;
},
ttAdapter: function ttAdapter() {
return this.__ttAdapter();
}
});
return Bloodhound;
}();
return Bloodhound;
});

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -96,7 +96,8 @@
<script type="text/javascript">
var what = "{{ what }}";
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/jquery-ui.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/modernizr-custom.js?v={{ FF_VERSION }}"></script>

View File

@ -7,9 +7,18 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
<base href="{{ route('index') }}/">
<link rel="stylesheet" href="css/app.css?v={{ FF_VERSION }}" type="text/css" media="all"/>
{# libraries #}
<link href="lib/bs/css/bootstrap.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"/>
<link href="lib/fa/css/font-awesome.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"/>
{# the theme #}
<link href="lib/adminlte/css/AdminLTE.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"/>
<link href="lib/adminlte/css/skins/skin-blue-light.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"/>
{# Firefly III customisations #}
<link href="css/firefly.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"/>
{% include('partials.favicons') %}
</head>
<body class="ff-error-page">

View File

@ -194,7 +194,8 @@
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="js/lib/modernizr-custom.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/jquery-ui.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="lib/fc/fullcalendar.min.js?v={{ FF_VERSION }}"></script>

View File

@ -215,7 +215,8 @@
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="js/lib/modernizr-custom.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/jquery-ui.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="lib/fc/fullcalendar.min.js?v={{ FF_VERSION }}"></script>

View File

@ -145,7 +145,7 @@
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript">
var triggerCount = {{ triggerCount }};
var actionCount = {{ actionCount }};

View File

@ -128,7 +128,7 @@
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript">
var triggerCount = {{ triggerCount }};
var actionCount = {{ actionCount }};

View File

@ -128,7 +128,8 @@
<script type="text/javascript">
var what = "";
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/transactions/mass/edit-bulk.js?v={{ FF_VERSION }}"></script>
{% endblock %}

View File

@ -215,10 +215,7 @@
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
{% endblock %}
{% block styles %}
<link href="css/bootstrap-tagsinput.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all">
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/transactions/convert.js?v={{ FF_VERSION }}"></script>
{% endblock %}

View File

@ -136,6 +136,7 @@
<script type="text/javascript">
var what = "";
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/transactions/mass/edit.js?v={{ FF_VERSION }}"></script>
{% endblock %}

View File

@ -463,6 +463,6 @@
<script type="text/javascript">
var autoCompleteUri = "{{ route('json.journals-with-id',[journal.id]) }}";
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/transactions/show.js?v={{ FF_VERSION }}"></script>
{% endblock %}

View File

@ -240,8 +240,11 @@
var overruleCurrency = {{ preFilled.amount_currency_id_amount|default(0) }};
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/modernizr-custom.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/jquery-ui.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="jscript/accounts?ext=.js&amp;v={{ FF_VERSION }}"></script>

View File

@ -248,7 +248,8 @@
<script type="text/javascript">
var what = "{{ what }}";
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/jquery-ui.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/modernizr-custom.js?v={{ FF_VERSION }}"></script>

View File

@ -328,7 +328,8 @@
var originalForeignSum = {{ preFilled.journal_foreign_amount }};
var what = "{{ preFilled.what }}";
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/ff/common/autocomplete.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/jquery-ui.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="js/lib/modernizr-custom.js?v={{ FF_VERSION }}"></script>

View File

@ -75,7 +75,7 @@
<div class="buttons">
<!-- Authorize Button -->
<form method="post" action="/oauth/authorize">
<form method="post" action="{{ route('index') }}/oauth/authorize">
{{ csrf_field() }}
<input type="hidden" name="state" value="{{ request.state }}">
@ -84,7 +84,7 @@
</form>
<!-- Cancel Button -->
<form method="post" action="/oauth/authorize">
<form method="post" action="{{ route('index') }}/oauth/authorize">
{{ csrf_field() }}
{{ method_field('DELETE') }}

View File

@ -73,7 +73,6 @@ class CreateControllerTest extends TestCase
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Debt'])->andReturn(AccountType::find(11))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Loan'])->andReturn(AccountType::find(9))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Mortgage'])->andReturn(AccountType::find(12))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Credit card'])->andReturn(AccountType::find(13))->once();
$this->be($this->user());
$response = $this->get(route('accounts.create', ['asset']));

View File

@ -86,7 +86,6 @@ class EditControllerTest extends TestCase
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Debt'])->andReturn(AccountType::find(11))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Loan'])->andReturn(AccountType::find(9))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Mortgage'])->andReturn(AccountType::find(12))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Credit card'])->andReturn(AccountType::find(13))->once();
$this->be($this->user());
@ -131,7 +130,6 @@ class EditControllerTest extends TestCase
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Debt'])->andReturn(AccountType::find(11))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Loan'])->andReturn(AccountType::find(9))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Mortgage'])->andReturn(AccountType::find(12))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Credit card'])->andReturn(AccountType::find(13))->once();
$this->be($this->user());
@ -178,7 +176,6 @@ class EditControllerTest extends TestCase
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Debt'])->andReturn(AccountType::find(11))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Loan'])->andReturn(AccountType::find(9))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Mortgage'])->andReturn(AccountType::find(12))->once();
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Credit card'])->andReturn(AccountType::find(13))->once();
$this->be($this->user());
$account = $this->user()->accounts()->where('account_type_id', 3)->whereNull('deleted_at')->first();

View File

@ -81,7 +81,7 @@ class UserEventHandlerTest extends TestCase
{
$repository = $this->mock(UserRepositoryInterface::class);
$user = $this->user();
$event = new Login($user, true);
$event = new Login(null, $user, true);
$listener = new UserEventHandler();
// mock stuff
@ -99,7 +99,7 @@ class UserEventHandlerTest extends TestCase
{
$repository = $this->mock(UserRepositoryInterface::class);
$user = $this->emptyUser();
$event = new Login($user, true);
$event = new Login(null, $user, true);
$listener = new UserEventHandler();
// mock stuff
@ -121,7 +121,7 @@ class UserEventHandlerTest extends TestCase
{
$repository = $this->mock(UserRepositoryInterface::class);
$user = $this->emptyUser();
$event = new Login($user, true);
$event = new Login(null, $user, true);
$listener = new UserEventHandler();
// mock stuff
@ -141,7 +141,7 @@ class UserEventHandlerTest extends TestCase
{
$repository = $this->mock(UserRepositoryInterface::class);
$user = $this->user();
$event = new Login($user, true);
$event = new Login(null, $user, true);
$listener = new UserEventHandler();
// mock stuff
@ -159,7 +159,7 @@ class UserEventHandlerTest extends TestCase
{
$repository = $this->mock(UserRepositoryInterface::class);
$user = $this->emptyUser();
$event = new Login($user, true);
$event = new Login(null, $user, true);
$listener = new UserEventHandler();
// mock stuff