Compare commits

..

17 Commits

Author SHA1 Message Date
github-actions
746f1fd300 Auto commit for release 'develop' on 2024-04-02 2024-04-02 07:47:24 +02:00
James Cole
9e5faf919f Add space 2024-04-02 07:40:40 +02:00
James Cole
cc6cbe6605 Add something to changelog before I forget. 2024-04-02 07:01:13 +02:00
James Cole
dc6d708897 Replace frontpageAccounts variable name. 2024-04-02 06:59:40 +02:00
James Cole
6189d24b98 Replace variable names. 2024-04-01 20:26:02 +02:00
James Cole
f6e28dc88f Fine tune preferences to handle multi-administration options. 2024-04-01 19:59:21 +02:00
James Cole
75ea035630 Preferences can be administration specific. Not yet really working. 2024-04-01 18:18:48 +02:00
James Cole
4cdb14301d Add administration specific thing to preferences. 2024-04-01 18:03:43 +02:00
James Cole
9f95221ba3 Various code cleanup. 2024-04-01 17:43:31 +02:00
James Cole
e3a67be412 Clear user groups when loading 2024-04-01 16:07:14 +02:00
James Cole
5749b642ce Wrong response code. 2024-04-01 16:07:03 +02:00
James Cole
baff7c67f9 Expand user group views and translations 2024-04-01 15:41:16 +02:00
James Cole
ccc005942f Expand user group pages. 2024-04-01 15:40:53 +02:00
James Cole
5b83c33039 Turns out it's pointless to add "default_administration", you can force that already through the owner role (which is already in place). 2024-04-01 14:15:35 +02:00
James Cole
cc32578c5f Add new strings. 2024-04-01 14:04:36 +02:00
James Cole
80f410835b Expand models, user groups need more properties. 2024-04-01 14:04:22 +02:00
James Cole
b537a3145d Expand financial administrations views. 2024-04-01 14:01:52 +02:00
129 changed files with 85470 additions and 84448 deletions

View File

@@ -9,16 +9,16 @@
"packages-dev": [
{
"name": "composer/pcre",
"version": "3.1.2",
"version": "3.1.3",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace"
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace",
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace",
"url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"shasum": ""
},
"require": {
@@ -60,7 +60,7 @@
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.1.2"
"source": "https://github.com/composer/pcre/tree/3.1.3"
},
"funding": [
{
@@ -76,20 +76,20 @@
"type": "tidelift"
}
],
"time": "2024-03-07T15:38:35+00:00"
"time": "2024-03-19T10:26:25+00:00"
},
{
"name": "composer/xdebug-handler",
"version": "3.0.3",
"version": "3.0.4",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
"reference": "ced299686f41dce890debac69273b47ffe98a40c"
"reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c",
"reference": "ced299686f41dce890debac69273b47ffe98a40c",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
"reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
"shasum": ""
},
"require": {
@@ -100,7 +100,7 @@
"require-dev": {
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-strict-rules": "^1.1",
"symfony/phpunit-bridge": "^6.0"
"phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
},
"type": "library",
"autoload": {
@@ -124,9 +124,9 @@
"performance"
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/xdebug-handler/issues",
"source": "https://github.com/composer/xdebug-handler/tree/3.0.3"
"source": "https://github.com/composer/xdebug-handler/tree/3.0.4"
},
"funding": [
{
@@ -142,7 +142,7 @@
"type": "tidelift"
}
],
"time": "2022-02-25T21:32:43+00:00"
"time": "2024-03-26T18:29:49+00:00"
},
{
"name": "pdepend/pdepend",

View File

@@ -83,17 +83,17 @@ class AccountController extends Controller
// user's preferences
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray();
/** @var Preference $frontPage */
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
/** @var Preference $frontpage */
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
$default = app('amount')->getDefaultCurrency();
if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) {
$frontPage->data = $defaultSet;
$frontPage->save();
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
$frontpage->data = $defaultSet;
$frontpage->save();
}
// get accounts:
$accounts = $this->repository->getAccountsById($frontPage->data);
$accounts = $this->repository->getAccountsById($frontpage->data);
$chartData = [];
/** @var Account $account */

View File

@@ -98,14 +98,14 @@ class AccountController extends Controller
// user's preferences
if (0 === $accounts->count()) {
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray();
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) {
$frontPage->data = $defaultSet;
$frontPage->save();
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
$frontpage->data = $defaultSet;
$frontpage->save();
}
$accounts = $this->repository->getAccountsById($frontPage->data);
$accounts = $this->repository->getAccountsById($frontpage->data);
}
// both options are overruled by "preselected"

View File

@@ -54,6 +54,14 @@ class UpdateController extends Controller
);
}
public function useUserGroup(UserGroup $userGroup): JsonResponse
{
// group validation is already in place, so can just update the user.
$this->repository->useUserGroup($userGroup);
return response()->json([], 204);
}
public function update(UpdateRequest $request, UserGroup $userGroup): JsonResponse
{
$all = $request->getAll();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V2\Request\UserGroup;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\UserGroup;
use FireflyIII\Rules\IsDefaultUserGroupName;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
@@ -53,7 +54,7 @@ class UpdateRequest extends FormRequest
$userGroup = $this->route()->parameter('userGroup');
return [
'title' => sprintf('required|min:1|max:255|unique:user_groups,title,%d', $userGroup->id),
'title' => ['required', 'min:1', 'max:255', sprintf('unique:user_groups,title,%d', $userGroup->id), new IsDefaultUserGroupName($userGroup)],
];
}
}

View File

@@ -73,6 +73,7 @@ class CorrectDatabase extends Command
// new!
'firefly-iii:unify-group-accounts',
'firefly-iii:trigger-credit-recalculation',
'firefly-iii:migrate-preferences',
];
foreach ($commands as $command) {
$this->friendlyLine(sprintf('Now executing command "%s"', $command));

View File

@@ -50,7 +50,7 @@ class FixFrontpageAccounts extends Command
/** @var User $user */
foreach ($users as $user) {
$preference = app('preferences')->getForUser($user, 'frontPageAccounts');
$preference = app('preferences')->getForUser($user, 'frontpageAccounts');
if (null !== $preference) {
$this->fixPreference($preference);
}
@@ -83,6 +83,6 @@ class FixFrontpageAccounts extends Command
}
}
}
app('preferences')->setForUser($preference->user, 'frontPageAccounts', $fixed);
app('preferences')->setForUser($preference->user, 'frontpageAccounts', $fixed);
}
}

View File

@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
/*
* MigratePreferences.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Preference;
use FireflyIII\User;
use Illuminate\Console\Command;
use Symfony\Component\Console\Command\Command as CommandAlias;
class MigratePreferences extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:migrate-preferences';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Give Firefly III preferences a user group ID so they can be made administration specific.';
/**
* Execute the console command.
*/
public function handle(): int
{
$items = config('firefly.admin_specific_prefs');
$users = User::get();
/** @var User $user */
foreach ($users as $user) {
$count = 0;
foreach ($items as $item) {
$preference = Preference::where('name', $item)->where('user_id', $user->id)->first();
if (null === $preference) {
continue;
}
if (null !== $preference->user_group_id) {
$preference->user_group_id = $user->user_group_id;
$preference->save();
++$count;
}
}
if ($count > 0) {
$this->info(sprintf('Migrated %d preference(s) for user #%d ("%s").', $count, $user->id, $user->email));
}
}
return CommandAlias::SUCCESS;
}
}

View File

@@ -80,7 +80,7 @@ class CreateGroupMemberships extends Command
// check if membership exists
$userGroup = UserGroup::where('title', $user->email)->first();
if (null === $userGroup) {
$userGroup = UserGroup::create(['title' => $user->email]);
$userGroup = UserGroup::create(['title' => $user->email, 'default_administration' => true]);
}
$userRole = UserRole::where('title', UserRoleEnum::OWNER->value)->first();

View File

@@ -69,6 +69,7 @@ class UpgradeDatabase extends Command
'firefly-iii:create-group-memberships',
'firefly-iii:upgrade-group-information',
'firefly-iii:upgrade-currency-preferences',
'firefly-iii:correct-database',
];
$args = [];
if ($this->option('force')) {

View File

@@ -145,13 +145,13 @@ class CreateController extends Controller
Log::channel('audit')->info('Stored new account.', $data);
// update preferences if necessary:
$frontPage = app('preferences')->get('frontPageAccounts', [])->data;
if (!is_array($frontPage)) {
$frontPage = [];
$frontpage = app('preferences')->get('frontpageAccounts', [])->data;
if (!is_array($frontpage)) {
$frontpage = [];
}
if (AccountType::ASSET === $account->accountType->type) {
$frontPage[] = $account->id;
app('preferences')->set('frontPageAccounts', $frontPage);
$frontpage[] = $account->id;
app('preferences')->set('frontpageAccounts', $frontpage);
}
// store attachment(s):

View File

@@ -301,14 +301,14 @@ class AccountController extends Controller
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
$defaultSet = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray();
app('log')->debug('Default set is ', $defaultSet);
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
$frontPageArray = !is_array($frontPage->data) ? [] : $frontPage->data;
app('log')->debug('Frontpage preference set is ', $frontPageArray);
if (0 === count($frontPageArray)) {
app('preferences')->set('frontPageAccounts', $defaultSet);
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
app('log')->debug('Frontpage preference set is ', $frontpageArray);
if (0 === count($frontpageArray)) {
app('preferences')->set('frontpageAccounts', $defaultSet);
app('log')->debug('frontpage set is empty!');
}
$accounts = $repository->getAccountsById($frontPageArray);
$accounts = $repository->getAccountsById($frontpageArray);
return response()->json($this->accountBalanceChart($accounts, $start, $end));
}

View File

@@ -116,8 +116,8 @@ class CategoryController extends Controller
return response()->json($cache->get());
}
$frontPageGenerator = new FrontpageChartGenerator($start, $end);
$chartData = $frontPageGenerator->generate();
$frontpageGenerator = new FrontpageChartGenerator($start, $end);
$chartData = $frontpageGenerator->generate();
$data = $this->generator->multiSet($chartData);
$cache->store($data);

View File

@@ -129,10 +129,10 @@ class HomeController extends Controller
}
$subTitle = (string)trans('firefly.welcome_back');
$transactions = [];
$frontPage = app('preferences')->getFresh('frontPageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray());
$frontPageArray = $frontPage->data;
if (!is_array($frontPageArray)) {
$frontPageArray = [];
$frontpage = app('preferences')->getFresh('frontpageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray());
$frontpageArray = $frontpage->data;
if (!is_array($frontpageArray)) {
$frontpageArray = [];
}
/** @var Carbon $start */
@@ -140,13 +140,13 @@ class HomeController extends Controller
/** @var Carbon $end */
$end = session('end', today(config('app.timezone'))->endOfMonth());
$accounts = $repository->getAccountsById($frontPageArray);
$accounts = $repository->getAccountsById($frontpageArray);
$today = today(config('app.timezone'));
// sort frontpage accounts by order
$accounts = $accounts->sortBy('order');
app('log')->debug('Frontpage accounts are ', $frontPageArray);
app('log')->debug('Frontpage accounts are ', $frontpageArray);
/** @var BillRepositoryInterface $billRepository */
$billRepository = app(BillRepositoryInterface::class);

View File

@@ -115,7 +115,7 @@ class NewUserController extends Controller
// store frontpage preferences:
$accounts = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray();
app('preferences')->set('frontPageAccounts', $accounts);
app('preferences')->set('frontpageAccounts', $accounts);
// mark.
app('preferences')->mark();

View File

@@ -89,10 +89,10 @@ class PreferencesController extends Controller
/** @var array<int, int> $accountIds */
$accountIds = $accounts->pluck('id')->toArray();
$viewRange = app('navigation')->getViewRange(false);
$frontPageAccountsPref = app('preferences')->get('frontPageAccounts', $accountIds);
$frontPageAccounts = $frontPageAccountsPref->data;
if (!is_array($frontPageAccounts)) {
$frontPageAccounts = $accountIds;
$frontpageAccountsPref = app('preferences')->get('frontpageAccounts', $accountIds);
$frontpageAccounts = $frontpageAccountsPref->data;
if (!is_array($frontpageAccounts)) {
$frontpageAccounts = $accountIds;
}
$language = app('steam')->getLanguage();
$languages = config('firefly.languages');
@@ -128,8 +128,8 @@ class PreferencesController extends Controller
$locales = ['equal' => (string)trans('firefly.equal_to_language')] + $locales;
// an important fallback is that the frontPageAccount array gets refilled automatically
// when it turns up empty.
if (0 === count($frontPageAccounts)) {
$frontPageAccounts = $accountIds;
if (0 === count($frontpageAccounts)) {
$frontpageAccounts = $accountIds;
}
// for the demo user, the slackUrl is automatically emptied.
@@ -139,7 +139,7 @@ class PreferencesController extends Controller
$slackUrl = '';
}
return view('preferences.index', compact('language', 'groupedAccounts', 'isDocker', 'frontPageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart'));
return view('preferences.index', compact('language', 'groupedAccounts', 'isDocker', 'frontpageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart'));
}
/**
@@ -155,12 +155,12 @@ class PreferencesController extends Controller
public function postIndex(Request $request)
{
// front page accounts
$frontPageAccounts = [];
if (is_array($request->get('frontPageAccounts')) && count($request->get('frontPageAccounts')) > 0) {
foreach ($request->get('frontPageAccounts') as $id) {
$frontPageAccounts[] = (int)$id;
$frontpageAccounts = [];
if (is_array($request->get('frontpageAccounts')) && count($request->get('frontpageAccounts')) > 0) {
foreach ($request->get('frontpageAccounts') as $id) {
$frontpageAccounts[] = (int)$id;
}
app('preferences')->set('frontPageAccounts', $frontPageAccounts);
app('preferences')->set('frontpageAccounts', $frontpageAccounts);
}
// extract notifications:

View File

@@ -36,7 +36,7 @@ class CreateController extends Controller
public function create()
{
$title = (string)trans('firefly.administrations_page_title');
$subTitle = (string)trans('firefly.administrations_page_sub_title');
$subTitle = (string)trans('firefly.administrations_page_create_sub_title');
$mainTitleIcon = 'fa-book';
app('log')->debug(sprintf('Now at %s', __METHOD__));

View File

@@ -0,0 +1,46 @@
<?php
/*
* CreateController.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\UserGroup;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\UserGroup;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Application;
class EditController extends Controller
{
/**
* @return Application|Factory|\Illuminate\Contracts\Foundation\Application|View
*/
public function edit(UserGroup $userGroup)
{
$title = (string)trans('firefly.administrations_page_title');
$subTitle = (string)trans('firefly.administrations_page_edit_sub_title', ['title' => $userGroup->title]);
$mainTitleIcon = 'fa-book';
app('log')->debug(sprintf('Now at %s', __METHOD__));
return view('administrations.edit')->with(compact('title', 'subTitle', 'mainTitleIcon'));
}
}

View File

@@ -110,6 +110,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static EloquentBuilder|Account whereUserGroupId($value)
*
* @property null|UserGroup $userGroup
* @property mixed $account_id
*
* @mixin Eloquent
*/

View File

@@ -61,6 +61,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static Builder|InvitedUser whereUpdatedAt($value)
* @method static Builder|InvitedUser whereUserId($value)
*
* @property mixed $user_group_id
*
* @mixin Eloquent
*/
class InvitedUser extends Model

View File

@@ -54,6 +54,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static Builder|Preference whereUpdatedAt($value)
* @method static Builder|Preference whereUserId($value)
*
* @property mixed $user_group_id
*
* @mixin Eloquent
*/
class Preference extends Model
@@ -79,22 +81,39 @@ class Preference extends Model
{
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
$user = auth()->user();
// some preferences do not have an administration ID.
// some need it, to make sure the correct one is selected.
$userGroupId = (int)$user->user_group_id;
$userGroupId = 0 === $userGroupId ? null : $userGroupId;
/** @var null|Preference $preference */
$preference = $user->preferences()->where('name', $value)->first();
$preference = null;
$items = config('firefly.admin_specific_prefs');
if (null !== $userGroupId && in_array($value, $items, true)) {
// find a preference with a specific user_group_id
$preference = $user->preferences()->where('user_group_id', $userGroupId)->where('name', $value)->first();
}
if (!in_array($value, $items, true)) {
// find any one.
$preference = $user->preferences()->where('name', $value)->first();
}
// try again with ID, but this time don't care about the preferred user_group_id
if (null === $preference) {
$preference = $user->preferences()->where('id', (int)$value)->first();
}
if (null !== $preference) {
return $preference;
}
$default = config('firefly.default_preferences');
$default = config('firefly.default_preferences');
if (array_key_exists($value, $default)) {
$preference = new self();
$preference->name = $value;
$preference->data = $default[$value];
$preference->user_id = (int)$user->id;
$preference = new self();
$preference->name = $value;
$preference->data = $default[$value];
$preference->user_id = (int)$user->id;
$preference->user_group_id = in_array($value, $items, true) ? $userGroupId : null;
$preference->save();
return $preference;

View File

@@ -81,6 +81,7 @@ use Illuminate\Database\Query\Builder;
* @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionTypeId($value)
*
* @property null|TransactionType $transactionType
* @property mixed $user_id
*
* @mixin Eloquent
*/

View File

@@ -98,7 +98,7 @@ class UserGroup extends Model
{
use ReturnsIntegerIdTrait;
protected $fillable = ['title'];
protected $fillable = ['title', 'default_administration'];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).

View File

@@ -392,4 +392,28 @@ class UserRepository implements UserRepositoryInterface
return null !== $invitee;
}
#[\Override]
public function getUserGroups(User $user): Collection
{
$memberships = $user->groupMemberships()->get();
$set = [];
$collection = new Collection();
/** @var GroupMembership $membership */
foreach ($memberships as $membership) {
/** @var null|UserGroup $group */
$group = $membership->userGroup()->first();
if (null !== $group) {
$groupId = (int)$group->id;
if (in_array($groupId, $set, true)) {
continue;
}
$set[$groupId] = $group;
}
}
$collection->push(...$set);
return $collection;
}
}

View File

@@ -59,6 +59,8 @@ interface UserRepositoryInterface
public function changeStatus(User $user, bool $isBlocked, string $code): bool;
public function getUserGroups(User $user): Collection;
/**
* Returns a count of all users.
*/

View File

@@ -288,4 +288,11 @@ class UserGroupRepository implements UserGroupRepositoryInterface
return $roles;
}
#[\Override]
public function useUserGroup(UserGroup $userGroup): void
{
$this->user->user_group_id = $userGroup->id;
$this->user->save();
}
}

View File

@@ -38,6 +38,8 @@ interface UserGroupRepositoryInterface
public function get(): Collection;
public function useUserGroup(UserGroup $userGroup): void;
public function getAll(): Collection;
public function setUser(null|Authenticatable|User $user): void;

View File

@@ -74,5 +74,7 @@ interface AccountRepositoryInterface
public function setUserGroup(UserGroup $userGroup): void;
public function getUserGroup(): UserGroup;
public function update(Account $account, array $data): Account;
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* IsTransferAccount.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Rules;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Contracts\Validation\ValidationRule;
/**
* Class IsDefaultUserGroupName
*/
class IsDefaultUserGroupName implements ValidationRule
{
private UserGroup $userGroup;
public function __construct(UserGroup $userGroup)
{
$this->userGroup = $userGroup;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
app('log')->debug(sprintf('Now in %s(%s)', __METHOD__, $value));
// are you owner of this group and the name is the same? fail.
/** @var User $user */
$user = auth()->user();
/** @var UserRepositoryInterface $userRepos */
$userRepos = app(UserRepositoryInterface::class);
$roles = $userRepos->getRolesInGroup($user, $this->userGroup->id);
if ($this->userGroup->title === $user->email && in_array('owner', $roles, true)) {
$fail('validation.administration_owner_rename')->translate();
}
}
}

View File

@@ -26,7 +26,10 @@ namespace FireflyIII\Support;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Preference;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Session;
/**
* Class Preferences.
@@ -40,20 +43,17 @@ class Preferences
return new Collection();
}
return Preference::where('user_id', $user->id)->get();
return Preference::where('user_id', $user->id)
->where(function (Builder $q) use ($user): void {
$q->whereNull('user_group_id');
$q->orWhere('user_group_id', $user->user_group_id);
})
->get()
;
}
/**
* @param mixed $default
*
* @throws FireflyException
*/
public function get(string $name, $default = null): ?Preference
public function get(string $name, null|array|bool|int|string $default = null): ?Preference
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
/** @var null|User $user */
$user = auth()->user();
if (null === $user) {
@@ -66,15 +66,12 @@ class Preferences
return $this->getForUser($user, $name, $default);
}
/**
* @throws FireflyException
*/
public function getForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
$preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'user_id', 'name', 'data', 'updated_at', 'created_at']);
// don't care about user group ID, except for some specific preferences.
$userGroupId = $this->getUserGroupId($user, $name);
$preference = Preference::where('user_group_id', $userGroupId)->where('user_id', $user->id)->where('name', $name)->first(['id', 'user_id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $preference && null === $preference->data) {
$preference->delete();
$preference = null;
@@ -92,17 +89,11 @@ class Preferences
return $this->setForUser($user, $name, $default);
}
/**
* @throws FireflyException
*/
public function delete(string $name): bool
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
$fullName = sprintf('preference%s%s', auth()->user()->id, $name);
if (\Cache::has($fullName)) {
\Cache::forget($fullName);
if (Cache::has($fullName)) {
Cache::forget($fullName);
}
Preference::where('user_id', auth()->user()->id)->where('name', $name)->delete();
@@ -111,29 +102,19 @@ class Preferences
public function forget(User $user, string $name): void
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
$key = sprintf('preference%s%s', $user->id, $name);
\Cache::forget($key);
\Cache::put($key, '', 5);
Cache::forget($key);
Cache::put($key, '', 5);
}
/**
* @param mixed $value
*
* @throws FireflyException
*/
public function setForUser(User $user, string $name, $value): Preference
public function setForUser(User $user, string $name, null|array|bool|int|string $value): Preference
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
$fullName = sprintf('preference%s%s', $user->id, $name);
\Cache::forget($fullName);
$groupId = $this->getUserGroupId($user, $name);
Cache::forget($fullName);
/** @var null|Preference $pref */
$pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']);
$pref = Preference::where('user_group_id', $groupId)->where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $pref && null === $value) {
$pref->delete();
@@ -144,18 +125,14 @@ class Preferences
return new Preference();
}
if (null === $pref) {
$pref = new Preference();
$pref->user_id = (int)$user->id;
$pref->name = $name;
$pref = new Preference();
$pref->user_id = (int)$user->id;
$pref->user_group_id = $groupId;
$pref->name = $name;
}
$pref->data = $value;
try {
$pref->save();
} catch (\PDOException $e) {
throw new FireflyException(sprintf('Could not save preference: %s', $e->getMessage()), 0, $e);
}
\Cache::forever($fullName, $pref);
$pref->save();
Cache::forever($fullName, $pref);
return $pref;
}
@@ -165,19 +142,25 @@ class Preferences
return Preference::where('user_id', $user->id)->where('name', 'LIKE', $search.'%')->get();
}
/**
* Find by name, has no user ID in it, because the method is called from an unauthenticated route any way.
*/
public function findByName(string $name): Collection
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
return Preference::where('name', $name)->get();
}
public function getArrayForUser(User $user, array $list): array
{
$result = [];
$preferences = Preference::where('user_id', $user->id)->whereIn('name', $list)->get(['id', 'name', 'data']);
$preferences = Preference::where('user_id', $user->id)
->where(function (Builder $q) use ($user): void {
$q->whereNull('user_group_id');
$q->orWhere('user_group_id', $user->user_group_id);
})
->whereIn('name', $list)
->get(['id', 'name', 'data'])
;
/** @var Preference $preference */
foreach ($preferences as $preference) {
@@ -192,17 +175,8 @@ class Preferences
return $result;
}
/**
* @param mixed $default
*
* @throws FireflyException
*/
public function getFresh(string $name, $default = null): ?Preference
public function getFresh(string $name, null|array|bool|int|string $default = null): ?Preference
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
/** @var null|User $user */
$user = auth()->user();
if (null === $user) {
@@ -212,22 +186,6 @@ class Preferences
return $preference;
}
return $this->getFreshForUser($user, $name, $default);
}
/**
* TODO remove me.
*
* @param null $default
*
* @throws FireflyException
*/
public function getFreshForUser(User $user, string $name, $default = null): ?Preference
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
return $this->getForUser($user, $name, $default);
}
@@ -252,19 +210,12 @@ class Preferences
public function mark(): void
{
$this->set('lastActivity', microtime());
\Session::forget('first');
Session::forget('first');
}
/**
* @param mixed $value
*
* @throws FireflyException
*/
public function set(string $name, $value): Preference
public function set(string $name, null|array|bool|int|string $value): Preference
{
if ('currencyPreference' === $name) {
throw new FireflyException('No longer supports "currencyPreference", please refactor me.');
}
/** @var User $user */
$user = auth()->user();
if (null === $user) {
// make new preference, return it:
@@ -275,6 +226,17 @@ class Preferences
return $pref;
}
return $this->setForUser(auth()->user(), $name, $value);
return $this->setForUser($user, $name, $value);
}
private function getUserGroupId(User $user, string $preferenceName): ?int
{
$groupId = null;
$items = config('firefly.admin_specific_prefs');
if (in_array($preferenceName, $items, true)) {
$groupId = (int)$user->user_group_id;
}
return $groupId;
}
}

View File

@@ -35,12 +35,15 @@ class PreferenceTransformer extends AbstractTransformer
*/
public function transform(Preference $preference): array
{
$userGroupId = 0 === $preference->user_group_id ? null : $preference->user_group_id;
return [
'id' => $preference->id,
'created_at' => $preference->created_at->toAtomString(),
'updated_at' => $preference->updated_at->toAtomString(),
'name' => $preference->name,
'data' => $preference->data,
'id' => $preference->id,
'created_at' => $preference->created_at->toAtomString(),
'updated_at' => $preference->updated_at->toAtomString(),
'user_group_id' => $userGroupId,
'name' => $preference->name,
'data' => $preference->data,
];
}
}

View File

@@ -84,13 +84,13 @@ class UserGroupTransformer extends AbstractTransformer
public function transform(UserGroup $userGroup): array
{
return [
'id' => $userGroup->id,
'created_at' => $userGroup->created_at->toAtomString(),
'updated_at' => $userGroup->updated_at->toAtomString(),
'in_use' => $this->inUse[$userGroup->id] ?? false,
'title' => $userGroup->title,
'can_see_members' => $this->membershipsVisible[$userGroup->id] ?? false,
'members' => $this->memberships[$userGroup->id] ?? [],
'id' => $userGroup->id,
'created_at' => $userGroup->created_at->toAtomString(),
'updated_at' => $userGroup->updated_at->toAtomString(),
'in_use' => $this->inUse[$userGroup->id] ?? false,
'title' => $userGroup->title,
'can_see_members' => $this->membershipsVisible[$userGroup->id] ?? false,
'members' => $this->memberships[$userGroup->id] ?? [],
];
// if the user has a specific role in this group, then collect the memberships.
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.1.14 - 2024-xx-xx
### Changed
- You will have to define again which asset accounts you want to see on the dashboard. Sorry about that.
## 6.1.13 - 2024-04-01
### Added

36
composer.lock generated
View File

@@ -8897,23 +8897,23 @@
"packages-dev": [
{
"name": "barryvdh/laravel-debugbar",
"version": "v3.12.3",
"version": "v3.13.0",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-debugbar.git",
"reference": "aac8f08b73af8c5d2ab6595c8823ddb26d1453f1"
"reference": "354a42f3e0b083cdd6f9da5a9d1c0c63b074547a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/aac8f08b73af8c5d2ab6595c8823ddb26d1453f1",
"reference": "aac8f08b73af8c5d2ab6595c8823ddb26d1453f1",
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/354a42f3e0b083cdd6f9da5a9d1c0c63b074547a",
"reference": "354a42f3e0b083cdd6f9da5a9d1c0c63b074547a",
"shasum": ""
},
"require": {
"illuminate/routing": "^9|^10|^11",
"illuminate/session": "^9|^10|^11",
"illuminate/support": "^9|^10|^11",
"maximebf/debugbar": "~1.21.0",
"maximebf/debugbar": "~1.22.0",
"php": "^8.0",
"symfony/finder": "^6|^7"
},
@@ -8926,7 +8926,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.10-dev"
"dev-master": "3.13-dev"
},
"laravel": {
"providers": [
@@ -8965,7 +8965,7 @@
],
"support": {
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.12.3"
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.13.0"
},
"funding": [
{
@@ -8977,7 +8977,7 @@
"type": "github"
}
],
"time": "2024-03-31T18:35:30+00:00"
"time": "2024-04-01T16:39:30+00:00"
},
{
"name": "barryvdh/laravel-ide-helper",
@@ -9606,25 +9606,27 @@
},
{
"name": "maximebf/debugbar",
"version": "v1.21.3",
"version": "v1.22.1",
"source": {
"type": "git",
"url": "https://github.com/maximebf/php-debugbar.git",
"reference": "0b407703b08ea0cf6ebc61e267cc96ff7000911b"
"reference": "d7b6e1dc2dc85c01ed63ab158b00a7f46abdebcc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/0b407703b08ea0cf6ebc61e267cc96ff7000911b",
"reference": "0b407703b08ea0cf6ebc61e267cc96ff7000911b",
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/d7b6e1dc2dc85c01ed63ab158b00a7f46abdebcc",
"reference": "d7b6e1dc2dc85c01ed63ab158b00a7f46abdebcc",
"shasum": ""
},
"require": {
"php": "^7.1|^8",
"php": "^7.2|^8",
"psr/log": "^1|^2|^3",
"symfony/var-dumper": "^4|^5|^6|^7"
},
"require-dev": {
"phpunit/phpunit": ">=7.5.20 <10.0",
"dbrekelmans/bdi": "^1",
"phpunit/phpunit": "^8|^9",
"symfony/panther": "^1|^2.1",
"twig/twig": "^1.38|^2.7|^3.0"
},
"suggest": {
@@ -9635,7 +9637,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.21-dev"
"dev-master": "1.22-dev"
}
},
"autoload": {
@@ -9666,9 +9668,9 @@
],
"support": {
"issues": "https://github.com/maximebf/php-debugbar/issues",
"source": "https://github.com/maximebf/php-debugbar/tree/v1.21.3"
"source": "https://github.com/maximebf/php-debugbar/tree/v1.22.1"
},
"time": "2024-03-12T14:23:07+00:00"
"time": "2024-04-01T10:44:20+00:00"
},
{
"name": "mockery/mockery",

View File

@@ -117,9 +117,9 @@ return [
'expression_engine' => false,
// see cer.php for exchange rates feature flag.
],
'version' => '6.1.13',
'version' => 'develop/2024-04-02',
'api_version' => '2.0.13',
'db_version' => 23,
'db_version' => 24,
// generic settings
'maxUploadSize' => 1073741824, // 1 GB
@@ -215,11 +215,14 @@ return [
'zoom_level' => env('MAP_DEFAULT_ZOOM', '6'),
],
// administration specific preferences
'admin_specific_prefs' => ['frontpageAccounts', 'lastActivity'],
// default user-related values
'darkMode' => 'browser',
'list_length' => 10, // to be removed if v1 is cancelled.
'default_preferences' => [
'frontPageAccounts' => [],
'frontpageAccounts' => [],
'listPageSize' => 50,
'currencyPreference' => 'EUR',
'language' => 'en_US',

View File

@@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
private const string QUERY_ERROR = 'Could not execute query (table "%s", field "%s"): %s';
private const string EXPL = 'If the index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.';
private const string EXPL = 'If the index already exists (see error), or if MySQL can\'t do it, this is not an problem. Otherwise, please open a GitHub discussion.';
/**
* Run the migrations.

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\QueryException;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
try {
Schema::table(
'preferences',
static function (Blueprint $table): void {
if (!Schema::hasColumn('preferences', 'user_group_id')) {
$table->bigInteger('user_group_id', false, true)->nullable()->after('user_id');
$table->foreign('user_group_id', 'preferences_to_ugi')->references('id')->on('user_groups')->onDelete('set null')->onUpdate('cascade');
}
}
);
} catch (QueryException $e) {
app('log')->error(sprintf('Could not execute query: %s', $e->getMessage()));
app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.');
}
}
/**
* Reverse the migrations.
*/
public function down(): void {}
};

View File

@@ -3,7 +3,7 @@
"html_language": "nl",
"date_time_fns": "d MMMM yyyy @ HH:mm:ss",
"month_and_day_fns": "d MMMM y",
"date_time_fns_short": "MMMM do, yyyy @ HH:mm"
"date_time_fns_short": "d MMMM yyyy @ HH:mm"
},
"validation": {
"bad_type_source": "Firefly III kan het transactietype niet bepalen op basis van deze bronrekening.",

View File

@@ -3,7 +3,7 @@
"html_language": "nl",
"date_time_fns": "d MMMM yyyy @ HH:mm:ss",
"month_and_day_fns": "d MMMM y",
"date_time_fns_short": "MMMM do, yyyy @ HH:mm"
"date_time_fns_short": "d MMMM yyyy @ HH:mm"
},
"validation": {
"bad_type_source": "Firefly III kan het transactietype niet bepalen op basis van deze bronrekening.",

View File

@@ -23,15 +23,15 @@ import format from "date-fns/format";
export default class Get {
// /**
// *
// * @param identifier
// * @param params
// * @returns {Promise<AxiosResponse<any>>}
// */
// show(identifier, params) {
// return api.get('/api/v2/accounts/' + identifier, {params: params});
// }
/**
*
* @param identifier
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
show(identifier, params) {
return api.get('/api/v2/user-groups/' + identifier, {params: params});
}
/**
*

View File

@@ -0,0 +1,33 @@
/*
* post.js
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
import {api} from "../../../../boot/axios";
export default class Post {
post(submission) {
let url = './api/v2/user-groups';
return api.post(url, submission);
}
use(groupId) {
let url = './api/v2/user-groups/' + groupId + '/use';
return api.post(url, {});
}
}

View File

@@ -0,0 +1,28 @@
/*
* post.js
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {api} from "../../../../boot/axios";
export default class Put {
put(submission, params) {
let url = '/api/v2/user-groups/' + parseInt(params.id);
return api.put(url, submission);
}
}

View File

@@ -0,0 +1,111 @@
/*
* template.js
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
import '../../boot/bootstrap.js';
import dates from "../shared/dates.js";
import Post from "../../api/v2/model/user-group/post.js";
import i18next from "i18next";
let administrations = function () {
return {
title: '',
errors: {
title: []
},
// notifications
notifications: {
error: {
show: false, text: '', url: '',
}, success: {
show: false, text: '', url: '',
}, wait: {
show: false, text: '',
}
},
// state of the form is stored in formState:
formStates: {
isSubmitting: false,
returnHereButton: false,
saveAsNewButton: false, // edit form only
resetButton: false,
},
// form behaviour
formBehaviour: {
formType: 'create', // or 'update'
},
changedTitle() {
},
pageProperties: {},
submitForm() {
this.errors.title = [];
(new Post()).post({title: this.title}).then(response => {
if (this.formStates.returnHereButton) {
this.notifications.success.show = true;
this.notifications.success.text = i18next.t('firefly.new_administration_created', {title: response.data.data.attributes.title});
this.notifications.success.url = './administrations';
}
if (this.formStates.resetButton) {
this.title = '';
}
if (!this.formStates.returnHereButton) {
window.location.href = './administrations?user_group_id=' + parseInt(response.data.data.id) + '&message=created';
}
}).catch(error => {
this.errors.title = error.response.data.errors.title;
});
},
cancelForm() {
window.location.href = './administrations';
},
init() {
}
}
}
let comps = {administrations, dates};
function loadPage() {
Object.keys(comps).forEach(comp => {
console.log(`Loading page component "${comp}"`);
let data = comps[comp]();
Alpine.data(comp, () => data);
});
Alpine.start();
}
// wait for load until bootstrapped event is received.
document.addEventListener('firefly-iii-bootstrapped', () => {
console.log('Loaded through event listener.');
loadPage();
});
// or is bootstrapped before event is triggered.
if (window.bootstrapped) {
console.log('Loaded through window variable.');
loadPage();
}

View File

@@ -0,0 +1,121 @@
/*
* template.js
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
import '../../boot/bootstrap.js';
import dates from "../shared/dates.js";
import Post from "../../api/v2/model/user-group/post.js";
import i18next from "i18next";
import Get from "../../api/v2/model/user-group/get.js";
import Put from "../../api/v2/model/user-group/put.js";
let administrations = function () {
return {
title: '',
id: 0,
errors: {
title: []
},
// notifications
notifications: {
error: {
show: false, text: '', url: '',
}, success: {
show: false, text: '', url: '',
}, wait: {
show: false, text: '',
}
},
// state of the form is stored in formState:
formStates: {
isSubmitting: false,
returnHereButton: false,
saveAsNewButton: false, // edit form only
resetButton: false,
},
// form behaviour
formBehaviour: {
formType: 'update', // or 'update'
},
changedTitle() {
},
pageProperties: {},
submitForm() {
console.log('submitForm');
(new Put()).put({title: this.title}, {id: this.id}).then(response => {
if (this.formStates.returnHereButton) {
this.notifications.success.show = true;
this.notifications.success.text = i18next.t('firefly.updated_administration', {title: response.data.data.attributes.title});
// TODO needs a better redirect.
this.notifications.success.url = './administrations';
}
if (this.formStates.resetButton) {
this.title = '';
}
if (!this.formStates.returnHereButton) {
// TODO needs a better redirect.
window.location.href = './administrations?user_group_id=' + parseInt(response.data.data.id) + '&message=updated';
}
}).catch(error => {
this.errors.title = error.response.data.errors.title;
});
},
cancelForm() {
window.location.href = './administrations';
},
init() {
const page = window.location.href.split('/');
const groupId = parseInt(page[page.length - 1]);
(new Get()).show(groupId, {}).then(response => {
this.title = response.data.data.attributes.title;
this.id = parseInt(response.data.data.id);
});
}
}
}
let comps = {administrations, dates};
function loadPage() {
Object.keys(comps).forEach(comp => {
console.log(`Loading page component "${comp}"`);
let data = comps[comp]();
Alpine.data(comp, () => data);
});
Alpine.start();
}
// wait for load until bootstrapped event is received.
document.addEventListener('firefly-iii-bootstrapped', () => {
console.log('Loaded through event listener.');
loadPage();
});
// or is bootstrapped before event is triggered.
if (window.bootstrapped) {
console.log('Loaded through window variable.');
loadPage();
}

View File

@@ -27,6 +27,7 @@ import '@ag-grid-community/styles/ag-grid.css';
import '@ag-grid-community/styles/ag-theme-alpine.css';
import '../../css/grid-ff3-theme.css';
import Get from "../../api/v2/model/user-group/get.js";
import Post from "../../api/v2/model/user-group/post.js";
let index = function () {
return {
@@ -53,8 +54,16 @@ let index = function () {
this.notifications.wait.text = i18next.t('firefly.wait_loading_data')
this.loadAdministrations();
},
useAdministration(id) {
let groupId = parseInt(id);
// try to post "use", then reload administrations.
(new Post()).use(groupId).then(response => {
this.loadAdministrations();
});
},
loadAdministrations() {
this.userGroups = [];
this.notifications.wait.show = true;
this.notifications.wait.text = i18next.t('firefly.wait_loading_data')
this.accounts = [];
@@ -73,16 +82,13 @@ let index = function () {
membersVisible: current.attributes.can_see_members,
members: [],
};
console.log('Processing group #' + group.id + ' (' + group.title + ')' );
let memberships = {};
for (let j = 0; j < current.attributes.members.length; j++) {
let member = current.attributes.members[j];
console.log('Found member ' + member.user_email, member.you, member.role);
if ('owner' === member.role) {
group.owner = i18next.t('firefly.administration_owner', {email: member.user_email});
}
if (true === member.you && 'owner' === member.role) {
console.log('You are owner of group ' + group.title );
group.isOwner = true;
}
if (true === member.you) {

View File

@@ -25,7 +25,6 @@ import formatMoney from "../../util/format-money.js";
import Get from "../../api/v2/model/account/get.js";
import {Chart} from 'chart.js';
import {getDefaultChartSettings} from "../../support/default-chart-settings.js";
import {getColors} from "../../support/get-colors.js";
import {getCacheKey} from "../../support/get-cache-key.js";
import {getConfiguration} from "../../store/get-configuration.js";

View File

@@ -35,6 +35,7 @@ export default () => ({
getFreshData() {
const start = new Date(window.store.get('start'));
const end = new Date(window.store.get('end'));
// needs user data.
const cacheKey = getCacheKey(PIGGY_CACHE_KEY, start, end);
const cacheValid = window.store.get('cacheValid');

View File

@@ -24,6 +24,33 @@ import dates from "./shared/dates.js";
let somethings = function() {
return {
// notifications
// TODO duplicate code
notifications: {
error: {
show: false, text: '', url: '',
}, success: {
show: false, text: '', url: '',
}, wait: {
show: false, text: '',
}
},
// state of the form is stored in formState:
// TODO duplicate code
formStates: {
isSubmitting: false,
returnHereButton: false,
saveAsNewButton: false, // edit form only
resetButton: false,
},
// form behaviour
// TODO duplicate code
formBehaviour: {
formType: 'create', // or 'update'
},
pageProperties: {},
functionName() {

View File

@@ -73,7 +73,8 @@ let transactions = function () {
// form behaviour during transaction
formBehaviour: {
formType: 'create', foreignCurrencyEnabled: true,
formType: 'create',
foreignCurrencyEnabled: true,
},
// form data (except transactions) is stored in formData

View File

@@ -19,9 +19,11 @@
*/
import {format} from "date-fns";
import store from "store";
function getCacheKey(string, start, end) {
const cacheKey = format(start, 'y-MM-dd') + '_' + format(end, 'y-MM-dd') + '_' + string;
const localValue = store.get('lastActivity');
const cacheKey = format(start, 'y-MM-dd') + '_' + format(end, 'y-MM-dd') + '_' + string + localValue;
console.log('getCacheKey: ' + cacheKey);
return String(cacheKey);
}

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'No tens accés a aquesta administració.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Du har ikke de korrekte adgangsrettigheder for denne administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Für diese Verwaltung haben Sie nicht die erforderlichen Zugriffsrechte.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Δεν έχετε τα σωστά δικαιώματα πρόσβασης για αυτή τη διαχείριση.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -264,6 +264,7 @@ return [
// no access to administration:
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
// Ignore this comment

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'No tiene permisos para esta administración.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Vous n\'avez pas les droits d\'accès corrects pour cette administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'You do not have the correct access rights for this administration.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Non hai i diritti di accesso corretti per questa amministrazione.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'この管理のための適切なアクセス権がありません。',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => '이 관리에 대한 올바른 액세스 권한이 없습니다.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Du har ikke rettigheter til denne handlingen.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

View File

@@ -64,7 +64,7 @@ return [
// 'date_time' => '%B %e, %Y, @ %T',
'date_time_js' => 'D MMMM YYYY @ HH:mm:ss',
'date_time_fns' => 'd MMMM yyyy @ HH:mm:ss',
'date_time_fns_short' => 'MMMM do, yyyy @ HH:mm',
'date_time_fns_short' => 'd MMMM yyyy @ HH:mm',
// 'specific_day' => '%e %B %Y',
'specific_day_js' => 'D MMMM YYYY',

View File

@@ -42,7 +42,7 @@ return [
'fatal_error' => 'Er is een fatale fout opgetreden. Controleer de logbestanden in "storage/logs" of gebruik "docker logs -f [container]" om te zien wat er gebeurde.',
'maintenance_mode' => 'Firefly III is in onderhoudsmodus.',
'be_right_back' => 'Zo terug!',
'check_back' => 'Firefly III is down for some necessary maintenance. Please check back in a second. If you happen to see this message on the demo site, just wait a few minutes. The database is reset every few hours.',
'check_back' => 'Firefly III is offline voor hoognodig onderhoud. Kom later terug. Als je dit bericht op de demo site ziet, wacht dan een paar minuten. De database wordt elke paar uur gereset.',
'error_occurred' => 'Oeps! Er is een fout opgetreden.',
'db_error_occurred' => 'Oeps! Er is een database-fout opgetreden.',
'error_not_recoverable' => 'Helaas was deze fout niet te herstellen :(. Firefly III is stuk. De fout is:',

File diff suppressed because it is too large Load Diff

View File

@@ -70,5 +70,5 @@ return [
'cannot_find_budget' => 'Firefly III kan budget ":name" niet vinden',
'cannot_find_category' => 'Firefly III kan categorie ":name" niet vinden',
'cannot_set_budget' => 'Firefly III kan budget ":name" niet instellen op een transactie van het type ":type"',
'journal_invalid_amount' => 'Firefly III can\'t set amount ":amount" because it is not a valid number.',
'journal_invalid_amount' => 'Firefly III kan bedrag ":amount" niet instellen omdat dit geen geldig getal is.',
];

View File

@@ -55,11 +55,11 @@ return [
'reconciled_forbidden_field' => 'Deze transactie is al afgestemd, dus je kan ":field" niet wijzigen',
'deleted_user' => 'Je kan je niet registreren met dit e-mailadres.',
'rule_trigger_value' => 'Deze waarde is niet geldig voor de geselecteerde trigger.',
'rule_action_expression' => 'Invalid expression. :error',
'rule_action_expression' => 'Ongeldige expressie (foutmelding in het Engels): :error',
'rule_action_value' => 'Deze waarde is niet geldig voor de geselecteerde actie.',
'file_already_attached' => 'Het geuploade bestand ":name" is al gelinkt aan deze transactie.',
'file_attached' => 'Bestand ":name" is succesvol geüpload.',
'file_zero' => 'The file is zero bytes in size.',
'file_zero' => 'Het bestand is nul bytes.',
'must_exist' => 'Het ID in veld :attribute bestaat niet.',
'all_accounts_equal' => 'Alle rekeningen in dit veld moeten gelijk zijn.',
'group_title_mandatory' => 'Een groepstitel is verplicht wanneer er meer dan één transactie is.',
@@ -197,7 +197,7 @@ return [
*
*/
'secure_password' => 'This is not a secure password. Please try again. For more information, visit https://bit.ly/FF3-password',
'secure_password' => 'Dit is geen veilig wachtwoord. Probeer het nog een keer. Zie ook: https://bit.ly/FF3-password',
'valid_recurrence_rep_type' => 'Dit is geen geldige herhaling voor periodieke transacties.',
'valid_recurrence_rep_moment' => 'Ongeldig herhaalmoment voor dit type herhaling.',
'invalid_account_info' => 'Ongeldige rekeninginformatie.',
@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Je hebt niet de juiste toegangsrechten voor deze administratie.',
'administration_owner_rename' => 'Je kan je standaardgrootboek niet hernoemen.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Du har ikkje rettigheter til denne handlinga.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Nie masz odpowiednich praw dostępu dla tej administracji.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Você não direitos de acesso suficientes para esta administração.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Não tem as permissões de acesso necessárias para esta administração.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

View File

@@ -300,6 +300,7 @@ return [
// no access to administration:
'no_access_user_group' => 'Nu aveți drepturile de acces corecte pentru această administrare.',
'administration_owner_rename' => 'You can\'t rename your standard administration.',
];
/*

File diff suppressed because it is too large Load Diff

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